Commit Graph

112 Commits

Author SHA1 Message Date
claude_dev 3b3e42985e fix(datareader): read_index_daily 切 dbbardata(治 benchmark 读陈旧 vnpy DbBarData)
CI/CD / test (push) Successful in 9s
CI/CD / nas-deploy (push) Successful in 24s
CI/CD / nas-verify (push) Successful in 3s
cta_engine benchmark 经 read_index_daily 读 vnpy DbBarData(方案A前陈旧源)。
切 dbbardata(指数点位已由 sina_index_eod 灌 exchange=SSE)。

- 数据源:vnpy get_database.load_bar_data → sqlite3 直连 dbbardata
- 消除覆写 vnpy SETTINGS 副作用(cta_engine.py:73/199)
- 回归坑1(000300读空):CODES 已加 000300 → dbbardata 有数据(本轮修复)
- 回归坑2(sz000905):默认 benchmark sh000300 不触发;中证指数用 sh 前缀
- 复用 provider 模式:substr(datetime,1,10) 比日期规避混合格式
- 测试:mock vnpy → 真实 tmp sqlite 库测 SQL 路径(2测重写)
- 验证:Mac 4+5 passed + VPS 真实库 sh000300 7月23行 close4588.197
2026-08-01 08:41:57 +08:00
claude_dev 8862816557 feat(portfolio): B fundamentals批量 + C涨跌停filter修复(get_limit_status_batch接入)
B: value_selection 逐只 get_value_metrics → get_value_metrics_batch(数据session)
- 01 验证 -21.85% vs 改前 -21.63%(微差0.22%, batch实现微差,可接受)

C: filters filter_limitup/limitdown/paused 接入 get_limit_status_batch(数据session)
- 修复回测死代码: filter 取 tick.get(last_price/paused) 恒None → 照买涨停/照卖跌停/照交易停牌
- 三策略调仓预取 status_map 共享一次查询, 向后兼容 all_weather(不传参=原行为)
- 03 短区间(2024Q1)验证: C前+138.7%虚高 → C后+101.6%, filter修复减少照买涨停虚增

验收: 101单测(filters 30含14新status_map口径 + 三策略71)
注意: get_limit_status_batch 44s/800只(数据session待批量化优化), 02/03全周期待优化后
2026-07-30 07:37:03 +08:00
claude_dev bdad396243 test(data): 重写 read_index_daily 测试对齐 vnpy 实现(消 latent fail)
原 2 测试测"读 parquet"(旧实现),实现早已改读 vnpy DbBarData→一直 latent fail
(KeyError→防崩溃后 RuntimeError)。套 test_datareader 的 vnpy mock 模式重写:
测 load_bar_data 返回 DataFrame + start/end 委托 vnpy 过滤。

注:read_index_daily 切 dbbardata 根治待定(000300 不在 dbbardata/000905 exchange
约定不匹配),见 memory read-index-daily-dbbardata-migration-pending。
2026-07-29 21:29:14 +08:00
claude_dev 384bcc56d7 fix(data): 修三环境 session 反馈的 3 个数据层问题
D1: 删 test_circuit_breaker.py(测已归档 raw_redownload.check_circuit_breaker 死代码,全仓零活跃引用,致 data_platform 套件 collection error)
D2: datareader.py read_db_daily/read_index_daily 两处 vnpy_db 硬访问→.get()+清晰报错防崩溃(根治切 dbbardata 读指数列待办)
D3: high_limit/low_limit close±10% 兜底是有意设计非 bug(填 NaN 会复活 bullet_trade 误判停牌)—get_price 加 round(.,2) 对齐 get_current_tick 口径;测试期望从 NaN 改兜底估算
2026-07-29 21:17:15 +08:00
claude_dev e91b103f7a docs(data): Phase2 归档旧回填/import链+15m灌库链(VPS schtask实证线头死)
VPS schtasks /query: 调用这些脚本的 schtask 全已禁用(baostock_day1/2,
xtdata-build, index-hist, bs-daily-increment 等),且不被8个活跃wrapper引用。
活跃白名单(保留): bs_eod/xt_eod/sina_index_eod/akshare_static_download/
index_const_hist_download/merge_constituent/migrate_constituent/parse_csindex_announce

