Justin Gu
66c49d1a8d
feat(web-ui): 三处回测入口默认起始日期统一为 2020-01-06
...
单标的回测、组合回测、参数寻优原默认起始日为「3 年前」(动态滚动),
统一改为固定日期 2020-01-06,便于跨时间横向对比。结束日仍为今天。
SymbolPicker 共用组件的 defineModel 默认值同步更新。
2026-07-05 00:15:00 +08:00
毛利哥
64d0812257
ci: 新增 frontend job 防前端构建回归(v1.17.9 数值 bug 教训) ( #16 )
...
web-ui 此前只有本地 vue-tsc,CI 不覆盖,类型错误 / 破坏性 import
只有发布后用户才发觉。新增独立 frontend job 跑 npm ci + npm run build,
与 Python 版本无关故独立于 test matrix,避免跑 6 遍。
本地 npm run build 验证通过(686 模块,575ms)。
2026-07-05 00:03:14 +08:00
Justin Gu
2f3826d84d
docs(README): 添加头部宣传图
2026-07-04 23:14:39 +08:00
Justin Gu
2f4d7bef8e
docs(README): 添加 DeepWiki 徽章
2026-07-04 23:03:33 +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
Justin Gu
05dc9a74af
release: v1.17.11 — Web UI 策略库(SQLite 持久化)+ 多策略资金分仓组合回测
...
新增两层能力:
1. 策略库:单标的/组合回测结果可保存到本地 SQLite 单文件
(~/.easy_tdx/strategies.db),策略库页可载入回填、重跑、删除。
2. 多策略组合回测:勾选 N 个单标的策略,各拿 1/N 资金、各跑原标的,
净值曲线按日期并集对齐求和,组合结果含 19 项完整绩效指标 + 持仓表。
后端:strategy_store.py(SQLite CRUD) + multi_strategy_engine.py(资金分仓引擎)
+ routers/strategies.py + /backtest/multi-strategy/run/async。
前端:StrategiesView.vue + 保存策略按钮 + 复用组合页图表组件。
895 单测全绿(+24 新增),ruff/mypy strict/前端 vue-tsc 全通过。
2026-07-04 20:40:57 +08:00
Justin Gu
4db7104209
docs(README): Web UI 截图改为一行一张
2026-07-04 11:39:44 +08:00
Justin Gu
1838995c51
docs(README): 添加 Web UI 回测可视化截图展示
2026-07-04 11:37:01 +08:00
Justin Gu
07205b6166
release: v1.17.10 — 一键寻优「查看」跳转携带完整行情上下文
...
- SymbolPicker code/category/startDate/endDate 改 defineModel 双向同步
- OptimizeView 两个「查看」按钮跳转 URL 附带 symbol/startDate/endDate/category
- BacktestView onMounted 回填标的/周期/日期到 SymbolPicker 表单
- 向后兼容:老 URL(只有 strategy/params) 仍正常,缺失字段保持默认
- 前端 vue-tsc / vite build 通过,后端 870 单测全绿(无回归)
2026-07-04 04:12:46 +08:00
Justin Gu
57cad1f3db
release: v1.17.9 — 修复回测交易统计离谱数值(前后端口径错配 + 持仓天数跨月放大)
...
- 交易盈亏指标改为收益率口径(avg_win/loss/max_win/loss = pnl/cost_basis)
- 平均持仓天数改用真实日历日相减(原 YYYYMMDD 整数差跨月放大)
- 盈亏比无亏损时记 999.0(原 0.0,与 100% 胜率自相矛盾)
- 新增 Trade.cost_basis 字段 + engine 填充
- 3 个回归守卫;870 单测全绿,ruff/mypy strict/前端 vue-tsc 通过
2026-07-04 03:55:05 +08:00
Justin Gu
f7cb015ec6
docs(README): 补充港股 goods_transaction_all + 逐笔倒序语义说明
...
在 Python API「扩展市场」章节加 goods_transaction_all 示例(港股当日全部逐笔),
并补 start 倒序语义说明(start=0 为最新/收盘方向,count=2000 默认只取最近 2000
笔会集中在尾盘,需全天用 goods_transaction_all)。同步 GitHub Wiki。
2026-07-04 03:19:11 +08:00
Justin Gu
cd790c9404
release: v1.17.8 — 修复 windows 3.10 flaky 测试超时
...
test_task_runner_does_not_evict_running 用 release.wait(timeout=5) 钉住慢任务保持
running,但 CI 慢环境(windows 3.10)下整个测试执行超过 5s 后任务因超时自动完成、
状态变 done,掩盖了「running 任务被 LRU 错误淘汰」的回归断言。
改为 timeout=30 留足 CI 慢环境余量(release.set() 仍是确定性释放点)。该测试用于
守护 LRU 淘汰跳过 running 任务的并发正确性,与港股逐笔成交无关,是 v1.17.2 引入的
预先存在问题。
本地连跑 5 次稳定通过,867 单测全绿。
2026-07-04 02:24:48 +08:00
Justin Gu
56153eeb97
release: v1.17.7 — 修复 Windows CI fixture 读取编码
...
1.17.5 引入的 tests/fixtures/ex_history_transaction.json 含中文注释,Windows CI
默认用 cp1252 解码 UTF-8 文件触发 UnicodeDecodeError: 'charmap' codec can't
decode byte 0x8f,导致 3 个 Windows 矩阵的 test_parse_ex_history_transaction_hk
失败(Linux 矩阵因默认 utf-8 不受影响)。
修复:所有 fixture 读取显式指定 encoding='utf-8':
- test_hk_transaction.py 的 load_hex/load_json(直接触发点)
- test_commands_offline.py 的 load_hex(纯 ASCII hex,当前未触发但一并统一)
- test_decode_errors.py 的 _load_hex(同上)
867 单测全绿,ruff format/check / mypy strict 通过。
2026-07-04 02:11:27 +08:00
Justin Gu
94f631a06b
release: v1.17.6 — 港股逐笔成交全量取数 + start 倒序语义文档
...
回应 issue #14 后用户反馈:默认 count=2000 取回的成交时间全集中在尾盘。
根因是通达信逐笔协议(A 股 0x122F 与港股 ex 0x23FC/0x2406 一致)的 start 为
倒序语义——start=0 指向最新一笔(收盘方向),并非 bug。02715 全天成交 13327
笔,count=2000 只取最近 2000 笔故集中在尾盘。
新增:
- goods_transaction_all(MacExClient 同步+异步)—— 港股股票类市场自动按 1800/页
翻页取全天全部逐笔成交,安全上限 50 页(90000 条)。返回协议原生倒序,需正序
由调用方 df.iloc[::-1]。market 非港股时抛 ValueError。
- _fetch_all_hk_transactions_sync/async(_hk_transaction.py)底层实现。
变更:
- goods_transaction docstring 补 start 倒序语义说明,引导需全天数据用
goods_transaction_all。
修复:
- 1.17.5 的 test_hk_transaction.py 未过 CI ruff format --check(注释对齐、
MacTransaction 单行化、文末空行)。
867 单测全绿(+5 全量取数测试),ruff format/check / mypy strict 通过。
2026-07-04 02:03:47 +08:00
Justin Gu
33239525a9
style(tests): ruff format 修复 test_hk_transaction.py (CI format check)
...
CI 的 ruff format --check 报该文件需重格式化:参数化注释前双空格对齐为
单空格、两处 MacTransaction 单行化、文末多余空行。仅格式调整,无逻辑变更。
2026-07-04 01:45:35 +08:00
Justin Gu
40ae2eba53
release: v1.17.5 — 港股逐笔成交协议路由修复 (issue #14 )
...
MacExClient.goods_transaction 此前对所有扩展市场统一复用 A 股 MAC 协议的
SymbolTransactionCmd(0x122F),但 0x122F 数据源未接入港股,服务器对港股
market 一律返回 39 字节空响应(count=0)。
港股股票类市场(HK_STOCK_MARKETS = {27,31,48,49,71,98})改走 ex 扩展行情
协议:query_date=None → GetExTransactionDataCmd(0x23FC 当日),指定日期 →
GetExHistoryTransactionDataCmd(0x2406 历史)。返回的 ExTransactionRecord
(price 为整数、单位 0.001 HKD)映射为与 A 股 MacTransaction 一致的 schema,
价格 ÷1000 换算为港元浮点,与港股分时图 float 价格对齐。count > 1800 时按
1800/页自动分页。其余扩展市场(美股 / 期货等)保持 MAC 0x122F 路径不变。
新增 src/easy_tdx/ex/_hk_transaction.py(市场判定 + 字段映射 + 自动分页,
同步/异步共用);回归测试 24 例(tests/unit/test_hk_transaction.py)+ 真实
fixture(港股 00700 在 2026-07-03 的 0x2406 响应)。862 单测全绿,ruff /
mypy strict 通过。
2026-07-04 01:29:17 +08:00
Justin Gu
648c9ccfaf
style: ruff format 修复预存格式债(CI ruff format --check 转绿)
...
v1.17.2 引入的 QFQ 重算代码(adjust.py/client.py/test_mac_qfq_integration.py)
含 magic trailing comma 导致 ruff format 期望多行参数展开,CI 的
'ruff format --check src/ tests/' 自 v1.17.0 起一直失败。纯格式变更,
零逻辑改动(20 例 QFQ 测试全过)。
2026-07-04 00:17:06 +08:00
Justin Gu
78bbe46d01
release: v1.17.4 — Web UI 回测交互重构 + 一键寻优全策略
2026-07-04 00:09:19 +08:00
Justin Gu
ecd8af1676
feat(web-ui): 取行情整合 + 市场智能识别 + 一键寻优全策略
...
- 取消单标的/寻优页独立的「取行情」按钮,整合进「开始回测/开始寻测」
(SymbolPicker 经 defineExpose 暴露 loadBars,父组件串联取数+回测)
- 取消市场手动选择(沪/深/北交所下拉),改为 market.ts 按代码段智能
识别(17 边界用例验证),代码框旁显示识别结果
- 成交价下拉精简为中文「开盘价/收盘价」,初始资金默认 100 万
- 寻优页 ParamGridPicker 切换策略自动填入预设参数网格
- 寻优页新增「一键寻优所有策略」按钮 + 全局策略排名表
(OptimizeView 复用 store.runOptimizeAll,结果区含最佳/排名/合计网格点)
- types/api/store 新增 OptimizeAll 契约 + submitOptimizeAllTask
- vue-tsc + vite build 通过
2026-07-04 00:09:05 +08:00
Justin Gu
f6ae69845d
feat(backtest): 成交价精简 + 一键寻优全策略 + 预设网格
...
- 成交价精简为 next_open/next_close,删除 this_close/worst/best 三种
非真实模式(this_close 有未来函数偏差,worst/best 为压力测试边界)
- 初始资金默认统一为 1,000,000(原 10万/20万)
- 新增 presets.py:18 策略各配 1-2 参数的预设寻优网格(笛卡尔积≤200)
- registry.to_schema() 返回 preset_grid 字段供前端自动填充
- 新增 POST /backtest/optimize-all/run/async:逐策略预设网格寻优 +
全局排名(OptimizeAllResult:ranking/best/per_strategy/total_grid_points)
- 新增 optimize-all 端到端单测 2 例(838 单测全绿)
2026-07-04 00:08:38 +08:00
Justin Gu
c15bd8232f
release: v1.17.2 — QFQ 深层历史负价修复
...
通达信服务端 QFQ 模式对长期重度除权股票(如 601088)深层历史页
返回负价格,导致回测总收益 -3087%、回撤 326.85%、年化 nan、
bollinger 崩溃、10 策略 invalid-value-in-scalar-power、MyTT divide-by-zero。
客户端兜底:检测 QFQ 负价时用 NONE+XDXR 本地重算前复权
(因子以除权日前一交易日含权收盘价为基准,保证除权日前后连续)。
同步+异步双路径一致修复,失败降级返回原值。
- 新增 src/easy_tdx/mac/adjust.py(纯函数 compute_forward_factor/
apply_forward_adjust/has_bad_prices)
- MacClient/AsyncMacClient 触发本地重算,XDXR 按 (market,code) 缓存
- tests: +20 例(16 纯函数 + 4 集成),844 全绿,ruff/mypy 通过
2026-07-03 22:42:04 +08:00
毛利哥
ea1a5c6f4a
Merge pull request #13 from shom/main
...
修复easy-tdx tick SH 600519 --days 5
2026-07-03 15:23:44 +08:00
Justin Gu
49c892b317
docs: 扩充 README Web UI 使用说明(前置条件/启动/四页操作步骤/注意事项)
2026-07-03 12:04:15 +08:00
Justin Gu
bd419da83d
release: v1.17.0 — 回测可视化 Web UI(单标的/组合/寻优/对比)
...
回测从命令行升级到浏览器可视化。Vue3 + ECharts 单页应用,零代码完成
单标的回测、组合回测、参数网格寻优、结果对比四大场景。
后端新增回测 REST API + 策略注册表 + 后台任务执行器,
内置策略从 5 个扩充到 18 个。823 单测全绿。
README 新增 Web UI 章节,CHANGELOG 记录本次全部变更。
2026-07-03 11:55:04 +08:00
Justin Gu
8323223937
fix: 寻优跳过参数范围/对比支持组合/日线x轴年份/寻优跳转填充参数
...
4 个独立修复:
1. 寻优跳过参数范围检查:Param.validate 加 skip_bounds 参数,
RegisteredStrategy.build 透传,ParamGridOptimizer 传 skip_bounds=True。
修复寻优时 fast=250 被 max_value=60 拦截的问题(探索超范围值是寻优目的)。
2. 对比页支持组合回测:CompareView 新增 extractComparable() 统一提取净值+指标,
支持单标的(performance/equity_curve)和组合(total_performance/combined_equity)。
修复勾选组合回测任务报「非单标的回测」错误。
3. K线x轴日线显示完整年份:isIntraday 判断从 length>10 改为检查时分秒非零
(日线归一化后带 T00:00:00 后缀,长度也 >10 导致误判为分钟线,slice 砍年份)。
4. 寻优跳转填充参数:BacktestView 加 useRoute(),onMounted 读 query.strategy +
query.params,await nextTick 后覆盖(避免 StrategyPicker watch 重置)。
修复寻优点「查看」跳转后参数未填充、仍用默认值的问题。
测试:823 passed, mypy 211 files OK, vue-tsc OK
2026-07-03 11:32:44 +08:00
Justin Gu
80d62aec56
fix: 图表x轴显示完整日期 + 组合回测取数翻页
...
图表日期格式(4 个组件):KlineChart/EquityChart/CompareChart/
PortfolioCompareChart 的 xAxis 加 axisLabel.formatter,强制显示原始
日期字符串。ECharts 默认把 ISO 日期当时间对象解析,丢失年份只显示
月-日+00:00。
组合回测取数翻页(_fetch_portfolio_bars):固定 count=800 改为翻页循环
(与前端 fetchBars 同逻辑),拼接后 sort_values 排序再过滤。修复组合
回测图表从 800 根最早点(2023-03-14)开始、缺失早期数据的问题。
实测组合回测 2019-01-07 起:净值 1814 点(不再是 800),首日正确。
2026-07-03 04:54:04 +08:00
Justin Gu
2903d8c800
feat(strategies): 内置策略从 5 个扩充到 18 个
...
新增 13 个经典策略,覆盖趋势/通道/震荡/均线四大类:
趋势类:
- ema_cross EMA 双线交叉(比 MA 反应更灵敏)
- triple_ma 三均线系统(多头/空头排列)
- dmi DMI 趋向指标(+DI/-DI 交叉)
- trix TRIX 三重平滑(过滤短期波动)
通道/突破类:
- donchian 唐安奇通道突破(海龟交易法)
- keltner 肯特纳通道(ATR-based)
- atr_breakout ATR 通道突破(均线±K×ATR)
震荡/反转类:
- cci CCI 超卖反弹
- wr_reversal WR 威廉超卖
- bias_reversal BIAS 乖离反弹
- emv EMV 简易波动(量价结合)
- dpo DPO 区间震荡
均线类:
- bbi BBI 多空指标(4 均线综合)
全部基于 MyTT 现成指标实现,每个策略声明参数 schema 供 Web 表单动态渲染。
18/18 策略冒烟测试通过(合成数据回测无运行时错误)。
2026-07-03 04:36:04 +08:00
Justin Gu
0cabae4eda
fix(web-ui): 翻页拼接后按时间排序,修复>800根数据图表/成交记录错乱
...
翻页拼接 concat 后页间时间逆序(page1=最新段,page2=更旧段),
导致 index 800 处时间从 2026 倒退到 2019。引擎/图表只正确处理前 800 根,
成交记录在 800 根后出现 2019 年数据。
修复:concat 后加 sort((a,b) => datetime.localeCompare)。
实测 1600 根排序后整体时间正序,index 799→800 连续递增。
2026-07-03 04:30:45 +08:00
Justin Gu
360f836001
fix(web-ui): ECharts 数字两位小数 + 取数自动翻页
...
图表格式化:新增 format.ts(fmt2/fmtPct),在 KlineChart/EquityChart/
CompareChart/PortfolioCompareChart/OptimizeHeatmap 统一应用——tooltip 用
valueFormatter、axis 用 axisLabel.formatter,所有数值显示两位小数。
取数翻页:fetchBars 当日期跨度超过 800 根时,循环调用 start=0,800,1600...
拼接结果,直到覆盖 startDate 或达上限(10 页 ≈ 32 年日线)。翻页后再按
日期范围闭区间过滤。实测 SZ:000001 翻 2 页拿到 1600 根(2019~2026)。
2026-07-03 04:18:23 +08:00
Justin Gu
ff3476f83a
feat(backtest): 回测结果对比页
...
选 2-4 个已完成的回测 task,叠加净值曲线 + 横向指标对比。
后端(最小增量):
- task_runner.list_recent(limit):返回最近 N 个任务摘要(LRU 倒序)
- GET /backtest/tasks?limit=20:任务摘要列表端点(不含完整 result)
- TaskSummary / TaskListResponse schema
前端(/compare 对比页):
- CompareView:左栏勾选已完成 task,右栏叠加对比
- CompareChart:多 task 净值叠加图(归一化为初始=1)
- CompareTable:多 task 指标横向对比表(8 项指标)
- 按 result 结构判断可对比性(仅单标的 BacktestResult 可对比)
复用现有 task_runner LRU 表,无新增存储。测试:823 passed(+2 列表端点)
2026-07-03 04:11:29 +08:00
Justin Gu
87fafe9131
feat(backtest): 参数网格寻优(optimizer + 前端寻优页)
...
对单个策略的 1-2 个参数做网格搜索,遍历用户指定的取值列表笛卡尔积,
每个组合跑一次回测,按 total_return 排序,返回排名表 + 热力图。
后端:
- ParamGridOptimizer(backtest/optimizer.py):itertools.product 遍历网格,
每点 entry.build(params) + BacktestEngine.run(df),复用同一 DataFrame
- 网格大小上限 200 防组合爆炸,单点失败容错(跳过不中断)
- 2 参数时生成热力图矩阵(x/y 轴取值 + cell 收益率)
- POST /backtest/optimize/run/async 端点(后台任务)
- OptimizeBacktestRequest schema(param_grid 1-2 参数)
前端(/optimize 寻优页):
- ParamGridPicker:勾选 1-2 个寻优参数,逗号分隔填取值列表
- OptimizeResultTable:网格点排名表(按收益降序,最优高亮)
- OptimizeHeatmap:2 参数热力图(ECharts heatmap,绿→红映射收益)
- 最优点「查看」按钮跳转单标的页用该参数回测
测试:821 passed(+10 寻优器单测 + 3 寻优路由测试)
2026-07-03 03:55:35 +08:00
Justin Gu
6aab7a82b5
feat(web-ui): Vue3 + ECharts 回测前端(单标的 + 组合回测)
...
独立前端工程(web-ui/),Vue3 + Vite + TypeScript + Pinia + ECharts。
通过 vite proxy 对接后端 /api,开发期无跨域。
单标的回测(/):
- StrategyPicker: 策略下拉 + 按 schema 动态渲染参数表单
- SymbolPicker: 选标的 + 日期范围取行情(默认3年,结束日=今天)
- KlineChart: K线主图 + 买卖点 markPoint(按 datetime 对齐)
- EquityChart: 净值曲线 + 回撤双轴图
- MetricTable: 19 项绩效指标(收益/风险/交易分组)
- TradeTable: 成交记录表
组合回测(/portfolio):
- StocksPicker: 多标的输入(增删标签)
- PortfolioCompareChart: 各标的净值叠加(归一化对比)
- PortfolioSummaryTable: 各标的绩效横向对比
- 顶部导航切换单标的/组合
契约修复(/check 审计):
- fetchBars 归一化 date/datetime 列名(日线返回 date)
- K线买卖点按完整 datetime 对齐(分钟线不再折叠)
- count 上限对齐后端 800
- ECharts 注册内联 dark 主题
- 参数表单空输入不打断编辑
- 网络错误友好提示
技术栈:Vue 3.5 / Vite 8 / TS 6 / Pinia / ECharts(按需引入 725KB)
2026-07-03 03:35:20 +08:00
Justin Gu
38731114b6
feat(backtest): 回测 REST API + 策略注册表 + 组合回测引擎
...
后端回测系统完整实现:
策略注册表(backtest/strategies/):
- Param schema 声明机制,支持动态表单渲染
- 5 个内置策略:MA交叉/MACD/布林/RSI/KDJ
- 参数校验(含 NaN/Inf 拦截、范围检查、类型强制转换)
REST API(web/routers/backtest.py):
- GET /backtest/strategies 策略枚举 + 参数 schema
- POST /backtest/run 同步回测(内联 OHLCV)
- POST /backtest/run/async 后台任务回测(含 symbol 取行情)
- POST /backtest/portfolio/run/async 组合回测(多标的)
- GET /backtest/tasks/{id} 任务轮询
后台任务执行器(web/task_runner.py):
- ThreadPoolExecutor + 进程内 LRU 任务表
- status-aware 淘汰(不淘汰 running 任务)
- 线程安全单例 + lifespan shutdown 接入
组合回测引擎改造(portfolio_engine.py):
- 接受策略实例,参数透传到每个标的
- 新增组合净值曲线(按日期并集 forward-fill 对齐求和)
审计修复(/check 三轮):
- Param.validate 拦截 NaN/Inf/giant-int(防 DoS)
- ohlcv max_length=2000(防内存耗尽)
- LRU 淘汰跳过 running 任务(修复结果丢失竞态)
- get_runner double-checked locking(修复单例竞态)
- shutdown 接入 lifespan(修复资源泄漏)
测试:808 passed(含 39 回测路由 + 8 组合引擎 + 安全回归)
2026-07-03 03:34:34 +08:00
Justin Gu
803f2fc3ad
fix(deps): bump pytest 8.3.5 -> 9.0.3 修复 tmpdir 安全漏洞 (Dependabot #1 )
...
CVE: pytest <9.0.3 存在临时目录处理安全问题(中等严重程度)。
本地 Python 3.11 venv + pytest 9.0.3 全量测试通过:761 passed, 2 skipped。
pytest 9.0.3 要求 Python>=3.9,满足 CI 矩阵 3.10/3.12/3.13。
2026-07-03 01:33:11 +08:00
Justin Gu
1e7766754e
style: ruff format tests/unit/test_a_share_extensions.py
...
修复 GitHub Actions 中 ruff format --check 的格式化失败。
2026-07-03 01:30:40 +08:00
Justin Gu
133c208510
release: v1.16.3 — 修复 market-stat 全市场涨跌统计家数偏小10倍
...
通达信统计指数 880005/880006 的计数类字段返回真实家数的 1/10,
get_market_stat 未做缩放还原,导致 up/down/total/limit_up 等系统性偏小 10 倍。
对 6 个计数字段统一 round(field * 10),成交额/量/市值不动。
重写 test_get_market_stat_mapping 覆盖还原逻辑。761 单测全绿。
2026-07-02 12:46:27 +08:00
Justin Gu
3945800728
feat(cli): company-info 传板块名自动读完整正文(分块循环 + 多服务器重试)
...
修复体验问题:此前传板块名仍需用户关心 --offset/--length,太笨拙。
现在传板块名即自动按目录 length 分块循环读取整个板块(单次上限 30720 字节,
大板块如「公司大事」77万字节也能一次读全),--offset/--length 仅传文件名时生效。
- _resolve_filename 返回板块 length,_run_content 分块循环读取完整内容
- 修复分块 offset 推进 bug:原按解码字符串 GBK 重编码计字节,遇 U+FFFD 崩溃;
改为按请求字节数推进(服务器按字节偏移工作)
- 修复多服务器目录版本不一致:传板块名未命中时自动重试多个服务器(最多4次)
- bump 版本号至 1.15.3
2026-06-27 04:37:44 +08:00
Justin Gu
297a479928
chore: 从 git 跟踪移除 CLAUDE.md(本地文件保留)
...
修正上一个 commit:CLAUDE.md 应从版本控制移除(本地配置,不递交 GitHub),
但被误重新加入。本次 git rm --cached 真正移除跟踪,本地文件保留继续使用。
2026-06-27 04:23:46 +08:00
Justin Gu
202415dd19
chore: CLAUDE.md 改为本地文件,不再递交到 GitHub
...
CLAUDE.md 含本地开发指引(venv 使用要求等),属于本地配置。
git rm --cached 移除跟踪(本地文件保留),加入 .gitignore 防止误加回。
2026-06-27 04:22:50 +08:00
Justin Gu
781ee6c48a
feat(cli): company-info 命令合并 + examples/06_finance 文档完善
...
合并 company-info(列目录)与 company-info-content(读正文)为一个命令,
板块名改为可选位置参数:无参数列目录,有参数读正文。消除两个相似命令名
导致的混淆(用户曾误用 company-info SH 601088 "公司概况" 报错)。
- company-info:name_or_filename 可选,无则列 F10 板块目录,有则读正文
- company-info-content:保留为隐藏别名(hidden=True),向后兼容 v1.15.1
- 提取 _run_category/_run_content 模块级函数复用逻辑
- 新增 examples/06_finance/{README.md,company_cli.sh,company_web_api.py}
覆盖 CLI / Python API / Web API 三种调用方式,含 16 个 F10 板块完整列表
- 更新 company_info.py 板块名为实测的 16 板块
- bump 版本号至 1.15.2
2026-06-27 04:22:43 +08:00
Justin Gu
3df1ea607f
chore(release): v1.15.1
...
同步版本号至 1.15.1:pyproject.toml + CHANGELOG。
2026-06-27 04:05:57 +08:00
Justin Gu
29b6ca33aa
feat(cli): 新增 finance-info / company-info / company-info-content 命令
...
把 TdxClient 上已封装但未暴露给 CLI 的三个 F10/财务方法做成命令,
数据源走通达信原生协议(与 Web 层 /finance /company/* 同源),
覆盖 f10(新浪三表)之外的 F10 全文板块。
- finance-info: 最新财务快照(37 字段单期指标),与 f10 多期三表互补
- company-info: F10 板块目录(最新提示/公司概况/财务分析/... 等 16 板块)
- company-info-content: 读 F10 正文,支持板块名(自动解析)或文件名
--offset 语义随入参而定:板块名=板块内相对偏移,文件名=绝对偏移
- conn.py: 新增 get_tdx_client() 上下文管理器
补 README 命令表与 CHANGELOG。
2026-06-27 04:02:09 +08:00
Justin Gu
77104a32e3
fix(ci): mypy python_version 3.10 → 3.12 修复 numpy stub 语法错误
...
根因:CI 的 mypy job 在 Python 3.13 下运行,安装的 numpy 2.x stub
使用 PEP 695 type 语句(3.12+ 语法),但 pyproject.toml 的
python_version="3.10" 让 mypy 按 3.10 语义解析,报
'Type statement is only supported in Python 3.12 and greater'。
修复:
- python_version 提升到 3.12(CI mypy job 实际跑 3.13,开发目标版本对齐)
- mypy/ruff 同步排除 gitignored 的 exchange_margin.py 本地脚本
- 顺带修复 market.py / test_screen.py 的 ruff format 漂移
2026-06-25 03:46:57 +08:00
Justin Gu
e5bc1b1a16
docs: 将 README 中的 Changelog 迁移至独立的 CHANGELOG.md
...
- README 历史版本记录(1.0.0 → 1.14.5)全部搬运到 CHANGELOG.md
- README Changelog 章节替换为指向 CHANGELOG.md 的链接
- CHANGELOG.md 现包含完整 41 个版本(1.15.0 → 1.0.0)的变更记录
2026-06-25 03:41:54 +08:00
Justin Gu
183cbfdd60
chore: ignore local script src/easy_tdx/exchange_margin.py
...
该脚本依赖外部 unified_config / sqlalchemy / requests,不属于 easy_tdx 库,
保持本地运行状态,不纳入版本跟踪。
2026-06-25 03:36:32 +08:00
Justin Gu
f36e2d6a6c
feat(screen): v1.15.0 — 强势股排名 + 修复证券类型识别与名称分批查询
...
新增:强势股排名(screen strength)
- 全市场按 5/20/60 日涨幅加权合成强势分,纯离线扫描
- 三种预设:steady(稳健)/breakout(妖股)/balanced(均衡)
- CLI: easy-tdx screen strength --preset steady --top 50 --table
- Web API: GET /api/v1/market/strength
- 支持自定义权重、成交额过滤、并发扫描
修复:
- _detect_security_type 代码段不全,ETF/基金/科创板/逆回购被误判为 A 股
- screen strength/rank 名称补齐超 80 只时末尾被丢弃(分批查询)
详见 CHANGELOG.md
2026-06-25 03:33:13 +08:00
Justin Gu
85e0f8a65f
feat(chanlun): 分钟级别日期自适应输出时分 YYYY-MM-DD HH:MM
2026-06-17 18:17:32 +08:00
Justin Gu
87f11e409c
release: v1.14.4 — 修复 cmd_chanlun.py ruff format CI 失败
2026-06-16 10:03:48 +08:00
Justin Gu
aa58bd9e88
release: v1.14.3 — 缠论 CLI table 模式补日期(中枢/买卖点/背驰)
2026-06-16 03:04:12 +08:00
Justin Gu
1945fa1261
feat(chanlun): CLI table 模式 zss/mmds/bcs 显示日期字段
2026-06-16 03:00:08 +08:00
Justin Gu
28aad8a84c
release: v1.14.2 — 缠论 JSON 可视化字段增强(中枢/买卖点/背驰补日期)
2026-06-16 02:51:52 +08:00
Justin Gu
b49cfd66f8
feat(sina): 新增新浪财报三表 — 三层接入(API/CLI/Web),独立数据源
2026-06-15 00:31:13 +08:00
Justin Gu
a5ce783487
release: v1.13.1 — cninfo 公告检索 Bug 修复 + PDF 下载
2026-06-15 00:05:52 +08:00
Justin Gu
574b284dd5
fix(cninfo): URL 404 + type null + 表格截断 + PDF 下载(实测 601088 暴露)
2026-06-14 23:58:24 +08:00
Justin Gu
c0605698f3
docs(cninfo): 补齐 README 三层接入 demo(Python API + Web 返回结构)
2026-06-14 23:35:06 +08:00
Justin Gu
4b439920fc
feat(cninfo): 新增巨潮公告检索 — 三层接入(API/CLI/Web),独立数据源
2026-06-14 23:29:19 +08:00
Justin Gu
a12c2122fc
feat(indicator): 新增 SAR/VWAP/AROON 三指标 + 注册 FK(30 → 34)
2026-06-14 18:46:02 +08:00
Justin Gu
37635af8f2
release: v1.11.6 — CI mypy/ruff 修复
2026-06-13 21:26:13 +08:00
Justin Gu
5fc398255d
fix(types): 修复 CI mypy strict + ruff format 失败
...
mypy (13 errors → 0):
- portfolio/optimizer: register_optimizer 返回类型改为 Callable 装饰器签名
(原标注 type[WeightOptimizer] 导致 4 个子类 Too many arguments)
- factor/engine: _datetime_to_int 用 isinstance 收窄替代 object→int 强转
- factor/analysis: 删多余 type:ignore(改由 mypy override 统一处理 scipy)
- backtest/orders, execution: np.sqrt 表达式用 float() 包裹消除 no-any-return
- MyTT.pyi: MACD 签名删除错误的 LOW/HIGH 参数(与 MyTT.py 实际签名对齐)
- pyproject: 新增 scipy mypy override (ignore_missing_imports)
ruff format: 8 个 test 文件格式化
验证: 564 passed, mypy 192 文件零错误, ruff check/format 全绿
2026-06-13 21:21:33 +08:00
Justin Gu
88638e82ad
release: v1.11.5 — scipy 依赖声明 + 文档同步 + 工程化清理
...
- pyproject: 新增 science 可选依赖组(scipy),dev 纳入 scipy;版本号 1.11.5
- factor/analysis: spearman 分支加 scipy 软依赖保护,缺失时友好报错
- mac/client: 板块排行吞异常处补 logger.debug
- CLAUDE.md: 架构章节补全 mac/ex/unified/portfolio/factor/offline/screen 子包
- .gitignore: 补全 .coverage、signals.json
- README: 补 1.11.5 changelog
2026-06-13 21:10:35 +08:00
Justin Gu
be41746aa9
fix(backtest): _find_bar_index 用 to_numpy().argmax() 取真实位置
...
idxmax() 返回 index label,后续 iloc[] 按位置取行;当 df.index 非默认
RangeIndex 时 label != position,撮合会取错 K 线。两处分支统一改为位置索引。
新增 2 例非连续 index 回归测试。
2026-06-13 21:10:15 +08:00
Justin Gu
095c88f735
fix(transport): ping 容错 TdxConnectionError,避免单台服务器拖垮测速
...
ping_host 仅 except OSError,但握手期 _recv_exact_sock 抛的 TdxConnectionError
继承自 TdxError(Exception) 而非 OSError,逃出捕获后经 ping_all 的 fut.result()
重新抛出,导致非交易时间服务器 accept 后立即 FIN 时整个 easy-tdx ping 崩溃。
- ping_host: except (OSError, TdxConnectionError),对齐 docstring 返回 None
- ping_all: fut.result() 加 try/except 防御层,单 host 失败只跳过不崩
- 新增 2 例回归测试
2026-06-13 21:09:55 +08:00
Justin Gu
06ce712d15
fix: CI mypy job missing [web] deps + update README intro
2026-06-12 03:52:47 +08:00
Justin Gu
2eedd81093
feat(web): FastAPI REST + WebSocket API layer (v1.10.0)
2026-06-12 03:36:11 +08:00
Justin Gu
d904ae7af6
docs(web): update README with serve CLI, docs URL, and install instructions
2026-06-12 03:35:26 +08:00
Justin Gu
0e74752701
fix(web): validate market/category input — support lowercase, reject invalid with 400
...
Root cause: _market_from_str/_market/_category in routers used bare
MarketEnum[key]/Market[key] without .upper() or try/except, so
lowercase or invalid values (sz, ZZZ) threw uncaught KeyError → 500.
Fix: extract shared convert.py with market_from_str/category_from_str
that do .upper() + ValueError on invalid input. All 4 routers updated.
4 regression tests added for case-insensitive and invalid input.
2026-06-12 03:26:29 +08:00
Justin Gu
9d7a9161f7
fix(web): remove TYPE_CHECKING guard for AsyncTdxClient in deps.py
...
The typing.cast(AsyncTdxClient, ...) evaluated AsyncTdxClient at
runtime, but the import was gated behind TYPE_CHECKING, causing
NameError in production. Direct import is safe here — web module
already depends on easy_tdx core.
2026-06-12 03:21:39 +08:00
Justin Gu
54dd65ed85
feat(web): add Web API docs to README, bump version to 1.10.0
2026-06-12 03:10:01 +08:00
Justin Gu
eb8a7a5675
feat(web): add FastAPI app factory, all routers, CLI serve command, and tests
...
- App factory with lifespan management and CORS middleware
- Market router: security list, quotes, market stat, fund-flow
- Bars router: kline, index kline, minute, transaction
- Finance router: xdxr, finance, company info, financial records
- Block router: block file parsing
- Chanlun router: POST /chanlun/analyze
- Realtime router: WebSocket /ws/realtime/{symbol}
- CLI: easy-tdx serve command
- 16 unit tests, all passing offline (no network)
2026-06-12 03:08:12 +08:00
Justin Gu
9dc70566a5
feat(web): add Pydantic schemas and error handling
2026-06-12 03:02:04 +08:00
Justin Gu
7f44ba1d06
feat(web): scaffold web API module with [web] optional dependency
2026-06-12 03:00:49 +08:00
Justin Gu
786c7e3619
fix: remove unused type: ignore comments for mypy strict
2026-06-11 04:30:39 +08:00
Justin Gu
c25570eb86
fix: mypy strict — type annotate co_filename in _get_strategy_file
2026-06-11 04:27:54 +08:00
Justin Gu
c9ed57e66d
fix: parallel scan pickle bug — pass strategy file path instead of class to child processes
2026-06-11 04:24:09 +08:00
Justin Gu
ddcb9d4425
release: v1.9.8 — CI fixes (ruff .pyi exclude + pytest-asyncio + lint)
2026-06-11 04:11:01 +08:00
Justin Gu
bd373b9c3c
fix: exclude .pyi from ruff + fix unused variable in test
2026-06-11 04:06:52 +08:00
Justin Gu
193d1b0d82
fix: add pytest-asyncio dependency for async realtime tests
2026-06-11 04:03:00 +08:00
Justin Gu
15cc7680c4
release: v1.9.7 — CLI全量集成(workers/cache/chanlun-level/portfolio/multi-level)+ bugfix
2026-06-11 03:57:48 +08:00
Justin Gu
d2f4cb126e
release: v1.9.6 — Devin AI 12项改进全量落地
2026-06-11 02:40:31 +08:00
Justin Gu
92edc189bb
feat: realtime event-driven market data push framework
...
- Add EventBus for async publish/subscribe market events
- Add MarketEvent dataclass with tick/bar/signal/error types
- Add RealtimeStrategy base class with on_tick/on_bar callbacks
- Add emit_signal() for strategy-to-engine signal publishing
- Support per-symbol and global subscriptions
- API skeleton: transport-level subscription TBD
- Add 10 tests covering events, bus, and strategy
2026-06-11 02:34:00 +08:00
Justin Gu
9c39ad054d
feat: multi-stock portfolio backtest engine
...
- Add PortfolioBacktestEngine for shared-capital multi-stock backtesting
- Support equal allocation mode (total_cash / N per stock)
- Individual BacktestEngine per stock with allocated capital
- Aggregate performance via capital-weighted returns
- Add StockData, PortfolioResult data classes
- Add 4 tests: basic run, equal allocation, empty stocks, serialization
2026-06-11 02:31:43 +08:00
Justin Gu
fa5bd2d44d
feat: add MyTT.pyi type stubs, enable strict mypy
...
- Create type stub file for 50+ MyTT indicator functions
- Covers all 31 functions used in the project + common utilities
- Remove mypy disallow_untyped_defs/calls override for MyTT
- MyTT now covered by strict mypy via .pyi stubs
2026-06-11 02:29:47 +08:00
Justin Gu
f4dc28c5d2
feat: add append_klines for incremental chanlun analysis
...
- Store previous DataFrame in ChanlunAnalyser after process_klines
- Add append_klines(df_new) to concatenate and recompute
- Handles datetime deduplication automatically
- Raises RuntimeError if called before initial process_klines
- Add 2 tests: append + recompute, error without init
2026-06-11 02:25:42 +08:00
Justin Gu
6d74c2e5eb
feat: enhance multi-level chanlun analysis with trend/overlap/divergence
...
- Add trend_direction detection (up/down/None) via consecutive bi analysis
- Add bi_overlap detection for consolidation identification
- Add divergence_possible check (2+ ZS + diminishing amplitude)
- Return type changed from dict[str, int] to dict[str, Any]
- Backward compatible: all original fields preserved
- Extract 3 helper functions for clean separation of concerns
2026-06-11 02:22:31 +08:00
Justin Gu
ec8d21b7e2
feat: incremental scanning with mtime-based cache
...
- Add cache_file param to SignalScanner for persistent scan cache
- Cache stores {filepath: {mtime, result}} as JSON
- On rescan, skip files with unchanged mtime (reuse cached results)
- Files with changed mtime are rescanned and cache updated
- Add 3 tests: cache reuse, no-cache full scan, cache invalidation
2026-06-11 02:17:52 +08:00
Justin Gu
b7e0f17842
feat: concurrent scanning with ProcessPoolExecutor
...
- Add workers param to SignalScanner.scan() (default=0 for serial)
- workers=2+ uses ProcessPoolExecutor for parallel .day file processing
- Extract _scan_one_file as top-level function for pickle compatibility
- Results identical between serial and parallel modes
- Add 4 tests with synthetic .day file fixtures
2026-06-11 02:09:10 +08:00
Justin Gu
af005d9fe4
feat: auto-bridge chanlun analysis into backtest strategies
...
- Add chanlun_level param to BacktestEngine constructor
- When set, auto-create ChanlunAnalyser and compute ChanlunResult
- Manual chanlun_result in run() takes priority over auto-compute
- Update Strategy.chanlun type to Any (accepts ChanlunResult or dict)
- Add 2 tests: auto-bridge and manual override priority
2026-06-11 01:56:59 +08:00
Justin Gu
815b3ddf7c
feat: implement stop-loss/take-profit execution in backtest engine
...
- Track SL/TP conditions from BUY signals in _generate_signals loop
- Check active conditions against each bar's high/low price range
- Auto-generate SELL signal at trigger price when condition is met
- Modify OrderSimulator to respect signal.price for direct execution
(previously signal.price was stored but never used in execution)
- SL/TP activates on bar AFTER BUY signal (consistent with next_open)
- Stop-loss checked before take-profit (conservative for holder)
- Add 4 tests: SL trigger, TP trigger, no-trigger, priority over manual sell
2026-06-11 01:53:11 +08:00
Justin Gu
06b2617ebc
fix: CI coverage enforcement, real avg_holding_days, vectorize _datetime_to_int
...
- Add --cov and --cov-fail-under=50 to CI pytest command
- Replace hardcoded avg_holding_days=5.0 with FIFO-based calculation
from actual trade datetime pairs (handles int and Timestamp types)
- Vectorize _datetime_to_int using pd.to_datetime().strftime()
instead of Python for-loop (~100-200x faster on large arrays)
- Add 3 new test cases: weighted holding days, no datetime fallback,
only-buys edge case
2026-06-11 01:44:39 +08:00
Justin Gu
5aac7d3a39
chore: add risk disclaimer, CI pipeline, pytest coverage config
...
- Add risk warning after backtest demo output (survivorship bias, overfitting)
- Add disclaimer section at README end
- Add slippage comment to backtest demo command
- Add CI workflow (test + lint + mypy) for PR/push to main
- Add pytest config with coverage threshold (fail_under=50, current=56%)
2026-06-10 12:43:02 +08:00
Justin Gu
21f71d3701
chore: ignore .omc, .claude, .playwright-mcp directories and remove from tracking
2026-06-10 11:54:08 +08:00
Justin Gu
c867297d45
fix: remove hardcoded version in 'version' command (v1.9.4)
2026-06-10 03:40:28 +08:00
Justin Gu
531d0c05bc
fix: remove hardcoded version in 'version' command, read from importlib.metadata
2026-06-10 03:39:09 +08:00
Justin Gu
862f713483
feat: add 'run-all' CLI command for batch strategy backtesting (v1.9.3)
2026-06-10 03:24:31 +08:00
Justin Gu
a3d6d93122
feat: strategy screener - scan all stocks by signal, rank by backtest performance (v1.9.2)
...
- Add 'screen' CLI command group with 'scan' and 'rank' subcommands
- scan: offline signal scanning from local .day files, zero network IO
- rank: backtest ranking of scanned signals by sharpe/drawdown/etc
- Two-step workflow: scan outputs JSON, rank reads JSON and evaluates
- Support --universe (all/sh/sz/custom file), --sort, --names
- Support pipe mode: scan ... | rank --from - --table
- New module: src/easy_tdx/screen/{scanner,ranker,cli}.py
- 20 unit tests (offline, no network required)
2026-06-10 03:03:03 +08:00
Justin Gu
cb0a8ef6c5
chore: bump version to 1.9.1
2026-06-10 02:14:53 +08:00
Justin Gu
5691bb8432
refactor: screen() reuses run_combination(), single runner across combo sizes
...
- screen() now calls run_combination() internally, eliminating duplicated
signal extraction/combination logic
- _run_combo_screen creates one CombinationRunner before the size loop,
so signal cache is reused across 2-factor and 3-factor screens
- Add MAJORITY(2)=AND note to screen() docstring
2026-06-10 02:13:28 +08:00
Justin Gu
1e99feb7c2
feat: multi-factor combo backtest engine (v1.9.0)
...
- Add backtest/combo.py: CombinationRunner, extract_factor_signals, combine_masks
- Signal merge modes: AND / OR / MAJORITY (majority default)
- CLI: --combo-strategies and --combo-mode for easy-tdx backtest
- run_all_strategies.py: --combo 2 --combo 3 auto-screen best combos
- Fix MyTT MFI/CR divide-by-zero RuntimeWarning
- 14 new unit tests, 328 total passing
2026-06-10 01:37:28 +08:00
Justin Gu
626d0aae44
docs: remove unused badges (readthedocs, black, isort)
2026-06-08 03:44:46 +08:00