Commit Graph
342 Commits
Author SHA1 Message Date
Justin Gu 90f2595d3d release: v1.27.2 — 市场异动类型解析补齐(#62):盘中强弱/竞价尾盘/急速拉跌/竞价试盘方向 2026-09-02 10:44:20 +08:00
Justin Gu 4dde339f26 fix(mac): 异动 0x1237 补齐 0x15/0x16/0x1D/0x1E 解析并修正 0x13 试盘方向(#62)
- 0x16 盘中强势/弱势:09:25 样本 v2 与开盘涨幅 49/49 精确一致,v1 为带符号 ±1~3 级强弱等级
- 0x15 竞价/尾盘异动:09:25 与 15:00 双时刻触发,desc 按时刻区分竞价/尾盘前缀
- 0x1D/0x1E 急速拉升/急速下跌:阈值下限 ±0.6%
- 0x13 竞价试盘:v1=0x00 试买/0x01 试卖(552 条对照昨收 549 条一致),旧实现试卖方向描述错误
- 新增顶层常量 UNUSUAL_TYPE_NAMES(19 种类型码→名称)
- 协议探索结论与完整证据链文档化 docs/protocol-unknown-fields.md §3.4
- 测试 test_unusual.py 21 例(全部真实抓包字节 fixture)
2026-09-02 10:44:09 +08:00
GitHub 1aacce9b7d release: v1.27.1 — 回测页附加分析布局修复 + WebSocket 实时推送收录 2026-09-01 23:30:02 +08:00
GitHub b693af5ca2 fix(web-ui): 附加分析开关复选框被全局 input{width:100%} 撑满整行导致文字折行
根因:全局样式 input,select,textarea{width:100%}(style.css:75)把勾选框也撑满
整行(实测 156px),文字被挤到下一行、窗口数控件块状堆叠。修复:
- 复选框显式 width:auto 恢复原生 13px(.check-label input[type=checkbox])
- 布局重写为单行 flex:勾选框+文字+窗口数同行,white-space:nowrap 不折行,
  显式覆盖全局 label{display:block} 与 margin
- 实机验证(Chrome CDP):复选框 13px 靠左、两行均单行(行高 18-21px)、
  窗口数输入框 44px 同行跟排、区域零横向溢出
2026-09-01 23:26:39 +08:00
GitHub e58de789ab feat(ws): /ws/realtime 接通 RealtimeDataFeed — 按需轮询 hub + fan-out + 冒烟脚本
- web/realtime_hub.py:RealtimeStreamHub——订阅集合变化按需启停 RealtimeDataFeed,
  无人订阅完全停止轮询(对齐 QuoteStreamer 节能);EventBus → 每连接队列
  fan-out(丢最旧保最新);去重标的上限 80;lifespan 挂载/关闭
- routers/realtime.py 重写:连接即订阅、断开退订、subscribe/unsubscribe 控制帧、
  30s ping 心跳;单一写者泵模型(全部出站帧经队列串行,防并发 send 交错)
- 修复 RealtimeDataFeed stop-before-start 竞态(_stop_requested 标志),
  补 2 个回归用例;hub 单测 10 例(并发订阅/退订竞态、跨标的串扰、背压、
  TestClient 端到端)
- 前端接入选文档方案(api_reference.md 协议+重连/心跳骨架;README 撤「未联动」):
  SSE 已覆盖看板/自选实时刷新,WS 定位按需单标的 tick,双通道冗余无必要
- scripts/ws_smoke.py:手动冒烟(mock 模式实测 tick 帧与动态订阅确认可见)
2026-09-01 23:21:42 +08:00
GitHub bec231e8be test: 修复时段门控用例的时间相关 flaky(当地 23 点档必炸)
test_outside_session_no_fetch 此前用固定 23:00-23:59 模拟盘外时段,
但当测试跑在当地 23:00-23:59 时当前时刻恰好落在该时段内,断言必炸(本地
23 点后全量回归复现)。改为动态构造「当前时刻 +5~+6 分钟」的 1 分钟未来
时段,任何时刻运行都保证盘外。1263 全量通过。
2026-09-01 23:05:14 +08:00
GitHub 697fad71e1 docs: README 介绍补充 v1.24~v1.27 能力,CHANGELOG 补录 1.27.0 修复与文档
- README:介绍部分新增「防过拟合验证链」「通达信公式 + 轮动组合」「本地 K 线数据仓库」
  三段;回测可视化段补充附加分析开关;CLI 参考新增 --wf/--evaluate 示例与
  formula / warehouse 两个命令组小节
- CHANGELOG:1.27.0 条目补录「修复」(CI UP038 版本漂移、任务状态跃迁锁内
  落盘竞态)与「文档」两节
2026-09-01 23:01:42 +08:00
GitHub 211052f0e3 feat(e2e): Playwright E2E 前端测试基建 — 后端合成数据 mock 模式 + 5 用例 + CI
- web/e2e_mock.py:EASY_TDX_E2E_MOCK=1 时 lifespan 替换 TDX/MAC 客户端为
  确定性合成数据(CRC32 播种随机游走,分页语义对齐真实 /bars);回测/WF/
  评估/自选/策略库仍走真实后端,SSE 由 QuoteStreamer 真轮询(mock 下 2s 一拍)
- web-ui:@playwright/test + playwright.config.ts(webServer 自动起 serve,
  EASY_TDX_CONFIG_DIR 指向每轮临时目录);e2e/ 5 用例覆盖看板五指数/SSE、
  自选增删、回测全流程、附加分析(WF 柱状图+一条龙评估卡)、策略库保存
- package.json 加 test:e2e;CI frontend job 追加 E2E;verify_ci.sh 补前端段
- tests/unit/test_e2e_mock.py(11 例)守护 mock 契约;本地 npx playwright test 全绿
2026-09-01 22:56:38 +08:00
GitHub 6b67885588 fix(web): 任务状态跃迁在锁内先落盘再对内存可见,消除 done/running 竞态窗口
CI(ubuntu 3.12 + coverage 插桩)复现:_run 在锁内置内存状态为 done 后才在锁外
persist SQLite,测试轮询到内存 done 立即读库仍见 running。慢速环境下窗口被
放大。修法与 submit 的 pending 写法对齐:running/done/failed 三次跃迁均在
同一把锁内先 _persist_locked 再改内存字段,状态对外可见前必已落盘(本地
15 次压测 + 1252 全量回归通过)。
2026-09-01 22:46:01 +08:00
GitHub 561e2d3e61 fix(lint): isinstance 元组写法改 PEP 604 联合类型,修复 CI 的 UP038
CI 经 requirements-dev.txt 锁定 ruff==0.11.11(UP038 仍生效),本地 0.16.4 已移除该
规则导致漏检。10 处 isinstance(x, (A, B)) 统一改为 isinstance(x, A | B)(项目
requires-python>=3.10,联合类型 isinstance 合法;strategy.py 已有同风格先例)。
已用 CI 同版本工具链本地复验:ruff 0.11.11 check/format、mypy 2.1.0 strict、
1252 单测全过。
2026-09-01 22:36:37 +08:00
GitHub 917295edaf release: v1.27.0 — 通达信公式解析器三通道 + 轮动组合引擎 + 回测页WF/评估开关 + Docker 部署
升级计划 P3 + P4(部分)。全量 1252 单测、ruff/mypy strict、前端 vue-tsc+vite build 全绿。

- 通达信公式解析器(formula.py):自建 tokenizer + 递归下降 AST + 30+ 函数白名单求值
  (不走 Python eval);命名布尔输出=信号列、数值输出=排名列;除零→NaN、预热期不出信号
- 公式三通道:CLI easy-tdx formula compute|screen|backtest;REST /formula/validate|compute|
  backtest|screen(run/async);Python API run_formula_backtest(买/卖列自动挑选)
- 轮动组合引擎(rotation.py):排名定期换仓(打分只用截至当日数据)、槽位等额、
  跌出排名自动补位、日/周/月刷新、槽内止盈止损;momentum_score/formula_score 打分;
  REST /backtest/rotation/run/async
- 回测页附加分析开关(Web UI):勾选后随回测并行跑 WF(逐窗红涨绿跌柱状图+汇总卡,
  窗口数 2~12)与一条龙评估(评分分项条/高适配徽标/买入持有对比/8 项适配检查);
  新增 WalkForwardPanel/EvaluatePanel 组件与 store runWalkforward/runEvaluate;
  WF 端点 ?n_windows= 透传;修复报告 numpy 标量 REST 400(源头清洗)
- Docker 部署(Dockerfile + docker-compose.yml,/data 卷 + 健康检查)与
  scripts/verify_ci.sh 一键门禁
- 升级计划文档 docs/upgrade-plan-2026H2.md(四阶段全部完成 + 诚实实测数据)
- 未做(独立排期):Playwright E2E、WebSocket 实时联动、引擎逐 bar 向量化
2026-09-01 22:17:57 +08:00
GitHub 9569b2653c release: v1.26.0 — DuckDB K线仓库 + provisional 状态机 + 增量同步 + 健康自检
升级计划 P2(P2-2 评级后端化已随 1.25.0 提前交付):把碎片化缓存升级为统一数据底座,
对齐 indicator-lab 自建 DuckDB 层的定位。

- warehouse/ 包:DuckDB 单文件(~/.easy_tdx/warehouse.duckdb,随 EASY_TDX_CONFIG_DIR),
  列存 + 主键 upsert 去重;duckdb 为可选依赖(pip install easy-tdx[warehouse])惰性导入
- provisional/completed 状态机:15:05 前当日 bar 逐行标记临时,查询/回测默认忽略,
  promote_provisional 过期转正,include_provisional 显式可见
- 增量同步器:首同步全量(默认 8000 根)此后只补尾部 15 根,批次进度回调、单标失败不中断
- 健康自检:疑似缺口/异常跳变(复用 QFQ 对拍跳空检测)/最新度/provisional 统计
- CLI easy-tdx warehouse sync|query|stats|check(sync 支持 @文件标的列表)
2026-09-01 22:17:28 +08:00
GitHub b2509a8d0e release: v1.25.0 — Walk-Forward/适配性/一条龙评估防过拟合链 + 评分评级后端化 + 寻优加速
升级计划 P1:补上两个下游项目都在自研的样本外验证空白。

- Walk-Forward 引擎(walkforward.py):7 窗样本外、每窗独立开仓(backtest-system v1.2.1 踩坑语义)、
  上下文预热不污染;CLI --wf、REST /backtest/wf/run/async
- 适配性评估(fitness.py):train/valid/test 三段 + 8 项可解释检查 + 高适配标记;
  evaluate_prefix 滚动过滤原语(无未来泄漏)
- 一条龙评估(benchmark.py evaluate_strategy):回测+WF+适配性+评分+评级+买入持有基准对比;
  CLI --evaluate、REST /backtest/evaluate/run/async
- 综合评分(scoring.py,收益50/夏普15/回撤10/Sortino5/WF20)+ 评级后端化(grading.py,
  前端 TS 忠实移植,REST 响应新增 grade/score 字段)
- 多 seed 验证 + 四项晋级门槛(validation.py);REST /backtest/multiseed/run/async
- 寻优两段式加速:IndicatorCache(36 点网格命中率 41.7%)+ workers 进程并行(实测约 2x);
  诚实注:指标缓存墙钟 ~1.01x,瓶颈在逐 bar 循环,后续向量化
- strategy.I() 指标缓存钩子 + 数据代理零拷贝(astype copy=False);
  types.to_json_native 统一 numpy 清洗
2026-09-01 22:17:08 +08:00
GitHub 1fc1d00c90 release: v1.24.0 — QFQ 对拍验证体系 + 回测任务持久化 + 品种感知费率
升级计划 P0(docs/upgrade-plan-2026H2.md)。源自 backtest-system / indicator-lab 两个下游项目的逆向调研。

- QFQ 对拍验证:公式法(NONE+XDXR)与跳空检测法(板块感知涨跌停阈值)双证据链互检,
  检出负价/残留跳空/方向反演/XDXR 缺记录四类问题,接入 MAC 同步/异步客户端(mac/qfq_check.py);
  含茅台式多重分红、浦发式送转方向合成案例回归(13 用例)
- 回测任务 SQLite 持久化:~/.easy_tdx/tasks.db 双写内存 LRU + 磁盘(保留 500 条),serve 重启不丢;
  重启恢复中断任务标记;GET /backtest/tasks/{id}/export?format=json|csv 导出端点
- 品种感知费率:ETF/可转债免印花税等法定差异(backtest/fees.py),CLI --auto-fees、
  REST auto_fees 字段、组合引擎逐标的解析(34 用例)
- 修正 avg_holding_days 过时注释(实现早已是 FIFO 真实口径)
- tests/conftest.py 默认 EASY_TDX_NO_TASK_DB=1 防止单测污染用户任务库
- 注:engine/cli/routers/schemas 为跨版本累积态,后续版本提交继续演进
2026-09-01 22:16:44 +08:00
GitHub 6412880dbe release: v1.23.3 — serve --no-ui 纯 API 模式 + 概念冷榜修复 + 文档截图 2026-09-01 18:37:00 +08:00
GitHub ac4ac6b2aa feat(cli): serve 新增 --no-ui 纯 API 模式
- --no-ui:不托管 Web UI 前端(根路径 404),仅 /api/v1/* REST 接口,
  且不自动打开浏览器——给 AI Agent / 程序化调用省去前端资源
- create_app/_create_app 新增 enable_ui 参数(默认 True 行为不变)
- 测试锁定:no-ui 模式无 web-ui Mount 且 API 路由完整;四道门禁全绿
  (ruff / ruff format / mypy / 1079 tests),实测根路径 404 + API 正常
2026-09-01 18:33:49 +08:00
GitHub 9d206c121b docs: README 三通道介绍下方新增 CLI 输出示例截图(cli-page-1) 2026-09-01 18:28:08 +08:00
GitHub 62ae92e7f0 docs: README 数据评级系统段落下方新增评级徽章截图(web-ui-page-5) 2026-09-01 18:25:04 +08:00
GitHub 8b20bbe17e docs: README 简介区行情终端段落下方新增看板截图(web-ui-page-4) 2026-09-01 18:22:04 +08:00
GitHub b65870fa79 fix(webui): 概念板块冷榜拉全量——120 截断致冷榜全为正值板块
概念板块约 269 个,降序拉 120 时第 113-120 名仍在 +0.7% 附近(正值),
slice(-8) 展示的是'涨幅第 120 名附近'而非跌幅榜。行业板块仅 86 个,
120 已全量故表现正常。概念改为拉 500(MAC 分页 150/批共 2 页请求,
实测尾部 -2.35%~-3.83% 恢复真冷榜)。
2026-09-01 18:10:42 +08:00
GitHub 6bfe5a2cee release: v1.23.2 — 1.23.1 质量门禁补丁(mypy strict + ruff 全绿) 2026-09-01 18:02:40 +08:00
GitHub dffcd9f792 style: ruff format 三文件重排(quote_streamer/watchlist_store/test_ex_tick_chart_date)
本地预演 CI 全部四道门禁通过:ruff check / ruff format --check /
mypy (226 files) / 1078 tests
2026-09-01 17:58:12 +08:00
GitHub 6fe3f0f8c4 fix: 补齐 CI 质量门禁——mypy strict 类型标注 + ruff 行宽/导入排序
- watchlist_store/routers/watchlist: 裸 dict 补泛型参数 dict[str, object]
- routers/stream: event_gen 返回 AsyncGenerator[str, None] 注解
- app: _watch_symbols 返回类型;teardown 变量改名消除类型冲突
- ruff: 5 处超长行拆行、3 处导入排序自动修复

ruff / mypy (226 files) / 1078 tests 全绿
2026-09-01 17:52:36 +08:00
GitHub a79861af76 release: v1.23.1 — 行情终端 WebUI 重大升级 2026-09-01 17:45:12 +08:00
GitHub 33d44ba5d6 feat(webui)!: 行情终端重大升级——市场看板/自选/个股与板块弹窗 + SSE 实时推送
展示层对标专业看盘终端(tick-stock-panel 模式),数据全部来自通达信协议直连:

- 市场看板:五大指数实时条(迷你分时)、涨跌统计、四维情绪雷达、
  全市场涨跌分布直方图(约 5500 只 22 桶、鼠标跟随浮窗)、涨停雷达、
  行业/概念热冷榜(可下钻)、涨幅/跌幅/成交额/换手四联排行榜、异动雷达
- 自选行情:6 位代码即加(symbol-info 自动取名)、SSE 实时全表刷新、
  行内迷你分时;SQLite 持久化(watchlist.db,幂等加删)
- 个股弹窗:五档盘口 + 1/3/5 日分时 + 日K(MA/BOLL/EMA + MACD/KDJ/RSI
  前端本地计算)+ 一键加自选 + 一键全策略寻优
- 板块弹窗:板块分时/日K + 成分股涨跌榜直达个股
- SSE 架构:QuoteStreamer 单循环轮询 fan-out(独立队列 + 背压丢旧、
  无订阅休眠、盘中 8s/盘外 60s 降频);前端 pinia 单连接指数退避重连
- fix(codec): SH 000 系大盘指数与 881/885 板块指数报价按 2 位小数解析
  (曾缩小 10 倍:科创50 1647.53→164.753);880 统计指数保持 3 位
- fix: 前端批量五档路径 /security/quotes→/quotes、SSE 五档白名单
  bid1_vol→bid_vol1、MAC 排行 close 列归一化、日K v-if 撑满、
  寻优查看跳转 /→/backtest、FastAPI 0.141 _IncludedRouter 测试适配

新增 27 个单测(streamer/自选/小数位语义),全套 1078 passed
2026-09-01 17:45:07 +08:00
毛利哥andGitHub 12d308e1a4 fix!: 扩展日线槽位语义——第6槽为 float32 成交额,移除误导字段 hk_stock_amount(#57) (#61)
read_ex_daily_bars 原把第7槽(成交量)同时赋给 amount 与 vol(amount=vol),
真实成交额藏在第6槽 float32 重解释值里、以 hk_stock_amount 之名暴露。
实测对照:47#IF300 2023-09-11 第6槽 float32=186871758848(元)、第7槽
uint32=105358016(手),与标准市场 sh000300.day 同日 amount/vol 完全一致。

- _EX_DAILY_FMT: <IffffIIf → <IfffffIf,第6槽正名为 float32 成交额
- ExDailyBar.amount: int → float;移除语义错误的 hk_stock_amount 字段
- encode 端沿新 fmt 自动正确:大成交额按 float32 编码,不再溢出 uint32
- CLI offline ex-daily 输出新增 amount 列
- 新增真实样本回归测试(47#IF300 原始记录)+ 补齐缺失的 amount 往返断言

Co-authored-by: GitHub <action@github.com>
2026-08-31 20:43:35 +08:00
GitHub 6c30b72e29 release: v1.20.13 — sync-daily 成交量单位修复(#60):股→手换算,修复大盘股 uint32 溢出 2026-08-31 20:28:32 +08:00
ac512d791c fix(offline): sync-daily 成交量单位换算(股→手),修复大盘股 uint32 溢出 (#60)
encode_daily_bar 按 vol/vol_coeff(vol_coeff=0.01 时 ×100)写入 .day,
期望输入为手;而 K 线协议返回的成交量单位是股。原 _sync_one_daily 直接把
股喂给 append_daily_bars,日成交 >4295 万股的股票(招商银行等)编码后
溢出 uint32,struct.pack('I') 报错,sync-daily/sync-all 失败。

在 vol_coeff == 0.01 的证券类型写入前换算 股→手(读取端 ×0.01 还原,
方向对称)。用服务器原生 .day 文件实测验证:浦发银行 2026-08-31 原始
vol 字段 99,682,464 与协议 API(股)完全一致。

Co-authored-by: awayings <awayings@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-31 20:28:15 +08:00
GitHub a7b14d3388 release: v1.20.12 — ex tick --date 修复(#56):接受 YYYYMMDD 整数,对齐 A 股日期语义 2026-08-28 22:41:24 +08:00
毛利哥 0b349e67f4 Merge pull request #56 from Harveyliu007/main
fix(ex): ex tick --date 接受 YYYYMMDD 整数,修复 AttributeError
2026-08-28 22:38:31 +08:00
GitHub 05db452cc0 release: v1.20.11 — 资金流口径标注(#55):聚合逐笔成交额分档,实质为主动买卖总失衡 2026-08-28 20:14:41 +08:00
GitHub 1b7d8a5fff docs(client): 资金流口径标注——0x0fb5 逐笔为聚合记录、按成交额分档,与东财主力净额不可比(issue #55) 2026-08-28 20:14:41 +08:00
Harveyliu007 349f44f959 fix(ex): ex tick --date 接受 YYYYMMDD 整数,修复 AttributeError 2026-08-28 19:53:51 +08:00
GitHub 31599a6e13 release: v1.20.10 — 板块列表涨速恒 0 修复(#53):暴露 sort_column 排序键 2026-08-26 15:21:53 +08:00
GitHub fd53ec4c90 fix(mac): 板块列表涨速恒 0——值槽实为排序键列值,暴露 sort_column(issue #53)
抓包+对值锚定确认:0x1231 响应中 price 与 pre_close 之间的 float 是
"当前排序列的值"(板块与领涨股各一份),并非固定涨速;此前硬编码
sort_column=0(涨跌幅,仅排序键、值槽恒 0),故该列永远全 0。

排序列映射(实测):0=涨跌幅(值槽恒0) 1=涨速 2=3日 3=20日 4=60日
5=年初至今 6=5日 7=10日。

- 新增 BoardSortColumn 枚举并公开导出;get_board_list(sync/async)
  新增 sort_column 参数,取涨速传 SPEED,默认涨跌幅排序行为不变
- BoardInfo 字段更名 rise_speed→sort_value、symbol_rise_speed→
  symbol_sort_value(旧名语义错误且恒 0,属破坏性更名)
- Web /board-mac/list 新增 sort_column 参数;CLI board-list 新增 --sort
- 新增 9 个测试;README 示例更新
2026-08-26 15:21:35 +08:00
GitHub 8c35a864e8 release: v1.20.9 — 历史资金流修复(#52):当日主力净额盘中可取 + main_net_inflow 列 2026-08-26 15:01:50 +08:00
GitHub 574ffdd2a4 fix(client): 历史资金流当日行全零 + 主力净额列缺失(issue #52)
三个根因(全部实测核实):
1. Category 22 直连接口为虚构协议——52 台已知服务器中 46 台可达的
   全部仅回 2 字节空包,从未成功过;移除死代码与臆造解析格式。
2. 历史逐笔接口当日数据要收盘清算后才有,日 K 盘中已含当日 bar,
   故 start=0 的最新一行恒为全 0——当日 bar 改走当日实时逐笔接口。
3. main_net_inflow 此前仅为 dataclass property,asdict 静默丢弃,
   返回 DataFrame 无主力净额列——新增 _fund_flow_df_with_net 物化
   (history 紧随 date 列、当日快照放首列)。

sync + async 双客户端同步修改;更新示例与三份文档;重写/新增回归
测试(当日实时逐笔路径、主力净额列断言)。
2026-08-26 15:00:53 +08:00
GitHub 7c9e19de93 release: v1.20.8 — 信号雷达:一键扫描全部已保存策略买卖信号 2026-08-21 16:57:16 +08:00
GitHub 9336273f17 feat(web): 信号雷达——一键扫描全部已保存策略的最近买卖信号
新增导航页 /signals:把策略库 single/portfolio/multi 策略统一展开成
"策略×标的"子任务,按标的去重取最近 800 根 K 线,用与回测引擎同口径的
逐 bar 信号流程(含仓位跟踪)判断最近 N 根(窗口 1/3/5/10 可选,默认 5)
的买/卖信号,汇总卡片 + 筛选 tab + 明细表展示;上次结果缓存 localStorage。

- 后端 signal_scan.py(展开/去重取数/信号评估/汇总)+ POST /backtest/signal-scan/run/async
- 只扫信号不重跑回测,不改写策略库业绩快照;单行失败(未知策略/停牌/参数非法)不中断整批
- normalize_symbol 按代码段纠正历史错标市场前缀(与前端 detectMarket 同规则)
- 新增 20 个单测(含与回测引擎成交序列一致性对照),全套 1030 个单测通过
2026-08-21 16:57:12 +08:00
GitHub 67a5e1d08a release: v1.20.7 — /bars MIN_1 时间修复(#49)+ 寻优参数语义约束(#39)+ 依赖安全升级 2026-08-19 19:06:59 +08:00
毛利哥 8eaafb93ee Merge pull request #51 from handsomejustin/fix/issue-39-param-order-constraints
fix(backtest): 策略参数加跨语义约束,杜绝寻优选出快慢倒挂组合(issue #39)
2026-08-19 19:05:42 +08:00
GitHub 1d3d45828d style: ruff format 调整新增测试换行 2026-08-19 19:00:42 +08:00
GitHub 3180ab3002 fix(backtest): 策略参数加跨语义约束,杜绝寻优选出快慢倒挂组合(issue #39)
寻优器网格探索不区分语义:ma_cross 的 fast∈[5..60] × slow∈[10..250]
笛卡尔积包含 {"fast":30,"slow":20} 这类倒挂组合,倒挂的双均线交叉
本质是反向策略,回测成绩可能反而突出,从而被选为"最优参数"展示
(即 issue #39 截图中 {"fast":30,"slow":20} 的来源)。presets.py 的
注释"快线<慢线才有意义;去重无效组合"早已写下意图但从未实现。

根因是参数校验只有单参数 min/max,缺跨参数语义约束:

- ParametrizedStrategy 新增 param_constraints 类属性 [(a,b), ...]
  表示要求 a<b,在 __init__ 解析后统一校验,报错带中文标签;
  语义约束不受 skip_bounds 影响(寻优跳过的只是数值边界)
- 7 个策略声明约束:ma_cross/ema_cross(fast<slow)、macd
  (short<long)、triple_ma(short<mid<long)、rsi_reversal/cci/
  wr_reversal(oversold<overbought)
- 寻优器 build 阶段的 ValueError 降为 info 级跳过(无堆栈噪音),
  回测异常仍走 warning;预设网格 36 组合 → 有效 25 个
- 新增 10 个回归测试(旧代码全失败、新代码全通过)
2026-08-19 18:55:48 +08:00
毛利哥 cc17ab650d Merge pull request #50 from handsomejustin/fix/issue-49-bars-min1-daily-plus
fix(web): /bars MIN_1 被误判为日线,datetime 归一化为 date 00:00:00(issue #49)
2026-08-19 18:26:49 +08:00
GitHub fc63873717 style: 拆行修复 ruff E501(test_web_api.py 超长行) 2026-08-19 18:22:49 +08:00
GitHub bcaee1404d fix(web): /bars MIN_1 被误判为日线,datetime 归一化为 date 00:00:00(issue #49)
KlineCategory 枚举值不按周期长短排序(MIN_1=7、MIN_3=8 均大于 DAY=4),
/bars 的 MAC 路径用 int(cat) >= int(KlineCategory.DAY) 判定"日线及以上",
把 1 分钟线误判为日线,_normalize_mac_df 因此将 datetime 截断为 00:00:00
并把列名改为 date。回退 TdxClient 路径与 MAC symbol_bar 均为显式判定,
仅此一处用整数比较,故只有 MAC 路径复现。

改为 _is_daily_plus() 查表判定(复用 _df._CATEGORY_MINUTES,与回退路径
同一判定源),保证两条路径 date/datetime 语义一致。新增表驱动单测 +
端点级回归测试(假 MAC 客户端,在未修复代码上失败、修复后通过)。
2026-08-19 18:19:07 +08:00
毛利哥 e533b705d4 Merge pull request #48 from handsomejustin/fix/dependabot-postcss-nanoid
fix(web): 升级 postcss/nanoid 修复 Dependabot 安全告警
2026-08-14 17:35:29 +08:00
GitHub 01a9823ed4 fix(web): 升级 postcss/nanoid 修复 Dependabot 安全告警(alerts #2 #3 #5)
- postcss 8.5.16 -> 8.5.26:修复路径遍历读取任意 .map 文件(GHSA 高危)及后续不完整修复
- nanoid 3.3.15 -> 3.3.18:修复非安全生成器负数 size 死循环(高危)

均为 vite / @vue/compiler-sfc 的 semver 兼容间接依赖,仅更新 lockfile;
npm run build 验证通过。
2026-08-14 17:30:13 +08:00
毛利哥 513ee15c83 Merge pull request #46 from handsomejustin/feat/issue-43-bars-mac-adjust
feat(web): /bars 迁移到 MacClient + 支持复权(issue #43)
2026-08-05 16:44:00 +08:00
GitHub 5a3ad15477 feat(web): /bars 迁移到 MacClient + 支持复权(issue #43)
Web 获取 K 线此前用 AsyncTdxClient.get_security_bars(标准协议不支持复权),
导致 REST API 无法取前复权/后复权数据。将 /bars(个股 K 线)迁移到
AsyncMacClient.get_stock_kline(MAC 协议,支持 NONE/QFQ/HFQ + QFQ 负价兜底),
保持旧输出契约不变(日线 date 列、分钟线 datetime 列、OHLC 顺序、无 float_shares),
新增 adjust 参数(默认 QFQ),MAC 主机不可用时自动回退标准 TdxClient。

⚠️ 半破坏性变更:/bars 默认复权从"不复权"改为 QFQ(前复权)。老调用方若需
不复权请显式传 ?adjust=NONE。输出 DataFrame 列名/顺序/字段与旧版完全一致
(_normalize_mac_df 规整),仅价格数值因复权变化。

改动:
- bars.py:/bars 改走 mac_client.get_stock_kline(adjust=...),MAC 不可用回退
  get_security_bars(无复权 + warning);新增 _normalize_mac_df 规整输出契约;
  新增 adjust 查询参数(默认 QFQ)。
- convert.py:period_times_from_category(KlineCategory→Period 映射,显式处理
  YEAR 9→YEARLY 11、SEASON→QUARTERLY)+ adjust_from_str。
- deps.py:get_mac_client_optional(未连接返回 None,供回退判断)。
- schemas.py:AdjustEnum(OpenAPI 文档用)。

测试:新增 7 个(period_times 映射全表 + 不可映射值、adjust 转换、_normalize_mac_df
日线/分钟线/空 df)。全套 996 passed;ruff format/check + mypy 改动文件零错误。

不在本次范围:/bars/index(指数 K 线,MAC 另一套接口)、/minute、/transaction*
(MacClient tick_chart 语义不同,暂不迁移)。
2026-08-05 16:37:10 +08:00