- _archive/backfill_legacy/: 31个(旧回填import链+旧baostock下载+禁用wrapper+Mac .sh链)
- _archive/backfill_15m/: 7个(15min灌库链+hardening测试,backfill已完成)
- 关键实证: index_const_hist_download 被 index_monthly_wrapper(活跃)调用→保留不归档
- 无残留import(fetch_with_fallback是sanguo_data函数非fallback模块)
2026-07-29 11:56:44 +08:00
claude_dev 1cc9126abb feat(portfolio): get_limit_status_batch 回测涨跌停/停牌批量接口
修 filter_limitup/limitdown/paused 回测失效(get_current_tick 无 last_price/paused
字段→恒不过滤→03/02 回测算出假收益)。

get_limit_status_batch(codes, date) → {code: {is_limit_up,is_limit_down,is_paused}|None}:
- dbbardata 无 high_limit 列 → high_limit=round(prev_close×(1+幅度),2) 精确算
  (pctChg 阈值高价股边界失真故不用); 窗口 ROW_NUMBER 取 T+T-1 两根日线。
- 幅度板块感知: 主板10/创业·科创20/北交30 + 历史 ST5%(valuation_baostock.isST)。
- 停牌=当日 volume==0; 方案A(返回判断好的状态); 缺失股 None。

Mac TDD 6 用例(涨停/跌停/停牌/创业板20%/正常/缺失)全绿; 45 回归通过。
策略层 filter 接入归策略 session(替 get_current_tick 逐只)。
2026-07-29 09:10:20 +08:00
claude_dev d2cd8fa945 feat(portfolio): get_security_info_batch + get_value_metrics_batch 批量接口
策略层提速第三轮(filters 通病 + 策略01):
- get_security_info_batch: 2 条 SQL(symbol IN chunk + GROUP BY sym,exc 拿
  min/max; constituent_unified 拿 name)替 N×2 逐只。filters.py filter_st_stock/
  filter_new_stock 自动探测批量(hasattr + isinstance dict 回退逐只, 向后兼容)。
  三策略 ST/次新过滤通病: 万次查询压成 2 条。
- get_value_metrics_batch: ThreadPool 并发逐只委托 lpp.get_value_metrics
  (多期 ROE/FCF/流动比率逻辑不变, 只并发)。策略01 价值精选提速。
- 接口3 get_ticks_batch 不做: 实证 get_current_tick 无 last_price/paused 字段
  → 回测涨跌停/停牌 filter 恒不过滤(死代码), 批量化无意义; 真问题是回测
  涨跌停检测失效(策略层另修)。

Mac TDD parity 测试全绿(batch==逐只); 93 回归通过。
2026-07-29 08:23:21 +08:00
claude_dev 32dbcb8958 feat(portfolio): P2/P3 策略层向量化 + fundamentals批量提速解锁长回测
P2 行情向量化(get_price→get_closes_panel,口径实证 max_abs_diff=0.0 零偏差):
- momentum_timing: _cal_rps/_select_stocks/_cal_buy_sign 三处向量化
- small_cap: _cal_momentum_score 用 close.min/max 代理 low/high(方案A)

P3 fundamentals 批量(small_cap _pick_stocks 加 fields=[market_cap,eps],
对接数据session f416a17 get_fundamentals_df 按需短路):
- 02 000985 全市场 5128只 ~19min卡死 → 195s 跑通解锁

验收: 72单测全过; VPS 03短回测+138%(口径与get_price一致diff=0)/
02全市场-11%(2024Q1小盘股灾期合理)/01沪深300可跑
2026-07-29 07:49:35 +08:00
claude_dev f416a17b6d feat(portfolio): get_fundamentals_df 批量提速(fields= 按需短路 + ThreadPool 并发)
策略02 _pick_stocks 对 5128 只按 market_cap+eps 排序, 旧实现逐只读 4 表(valuation+
income+balance+financial_abstract)+算 calc_roic, 首仓全 cache miss 卡死。

优化(就地, 策略代码零改动, fields=None 向后兼容):
- fields= 参数: 只读请求字段依赖的源表(策略02 只要 market_cap+eps → 跳 balance/
  financial_abstract/roic, 省一半 parquet 读)
- ThreadPool 并发逐只(>64 只; 本地文件 I/O 非 baostock 网络, 不触不并发铁律)
- _build_fundamental_row 加 need= 守卫读取(lpp + unified 两层)

Mac TDD 4 测试(子集/短路/回归/并发保序)全绿, 124 回归通过。
注: 策略02 要拿满提速需在其 get_fundamentals_df 调用加 fields=[market_cap,eps](策略层, 归策略session)
2026-07-28 23:31:13 +08:00
claude_dev de04a8904b feat(portfolio): 移植3聚宽策略到BulletTrade + 8bug修正 + 数据缺口文档
三策略(聚宽py2→BulletTrade 0.9.2,BrokerFacade注入跨版本兼容):
- momentum_timing 动量择时(牛熊分界+行业RPS+均线,切回10中证行业指数)
- value_selection 价值精选(6条基本面过滤,切回沪深300)
- small_cap 小市值(去IC对冲,切回000985中证全指)

