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
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
8a5ed52a1f
release: v1.28.1 — WebUI 名词解释折叠帮助 + AI 解读 Prompt 导出 + WF 窗口序列化修复(to_json_native 有限 float 落入 str 兜底,逐窗指标变字符串)
2026-09-02 15:49:41 +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
90f2595d3d
release: v1.27.2 — 市场异动类型解析补齐(#62):盘中强弱/竞价尾盘/急速拉跌/竞价试盘方向
2026-09-02 10:44:20 +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
1aacce9b7d
release: v1.27.1 — 回测页附加分析布局修复 + WebSocket 实时推送收录
2026-09-01 23:30:02 +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
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
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
6bfe5a2cee
release: v1.23.2 — 1.23.1 质量门禁补丁(mypy strict + ruff 全绿)
2026-09-01 18:02:40 +08:00
GitHub
a79861af76
release: v1.23.1 — 行情终端 WebUI 重大升级
2026-09-01 17:45:12 +08:00
毛利哥 and GitHub
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
a7b14d3388
release: v1.20.12 — ex tick --date 修复(#56):接受 YYYYMMDD 整数,对齐 A 股日期语义
2026-08-28 22:41:24 +08:00
GitHub
05db452cc0
release: v1.20.11 — 资金流口径标注(#55):聚合逐笔成交额分档,实质为主动买卖总失衡
2026-08-28 20:14:41 +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
7c9e19de93
release: v1.20.8 — 信号雷达:一键扫描全部已保存策略买卖信号
2026-08-21 16:57:16 +08:00
GitHub
67a5e1d08a
release: v1.20.7 — /bars MIN_1 时间修复(#49)+ 寻优参数语义约束(#39)+ 依赖安全升级
2026-08-19 19:06:59 +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
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
Justin Gu
3b7ce97f2a
chore(release): v1.20.4 服务器健康分引擎 + K线空数据故障转移
...
版本包含(PR #37):
- 新增 _health.py 服务器健康分引擎(降权/冷却/恢复)
- get_index_bars/get_security_bars 空数据自动逐台换台
- 8 个 client _execute 统一健康分联动
- select_best_host_*/find_working_host_* 应用 rank_by_health
- security_bars 截断日志区分空响应与末尾截断
- 26 个新增测试,CI 8/8 通过
2026-07-13 16:36:11 +08:00
GitHub
d84a08b7f2
fix(backtest): v1.20.3 修复回测绩效统计两个 bug(issues #30 #31)
...
#31 : RebalanceEngine 缺失价格导致净值假崩塌
已持仓标的当日缺 K 线(停牌/日历错位)时 prices.get(code,0)=0,
持仓市值记 0 → 净值单日暴跌(159915 在 20210208 缺一天,持仓 ~93%,
净值 1.1M→91,845,全期最大回撤 -92%)。
修复:last_known_price forward-fill,缺失日沿用最近已知收盘价。
附带:_compute_performance 最大回撤改正值口径(与 BacktestEngine 一致)。
验证:真实 ETF 数据 max_drawdown 24.01%(backtrader 基准 27%),
total_return 220.56% 不变。
#30 : PortfolioTracker 交易静默漏单
apply_trades 用 trade.datetime 作 dict key、df["datetime"].to_numpy()[i]
查找;两端类型不一致(int vs datetime64)时永不命中,交易被静默丢弃,
净值恒定(total_return=0 但 trades 表有 PnL)。
修复:改为按"位置索引"匹配(归一化 datetime 后查位置),类型无关。
验证:int-trade+datetime64-df 修复前净值=100000(恒定),修复后=100289。
测试:新增 4 回归测试(未修复代码上均失败,修复后通过);
全套 936 passed;mypy 改动文件零错误;ruff 全绿。
2026-07-10 15:12:47 +08:00
GitHub
a67e5588d0
fix(types): v1.20.2 修复 v1.20.1 引入的 CI mypy 失败
...
v1.20.1 把 BacktestResult.performance 扩大为 dict[str, float | str]
(为塞 diagnostic_warning 字符串),破坏 6 处下游消费方类型契约,
CI mypy job 转红。重构为更干净的设计:
- 诊断信息走独立的 BacktestResult.diagnostic 字段(performance.py
PerformanceAnalyzer.diagnostic 属性 → engine 透出 → to_dict 含该字段
→ CLI 显示),performance 字典回归 dict[str, float](保留别名键)。
- optimizer.py 补 npt.NDArray 标注,消除 3 个既有 ndarray type-arg 错误。
测试:932 passed;mypy backtest/+portfolio/ 零错误;ruff 全绿。
2026-07-09 20:34:23 +08:00
GitHub
00b8c6374a
fix(backtest): v1.20.1 修复回测引擎 3 个 bug(issues #22 #23 #25)
...
排查发现用户反馈的"回测统计数据缺失/异常"并非服务器连接问题,
而是回测引擎与组合优化器自身的代码缺陷:
#23 : 首根 bar 访问 close[-1] 崩溃
_SeriesAccessor 负向越界改返回 NaN(不抛 IndexError);
BacktestEngine 新增 warmup_bars 参数跳过指标预热期。
#25-A: FactorWeightedOptimizer 权重坍缩
n_stocks=2 且得分接近时,减最小值把低分标的权重压到 ~6e-8,
等于单股满仓、n_stocks 被无视,进而出现持仓1只/-99.98%回撤。
新增 _apply_weight_floor 权重下限保证入选标的都有实质权重。
#25-B: RebalanceEngine total_trades 统计错误
total_trades = len(equity_curve)(天数)改为 len(trades_df)(真实笔数)。
#22 : 绩效别名键 + 数据异常诊断
performance dict 新增 sharpe_ratio/start_cash/end_value 别名键;
资金曲线异常时返回 diagnostic_warning 而非静默全 0,CLI 显示提示。
文档:README 加入回测手册导航;backtest_usage.md 补充 warmup 说明。
测试:新增 6 个回归测试,更新 3 个;932 passed。
2026-07-09 20:16:12 +08:00
GitHub
d0d65d64b8
feat: v1.20.0 服务器失败自动 ping 切换(无需手动 easy-tdx ping)
...
服务器连不上或返回空数据时,自动测速切到延迟最低的可用服务器再试,
Python API / CLI / Web API 三入口全部自动生效。
核心改动:
- _reconnect.py 新增 select_best_host_sync/async(连接失败 failover)
和 find_working_host_sync/async(空数据逐台实测)
- 8 个 client 的 _execute 注入跨主机故障转移(复用 auto_reconnect 开关)
- get_market_stat 空数据时按延迟顺序逐台实测找返回数据的服务器
- 新增 _reconnect/_areconnect helper 收敛重建连接副本
- MacClient failover 用 save_best_mac_host(延续 v1.19.4 不污染 best_host)
- 顺手修复 test_commands_offline 未使用 import(main CI failure 根因)
测试:925 passed(新增 18 个 failover 测试),ruff/mypy 零新增错误。
2026-07-08 18:07:37 +08:00
Justin Gu
14debdb446
feat(web-ui): v1.19.7 服务器设置页面(测速+切换通达信服务器)
...
新增 web UI 第六个页面「服务器设置」,让用户在浏览器上:
1. 列出全部 50+ 候选服务器
2. 一键并发测速(显示延迟和可达性)
3. 点选切换到最快或可用的服务器(热重连,无需重启)
解决不同地区/运营商对通达信各服务器连通性不同的问题。
新增:
- AsyncTdxClient.reconnect_to(host) 热切换方法(持锁,关旧建新)
- GET/POST /api/v1/server/hosts|test|switch 三个端点
- ServerSettingsView.vue 页面(列表+测速+切换)
- 导航栏第六个入口
设计: 先reconnect成功再save_best_host(避免污染config),
host校验只允许候选列表内IP,不自动测速避免首屏卡顿。
2026-07-07 23:08:32 +08:00
Justin Gu
c2c3e7ffb4
fix(release): v1.19.6 release.yml 先构建前端再 pip install(修复EXE丢依赖)
...
v1.19.5 EXE 只有 11MB,双击报 ModuleNotFoundError: No module named 'pandas'。
根因:release.yml 先 pip install -e '.[web,packaging]' 再 Build frontend,
但 force-include 要求 web-ui/dist 在 pip install 时就存在——install 阶段
dist 不存在导致 editable install 静默降级,PyInstaller 收集不到第三方包。
修复:调换步骤顺序,Build frontend 在 Install Python deps 之前。
ci.yml 上一 commit 已同样修复。
2026-07-07 22:23:30 +08:00
Justin Gu
d5309454bc
fix(packaging): v1.19.5 PyPI wheel 含前端 dist(pip install 后不再 404)
...
根因:PyPI wheel 只有 Python 包,不含前端 dist。_resolve_web_dist_dir
三级探测(env/_MEIPASS/仓库根)在 PyPI 安装环境下全失败返回 None,
StaticFiles 不挂载,GET / 返回 404。
修复:
- pyproject.toml hatchling force-include 把 web-ui/dist 映射到包内
easy_tdx/web/dist
- publish.yml 在 python -m build 前先 npm ci && npm run build
- _resolve_web_dist_dir 新增第 4 级探测:包内 easy_tdx/web/dist
验证:本地构建 wheel 含 4 个前端文件,模拟 PyPI 安装后第 4 级探测
成功找到 dist。907 测试全过。
2026-07-07 20:46:26 +08:00
Justin Gu
3bc15e620c
fix(config): v1.19.4 MAC客户端不再污染标准best_host(取不到数据根因)
...
根因:mac/client.py 的 MacClient.from_best_host 和 AsyncMacClient.from_best_host
调用 save_best_host(best),把选中的 MAC 协议服务器(如 121.36.248.138)
写进了全局 best_host 字段——但这个字段是标准 TDX 协议用的。之后标准
AsyncTdxClient 用 get_best_host 读到这个 MAC host,用标准协议请求 MAC
服务器,返回空 body(偏移 2 剩余 0)。web app lifespan 启动 MAC 客户端
时触发污染。
修复:
- config.py 新增独立的 best_mac_host 字段 + get_best_mac_host / save_best_mac_host
- mac/client.py 4 处改用新字段(不再碰 save_best_host / get_best_host)
- get_best_host 加交叉污染校验:缓存 host 不在标准列表里自动重置
(自动修复已被污染的 config.json,用户无需手动操作)
测试:新增 5 个回归测试(污染检测 + MAC host 隔离),907 全过。
2026-07-07 20:36:44 +08:00
Justin Gu
032f5a40c0
fix(web): v1.19.3 K线空body不再500 + SPA路由刷新404
...
两个独立问题(日志证据):
1. K线空body 500:SH600519 等正常股票偶发请求时,通达信返回
ret_count>0 但 body 完全为空(偏移2 剩余0)。v1.18.3 的容错有
'if bars:' 条件,bars 为空时 raise → 500。移除该条件,始终 return
(空列表让前端分页重试比 500 友好)。GetIndexBarsCmd 同改。
2. SPA fallback 缺失:/optimize /portfolio 等前端路由刷新时 404。
子类化 StaticFiles 为 SPAStaticFiles,404 时返回 index.html。
API 路径不受影响。
测试:更新 test_security_bars_truncated_first_record(原断言 raise,
现断言返回空列表)+ 新增空 body 回归测试。902 全过。
2026-07-07 20:13:39 +08:00
Justin Gu
ecd6e298f5
fix(packaging): v1.19.2 修复干净 Windows 上 EXE 页面纯黑(MIME 类型)
...
根因:干净 Windows(无开发环境)注册表里没有 .js 的 Content Type 映射,
mimetypes.guess_type('.js') 返回 None,StaticFiles 回退到 text/plain。
浏览器 <script type="module"> 启用严格 MIME 检查,拒绝执行 text/plain
的 JS → Vue 不挂载 → 页面纯黑(/docs 不受影响因为它是 API 路由)。
修复:mount 前用 mimetypes.add_type 强制注册 .js/.mjs/.css/.svg 的正确
MIME,无论机器装没装开发工具都生效。
同时改 easy_tdx.spec:datas 用 pathlib.rglob 逐文件展开,更可靠。
2026-07-07 15:18:53 +08:00
Justin Gu
957cb8eeb0
Merge commit 'e3e8dd4'
...
# Conflicts:
# CHANGELOG.md
# pyproject.toml
# src/easy_tdx/commands/security_bars.py
2026-07-07 01:54:20 +08:00
Justin Gu
0b4ed9af62
feat(packaging): v1.19.1 支持 Windows 单 EXE 打包 + 系统托盘 + 自动发版
...
面向零基础老年用户,easy-tdx 可打包成单一 Windows EXE,双击即用。
新增:
- 后端同源托管前端 dist(app.py 三级探测:env → _MEIPASS → web-ui/dist)
- easy-tdx serve 默认 --open-browser,启动后自动开浏览器
- PyInstaller 打包入口(__main__.py)+ spec 配置(easy_tdx.spec)
- 系统托盘(tray.py):右下角图标,右键"打开浏览器/退出"
解决老人不会用任务管理器关闭的问题
- GitHub Actions release.yml:打 v* tag 自动构建并发布 EXE 到 Releases
- docs/packaging.md 打包使用文档
修复:
- K 线残缺尾记录导致 500(security_bars.py):通达信服务器偶发
ret_count 与 body 长度不匹配,改为 try/except 优雅降级丢弃残缺尾,
返回已解析的完整记录。GetIndexBarsCmd 同改。加 4 个回归测试。
- PyInstaller frozen 模式三个坑:
1. console=False 下 stdout/stderr 为 None → 重定向到日志文件
2. multiprocessing spawn 子进程重新 import __main__ → freeze_support + 子进程检测
3. 系统托盘需主线程消息泵 → uvicorn 挪到后台线程
文档:
- README/手册改为三档分流:EXE(零基础)/ Python(一条命令)/ 源码(打包)
- 删除 npm run dev / 5173 / 两个终端的过时说明
- 手册补虚拟环境配置 + EXE 打包附录 + EXE 排错 FAQ
2026-07-07 01:48:07 +08:00
GitHub
e3e8dd492e
fix: K线响应截断容错 + 一键寻优并发默认8进程
...
- security_bars: parse_response 遇末尾残缺记录时丢弃并返回前 N-1 条,
避免 TDX 服务端截断响应导致整页 500(如 000408 count=800 日线)
- OptimizeView: 一键寻优并发默认 8 进程,用户选择持久化到 localStorage
bump version to 1.18.3
2026-07-06 13:23:37 +08:00
Justin Gu
c901d198ca
chore: bump version to 1.18.2 + CHANGELOG
...
记录移除拼音声母搜索的回退发版
2026-07-06 02:55:58 +08:00
Justin Gu
c9d80617e8
revert: 移除拼音声母搜索,回到 6 位代码输入
...
拼音搜索的底层依赖(首次需爬沪深 A 股 5000 条全名单,几十次 TDX 协议
往返,慢机器几十秒到超时)太重,反复优化(按需加载/遮罩/单飞/预热)
都无法兼顾'不阻塞核心行情请求'与'首次可用'。用户决定放弃此功能,
回到简单稳定的 6 位代码输入。
回退 e2bf29e..2523605 共 6 个 commit 的全部改动:
- 删除 StockSearchInput / AppInitOverlay / useStockSearch
- 移除 pypinyin 依赖、/security/search-index 端点、lifespan 预热
- SymbolPicker / StocksPicker 恢复为纯 6 位代码输入
- README / CHANGELOG 同步回退
代码状态等同 v1.18.1(一键寻优多进程并发)发布后的干净基线
2026-07-06 02:50:51 +08:00
Justin Gu
e2bf29e931
feat(web-ui): 股票代码输入支持拼音声母搜索(zjxc→中际旭创)
...
所有股票代码输入框(回测/寻优/组合页)支持代码/中文名/拼音声母三路
匹配的下拉联想。复用项目里早已有但从未被前端消费的 security_list_all
数据(沪深 A 股 5206 只完整中文名表)。
后端:
- 新增 GET /api/v1/security/search-index 端点,返回 [{code,name,initials}]
- 声母用 pypinyin FIRST_LETTER 预计算,进程内缓存,首次算一次常驻
- web extra 新增 pypinyin>=0.50 依赖
前端:
- 新增 useStockSearch composable(模块级缓存 + 三路过滤 + 防抖)
- 新增 StockSearchInput.vue(下拉 + 键盘导航 + 市场标签)
- SymbolPicker / StocksPicker 接入新组件,保留直接输 6 位代码的快路径
实测 zjxc→中际旭创、gzmt→贵州茅台、cjxc→楚江新材,5000 条本地过滤 <5ms
2026-07-06 01:43:57 +08:00
Justin Gu
044e21064c
chore: bump version to 1.18.1 + CHANGELOG
...
本次发布两个主题:
1. 一键寻优所有策略支持多进程并发(串行/4/8/16)
2. 策略库组合回测结果加组合评级 + 市场前缀纠正
2026-07-06 00:59:41 +08:00
Justin Gu
c57cde156b
feat(web-ui): 策略组合保存 + Tab 分类 + 审计修复 (v1.18.0)
...
v1.18.0
策略库新增 kind='multi' 类型:组合回测结果可保存为组合,
卡片「↻ 重跑到今天」一键用今天重跑得到截至当天的策略信号
(持仓/空仓/浮盈/浮亏三态高亮)。策略库拆成「单标的」「组合」
两个 Tab,避免数量多了混排难找。
核心机制:保存配置 → 载入即重跑到今天 → 看持仓表 = 今日信号。
复用现有 SQLite 单表(kind 字段本就是 TEXT),零数据库迁移,
零后端逻辑改动,仅 schema Literal 加 'multi'。
含 10 项代码审计修复:multi-icon 字体误用、lastComboCash 漏赋、
弹窗 ARIA/ESC/focus、三态函数收敛 computed、警示样式合并、
重跑按钮改橙、items 运行时校验、ref 替代 querySelector、
selectedIds 静默替换、文案精简。
补记 v1.17.15 CHANGELOG(橙色按钮 + 名言轮播)。
- 改 web-ui/src/views/StrategiesView.vue(保存组合+Tab+三态+审计修复)
- 改 src/easy_tdx/web/backtest_schemas.py(kind Literal 加 'multi')
- 改 web-ui/src/types.ts(TS 类型同步)
- 改 CHANGELOG.md(补 v1.17.15 + 新增 v1.18.0)
- bump pyproject.toml 1.17.15 → 1.18.0
2026-07-05 03:01:09 +08:00
Justin Gu
cf843cebad
release: v1.17.14 — Web UI 数据评级系统(S/A/B/C/D 五档,三入口覆盖)
...
纯前端 TypeScript 实现,零后端改动。回测/组合/寻优三处结果顶部
显示评级徽章,让普通人 1 秒判断「适不适合经常参与」。
评级不看收益率(避免被近期大涨误导),只看风险调整后的持有体验:
卡玛/最大回撤/胜率/利润因子/夏普/波动率六维加权 + 一票否决。
京东方「收益 126% 但胜率 35%」案例 = D 档(核心验证点)。
长线低频策略(6年6笔)不会被冤枉:交易<10笔时只降权胜率/利润因子
维度,不否决整个评级(修复用户实测反馈)。
- 新增 web-ui/src/grading/ 核心模块(engine/thresholds/combinedMetrics/index)
- 新增 GradeBadge.vue + GradeDetails.vue 组件
- 接入 BacktestView/PortfolioView/OptimizeView(寻优排名表加评级列)
- 15 个自检测试(node:test + rolldown 打包),覆盖京东方 D / 长线 B 等场景
2026-07-05 01:21:18 +08:00
Justin Gu
239ff46ac8
release: v1.17.13 — 修复多策略组合回测最大回撤虚高(分母误用初始值而非峰值)
...
用户反馈:3 个策略各自最大回撤仅 45.53%/40.16%/16.89%,组合却显示 83.76%。
根因:_build_combined_equity 的 drawdown_pct 分母误用 initial(固定初始资金),
净值大涨后(总收益 545%,峰值≈6.45×初始)绝对回撤额除以小初始值被等比放大。
改为 drawdown / peak(相对当时峰值,0~1),与单标的 PortfolioTracker 一致。
连带修复卡玛比率(年化/最大回撤)。其余指标经核对均正确。加回归守卫。
2026-07-04 21:07:39 +08:00
Justin Gu
ee628c1b34
release: v1.17.12 — 修复 CI 在新版 FastAPI 上路由注册失败
...
DELETE /api/v1/strategies/{id} 原用 status_code=204,较新 FastAPI/Starlette
在路由注册阶段(add_api_route)就抛 AssertionError: Status code 204 must
not have a response body,导致 CI ubuntu 矩阵 21 个 web 测试因 fixture
导入 router 连带 ERROR。改为返回 200 + {"deleted": id} 确认体。
2026-07-04 20:50:07 +08:00