Justin Gu
9be6f156c7
fix: 热点相关性矩阵行列顺序跨重启随机互换 — set 迭代序非确定性
...
board-mac/hotspot-correlation 的入阵板块来自 set 的并集,
sorted(days_in, key=-days_in) 在上榜次数并列时保持 set 迭代序,
而字符串哈希随机化(PYTHONHASHSEED)使该顺序跨进程不稳定——
服务每次重启矩阵的行/列都可能互换(全量单测中按哈希种子复现)。
排序加 code 兜底使其确定性;热点矩阵 rows_out 的多级排序同样
补 code 兜底(全并列时此前也受 set 序影响)。
2026-09-05 11:42:05 +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
a9782b74b9
feat: 盘面洞察收官 — AI 盘面复盘 + 板块主力资金日历
...
- AI 盘面复盘(市场情绪页):一键汇总当前情绪/量能/涨停梯队数据生成
摘要 Prompt,走 /llm/chat/async 异步任务 + 轮询,回复页内展示并经
_record_history 自动归档「AI 解读历史」
- 板块资金日历:FundFlowSampler 每交易日 14:45 后经
get_board_ranking(main_net_amount) 采样行业主力净流入 Top10,
SentimentStore 新增 board_fund 表((date,rank) 幂等);口径为
"涨幅前 50 名中主力净流入最高 10 个"(逐板块 summary 太贵,非全市场
严格排序);/market/board-fund/history + 市场情绪页日历视图
- app.py lifespan 接线启停;路线图 ⑥⑩ 标记收官
2026-09-05 04:43:22 +08:00
Justin Gu
7f0f14532e
refactor: 移除异动雷达 — 数据源盘中几乎无记录,功能不达预期
...
- 删除 /radar 页面与导航、看板「异动雷达」卡片、fetchUnusual 封装
- 移除 /mac/unusual HTTP 端点(MAC 协议层 get_unusual 与 mock 保留,
属协议覆盖面);路线图 ③ 标记取消
2026-09-05 04:42:53 +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
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
5476ba0f33
feat: 参数寻优新增买入持有基准对比 — 两个寻优接口返回同口径 buy_hold(run_buy_hold_benchmark),全局最佳/最优结果总收益旁展示 [买入持有:xx.xx%],涨红跌绿
2026-09-04 22:42:13 +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
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
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
06b5d380d5
fix(ci): mypy 三处类型修正(lastrowid 直赋 / json.loads 收窄 / 移除失效 ignore)
2026-09-02 20:30:35 +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
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
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
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
GitHub
1b7d8a5fff
docs(client): 资金流口径标注——0x0fb5 逐笔为聚合记录、按成交额分档,与东财主力净额不可比(issue #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
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
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
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
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
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
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
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
25236056f7
fix(web): 单飞改用轮询解耦,预热超时不再波及端点请求
...
问题:上一版单飞用 asyncio.Future 共享,lifespan 预热用 wait_for(120s)
超时会 cancel 整个 Future,导致同时等待的 /security/search-index 端点
请求收到 CancelledError 返回 500。日志显示预热 TimeoutError + 端点 500。
修复:
- _build_search_index 改用轮询设计:后台 _do_build task 独立运行
(fire-and-forget),调用方通过轮询 _SEARCH_INDEX 每 0.5s 检查结果
- 调用方被 cancel(预热超时)只退出轮询,不影响后台构建 task
- lifespan 预热去掉 wait_for,改为纯 best-effort 触发,无权取消构建
- 冒烟验证:预热被 cancel 后,端点请求仍能正常拿到结果(5206 条)
另外从日志看慢机器爬全名单 >120s,轮询上限提到 300s 兜底
2026-07-06 02:39:02 +08:00
Justin Gu
04fd720a05
fix(web-ui): 搜索索引前端超时 15s→120s + 后端单飞去重
...
问题:另一台机器首次加载报 'signal is aborted without reason'——
不是取不到数据,是前端 AbortController 15 秒就掐断了请求,但遮罩
却告诉用户等 30-60 秒,自相矛盾。
修复:
- 前端 fetchSearchIndex 超时 15s → 120s:遮罩本就是要等的,不能自己掐断
- 后端 _build_search_index 单飞去重:用 asyncio.Future 让 lifespan 预热
与 /security/search-index 端点请求共享同一任务,避免并发各爬一次全名单
(两个任务抢同一把 _execute_lock 反而互相拖慢)
- lifespan 预热改为调 _build_search_index(复用单飞),加 120s 超时保护
2026-07-06 02:31:20 +08:00
Justin Gu
6b01c0687c
feat(web-ui): 股票搜索索引加载期间全站遮罩 + 后端启动预热
...
新机器首次打开页面时,拼音搜索索引构建要走全量 get_security_list_all
(几十秒),期间无任何反馈,体验极差。本次双管齐下解决:
前端:
- 新增 AppInitOverlay 全局遮罩:加载期间全屏灰底 + blur + spinner +
诚实文案(预计 30-60 秒)。就绪后 fade-out;失败也消失(不卡死)
- useStockSearch 改为模块级响应式状态,App 根 setup 时 eagerLoad 触发
- 方案 A:无跳过按钮强制等待(一次性代价),失败时降级为可手输代码
后端:
- lifespan 启动钩子加后台预热:asyncio.create_task 触发一次
get_security_list_all('all'),不阻塞服务启动,让本地缓存提前建立
- 用户打开页面时后端缓存大概率已就绪,遮罩可秒消失
2026-07-06 02:20:12 +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
d32c5e17b4
feat(web-ui): 一键寻优所有策略支持多进程并发(串行/4/8/16)
...
回测是 numpy/pandas 的 CPU 密集计算并持有 GIL,多线程无加速,必须用
多进程。照搬项目里已跑通的 screen/scanner.py 进程池模板。
后端:
- OptimizeAllBacktestRequest 新增 workers 字段(默认 1=串行,0-32)
- 抽出模块顶层函数 _optimize_one_strategy(可被 ProcessPoolExecutor
pickle),策略类在子进程内构造,避开跨进程 pickle 问题
- _run_optimize_all 在 workers>=2 时用进程池并行,否则走原串行逻辑
- 顺带修复 backtest_schemas.py 的 E501 历史遗留
前端:
- OptimizeView 配置区新增「一键寻优并发」选择器:自动检测 CPU 核数
+ 串行/4/8/16 进程下拉,默认串行,标注推荐档 min(CPU, 8)
- types.ts/api.ts 透传 workers 字段
实测 8 进程 vs 串行提速 4-6x;端到端冒烟验证并行与串行结果完全一致
2026-07-06 00:59:26 +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
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
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
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
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