框架:
- runner_backtest 加 --strategy 分发(原硬编码all_weather)
- provider 加 get_value_metrics(价值精选6条基本面,NOTICE_DATE治前视偏差)
- 72单测全过(21+27+24)

修8个回测实测发现的真bug:
- 01第⑥条EPS绝对值0.08~0.5与①大盘矛盾→6条交集恒空致全程空仓,按注释本意改净利润同比8~50%
- 03原帖calRPS取数区间错(get_price start=end只取1天)→涨跌幅恒0 RPS失效;date.today()取真实今天非回测日
- 02 universe 000985不在constituent_unified→候选池空

VPS实测(短区间验证逻辑,非长期表现): 01价值+23%/03行业轮动+48%/02选出20只小盘

数据缺口(详见docs/research/joinquant_strategies/SUMMARY.md + data_gaps_fix_plan.md):
- 三表"1/3损坏"误报已撤回(全扫5530文件/表0损坏,沪深95%+健康,仅北交所920xxx空,不做北交所)
- 真实缺口: 行业成份股(G1已补)/000985(G2已补)/IC期货(02对冲去掉)/provider批量接口(G5待做,解锁长回测)
2026-07-28 22:20:49 +08:00
claude_dev c8f26bef80 feat(portfolio): LocalUnifiedProvider 批量行情接口 get_closes_panel (G5-Phase1)
单条 dbbardata 参数化查询 (symbol,exchange) OR + pivot 返宽表, 替代 N 次 get_price, 为策略向量化提速铺路。raw close 口径一致, 缺失 NaN 列, symbol-exchange 配对防歧义。22 tests 含 batch-vs-逐只回归。本文件另含 get_value_metrics 透传(策略移植)。
2026-07-28 20:36:11 +08:00
claude_dev 670498ab01 fix(data): akshare 三表下载鲁棒性 — 原子写 + --repair (G4)
write_parquet_and_marker 改原子写(tmp→os.replace→marker) kill 不产残缺 parquet; 新增 --repair 只重取 missing/empty/corrupt 忽略 marker(周度补漏不必等财报季 --force 全量); is_parquet_healthy 辅助。top_holders 修复无回归, 106 tests。
2026-07-28 20:36:11 +08:00
claude_dev f94a145587 fix(data): bs_eod 卡死根治 — per-stock commit + baostock 超时包装 + 周期 relogin
根因(py-spy dump + netstat CLOSE_WAIT 实证): baostock 服务端关长连接→CLOSE_WAIT, send_msg 静默阻塞不抛异常, socket.setdefaulttimeout 不被 baostock 自己 socket 遵守, relogin 只在 error_code≠0 救不了; 一把大事务全程持 WAL 锁阻断全库。修复: per-stock commit 去大事务 + _with_timeout 线程超时包 fetch_k 打破静默 hang + 周期 relogin 每500主动刷连接。VPS --limit 3 验证 11s 不 hang。
2026-07-28 20:36:11 +08:00
claude_dev 955c05357a fix(data): ak-stock top_holders KeyError 'sdltgd' 根治(_safe 捕 KeyError+匹配sdltgd,跳北交所920/83/87/43) 2026-07-27 21:08:53 +08:00
claude_dev 8b2693423a feat(data): xt_eod 扩展补全北交所 920xxx 日线(exc_of+universe+import容错,复用×100 normalize)
- exc_of: 920 -> BJSE (3 位前缀优先于 2 位 SSE/SZSE 判断, 防 sym[:2]='92' 落 SZSE)
- universe: 沪深ETF/基金 ∪ 北交所920xxx (从 constituent_unified 932000 取, baostock 不覆盖)
- import xtquant 容错 (mac xd=None 可单测 exc_of, main() 开头 return 2)
- --full-bj: 北交所 backfill start=20240101 (默认 LOOKBACK=30 与 ETF 同窗)
- 复用 normalize_daily_dt + ×100 volume 口径, 不动已有 ETF 写入路径

Tests: tests/data_platform/test_xt_eod_bj.py 14 cases RED -> GREEN (3 位前缀 critical + ETF/沪深/深市 覆盖)
2026-07-27 20:56:16 +08:00
claude_dev 11f383a8e2 fix(data): akshare top_holders Length mismatch bug 根治
bug 根因 (实证 akshare 1.18.x stock_gdfx_em.py:418):
  报告期未披露 (如 sz002673 的 20260630 在 2026-07-26 还未发) 时,
  东财 PageSDLTGD 接口返 sdltgd=[], akshare pd.DataFrame([])+reset_index()
  得 1 列 df, 然后 columns=[12 列] 抛
  ValueError('Length mismatch: Expected axis has 1 elements, new values have 12')

  这是确定性无数据 (非瞬时故障), 但 call_ak_with_retry 当网络错重试 3 次
  (14s 退避) + 噪声 ERROR 日志, 浪费时间且消耗断路器配额。

修复:
  新增 _safe_top_10_em 包装 ak.stock_gdfx_free_top_10_em: 子串匹配
  'Length mismatch' (pandas 错误信息稳定) → 返空 df 带 8 列 schema
  (TOP_HOLDERS_COLUMNS), 不抛; 其他 ValueError/ConnectionError 透传重试。
  fetch_top_holders_one_period 改调此包装。

验证:
  - Mac: sz002673/20260630 从 ~14s (3 重试) 降到 0.04s, 返 0×8 空 df
  - 有效期 sz002673/20251231, sh600519/20250930 正常返 10×8 数据
  - 9/9 新单测通过 (tests/data_platform/test_top_holders_parse.py)
2026-07-26 11:54:19 +08:00
claude_dev b06af336c3 fix(data): bs_eod 15min datetime 格式 bug 根治 + --no-daily 选项
bug 根因(2026-07-25): baostock 15min 实测 date='2026-07-21'(带 -),
time='20260721094500000'(17 位 YYYYMMDDHHMMSSmmm)。原代码假设 date 纯数字 +
time[:6] 取年月, 产乱 datetime 致 dbbardata 15min 全市场停 2026-07-17(8 天
没正确累积), 日线正常(7-24)。

修复:
- 提纯函数 _build_15m_dt(date_series, time_series): date 直连 + 从 17 位 time
  第 8-12 位提取 HHMM, 产出 'YYYY-MM-DD HH:MM:00' (符合 GLOB 清理模式)
- upsert_15m 调用 _build_15m_dt 替代内联拼接
- 新增 --no-daily 选项(只跑 15min, 用于重灌快), 与 --no-15m 对称
- TDD: 8 case 覆盖(09:45/14:30/15:00/13:00/跨日/多行混合/GLOB 格式)
- conftest.py 修补 sibling import 在 pytest 下可用
2026-07-25 21:14:12 +08:00
claude_dev b237c2d7e7 fix(data): 中证1000/2000 历史成份股补全(parse+migrate 聚合)
parse_csindex_announce.py 3 处修复:
- 932000 launch xlsx header 定位"证券代码"列(原 row[0]=指数代码 bug, distinct=1)
- 000852 列表搜索用 indexCode payload 精准拉(28 -> 96 公告, 回溯到 2016)
- akshare current snapshot header 定位"成分券代码"列(原 iloc[0]=日期 bug, current=0)

migrate_constituent.py:
- 加 _read_announce_union_aggregated(): 000852/932000 用 announce_union 全集
  替换原 snapshot-only 路径, was_removed 治幸存者偏差
- 缺 announce_union 时回退旧 snapshot 逻辑(向后兼容)

TDD: tests/portfolio/test_migrate_announce_union.py 7 测全过(5 unit + 2 integration)
Mac 产出验证:
- 000852: distinct 1220 -> 1672, was_removed 0 -> 672
- 932000: distinct 1 -> 2684(launch 修复), was_removed 0 -> 684
2026-07-23 23:45:55 +08:00
claude_dev 114a69e997 fix(data): dbbardata 日线双行根治(统一纯日期+helper,留ROWID max去重)
根因: dbbardata UNIQUE(symbol,exchange,interval,datetime) 按字符串字面比较,
多写入路径混用 'YYYY-MM-DD' 与 'YYYY-MM-DD 00:00:00' -> 同一交易日双行,
INSERT OR REPLACE 不去重 -> 回测交易日翻倍/pivot duplicate/信号异常。

方案A (统一纯日期, 详见 Main Agent 诊断):
- 新增 scripts/data_platform/dbbardata_utils.py: normalize_daily_dt(s)
  取前 10 字符, None/短串安全
- 4 个日线写入脚本写入前调 helper:
  - bs_eod.py (sanguo-bs-eod 个股日线 baostock)
  - migrate_daily_baostock.py (历史迁移)
  - xt_eod.py (sanguo-xt-eod ETF/基金 xtata)
  - import_vnpy_daily_fast.py (NAS 日线 parquet 导入, 加防御)
- TDD: tests/data_platform/test_dbbardata_utils.py 9 cases 全过
- 回归: tests/data_platform + tests/portfolio 199 passed 12 skipped

peewee DateTimeField formats 含 '%Y-%m-%d' (阶段0 VPS 实测确认),
读纯日期不崩, 方案A 前提成立。

15min 干净, 不动 (分钟必须带时分)。只改日线 interval='d'。

数据层根治, 不在 provider 适配兜底 (用户铁律)。
2026-07-23 12:18:41 +08:00
claude_dev 48c67d05d2 fix(data): 成份股 merge/migrate pipeline 可重跑(idempotent, 读 bs_index_constituent_old) 2026-07-23 09:04:00 +08:00
claude_dev 41cc6d13bf feat(portfolio): LocalUnifiedProvider spec §6 使用层落地 + VPS E2E(Task6)
spec §6 使用层 provider — 读方案A 权威数据层, 零 online, 治幸存者偏差:
- get_price: dbbardata('d') raw + bs_adjust_factor 前复权(asof, qfq[t]=raw[t]*factor[t])
- get_index_stocks/get_constituent: constituent_unified 并集治偏差(300=940含被踢, 无date时点)
- get_fundamentals_df: baostock pe/pb/ps/pcf + akshare 市值 + 三表委托 LocalParquetProvider
- 辅助: trade_days/security_info/current_tick/split_dividend/all_securities

VPS E2E 实证修复(Mac fixture 盲区):
- dbbardata datetime 混合格式("2024-09-26" vs "2024-09-26 00:00:00")
  → pd.to_datetime format='mixed' + SQL substr(datetime,1,10) 比日期(字符串比漏边界)
- 补 TestMixedDatetimeFormat 单测覆盖

验证: VPS 真数据 E2E 全通过(600519在市raw/qfq复权/000005退市治偏差/510300ETF/
fundamentals市值+pe+eps全字段/辅助方法); Mac 37单测+149回归绿

交付: 使用说明 docs/portfolio_local_unified_provider.md(其他 session 直用)+
plan+probe+E2E 脚本
2026-07-23 08:25:44 +08:00
claude_dev 9243f86773 feat(portfolio): LocalUnifiedProvider 辅助方法(Task4) 2026-07-23 08:12:34 +08:00
claude_dev 011c9d4ddb feat(portfolio): LocalUnifiedProvider fundamentals baostock估值+akshare市值(Task3) 2026-07-23 08:11:35 +08:00
claude_dev b46a0c26c8 feat(portfolio): LocalUnifiedProvider 成份股并集治偏差(Task2) 2026-07-23 08:07:37 +08:00
claude_dev 4b4dca2f44 feat(portfolio): LocalUnifiedProvider get_price+前复权(Task1) 2026-07-23 08:06:45 +08:00
claude_dev e12c66acd5 feat(portfolio): LocalUnifiedProvider 代码转换+复权因子(Task0) 2026-07-23 08:05:09 +08:00
claude_dev b270faf4b9 feat(portfolio): 本地数据 provider 层(baostock/local_parquet)
- BaostockProvider: 读 VPS daily_baostock_full(本地,不调online,守 provider-local-data-only 铁律)
- LocalParquetProvider: 读 parquet 兜底,回测117交易日0.4s/月出JSON
- all_weather 策略 + runner_backtest 适配
- 数据源融合使用层(单 Provider 内部路由,见 data-fusion spec §6)
2026-07-22 10:35:23 +08:00
claude_dev 96b1924fd5 feat: 实盘模拟(live) + 组合回测MVP(portfolio)
[live] 实盘模拟 vnpy+miniQMT 直连(supervisor 轮询, 前后端):
- sanguo_live: LiveTradingEngine + AShareCtaTemplate(定寸/禁做空) + runner_supervisor(DB驱动) + persistence(4表WAL)
- sanguo_api/routes_live: 9路由(create/start/stop/positions/trades/account/status)
- frontend live: New/List/Monitor + api/live.ts; config/live.yaml

[portfolio] 组合回测 MVP(BulletTrade, 链路代码完成待验证):
- runner_backtest 加 JSON 入口(--json, BacktestEngine 顶层 import)
- sanguo_api/routes_portfolio: POST /portfolio/backtest SSH 触发 VPS 跑
- frontend PortfolioBacktest.vue + api/portfolio.ts: 表单+结果+净值曲线
- 路由/菜单注册(/backtest/portfolio 组合回测)
- 已知: MVP 链路未端到端验证, agent 改至中途被停; 待 Mac 起服务联调
2026-07-18 20:04:16 +08:00
claude_dev a68cf4905e feat(portfolio): sanguo_portfolio 组合策略框架(BulletTrade+miniQMT,不用jqdatasdk)
把聚宽"全天候轮动"(post48819)搬到 BulletTrade。融合=pip+扩展点注入
(SanguoMiniQmtProvider 继承 MiniQMTProvider 只 override get_fundamentals,
set_data_provider 公开 API 注入, BulletTrade 源码 0 改动)。

