Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AssetMonitor · 资产配比跟踪

个人用的永久投资组合配比跟踪工具。手动录入黄金 / 国债 / 股票三类资产的持仓, 自动计算相对目标的偏离,在触碰上下限阈值时高亮提醒并给出可以直接执行的具体金额。

  • 理论依据:哈利·布朗的永久投资组合(Harry Browne's Permanent Portfolio)
  • 阈值口径:目标 33% / 33% / 34%,下限 20%、上限 46%(触碰即触发)
  • 数据来源:手动录入(自动化行情抓取是后续计划,数据结构已预留)

它解决什么问题

永久投资组合的核心纪律是"再平衡":某一类涨得太多(或跌得太狠)时把它调回目标。 但这件事在实践中有两个坑——什么时候该动手,以及到底要买卖多少钱。

这个工具把这两件事都算出来:

  • 每次保存持仓自动记录一条当日快照,折线图能看到配比是怎么一步步漂移的
  • 越界时给出「卖出黄金 40,716.50 元 → 买入国债 19,333.50 元 / 买入股票 21,383.00 元」
  • 卖出总额与买入总额严格相等(有单元测试硬断言,四舍五入残差会被归并)
  • 额外给出书里的「资金变动法」:加钱补欠配最多的一类(不是"占比最低"的一类—— 占比最低 ≠ 最需要补;目标为 0 的类别缺口恒为负,不会被选中), 取钱优先动现金,比卖出再买入省手续费

功能

页面 内容
总览 状态横幅(绿/黄/红/灰)、环形饼图(内环目标 / 外环实际)、偏离柱状图(含阈值安全带)、再平衡建议表
持仓录入 按账户分组的可编辑表格,含市值与盈亏;保存即记录当日快照
历史趋势 三大类占比随时间的三条曲线,叠加目标线与 20%/46% 阈值带
设置 目标配比、上下限(含 15/35 与 30/20 两套换算预设)、是否计入现金、最小建议金额

数据口径(重要,务必先读)

  1. 分母默认不含现金。占比 = 该类市值 ÷ (黄金 + 国债 + 股票)。现金类资产仍会展示绝对值, 但不参与比例与阈值判定。页面上常驻显示当前口径,可在设置页切换。 切到"含现金"后,历史曲线的分母也会同步切换,且此时再平衡建议里会出现 "卖出(现金)"——那是正确行为:现金的目标是 0%,它是买入三大类的资金来源。
  2. 阈值判定是"等于也算触碰":占比 ≥ 46% 或 ≤ 20% 都触发。
  3. 百分比全链路是数值口径:33.0 就是 33%,不是 0.33。
  4. 同一天保存多次只留一条快照(幂等覆盖),避免曲线出现重复点。
  5. 补录历史日期写入的是当前持仓按该日期标记,本版本不支持编辑历史持仓。 历史页的补录按钮旁有同样的说明。
  6. 录入价格请用后复权等价价格,或把现金分红并入数量。否则长期收益率会被低估约 2 个百分点/年。
  7. 记价方式在标的创建时选定:"数量 × 单价"适合 ETF/基金,"直接填金额"适合 实物金条、银行金条这类不按份额计价的资产。选"直接填金额"时成本要填累计投入金额, 选"数量 × 单价"时填成本单价——填错会导致盈亏列恒显示"—"。
  8. 建议金额有一道最小交易额门槛(默认 1000 元)。当某笔建议低于门槛时, 工具会把超出的一侧按比例缩到与另一侧相等,而不是给出"卖 1900 但只买 1000" 这种会让账对不上的建议;如果缩完仍低于门槛,就整体归零并在表里说明原因。

快速开始

# 后端
cd backend
python -m venv .venv
.venv/bin/pip install -r requirements-dev.txt     # Windows: .venv\Scripts\python -m pip install -r requirements-dev.txt
.venv/bin/python -m uvicorn app.main:app --reload --port 8000

# 前端(另开一个终端)
cd frontend
pnpm install
pnpm dev            # http://127.0.0.1:5173,/api 自动代理到 8000

只跑后端也够用:先 pnpm build,然后直接访问 http://127.0.0.1:8000, FastAPI 会连同前端产物一起托管(含 SPA 深链接回退)。

首次使用顺序:设置页建账户与标的 → 持仓录入填数量与现价 → 保存 → 总览看配比。

常见标的参考

类别 标的
黄金 518880(华安,规模约 862 亿)、159934(易方达)
国债 511010(5 年)、511260(10 年)、511090(30 年,年化波动 6.6%,约为 5 年期的 2.6 倍)
股票 510300(沪深300)、510500(中证500)、563360(中证A500)

测试

cd backend  && .venv/bin/python -m pytest -q          # 90 个测试
cd frontend && pnpm test -- --run                     # 15 个测试

后端测试覆盖的边界:恰好 20%/46% 触发、含/不含现金两种口径、分母为 0 不产生 NaN、 卖出总额严格等于买入总额、建议方向不被残差归并翻转、四舍五入残差归并、 最小交易额归一化到不动点、同日快照幂等、未填写行不写入快照、 非法日期/Infinity 被 422 拦下且不落库、删标的后持仓不会被新标的继承。

其中 test_balance_invariant_holds_across_portfolios 是穷举不变量测试: 6 组阈值配置 × 每种 343 个组合,断言"卖出 == 买入"与"建议方向与真实缺口同号"。 这两条都曾被真实违反过(穷举 417 万组合时出现过 43 万个"有卖出、无买入"的失衡态)。

前端 thresholds.ts 与后端 valuation.py 是同源逻辑,thresholds.test.ts 里 有一组与后端 API 测试用同一份数字的跨端口径一致性断言,防止两端漂移。

部署

见 docs/DEPLOY.md(systemd + Nginx + certbot,含备份与排障)。

浏览器通知需要 HTTPS;页面高亮提醒在 HTTP 下也正常工作。

目录结构

backend/    FastAPI + SQLAlchemy + SQLite
  app/services/valuation.py    配比/占比/阈值(纯函数,无 IO)
  app/services/rebalance.py    再平衡与资金变动建议(纯函数)
  app/services/snapshots.py    日度快照(同日幂等)
frontend/   React + Vite + TypeScript + ECharts
docs/       设计文档、实施计划、调研报告、代码审查报告、部署说明
deploy/     systemd unit / nginx 配置 / deploy.sh

设计文档

阈值设定的出处:《哈利·布朗的永久投资组合》中译本的"调整带"——单一资产 >35% 或 <15% 即回各 25%,作者同时说明 30%/20% 亦可。本工具按三大类等比例换算为 20%/46% (30/20 档对应 26.67%/40%),并做成可配置。

已知限制

  • 手动录入会陈旧:页面显示"数据更新于 N 天前",超过 30 天横幅置灰, 浏览器通知也会在正文里标注陈旧天数。
  • 阈值可能数年不触发:这是永久投资组合的正常特征,可在设置页切到 30/20 换算档。
  • QDII 是 T+1 确认:录入时请以已确认份额为准,否则会有 1-2 个百分点的口径误差。
  • 补录用的是当前持仓:历史页的补录只是把当前持仓标记成你选的那一天, 不会还原那天的真实持仓。
  • 改了标的的记价方式不会搬移已有数据:从"数量 × 单价"改成"直接填金额"后, 原来的数量/单价会留在库里但不再参与估值。建议新建标的而不是改已有的。
  • 不做多币种、税费计算、交易流水:个人自用范围内的有意简化。
  • 前端产物约 1.2 MB(主要是 ECharts),个人内网使用无影响。

开发提示

  • Vite dev server 绑定 127.0.0.1:Vite 5.4 默认只监听 [::1](纯 IPv6), 那样浏览器打开 README 里写的 http://127.0.0.1:5173 会连接被拒绝。 配置里已显式指定 host: '127.0.0.1';如需 IPv6 访问,改用 http://[::1]:5173。
  • pnpm 10+ 默认拦截依赖构建脚本:frontend/pnpm-workspace.yaml 已白名单 esbuild, 否则 vite/vitest 会直接报 ERR_PNPM_IGNORED_BUILDS 而无法运行。
  • pip 镜像源:若 pip install 报"找不到 fastapi 的版本",多半是全局 index-url 指向了一个不可用的镜像。加 -i https://pypi.org/simple 绕过。

后续计划

数据结构已为以下扩展预留(instruments.code / pricing_mode / 快照表):

  1. 接入行情自动更新(东方财富 push2his kline 接口,免 token、支持后复权)
  2. 越界时推送到企业微信 / 飞书 / Server 酱
  3. 情景压力测试面板(输入"黄金跌 50%",直接显示组合总损失)

About

基于哈利·布朗的永久投资组合调整的个人资产比例显示项目

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages