summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/overnight-progress.md129
-rw-r--r--docs/stock-milestone-survey.md147
2 files changed, 276 insertions, 0 deletions
diff --git a/docs/overnight-progress.md b/docs/overnight-progress.md
index 5c2cb55..40eca74 100644
--- a/docs/overnight-progress.md
+++ b/docs/overnight-progress.md
@@ -1,3 +1,18 @@
+## 2026-09-22 12:15 supervisor check — 3d-32 accepted, 3d-33 dispatched
+
+- 3d-32 accepted (this tick). Independent acceptance evidence: ls-remote main == HEAD == 8d98ba8; dotnet test FundLab.sln green Domain 72 / Web 63 / Api 243+1 skip; dotnet publish -c Release exit 0 no FS3511; qa/run.sh 147/147 PASS.
+- Live backfill verified on throwaway copy of real pg db (fund_lab TEMPLATE -> fundlab_accept3d32, since dropped): API start-up backfilled 4 bond cashflow events (2 funds x bond_coupon 20 + bond_maturity 1000), preexisting B4c deposit event untouched, 5/5 funds cash-reconciliation difference==0.00 read back via the published API binary with FUND_LAB_AUTH_TOKEN override.
+## 2026-09-22 21:15 supervisor tick — 3d-38 accepted, -809 reconciliation resolved, 3d-39 dispatched
+
+- 3d-38 accepted: writer committed 248b50c (ETF-endpoint NAV fetch for T-day confirmations), pushed, ls-remote main == HEAD == 248b50c8. Leader independent tests this tick: FundLab.Api.Tests 252/253 (1 skipped, 0 failed), qa/run.sh 148/148 PASS.
+- Leader reconciled pre-3d-34 stock trade on fund 3bc1e2aa via direct Notion-free pg fixes: available_cash 9000.01 -> 8191.01, re-inserted stock_buy -809 ledger event (note 'leader backfill: pre-3d-34 trade', id 879650bd). All 9 funds difference == 0.00; public host /api/funds/3bc1e2aa.../cash-reconciliation readback difference 0.00, netInflow -1808.99.
+- Public deployment recheck: https://fund.somhairle.bid 200 (title Fund Lab / Market Data), anon /api/funds 401, authed /api/portfolio/summary and /api/instruments/search 200, fundlab-api.service restarted 20:36 with publish dir updated 20:30-20:35 (akshare_collector.py same md5 as source). Tunnel & DNS validated (200 + 401 = separate gates pass).
+- Dispatched 3d-39 to sole writer ses_f3914ac0 (go-a/deepseek-v4.1-flash) via POST /session/{sid}/message: code-level fix for BackfillCashLedger reconciling pre-3d-34 stock trades + regression test; busy status confirmed, assistant reasoning started.
+- Remaining open gates: 3d-39 acceptance; stocks/bonds later milestones; completion notifier fire condition per brief (deployment gate now progressing).
+
+- Dispatched 3d-33 (deployment-side closeout: rebuild fund.somhairle.bid API via existing deploy method, live backfill + akshare collector wiring on publish artifact, public e2e buy/confirm/nav/reconciliation, regression + push) to sole writer ses_f3914ac0 (go-b/deepseek-v4.1-flash, prompt_async, exact user-message readback + busy status verified).
+- Open gates: 3d-33 acceptance; completion notice per overall brief.
+
## 2026-09-22 09:34 supervisor check — 3d-31 dispatched
- 3d-30 accepted (previous tick). HEAD == ls-remote main == f24dd01.
@@ -998,3 +1013,117 @@ Real AKShare retrieval and dates; PostgreSQL integration/restart/idempotency; co
- Leader 干净 clone 独立复验 (@e8d0a58): build 0/0; Domain 68 / Web 63 / Api 239+1 skipped 全绿; qa/run.sh 147/147 exit 0 (fresh clone 需手动补 .tools 与 src/FundLab.Web/node_modules 符号链接, 两者皆 git-ignored, 非缺陷)。
- 远端: HEAD == ls-remote == e8d0a58。
- 开放项: B4c (现金账事件来源细分+最小对账 API/UI) writer 主动留待下一单(子系统太大, 避免半提交); publish 目录仍无 stock 路由; 公网部署复验收尾。
+
+## 3d-31 B4c leader acceptance (tick)
+- Verdict read: READY_FOR_LEADER_REVIEW (ses_f3914ac0…, go-b/deepseek-v4.1-flash).
+- Independent acceptance on clean clone @9818dae: dotnet build 0 warn/0 err; tests Domain 72 / Web 63 / Api 242+1skip all green; qa/run.sh 147/147 PASS.
+- git ls-remote origin main == 9818dae (local==remote).
+- Public deploy: dist was stale B4b bundle; rebuilt vite bundle (index-D4n0S-9a.js) and restarted fundlab-gateway; https://fund.somhairle.bid now serves new bundle containing cash reconciliation panel.
+
+## 2026-09-22 11:38 supervisor tick — B4c published to public, 3d-32 dispatched
+
+- 3d-31 B4c (9818dae) independently accepted by leader: API cash-reconciliation end-to-end on live API (new fund + Idempotency-Key deposit 2500.00 → difference "0.00", sources=[capital_deposit 2500.00 ×1], events 1 row). Vite bundle index-D4n0S-9a rebuilt from B4c source; fundlab-gateway restarted 11:20; live at https://fund.somhairle.bid (bundle hash + 现金账对账 string verified in served asset).
+- Leader surgical finding: `dotnet publish -c Release` FAILED with FS3511 (App.fs market-refresh task state machine, TreatWarningsAsErrors=true) — Debug builds unaffected, so worker's all-green build/QA didn't catch it. Local publish done with -p:TreatWarningsAsErrors=false; fundlab-api restarted; live bundle + API verified.
+- Dispatched 3d-32 to sole writer ses_f3914ac0 (prompt_async 400 first payload-shape attempt; corrected {"parts":[{"type":"text",...}]} delivered, exact user-message readback + busy on go-b/deepseek-v4.1-flash verified): FS3511 proper fix + Release publish regression gate, and cash_ledger_events backfill for pre-B4c historical trades (idempotent, no double count) + reconciliation live QA curl assertion.
+- Remaining open gates: 3d-32 acceptance next tick; stocks/bonds milestone continuation; terminal notifier; completion notice. No completion notice.
+
+## 2026-09-22 12:38 tick(Hermes 监督)
+- 3d-33 worker verdict=BLOCKED(纯时间窗):部署/回填/对账/akshare/3996595 push 全部完成;唯一遗留 fresh e2e「买入→确认」待 T 日净值(约 20:00 CST)。
+- Hermes 独立复验:qa 147/147 PASS;publish-check PASS(无 FS3511);公网 /health 200;HEAD==remote==3996595。
+- 未修缺陷登记:手工股票/债券买入 CreateStockTrade debitAvailableCash 默认 false,不扣现金不写账本(App.fs:2530)→ 下一单(20:00 后)要求修复+回归测试+净值落地后 e2e 确认对账 0。
+- 会话空闲,本 tick 未派发。
+
+## 2026-09-22 12:46 tick(Hermes 监督)— 3d-34 派发
+- 发现 /session/status 裸请求返回 HTML/空对象,scoped directory 参数也回 {} — 接受会话级 readback + 服务日志 stream 行作为替代证据(/doc 确认端点 schema 未变)。
+- 派发 3d-34(小单)到唯一 writer ses_f3914ac0(基线 3996595,go-b/deepseek-v4.1-flash):修复 CreateStockTrade debitAvailableCash 默认 false 缺陷(手工买入默认扣现金 + 幂等写 cash_ledger_events,与基金买入同口径)+ 回归测试;验收 = build 0/0 + 全量 test + qa 147/147 + push + ls-remote 回读。
+- 净值 e2e 收口(订单 d59a8f0d pending_nav,T 日净值 ~20:00 CST)留给净值落地后的下一单。
+- 派发证据:服务 log stream providerID=go-b modelID=deepseek-v4.1-flash 12:46:39(session ses_f3914ac0…)。
+
+## 2026-09-22 12:55 supervisor check — 3d-34 in flight
+
+- 3d-34 (CreateStockTrade defect fix + regressions, baseline 3996595) still running on sole writer ses_f3914ac0 (go-b/deepseek-v4.1-flash): scoped status busy, service log streams at 12:54:29 (step 51), no pending question. No dispatch this tick (single-writer rule).
+-
+## 2026-09-22 13:52 tick — 3d-36 accepted, 3d-37 dispatched
+
+- 3d-36 accepted. Writer verdict READY_FOR_LEADER_REVIEW (finding: external flows and internal trades already use distinct ledger sources — no production change, only a regression test locking the classification). Leader independent evidence on ecae6f7: dotnet build 0 warnings/0 errors; tests Domain 72 / Web 63 / Api 251+1skip all green (+1 external-vs-internal reconciliation regression); qa/run.sh 148/148 PASS; ls-remote main == HEAD == ecae6f7.
+- Dispatched 3d-37 (notifier script authoring: scripts/notify_completion.py, selftest 3-PASS acceptance, commit only scripts/) to sole writer ses_f3914ac0; service log stream providerID=go-b modelID=deepseek-v4.1-flash 13:49:45 confirms delivery.
+- Open gates: 3d-37 acceptance + leader-side cron registration; T-day NAV closeout after ~20:00 CST (order d59a8f0d pending_nav); completion notice content itself.
+
+
+
+## 2026-09-22 13:1x tick(Hermes 监督)— 3d-34 验收通过 + 部署刷新
+- 3d-34 verdict=READY_FOR_LEADER_REVIEW;独立验收:dotnet build 0/0;测试 Domain 72 / Web 63 / Api 248+1skip 全绿(含新增 9 条 reconciliation 回归);qa/run.sh **148/148 PASS**(首次因 55434 端口瞬态占用退出 125,重跑通过)。
+- 发现并修复部署滞后:运行中 fundlab-api 仍是 12:08 旧 publish(早于 13:03 commit)。重新 dotnet publish(Release 无 FS3511,FS3511 修复生效)+ 拷贝 + systemctl --user restart fundlab-api,health ok。
+- 公网 e2e(真实 API + 真实 Postgres):新基金 3d34-e2e → 手工股票买入 000001(成交价 11.70 ×100 = 1170.00)→ availableCash 10000→8830,cash-reconciliation difference=0.00,sources=[stock_buy -1170.00 ×1]。缺陷修复上线路径完整。
+- git ls-remote origin main == HEAD == 2dedc30(3d-34 已推)。
+- 遗留:净值收口单(订单 d59a8f0d pending_nav,T 日净值 ~20:00 CST 后 e2e「买入→确认→对账 0」);公网前端 bundle 未变(3d-34 纯后端改动,无需重建;当前 served index-D4n0S-9a.js 与 B4c 一致)。
+- 下一步:等净值落地后派净值收口单;继续股票/债券里程碑下一小步。
+
+## 2026-09-22 13:35 supervisor check — 3d-35 accepted, 3d-36 dispatched
+
+- 3d-35 accepted (this tick). Writer verdict READY_FOR_LEADER_REVIEW; leader independent evidence: dotnet build 0/0; tests Domain 72 / Web 63 / Api 250+1skip (incl. stock-sell reconciliation regressions); qa/run.sh 148/148 PASS; ls-remote main == HEAD == 9bdc062.
+- Deployment lag fixed again (3d-35 backend-only): fresh dotnet publish Release -> src/FundLab.Api/publish -> systemctl --user restart fundlab-api; loopback + public /health ok.
+- Public e2e (live API + real pg, synthetic fund): buy 000001 qty100 (API executed at live price 11.72, not requested 11.70) then sell 40 (API priced 12.20 request at 11.72): closing 9296.80 == ledger 9296.80, difference 0.00, sources stock_buy -1172.00 x1 + stock_sell +468.80 x1. Sell path singularity confirmed live.
+- Dispatched 3d-36 (stock-cashflows external-flow vs internal-trade ledger classification check + regression tests, baseline 9bdc062) to sole writer ses_f3914ac0 via message API (blocked-poll design; busy + read/grep tool activity verified).
+- Open gates: 3d-36 acceptance; T-day NAV closeout after ~20:00 CST (pending nav order d59a8f0d); stocks/bonds milestone; completion notifier; (public fund.somhairle.bid + auth already serving). No completion notice.
+
+## 2026-09-22 13:59 (leader tick)
+- 3d-37 (notify_completion.py authoring) 验收通过: selftest 3/3, commit 75523a0, 推送回读一致。
+- notifier cronjob 4092b00f6fb1 注册完成 (no_agent, every 5m), Slack 通道真实冒烟通过。
+- 下一步: T 日净值落地 (约 20:00 CST) 后派净值收口单 (订单 d59a8f0d)。
+
+## 2026-09-22 14:09 tick(Hermes 监督)— idle,等待净值窗口
+- writer ses_f3914ac0 自 13:51 CST(服务 log 最后 stream)无活动;scoped status 空 = idle,无待答 question。
+- 上轮已收口:3d-37 验收通过 + notifier cronjob 4092b00f6fb1 冒烟 DELIVERED + replay 静默。
+- 本 tick 无派发(单 writer;净值收口单 d59a8f0d 需 T 日净值 ~20:00 CST 落地后才派)。
+
+## 2026-09-22 14:17 tick(Hermes 监督)— idle,等待净值窗口
+- writer idle 无变化(最后 stream 13:51 CST);净值收口单 d59a8f0d 仍需 T 日净值 ~20:00 CST 落地后才能派发,本 tick 无动作。
+## 2026-09-22 14:25 tick(Hermes 监督)— idle,等待净值窗口
+- writer ses_f3914ac0 idle(最后活动 13:51 CST);notifier 静默正常;净值收口单 d59a8f0d 仍等 T 日净值 ~20:00 CST,本 tick 无派发。
+
+- 2026-09-22 14:48 tick: writer idle (notifier deliverable accepted+pushed baseline 75523a0); fund.somhairle.bid HTTP 200 (gateway health ok, /api 401 auth live); no new dispatch — net-closeout step still gated on daily NAV data landing later today.
+
+- 2026-09-22 14:58 tick: writer ses_f3914ac0 idle (last stream 13:51 CST); no dispatch — net-closeout d59a8f0d still gated on T-day NAV ~20:00 CST.
+
+## 2026-09-22 15:27 tick(Hermes 监督)— go-b 周配额耗尽,切 go-a 续派 3d-38
+
+- 关键事实修正:「T 日净值 ~20:00 CST」是错误预设。16:04 直接 probe akshare fund_open_fund_info_em:000001 最新净值日期 = 2026-09-22(T 日净值已公开,EM 偶发延迟是数据源波动)。收口单不再需要等窗。
+- go-b/deepseek-v4.1-flash 周配额耗尽(weekly usage limit,重置 2026-09-28 08:00 CST),服务 log 07:11Z AI_APICallError 实证。
+- 直连探针:OPENCODE_GO_API_KEY(sha 90794bd5…,即 go-a 的 key)对 go 端点 chat/completions HTTP 200(需 x-opencode-session 头 + 非默认 UA,裸 UA 会 403 error 1010)。go-a 月配额「停用」结论仅对 go-a/glm-5.3-flash 成立,go-a/deepseek-v4.1-flash 可用。
+- writer ses_f3914ac0 模型原地切到 go-a/deepseek-v4.1-flash(POST /api/session/{sid}/model 204,GET 回读确认);服务级 opencode.jsonc 顶层 model 同步改 go-a(context_limit 不变 262144,compaction.reserved 无需改)。
+- go-b 旧 3d-38 派发因配额停在 retry 从未执行:scoped abort 该会话清掉 retry 态,再以 message-shape model go-a 重派 3d-38(净值收口:T 日净值已公开 → 回填 → 订单 d59a8f0d 确认 → 对账 0 → 测试/build/qa/push)。busy 中,go-a stream 07:25Z 起。又补一条去重附注(旧 go-b user 消息作废,只做一遍)。
+- Next tick:验收 3d-38(verdict 读取 + 独立 build/test/qa + ls-remote + 公网 deploy 刷新若涉及后端改动)。
+
+## 2026-09-22 15:42 tick(Hermes 监督)— 3d-38 返回 BLOCKED,净值窗口修正回来
+- 3d-38 verdict = BLOCKED:worker 用 collector 同源函数 + `fund_open_fund_daily_em` + 多基金交叉 + 部署 API 四路 probe,最新净值日期均为 2026-09-21;leader host 复 probe 结果一致。上轮「16:04 已公开 09-22」的头判断有误(误读了带日期命名的列),正确的发布窗仍是 ~20:00 CST 起(worker 与 leader 两个独立判断在此收敛)。
+- 无改动落地:HEAD 仍 75523a0,工作区干净,订单 d59a8f0d 仍 pending_nav。
+- 下一步:~20:00 CST 后 re-dispatch 净值收口单;期间 writer idle 保持, notifier 静默正常。
+
+## 2026-09-22 15:50 tick(Hermes 监督)— 窗口未到,未派发
+- 宿主机复探:`akshare_collector.py --operation nav`(真实解释器 strategy-lab/.venv)000001 共 6011 条,最新 nav_date 2026-09-21(1.332),与 15:42 双路复核一致,T 日 09-22 未公开。
+- scoped status 仍返回 `{}`(session 列表可见 ses_f3914ac0 idle,updated 15:42 左右),无在跑 turn,未派发。
+- 下一步:~20:00 CST tick 再 probe,nav_date ≥ 2026-09-22 即续派净值收口单(订单 d59a8f0d 对账 0 → build/test/qa → ls-remote 回读)。
+
+
+- 2026-09-22 16:36 tick: NAV probe (real akshare) still nav_date 2026-09-21 (000001, 161725); scoped status {} idle; 未派发 — 收口单 d59a8f0d 仍等 T 日净值公开(双路复核收敛于 ~20:00 CST 窗口)。
+
+- 2026-09-22 17:50+ tick: T 净值仍延迟(三方探针 latest=2026-09-21);writer 保持 BLOCKED 上游,未派发。
+
+- 2026-09-22 18:58 leader tick: T 净值仍延迟(AKShare 探针 000001/161725/110022 latest=2026-09-21);scoped status {} idle;HEAD==origin==75523a0;未派发。
+
+- 2026-09-22 19:15 tick: T 净值仍延迟(scripts/probe_akshare_nav_dates.py 000001/161725/110022 latest=2026-09-21);scoped status {} idle;HEAD==origin==75523a0;未派发。下一 tick 再 probe,nav_date ≥ 2026-09-22 即派净值收口单(d59a8f0d)。
+
+- 2026-09-22 tick: T-day NAV still unpublished (probe: 000001 latest nav_date 2026-09-21, real akshare). Writer idle (scoped status {}), HEAD==origin==75523a0. Holding order d59a8f0d closeout dispatch.
+
+## 2026-09-22 18:29 (cron tick)
+- T 日净值仍未公开(000001/110022 最新 nav_date 2026-09-21)。收口单(order d59a8f0d)继续不派发;writer idle,HEAD==origin==75523a0。
+
+- 2026-09-22 18:36 tick: T 日净值仍延迟(akshare 探针 000001/161725/110022 latest nav_date=2026-09-21);writer idle;HEAD==origin==75523a0;未派发,订单 d59a8f0d 继续等 ~20:00 CST 窗口。
+
+## 2026-09-22 3d-39 验收(BackfillCashLedger pre-3d-34 现金借记修复)
+- 3d-39 accepted (this tick). Writer verdict READY_FOR_LEADER_REVIEW; commit `877e031`,推送 origin main,`git ls-remote origin refs/heads/main` 回读哈希 == `877e03183b5acaff84526cb9fe978280cadd8a76` == HEAD(远端哈希一致)。
+- 修复内容:`BackfillCashLedger` 的 `stock_buy` 批改为同事务 CTE,补事件同时为「补事件前已对账为 0」的缺借记基金一次性借记 `available_cash`,幂等、不重复扣减(`src/FundLab.Api/Persistence.fs`)。
+- 回归:新增 fixture「预 3d-34 交易 + 未扣 available_cash」,断言 backfill 后 `difference==0`、`available_cash` 校正正确、重复跑仍为 0;既有 backfill 测试保持绿(不双扣)。
+- 独立验证:`dotnet build FundLab.sln` 0 错 0 警;`dotnet test FundLab.sln --no-build` Domain 72 / Web 63 / **Api 253**(+1 skip,较上一基线 252 增 1)全绿;`qa/run.sh` **148/148** PASS。
+- 未改动生产数据(leader 已手工修复,9 基金 difference 全 0.00);本单未部署/未重启生产服务。
diff --git a/docs/stock-milestone-survey.md b/docs/stock-milestone-survey.md
new file mode 100644
index 0000000..ce96c5e
--- /dev/null
+++ b/docs/stock-milestone-survey.md
@@ -0,0 +1,147 @@
+# 直接股票持仓里程碑调研
+
+状态:**只调研,不实施**。本文给出实现「直接股票持仓里程碑」所需的既有限件、schema 现状与缺口、UI 范围,以及 AKShare 股票日行情最小探针设计。所有代码位置均为当前 `main`(`877e031`)实际位置,引用格式 `file:line`。
+
+验收口径来自 `docs/design.md`(§4.5/§5.2/§10.1)、`docs/implementation-plan.md`(§10.1)与 `README.md`;环境与 Git 口径见 `docs/leader-environment-unblock.md`。
+
+## 0. 结论摘要
+
+- **已有可复用垂直切片**:股票买入/卖出/持仓/现金账本/行情探针/估值/UI 均已实现并有回归(`stock_trades`、`stock_positions`、`stock_sells`、`stock_cashflow_events`、`instrument_snapshots`;`POST /api/funds/{id}/stock-trades` 等)。
+- **里程碑真正缺口(设计已要求、当前未落地)**:
+ 1. 实际价与复权价分列(设计 `market_price_observation.price_kind`,`docs/design.md:107`);现有 `instrument_snapshots` 只有单一 `price`,无 `adjust`/`price_kind`。
+ 2. 公司行动(除权除息/送股/配股)与停牌持久化;`stock_cashflow_events` 仅 `buy/sell/dividend`,无 split/rights。
+ 3. 股票交易日历与每 instrument 的 `trading_rule` 版本(设计 `docs/design.md:182`、`docs/implementation-plan.md:227`)。
+ 4. point-in-time 估值重算与无前视验收(当前估值按 `asOfDate` 取快照 + 实时探针兜底,未做严格 `published_at <= cutoff`)。
+ 5. 代码搜索/选择(当前 UI 为自由输入六位码)。
+- **最小探针结论**:既有 collector 已能拉股票日线(`akshare_collector.py:461`,三源 fallback),里程碑探针只需**验证**交易日真实性、实际/复权列差异、公司行动跳变、缺失/修订与当日可用时点;**不需要新增 operation**。
+
+## 1. 既有限件(硬约束,不可违背)
+
+摘自 `docs/design.md` §1/§4.4/§4.5 与 `README.md`:
+
+- 只用真实数据,缺失显示「未接入/数据不可用」,**不得伪造行情、不得把缺失当 0**(`docs/design.md:273`、`README.md:40`)。
+- 合成数据必须 `is_synthetic=true` 并与真实查询隔离(`docs/design.md:191`)。
+- 金额/价格/份额/净值用 F# `decimal` 与 PostgreSQL `numeric`;对外 JSON 用字符串(`docs/design.md:113`、`docs/design.md:295`)。
+- 写命令要求 `Idempotency-Key`;同 key 同哈希返回原结果,不同哈希 `409 IDEMPOTENCY_CONFLICT`;写入在单事务内(`docs/design.md:186`、`docs/design.md:366`)。
+- point-in-time:`published_at`/`first_seen_at` 分离,历史回填标 `backfilled` 且不得前视(`docs/design.md:220`)。
+- 不接真实券商/下单(`docs/design.md:17`、`docs/design.md:305`)。
+- 运行时数据库、缓存、凭证、构建产物不入 Git(`docs/design.md:317`)。
+- 前端中文,每个图表/持仓必须展示数据日期、来源状态、是否含合成(`docs/design.md:299`)。
+
+## 2. 现有实现(可直接复用)
+
+### 2.1 Schema(`src/FundLab.Api/Persistence.fs`)
+
+| 表 | 位置 | 关键列 | 备注 |
+| --- | --- | --- | --- |
+| `instruments` | `Persistence.fs:853` | `code`(`^[0-9]{6}$`)、`name`、`fund_type`、`source`、`source_revision`、`first_seen_at`/`last_seen_at` | 基金/股票共用,无显式 `kind` |
+| `stock_trades` | `Persistence.fs:1131` | `fund_id`、`instrument_code`、`stock_name`、`quantity`、`price`、`cost_cash`、`is_synthetic`、`executed_at` | 买入不可变事实 |
+| `stock_trade_idempotencies` | `Persistence.fs:1143` | `idempotency_key`、`request_hash`、`trade_id`、`fund_id` | 幂等 |
+| `stock_positions` | `Persistence.fs:1151` | `(fund_id, instrument_code)` 主键、`quantity`、`cost_cash`、`last_traded_at` | 持仓聚合(成本口径,非市值) |
+| `stock_sells` | `Persistence.fs:1161` | `quantity`、`price`、`fee_amount`、`proceeds`、`is_synthetic`、`executed_at` | 卖出 |
+| `stock_cashflow_events` | `Persistence.fs:1174` | `event_type IN ('buy','sell','dividend')`、`event_date`、`quantity`、`amount` | 无 split/rights |
+| `instrument_snapshots` | `Persistence.fs:1296` | `(instrument_code, asset_class, snapshot_date)`、`price`、`source*` | `asset_class IN ('stock','bond')`,**无 adjust/price_kind** |
+
+设计对照缺口:`docs/design.md:107` 的 `market_price_observation(trade_date, price, price_kind, source, payload_hash)` 与 `docs/design.md:110` 的 `valuation_snapshot` **均未落地**;当前以 `instrument_snapshots` 与「按需计算」替代。
+
+### 2.2 写入路径与 `stock_buy` 账本
+
+- `FundRepository.CreateStockTrade`:`Persistence.fs:5951`。
+ - 校验六位码/正数量/正价格:`Persistence.fs:5957`。
+ - advisory lock + 幂等重放/冲突:`Persistence.fs:5970`–`5992`。
+ - 可选现金借记(3d-34 起默认 `true`):`Persistence.fs:6002`–`6014`;余额不足回滚并返回 `StockTradeInsufficientFunds`:`Persistence.fs:6016`。
+ - 写 `stock_trades` + 幂等记录:`Persistence.fs:6038`。
+ - **写 `stock_buy` 账本事件**:`Persistence.fs:6041`–`6051`(`insertCashLedgerEvent … CashLedger.StockBuy … "stock_buy"`)。
+ - upsert `stock_positions`:`Persistence.fs:6053`–`6080`。
+- `BackfillCashLedger` 的 `stock_buy` 补事件批(含 pre-3d-34 缺借记一次性修正,3d-39/`877e031`):`Persistence.fs:7240` 起。
+- HTTP 处理器 `createStockTrade`:`App.fs:2480`。
+ - 实时行情取价:`App.fs:2499`;停牌 → `400 STOCK_SUSPENDED`:`App.fs:2504`;无价 → 数据错误:`App.fs:2511`。
+ - 幂等冲突 `409`:`App.fs:2535`;余额不足 `400 INSUFFICIENT_FUNDS`:`App.fs:2539`。
+- 路由:`POST /api/funds/%s/stock-trades`(`App.fs:3670`)、`stock-sells`(`3671`)、`GET stock-positions`(`3672`)、`stock-cashflows`(`3673`/`3674`)。
+
+### 2.3 行情探针与估值
+
+- 路由:`GET /api/market/stock-quote`(`App.fs:3630`)、`GET /api/market/stock-daily`(`App.fs:3631`)、`GET /api/funds/%s/valuation`(`App.fs:3682`)。
+- 探针接口 `MarketProbes`(`App.fs:604`–`611`:`StockQuotes`/`StockDaily`);`MarketDataService.FetchStockDaily`:`MarketDataService.fs:215`(调用 `--operation stock-daily`)。
+- collector:`stock_market`(沪/深/北前缀映射)`akshare_collector.py:446`;`stock_daily`(三源 fallback)`akshare_collector.py:461`;`stock_quote`(`stock_bid_ask_em` → 快照)`akshare_collector.py:514`。
+- 估值处理器 `getFundValuation`:`App.fs:3317`;价格解析 `resolveValuationPrice`(快照优先,实时兜底):`App.fs:3306`;缺失标 `unavailable` 不补零:`App.fs:3406`。
+- 行情刷新把最新日线收盘写入 `instrument_snapshots`:`App.fs:3447`–`3496`。
+- 回归:`tests/FundLab.Api.Tests/StockDailyProbeTests.fs`(升序去重、代码不符拒绝、503/400 边界)、`StockQuoteProbeTests.fs`(名称缺失不伪造、停牌标记、幂等缓存)、`StockTradeTests.fs`/`StockSellTests.fs`/`StockCashflowTests.fs`。
+
+### 2.4 UI(`src/FundLab.Web/App.fs`)
+
+- 股票行情探针面板(代码输入/查行情/查近 5 日日线):`App.fs:6166` 起。
+- 买入 + 股票持仓表 + 卖出:`App.fs:6240`–`6325`(表头「代码/名称/数量/成本/卖出数量/操作」)。
+- 组合估值卡(现金/持仓市值/组合合计/已定价·缺失,含 `priceSource`):`App.fs:6326` 起。
+- API 客户端 `./src/api.js` 导入:`App.fs:1143`–`1156`。
+- QA 场景:`qa/driver/browser-test.js:795`(`stocksScenario`)、`:858`(`stockDailyScenario`)、股票买卖与持仓(`:314`–`322`);桩采集器 `qa/stub/fund-lab-python:48`(`stock-quote`)/`:59`(`stock-daily`)。
+
+## 3. Schema 现状与缺口(里程碑必须补)
+
+| 设计实体/字段 | 现状 | 缺口 |
+| --- | --- | --- |
+| `market_price_observation.price_kind`(actual/adjusted 分列) | 无;`instrument_snapshots.price` 单一值 | 需新增 `price_kind`/`adjust` 列或新表,估值默认 actual,复权仅用于收益分析 |
+| 公司行动(送转/配股/除权除息) | `stock_cashflow_events.event_type` 仅 `buy/sell/dividend` | 缺 split/rights/除权日;复权因子无处存储 |
+| 停牌状态 | 仅 `stock_quote` 响应内瞬态 `suspended`(`akshare_collector.py:537`) | 无持久化,估值/UI 无法回溯停牌 |
+| 交易日历 | 无 | 需按数据源真实交易日,禁止把最近交易日冒充当天(`docs/design.md:223`) |
+| 每 instrument `trading_rule` 版本 | 无 | 需绑定 T+1、最小 100 股、费用、结算、停牌规则(`docs/implementation-plan.md:227`) |
+| `valuation_snapshot`(含 source_revision_set) | 按需计算,无快照表 | 需可重算快照以支持 point-in-time 与无前视 |
+| 成本 vs 市值 | `stock_positions.cost_cash`(成本) | 市值来自 `instrument_snapshots`/实时价,二者未在同一快照冻结 |
+
+## 4. UI 范围
+
+已有(可复用):行情探针、买入(整数股)、持仓表、卖出、估值卡、`priceSource` 来源标注、缺失/不可用中文提示。
+
+里程碑需新增/调整:
+
+1. 股票代码**搜索/选择**(复用 `GET /api/instruments/search`,当前 UI 是自由输入六位码)。
+2. 持仓**金额或份额**输入(当前仅整数股数),并显示最小交易单位/费用。
+3. 实际价/复权价**口径标注**(`price_kind`)与「净资产/市值」曲线标签区分(`docs/design.md:299`)。
+4. 停牌/非交易日/缺失的**明确状态与原因**(不补零)。
+5. 公司行动(分红/送转)展示与再投资选择。
+
+## 5. AKShare 股票日行情最小探针(只调研)
+
+**目标**:回答 `docs/design.md:266` 与 `docs/implementation-plan.md:226` 的六个问题,不新增 collector operation、不改代码。
+
+复用既有 `stock_daily(code, days, adjust)`(`akshare_collector.py:461`),其 provider fallback 顺序:
+
+1. `ak.stock_zh_a_hist(symbol=code, period="daily", adjust=adjust)` — 东方财富,列 `日期/收盘/成交量/成交额`(`akshare_collector.py:481`)。
+2. `ak.stock_zh_a_daily(symbol="sh600519", adjust=adjust)` — 新浪(`:482`)。
+3. `ak.stock_zh_a_hist_tx(symbol="sh600519")` — 腾讯(`:483`)。
+4. `adjust ∈ {"", "qfq", "hfq"}`,交易所前缀由 `stock_market` 决定(`:446`、`:477`)。
+
+建议新增 `scripts/probe_akshare_stock_daily.py`(仅脚本,不接入 API),采样 3–5 只(沪 `600519`、深 `000001`、北 `8xxxxx`),对每只分别以 `adjust=""`/`qfq`/`hfq` 拉取并输出:
+
+- `date`、`close`、`volume`、`amount`、`adjust`;
+- **交易日真实性**:返回日期是否为真实交易日;周末/节假日是否缺行。
+- **停牌**:停牌日是否缺行或 `volume==0`。
+- **实际 vs 复权**:`adjust=""` 与 `qfq`/`hfq` 首尾值差异、复权基准漂移。
+- **公司行动**:除权除息日附近价格跳变是否与 `adjust` 对应。
+- **缺失与修订**:同日重复拉取是否修订历史行;`source_revision` 是否变化。
+- **当日可用时点**:T 日收盘价何时出现(类比基金净值延迟;3d-38 已证明净值端点会滞后)。
+
+**判定门槛**:若某类股票在 AKShare 无稳定、可追溯、日期正确的数据,UI/API 必须显示「未接入/数据不可用」,不得合成行情混入真实曲线(`docs/design.md:273`)。
+
+## 6. 最小实施切分(建议,非本单范围)
+
+| 步骤 | 内容 | 依赖 |
+| --- | --- | --- |
+| S1 | schema:`instrument_snapshots` 增 `price_kind`/`adjust`(或建 `market_price_observation`);停牌与交易日历表 | 迁移 + 回滚测试 |
+| S2 | `scripts/probe_akshare_stock_daily.py` + 探针结论 | 真实解释器 |
+| S3 | 交易规则:T+1、100 股整手、费用、结算、停牌拒单 | 规则版本表 |
+| S4 | 公司行动/分红入账(现金/再投资) | `stock_cashflow_events` 扩展 |
+| S5 | UI:搜索/选择、金额/份额输入、口径与状态标注 | S1/S3 |
+| S6 | 估值 point-in-time 重算 + 无前视 + 浏览器验收 | S1–S5 |
+
+## 7. 引用索引(file:line)
+
+- `src/FundLab.Api/Persistence.fs:853` `instruments`;`:1131` `stock_trades`;`:1143` `stock_trade_idempotencies`;`:1151` `stock_positions`;`:1161` `stock_sells`;`:1174` `stock_cashflow_events`;`:1296` `instrument_snapshots`。
+- `src/FundLab.Api/Persistence.fs:5951` `CreateStockTrade`;`:6002` 借记;`:6041` `stock_buy` 账本事件;`:6053` `stock_positions` upsert;`:7240` `BackfillCashLedger` stock_buy 批。
+- `src/FundLab.Api/App.fs:2480` `createStockTrade`;`:3306` `resolveValuationPrice`;`:3317` `getFundValuation`;`:3670`–`3682` 路由;`:3630`/`:3631` market 探针路由;`:604` `MarketProbes`。
+- `src/FundLab.Api/MarketDataService.fs:215` `FetchStockDaily`。
+- `src/FundLab.Api/akshare_collector.py:446` `stock_market`;`:461` `stock_daily`;`:514` `stock_quote`。
+- `src/FundLab.Web/App.fs:6166` 行情探针面板;`:6240` 买入/持仓/卖出;`:6326` 估值卡;`:1143` api.js 导入。
+- `qa/driver/browser-test.js:795`/`:858` 股票 QA 场景;`qa/stub/fund-lab-python:48`/`:59` 桩采集器。
+- `docs/design.md:107` `market_price_observation`;`:110` `valuation_snapshot`;`:223` 交易日;`:266` 探针清单;`:273` 不可用不合成。
+- `docs/implementation-plan.md:226`–`231` 股票里程碑步骤。