- providers: SanguoMiniQmtProvider 补 get_fundamentals(PershareIndex+自算PE/PS/PB/PCF/市值/ROIC)
- strategies/all_weather: 4选股函数+大小盘轮动+ETF兜底+涨停止损(聚宽风格翻译)
- factors(估值/ROIC自算) + filters(ST/涨跌停/次新/停牌)
- 88/88 测试 Mac+VPS 双过; VPS 回测 pipeline 跑通(修9bug:Capital单位/日期格式/百分数口径/11字段alias)
- 实盘 runner_live+runbook 就绪等交易日; DEFAULT_DATA_PROVIDER=miniqmt env 不装 jqdatasdk
- 文档: sanguo_portfolio_plan / portfolio_backtest_result / portfolio_live_runbook
2026-07-18 19:08:18 +08:00
claude_dev e3b688354f fix(data): data_platform硬化(增量merge/verify+raw_redownload/run_daily_update)+测试
merge_increment/verify_increment 增量staging→验证→合并工具; raw_redownload/run_daily_update/import_vnpy_daily 强化; 补 data_platform 与 index_downloader 测试.
2026-07-15 07:12:46 +08:00
claude_dev 0761342baf feat(trader): live bridge token 改从 config 读取,免容器重建
容器 docker run(非 compose)注入 BRIDGE_TOKEN env 需重建容器,风险大。改为 live_cfg.bridge_token 优先、fallback BRIDGE_TOKEN env。run_live_step 每次 load_config,改 config 免重启即生效。

- _shadow_trades_to_bridge + reconcile_from_bridge 两处 token 读取
- config: live.bridge_token 占位空值(真实值填 NAS gitignored config,不入库)
- tests: +6 测试(config优先/env fallback/都无跳过),191 passed
2026-07-13 19:30:58 +08:00
claude_dev c041227139 chore: gitignore放行tests/data+docs/data(老坑data/误伤),补回硬化测试
data/ 规则匹配任意 data 目录,误伤 tests/data(硬化测试)和 docs/data(15min设计文档),
此前靠 git add -f 临时绕。加 !tests/data/ !docs/data/ 例外一劳永逸。
2026-07-13 11:59:39 +08:00
claude_dev 8d55e414fa fix(backtest): A股适配层—定寸/做空拦截/真实费用/口径统一(Phase1+2)
审计发现包装层系统性失真(2 CRITICAL+7 HIGH),vnpy底座可信但A股场景未适配:
- C1 定寸: engine.size=N(满仓手数),策略volume=1手=N股,开平对称(pos归零)
- C2 做空拦截: SHORT+OPEN拒单,long-only,SHORT+CLOSE平多允许
- H3 A股费用: AShareDailyResult重算(佣金保底5元/印花税卖方/过户费沪市)
- H4 收益口径: simple return从balance算(不再用vnpy log return喂empyrical)
- H5+口径: benchmark ffill对齐不缩样本; sizing_shares_per_lot暴露
- H7 退化检测: 零成交/空数据标degenerate不静默done
- H8 task_id: optimize/factor用uuid4(原id()内存地址)
- 静默except改warning

验证: 容器内真实vnpy DoubleMa 600000 2022-2024, total_return 1e-6→42.3%,
end_balance 100万→142万, SHORT+OPEN成交0笔, N=7800股/手.
22 backtest测试全绿(含集成测试), API健康200.
2026-07-12 23:39:45 +08:00
claude_dev 59da65b839 fix(backtest): 结果页指标全显"—" + 任务重启后404
两个根因:

1. relative_metrics 缺字段(routes.py): get_result 的 relative_fields 列表
   漏了 total_return/annual_return/sharpe_ratio/max_drawdown 4 个字段, 导致
   这 4 个指标永远进不了响应 → 前端 MetricCards 显"—"。补全为 11 字段。

2. 任务 task_id 不持久(runner/cta_engine): submit_cta 用 cta_{symbol}_{id(params)}
   (内存地址) 作 runner id, 而 run_cta_backtest 内部另生成 cta_{uuid} 存 DB,
   两者持久层不相交 → 重启后 pool 内存映射丢失, get_result(runner_id) 的
   load_result_by_task_id 查不到 → /result 404 → 前端指标全 0 + 图表无数据。
   改为 submit 前置生成稳定 uuid, 透传给 run_cta_backtest 复用, 使
   runner-id == DB task_id (单一 id, 重启可查)。

