Commit Graph
171 Commits
Author SHA1 Message Date
Justin Gu 610092f968 test: ex_reconnect 测试密闭化 — 屏蔽跨主机故障转移的真实网络探测
TestMacExClientReconnect::test_all_retries_relogin_each_time 在部分机器
login 计数 5 != 4:4 次退避重试耗尽后 _execute 会走跨主机故障转移
(select_best_host_sync 真实测速),找到可切换主机时会再 connect+login
一次——测试未 mock 该阶段,断言结果依赖运行环境的网络可达性。

- 新增 autouse 夹具把 ex.client / ex.mac_client 的 select_best_host_sync/async
  统一 patch 为"找不到新主机",全部用例密闭化、不再发真实探测
- 新增 test_failover_stage_relogs_in_and_raises:显式钉住故障转移找到
  新主机时也必须 _login()(4+1=5 次)且仍失败则抛出的语义
2026-09-05 11:42:04 +08:00
Justin Gu aead122478 feat: 涨停生态 vipdoc 路径可配置 — 应用级 KV 设置 + 页面配置行
- 新增 AppSettingsStore(~/.easy_tdx/settings.db,KV 持久化,重启不丢)
- GET/PUT /settings/vipdoc:读取/保存本地 vipdoc 目录(保存即校验目录
  存在,并清空涨停生态/历史缓存,下次扫描立用新路径;空串=恢复自动检测)
- vipdoc 解析优先级:显式参数 > 已存设置 > 自动检测(TDX_HOME/常见路径)
- /limitup-ecology 响应携带 vipdoc_path 供页面展示生效目录
- 涨停生态页头部新增路径配置行(输入框+保存+恢复自动检测),
  未检测到数据的空态文案引导填写
2026-09-05 11:24:48 +08:00
Justin Gu 0a205ffbf3 feat: 盘面洞察第四批 — 板块相关性热力图 + 异动雷达 + 量能仪表盘
- 相关性 /hotspots 页内新增「相关性」视图:/board-mac/hotspot-correlation
  复用热点历史矩阵缓存,对窗口内活跃板块(每日前 per_day 名并集,按上榜次数
  取前 N)两两算日涨跌幅 Pearson 相关;红=同涨同跌(抱团)、绿=跷跷板(轮动),
  ECharts 热力图 + 双向色阶 visualMap;无缓存时透传 building 状态
- 异动雷达 /radar:沪深异动流时间线(封板/炸板/大笔买入/逼近涨停…),
  每分钟异动密度柱 + 类型筛选 chips(带计数),行点击直达个股弹窗,15s 轮询
- 量能仪表盘并入市场情绪页:两市(上证+深成 5 分钟线)累计成交额曲线
  vs 近 5 日同期均值(虚线),标题给出偏离百分比——放量/缩量一眼可辨
- 热点缓存判定重构为 _hotspot_history_or_build 公共入口,correlation 与
  hotspot 共用同一构建状态机;新增相关性矩阵回归单测
2026-09-05 04:09:03 +08:00
Justin Gu 7456f81c80 fix: 板块成分股/排行报价分页合并顺序颠倒 — 多页前插改按页序追加
BoardMembersQuotesCmd 分页合并曾用 batch + all_quotes 前插,而协议 start=0
返回排序后最前一页:成分股超过单页 80 只的板块(半导体 SH881319、
光学光电 SH881329、通信设备 SH881338 等)弹窗涨跌幅榜从第 81~100 名开头,
前 80 名被压到后面,肉眼即乱序;≤80 只的板块单页不受影响。

同步/异步 get_board_members 与 get_stock_quotes_list 共 4 处统一改为
按页序追加(all_quotes = all_quotes + batch),回归测试钉死多页合并顺序、
start 偏移推进与末页不足一页的终止行为。

注:test_ex_reconnect::test_all_retries_relogin_each_time 存量失败
(干净工作树同样失败),与本修复无关,另案处理。
2026-09-05 03:30:52 +08:00
Justin Gu 2b86a7d588 feat: 市场情绪栏目 — 盘中情绪采样器 + 宽度分时 + 涨停史离线回补
- /sentiment 市场情绪:温度卡(实时上涨占比/涨跌停/总成交,五档情绪判定)
  + 今日宽度分时(上涨/下跌/涨停家数三线)+ 近 60 日涨停跌停家数与上涨占比