附: Dashboard 最近任务"策略/因子"列 min-width 150→190(长策略名不再截断)
测试: test_runner task_id 断言同步新格式
2026-07-11 22:10:48 +08:00
claude_dev 5e5a6cf84a test(backtest): sys.modules mock 改条件式(find_spec)—容器里真模块可导入则不mock,根除collection期污染 2026-07-11 14:17:01 +08:00
claude_dev 41a855d400 test(backtest): 修复test_cta_engine的sys.modules全局污染—加模块级还原fixture(消9个级联失败) 2026-07-11 14:11:49 +08:00
claude_dev 304844903c feat(api): 回测结果API加relative_metrics+基准曲线/风险序列/持仓/日志4端点 2026-07-11 13:48:23 +08:00
claude_dev b9197a8889 feat(backtest): 回测流程集成基准对比—产出相对指标+时序json 2026-07-11 13:44:27 +08:00
claude_dev b50a0f97be feat(backtest): metrics模块—empyrical算10指标+5时序(聚宽同源口径) 2026-07-11 13:27:44 +08:00
claude_dev a1048690c1 test: 前后端对齐—容器复跑验证+清理僵尸测试
- test_main: FastAPI 0.139 _IncludedRouter 不再 flatten,改用 TestClient 探测路由
- datareader: 文件名(sh600000_daily)/patch target(vnpy.trader.database) 对齐 lazy import 实现
- alpha_lab/analyzer/data_adapter: vnpy.alpha/alphalens 容器专用本地 skip
- 删 4 个测废弃 sanguo_web 的僵尸测试(-1267 行死代码)
- pytest.ini: asyncio_mode=auto
- frontend: package.json 加 test script(npm test 可跑)
- NAS 容器 309 passed 全绿验证(Python 3.10,本机 303+6skip)
2026-07-11 11:45:26 +08:00
claude_dev cadc59e6dc feat(bridge): bridge稳定性完善—自动重连miniQMT+health探活+交易日判断
- xt_gateway: _open_session抽离, reconnect(stop旧trader+重建), is_alive(query探活), _retry_with_reconnect(query/order失败重连重试一次)
- query_account/positions/place_order包重试: 断线(异常/None)→reconnect→重试, broker拒单(order_id<=0)不重连
- bridge /health: is_alive真实探活(5s缓存)+断线后台reconnect(不阻塞), 不再假阳性
- trade_calendar: is_trading_day(周一-周五), /order非交易日加warning(120141提示)
- test_gateway15+test_trade_calendar6=NAS21passed, 回归bridge_client/d4a 10绿
- 修复Issue#4运维发现: miniQMT重启后bridge自动重连(无需手动重启)
2026-07-11 07:32:23 +08:00
claude_dev e77c9df0d4 feat(live): D-4c模式B reconcile—bridge回报驱动账本(真桥验证通过)
- bridge_client: from_bridge_code(sh/sz→纯数字码, to_bridge_code逆函数)
- live_orchestrator: reconcile_from_bridge 读bridge /account /positions校正account现金+持仓+持久化, 默认mode_b=false
- live_step step8: 影子后调reconcile(mode_b=true生效, mode_b=false跳过)
- config: live.mode_b开关(默认false模式A)
- test_reconcile: 10例(cash/positions校正+code转换+失败降级+mode_b跳过)
- NAS环境15 passed(reconcile10+shadow5无回归)
- 真桥集成: live_step mode_b=true → reconcile读bridge → account校正(1000万/空仓=bridge真实账本)+持久化
安全: mode_b默认关+bridge失败降级不阻断+token走env
2026-07-11 06:30:08 +08:00
claude_dev 2393097074 test(live): D-4a集成测试(mock bridge真HTTP, sanguo半边4例绿)
- _MockBridge HTTP server模拟bridge(health/order/account/positions+鉴权)
- bridge_client真发HTTP(urlopen+X-Bridge-Token)+响应解析+错token 401返回None
- _shadow_trades_to_bridge真HTTP链路:注入成交→真POST→mock收到下单+symbol转sh600000→paper_shadow_orders记录
- NAS容器4 passed
- D-4a sanguo半边端到端验证(真HTTP); miniQMT真报单另半边待Windows bridge实跑
2026-07-11 05:41:43 +08:00
claude_dev 38f5635b59 test(live): D-3持久化测试(bridge_client+影子下单集成, NAS环境11例绿)
- test_bridge_client: to_bridge_code转换(sh/sz/前缀) + HTTP mock(成功/失败返回None不抛)
- test_shadow_orders: 幂等persistence + enabled跳过 + 正向记录+symbol转换 + 幂等不重复 + bridge失败不阻断
- NAS容器真实环境(Python3.10/pytest) 11 passed
- 补 D-3 测试空缺(Sub Agent临时自测未沉淀成持久测试)
2026-07-11 05:35:20 +08:00
claude_dev 164690373f feat(trader): C期分期项收尾—资金占用成本+分红送股+_restore_ledger修复
- 资金占用成本(spec§195): StrategyRunner.daily_borrow_cost(used×risk_free/365)
  归因per_strategy_pnl(不碰account总账, account.equity真实净值不变);
  config risk_free_rate=0.02; engine.step mark_to_market后计扣; =0向后兼容跳过
- 分红送股(spec§295): dividend_source.py(akshare stock_history_dividend_detail,
  实测600000/000001纯现金分红); PositionLedger.apply_split(volume×factor/avg÷factor);
  Account.apply_cash_dividend; engine._apply_dividends(除权日调整,现金先split后);
  mark_to_market停牌prev_close兜底(今收→前收→均价); _run_replay注入dividends日历
- 修_restore_ledger预存bug: PositionLedger.__init__加volume/frozen/avg_price参数
  (原只symbol, live_orchestrator跨日恢复4参数调用会TypeError, 首次step空仓未暴露)
- 139 passed(119基准+20分红+3占用成本), 无回归
- live_step dividends注入待分期项(每日拉全市场分红慢, 需run_daily_update预拉日历)
2026-07-10 08:44:35 +08:00
claude_dev 252deb5ec7 feat(api): paper positions/pending 端点(实走监控用,Phase 3c Live页后端基础) 2026-07-09 22:33:35 +08:00
claude_dev 193064c953 feat(trader): 软限额max_allocation(分期项)—每策略资金额度消除顺序依赖
spec §195: 多策略并发下单"先到后到"不可复现 → 每策略独立max_allocation
- StrategyRunner: max_allocation字段(默认inf) + used_allocation(持仓市值)
- engine._match: BUY cash_enough后查 used+成交额>max_allocation → 拒单max_allocation_exceeded
- live_orchestrator: runner传max_allocation(默认initial_capital)
- routes_paper: StrategyCfg加max_allocation(API→DB→live_step数据流)
- test_soft_limit: 3测试(累计超限拒单/默认不限/SELL不受限)

116 passed(113旧+3新), 无回归.
2026-07-09 22:05:35 +08:00
claude_dev 0810259911 feat(data): 15min双源集成(data_source路由 + baostock格式适配)
- _resolve_dir_key: 15min支持raw/qfq双源(移除raw15min抛错)
- _check_adjust_cfg: 按interval查dir(minute_15_raw_dir/qfq_dir)
- read_parquet_15min: datetime列优先(baostock时分,旧date兼容)
- config: 加 minute_15_qfq_dir/minute_15_raw_dir
- test: 更新raw15min路由断言(6 passed)

实测容器: 600000 15min qfq 336bars close6.1977 / raw 336bars close6.6200,
datetime时分正确(09:45:00). 15min双源分期项落地.
2026-07-09 19:34:43 +08:00
claude_dev c6b19f4244 feat(data): 恢复双源(task#79)—撮合raw+策略qfq, 分红除权准确
用户要模拟=回测准确: raw除权缺口致MA假信号, 必须双源。
- data_source: qfq→qfq_dir(干净qfq), raw→raw_dir; _check_adjust_cfg(cfg提供才校验)
- engine 双bar流: step(raw_bars,qfq_bars)撮合/盯市raw+策略on_bar qfq; run zip(raw,qfq)
- live_orchestrator: warmup用qfq(信号am); 去adjust参数(双源固定)
- raw_redownload --adjust(''raw/'qfq'); config qfq_dir
- 113/113通过
2026-07-08 07:21:33 +08:00
claude_dev ab703e93ba feat(matcher): 集合竞价CALL_AUCTION撮合(分期项)—开盘价(最大成交量原则→open)
CALL_AUCTION枚举原拒单(unsupported), 现撮合用开盘价(open, 集合竞价确定开盘价).
当前定价同NEXT_OPEN(均为open); 未来区分开盘/尾盘集合竞价需扩枚举.
test: call_auction fills@open(原rejected用例改).
2026-07-08 07:02:51 +08:00