- SentimentSampler:交易时段每分钟采样全市场广度(get_market_stat),
  停牌/盘外自动跳过、失败不中断;SentimentStore 落 SQLite
  (~/.easy_tdx/sentiment.db,(date,minute) 幂等主键,重启不丢)
- /market/sentiment/today|history:当日分钟曲线 + 逐日聚合(收盘快照占比/峰值)
- /market/limitup-history:涨停跌停家数逐日历史由 vipdoc 离线回补,
  无需采样积累即时可用;缓存按 days 分键(修复 10 天缓存被 60 天请求命中)
- 采样历史需交易日积累,页面空态有明示;涨停/跌停历史开箱即有 60 天
2026-09-05 03:10:19 +08:00
Justin Gu e419f911b7 feat: 盘面洞察三栏目 — 热点滚动/大盘日历/涨停生态,移除龙头池
- 热点滚动 /hotspots:交易日×板块涨跌矩阵(行业/概念/风格FG、领涨领跌镜像、今日~30日窗口),
  每日名次徽标+连板/累计/首榜统计,统计卡可点击展开成员板块;后端 /board-mac/hotspot
  两段式数据(历史日K矩阵当日缓存 + 今日实时列,周末 pre_close 未滚动去重),
  单连接约束下后台构建+进度轮询;风格轮动 /styles 为同视图 FG 路由别名
- 大盘日历 /calendar:指数全年红绿热力图,方框大小编码成交额(年内四分位),
  悬停浮框显示收盘/涨跌幅/成交额
- 涨停生态 /limitup:本地 vipdoc 日线离线回算连板天梯/首板二板分布/炸板率/跌停;
  仅统计最后一根 bar 等于全市场最新交易日的股票(防停牌/退市/未下载陈旧文件污染),
  主板 5% ST 判定带低价护栏;名称前端经 symbol-info 懒加载补齐
- 移除龙头池栏目及 /market/core-leaders 端点(screen 模块 universe=core 保留)
- 设计文档:docs/hotspot-rolling-design.md、docs/market-insights-roadmap.md
2026-09-05 02:43:02 +08:00
GitHub 2359df8694 feat: warehouse sync 支持 --source auto/tdx/baostock + EXE 打包内置 baostock 兜底源 + README 使用说明 2026-09-04 19:40:28 +08:00
GitHub 01e8fc0cc1 feat: /bars 自动兜底数据源 baostock — TDX 全部路径失败时最后一级回退(仅日线及以上),响应带 source 字段标注数据来源 2026-09-04 19:24:05 +08:00
GitHub f6560f3c6d feat: 行业/概念总览 — WebUI 板块总览双栏目(统计条+热力图/表格+三榜/翻红翻绿+板块弹窗复用)+ /board-mac/overview 聚合端点(多排序归并+15s缓存) 2026-09-04 15:59:57 +08:00
Justin Gu d0b72e1bd1 release: v1.31.3 — 紧急修复盘中分时数据错乱(实时命令解析错位,全程改走历史分时) 2026-09-04 12:42:44 +08:00
Justin Gu a69fa4103a fix: 寻优指标缓存键改内容哈希 — 修复 v1.25 起两段式加速静默失效
缓存键此前用数组对象 id 做签名,而引擎每次 run 会重建数组对象,
id 逐网格点漂移导致跨点永不命中——ParamGridOptimizer 的指标缓存
全程 0 命中,两段式加速自 v1.25 引入起就没生效过。正确性不受影响
(缓存未命中即走重算路径,结果与无缓存逐位一致),属纯性能回归;
test_optimizer_cache_reuse_across_grid_points 因此一直在失败。

- IndicatorCache._atom 数组签名由 (id, shape) 改为
  (dtype, shape, blake2b 内容摘要 16 字节):同值不同对象视为同一
  数据;NaN 按字节参与哈希,位模式不同只多算一次、不会误命中
- 删除防 id 复用的 _array_refs 强引用表(内容寻址下不再需要,
  缓存不再延长数组生命周期)
- 新增缓存级回归测试:同内容不同对象必须命中;原失败测试转绿
- 实测 16 点网格命中率 38%(修复前恒为 0);全量 pytest 1613
  通过、ruff/mypy 全绿
2026-09-04 12:29:09 +08:00
Justin Gu c8535e2596 feat: CLI 对齐 WebUI/SDK 分析能力 — 一键参数寻优+策略列表+组合级WF/一条龙
CLI 此前缺失的三块补齐,引擎层复用现成实现(ParamGridOptimizer/
STRATEGY_PRESETS/evaluate_portfolio/PortfolioWalkForwardEngine),
CLI、Web API 与 Python SDK 三条通路能力对等:

- 新增 easy-tdx optimize 参数网格寻优命令:单策略网格搜索
  (--strategy 用预设网格或 --param 自定义),--all 一键寻优所有
  内置策略并按总收益率全局排名(对齐 WebUI /optimize 页与
  /backtest/optimize-all/run/async);--workers 进程级并行;
  策略名/参数名联网前前置校验快速失败
- optimizer 新增 optimize_all_strategies 规范实现(模块级 worker
  可 pickle、主进程解析 label、跨策略进程池并行、presets 可注入
  子集网格),CLI 与后续 Web 端共用
- 新增 easy-tdx strategies 内置策略列表命令(名称/参数默认值/
  预设网格/说明;--output json 与 GET /backtest/strategies 同构)
- portfolio 补 --evaluate(组合级一条龙)/ --wf(组合级 WF)/
  --auto-fees,输出与 WebUI /portfolio 页同构
- 测试新增 12 例(optimize 互斥/未知策略/未知参数校验、strategies
  表格与 JSON、portfolio 新旗标、optimize_all_strategies 排名序/
  skipped/JSON 原生类型);pytest 1611 通过、ruff/mypy 全绿
- 文档同步:README、docs/backtest_usage.md CLI 章节+目录、
  CHANGELOG 未发布小节、examples/20_cli/cli_examples.sh 补
  回测系列 §39-48(输出样例均为真实行情实测)
2026-09-04 12:17:36 +08:00
Justin Gu 9d2b0397bd release: v1.31.2 — 修复分时接口盘前/休市/指数返回空(最近交易日锚定+指数K线适配) 2026-09-04 04:40:50 +08:00
GitHub 497ac21e5a fix: 策略库「重跑到今天」补齐组合分析 — 多策略组合级WF/一条龙/AI解读
策略库(/strategies)多策略组合卡片此前只有主回测:本轮把 v1.31.0
的组合分析链路延伸到多策略组合(N 策略 × 各自原标的):

- walkforward:组合 WF 泛化为槽位模型(_ComboSlot/_ComboWalkForwardBase),
  PortfolioWalkForwardEngine 行为不变;新增 MultiStrategyWalkForwardEngine
  (N 个策略各跑各自原标的,key 形如 label@symbol),复用切窗语义与
  WalkForwardResult 结构(前端 WalkForwardPanel 直接渲染)
- benchmark:新增 evaluate_multi 一条龙(MultiStrategyEngine 回测 + 多策略
  组合 WF + 逐槽位三段体检多数口径聚合 + 综合评分 + 组合评级 + 各槽位标的
  等权买入持有基准对比),报告结构与单标的 evaluate_strategy 同构
- Web:新增 POST /backtest/multi-strategy/wf/run/async 与
  /backtest/multi-strategy/evaluate/run/async;多策略组合回测响应附带
  grade/score(与单标的/多标的组合响应同构)
- 前端 StrategiesView:组合结果区新增「WF 样本外验证 / 一条龙评估 / AI 解读」
  按钮与同构面板(按需触发,复用最近一次组合回测的 items/cash);
  绩效指标表补齐 v1.28 深度 6 项(SQN/最大连胜连亏/Ulcer/VaR/CVaR);
  aiPrompt 新增 multi 模式(策略明细语境 + 槽位表现段)
- 测试:多策略 WF 引擎 3 例、evaluate_multi 3 例、新端点 Web 级 2 例、
  aiPrompt multi 模式 1 例(pytest 1611 绿、node --test 5/5、E2E 9/9)
2026-09-03 23:54:29 +08:00
GitHub c49ba4c4b5 feat: 组合回测分析体系对齐单标的 — 组合级WF/一条龙/完整25项绩效/AI解读
组合回测(一策略×多标的)此前只能看 4 个数字,本轮把单标的的整条
分析链路在组合端补齐(WebUI/REST 双端):

- portfolio_engine:合并净值+汇总成交喂 PerformanceAnalyzer,输出
  完整 25 项指标(SQN/最大连胜连亏/Ulcer/VaR/CVaR 等)+ 组合层
  trades(symbol 列);修复假年化与回撤口径(负值+固定分母 →
  逐点峰值,与单标的/多策略一致)
- walkforward:新增 PortfolioWalkForwardEngine,按标的日期并集切窗、
  每窗独立开仓、合成组合窗内净值,复用 WalkForwardResult 结构
- benchmark:新增 evaluate_portfolio 一条龙(组合回测+组合WF+
  跨标的多数口径适配性体检+综合评分+组合评级+等权买入持有基准对比),
  报告结构与单标的 evaluate_strategy 同构
- performance:FIFO 持仓天数配对支持 symbol 分组
- Web:新增 POST /backtest/portfolio/wf/run/async 与
  /backtest/portfolio/evaluate/run/async;组合回测响应附带
  grade(组合净值口径)与 score;新增 _normalize_bars_dt 修复
  按标的取数路径的字符串日期/遗留 date 列崩溃(E2E 揭露)
- 前端:组合页新增附加分析勾选区与组合绩效指标/WF/一条龙/成交明细
  区块;buildPortfolioAiPrompt 组合版 Prompt;抽通用
  AiInterpretModal(回测页迁移共用,行为不变);TradeTable 支持
  showSymbol;EvaluatePanel 支持 gradeOverride
- 测试:后端 +17 例(pytest 1603 绿)、aiPrompt 组合版 2 例、
  Playwright 组合页 E2E 2 例(9/9 绿)
2026-09-03 23:22:48 +08:00
Justin Gu e866e82299 fix: 一键寻优所有策略补齐 35 个新策略预设网格(STRATEGY_PRESETS 19→54,网格点 174→328)+ 注册表↔预设一致性回归测试 2026-09-03 03:27:04 +08:00
Justin Gu 62e80d892e release: v1.30.2 — 无未来函数指标扩容16个(注册指标50个)+ 内置策略补齐54个:WebUI回测全覆盖,新增前缀一致性无未来函数回归测试 2026-09-03 03:07:57 +08:00
Justin Gu 1433f4eed0 release: v1.30.1 — 移除 ZIG 未来函数指标与 zig_breakout 策略:回测数字可信优先 2026-09-03 02:02:19 +08:00
Justin Gu 1e95bab476 release: v1.29.2 — 可编辑安装失效友好报错(#58):入口守卫 _editable_guard 经 force-include 存活于失效态 2026-09-03 00:46:28 +08:00
GitHub 99397a12e3 feat(ccpm): 中金所成交持仓排名采集——CLI/API/WebUI 三端 + 新手科普
- 新增 easy_tdx.ccpm 模块(独立数据源,标准库 urllib 零依赖):
  官网 /sj/ccpm/{YYYYMM}/{DD}/{品种}.xml,交易日约 16:15 发布,
  单文件含全部合约 × 三类排名(datatypeid 0=成交量/1=持买单/2=持卖单)× 前 20 名会员
- 协议要点:?id= 为 0~99 随机防缓存参数可省略;非交易日 302→error_404,
  禁用 urllib 自动重定向并翻译为 CcpmNoDataError(区别于网络错误 CcpmError);
  仅 http 可用;历史可回溯至 2012 年
- 8 品种:IF/IH/IC/IM 股指 + TS/TF/T/TL 国债;latest_rank() 自动回溯最近交易日;
  按日不可变 → ~/.easy_tdx/cache/ccpm/ 落盘缓存,历史二次查询零网络
- CLI:easy-tdx ccpm IF [--date] [--table] [--refresh] [--no-cache],all=全品种
- API:GET /ccpm/products(品种科普元数据)+ GET /ccpm/rank?product&date
  (404=非交易日/未发布,refresh 强制重抓)
- WebUI「期货持仓排名」页(行情组):品种下拉+日期+自动回溯开关+一键采集,
  合约页签自动标注主力,前 20 合计多/空/净持仓概览,三组排名并排表格
- 三段新手科普:「这是什么数据」「品种一览」「多单空单加减仓怎么看」
  (强调排名看不出套保还是投机,空单多 ≠ 看空市场)
- 测试 20 例(mock HTTP 零网络):解析/缓存/302 语义/回溯/路由/CLI
2026-09-02 20:54:59 +08:00
GitHub a9dabe4682 docs+style: README 隆重介绍 v1.29 AI 深度辅助(认知自由叙事);ruff format 对齐 CI 2026-09-02 20:23:27 +08:00
GitHub 4bd5b5d833 release: v1.29.0 — 借鉴社区 Fork 六项特性:ZIG 策略 + 交易时段感知刷新 + 120M K 线 + 逐 bar 衍生字段 + 159 龙头池 + 多 Provider LLM 直连
- ZIG 右侧突破回补策略:MyTT 新增 ZIG 之字转向(未来函数,含前视偏差警示);
  波谷启动建仓挂硬止损(OCO)→ 见顶清仓记前高 → 右侧突破回补;路径依赖不实现
  entry_exit_masks(向量化守护测试白名单);含寻优预设网格与 --strategy-file 独立文件
- 交易时段感知刷新:realtime/session.py(09:15~11:30:30 / 13:00~15:05)+
  GET /market/session;看板 30/60/120s 轮询休市自动暂停(三态状态栏 + 开关持久化
  + 手动刷新不受限);SSE/WS 既有会话语义不动
- 120 分钟 K 线:/bars?category=MIN_120(MAC 原生 Period.MINS×120 优先,
  2×60M 相邻聚合兜底,标准客户端上限 400 根);前端周期选择器同步
- 逐 bar 衍生字段:/bars 与 /bars/index 附带 pre_close/change/change_pct/
  amplitude_pct(pre_close≤0.01 兜底防除零)
- 159 只核心龙头池:数据资产取自 Fork(东财全行业龙头名单,四组分层);
  universe=core 接入 screen scan / SignalScanner / StrengthRanker / market strength;
  GET /market/core-leaders + WebUI「龙头池」页(搜索/个股详情)
- 多 Provider LLM 直连:easy_tdx.ai + /llm/*(DeepSeek/通义/智谱/Kimi/MiniMax/
  OpenAI/Claude/Ollama/自定义,openai 兼容 + anthropic 原生双协议);
  配置落盘 ~/.easy_tdx/llm.json(WebUI「AI 设置」页 ⇆ 手工编辑双向兼容,
  文件>环境变量>预设;key 脱敏回显/CLEAR 清除);「AI 解读」后台任务化
  (复用 task_runner,提交+轮询,不占 HTTP 连接);思考型模型空白正文防御
  (reasoning_content 耗尽 max_tokens → 可操作报错;默认 16000);
  AI 解读历史页(自动归档 Prompt/正文/策略上下文 + 去回测带参引导)
- WebUI 加固:SPA fallback 对未知 /api/* 返回 JSON 404(不再 200 HTML 伪装解析错);
  index.html 一律 Cache-Control: no-store(防缓存旧资源引用);路由兜底重定向;
  全局风险提示常驻底栏 + 龙头池/AI 解读针对性免责声明
- 测试:新增 9 个单测文件共 59 例;黄金基线仅新增 zig_breakout 条目(其余零漂移);
  全量 1448 例通过
2026-09-02 20:19:17 +08:00
GitHub eeed45b171 fix(bars): 指数/个股 K 线 vol 字段协议语义修正(#64)
通达信服务端 K 线记录第一个 4 字节字段的语义随周期/品种变化,此前原样
透传错误数据(逐字节拆包 + 新浪实时行情/东方财富三方交叉验证锁定):

- 指数分钟线(MIN_1/3/5/15/30/60,含 880xxx 板块指数):f1 实为
  成交额(百元),与 amount 恒差 100 倍,真实分钟成交量不在报文中
  (15:00 上证 5min 真值 13,954,814 手 vs 返回 208,748,512≈amount/100)
  → vol 置 NaN,不拿成交额冒充成交量;
- 指数与个股周/月/季/年线(cat 5/6/10/11):f1 = 真实成交量/100
  (上证本周三日日线 vol 合计 1,666,668,288 手 vs 周线 16,666,683)
  → ×100 还原,与日线单位对齐(指数=手、个股=股);
- 日线(cat 4)与 cat 9(日线变体,枚举名误标 YEAR,真年线是 cat 11)
  不受影响,cat 9 明确不套 ×100 并由测试锁定。

配套:DataFrameResponse NaN→null(Starlette allow_nan=False 透传会 500);
client/路由 docstring 写明各单位;回归测试 7 例(实抓原始字节构造报文);
验收脚本 scripts/verify_issue64.py 连真实服务器复测。附带发现仅记录:
指数分时 vol=成交额(万元)、/bars?category=YEAR 实际返回日线(cat 9)。
2026-09-02 17:12:00 +08:00
GitHub 534ab0e0c8 Merge branch 'main' of https://github.com/handsomejustin/easy_tdx 2026-09-02 13:16:41 +08:00
Justin Gu 0a00ca3f70 release: v1.28.0 — 深度风险报告+移动止损+黄金测试(借鉴 akquant):25 项绩效 / α·β·IR·TE 基准对比 / trail_stop OCO 2026-09-02 11:53:53 +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 36ea8497ae perf(engine): 信号管线提速 ×12.6 — 向量化快速路径 + 日期查找表 + 去 strftime
基准先行(scripts/bench_engine.py):ma_cross 800 根全流程基线 93.6ms。
profile 打破预期:85% 墙钟在 OrderSimulator._find_bar_index(每信号全列
strftime),而非计划认为的逐 bar 信号循环。三处优化(行为逐位一致):

- Strategy 新增 entry_exit_masks() 显式钩子,19 个内置策略全实现;引擎按
  掩码 + 候选事件 bar 状态机一次产出信号,持仓估算逐行复刻
  _update_strategy_position(含买不足 1 手退化路径);约束检测
  _vectorize_eligibility 显式可测,signal_path=auto/vector/loop 可指定;
  不满足约束(无钩子/缠论注入/掩码形状错)自动回退逐 bar
- OrderSimulator._build_dt_lookup:O(信号×bar) 全列扫描 → 每 simulate 一次
  O(bar) 查找表(重复日期取首个、未命中 None、object 恒不匹配语义对齐)
- _datetime_to_int 去 strftime:year*10000+month*100+day 整数算术
  (NaT→NaN 行为一致),_bind_data 与查找表共用

实测:ma_cross 全流程 93.6→7.4ms(×12.6),信号层 ×1.39~1.72(四策略);
32 点网格寻优 ~3.0s→231ms。对拍 39 例:19 策略×参数变体×warmup/低资金/
费率/缓存,performance/trades/equity/positions 逐位一致。
顺带记录:wr_reversal 默认阈值与 MyTT WR 刻度不匹配(策略恒不交易,既有问题未改)
2026-09-01 23:47:06 +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 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 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 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 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 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
Harveyliu007 349f44f959 fix(ex): ex tick --date 接受 YYYYMMDD 整数,修复 AttributeError 2026-08-28 19:53:51 +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 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 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 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
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
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
GitHub 60bd0586f9 style(tests): ruff format 修复 patch.object 参数换行(CI ruff format --check 失败) 2026-08-05 15:58:00 +08:00
GitHub 0490f67a16 fix(client): get_history_fund_flow 加空数据故障转移(issue #41)
用户反馈 get_history_fund_flow(SH, "600519") 返回空 DataFrame,日志显示
"K线响应为空(声称 800 条但首条即解析失败...)"。排查定位:当前 host 对
常见标的也返回 ret_count 撒谎的空 body,但资金流兼容回退路径(直连空 →
拉 K 线 + 历史逐笔重算)未接入 v1.20.4 的空数据故障转移,"服务器回包正常
但内容是假的空" 既非 TdxConnectionError 也不触发换台,用户卡在坏服务器上。

修复:get_history_fund_flow(sync+async)当前 host 直连与 K 线回退均空时,
按延迟顺序逐台实测找首台返回有效数据的服务器(与 get_security_bars/
get_index_bars 同源逻辑)。因资金流获取涉及多命令,无法用单 cmd 复用泛化版
_find_host_returning_data,故内联 _fund_flow_failover:每台候选上跑完整
_fetch_fund_flow_records(原直连+K线回退逻辑抽出成独立方法),返回首台非空
结果。全空返回空 DataFrame(不 raise,区分"真无历史数据"与"服务器缺数据")。

测试:新增 6 个(4 sync + 2 async)。全套 989 passed;ruff/mypy 改动文件零错误。
2026-08-05 15:53:49 +08:00