Commit Graph
12 Commits
Author SHA1 Message Date
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 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 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 9048dfecaa fix(web-ui): 拼音搜索索引改为按需加载,避免首次构建阻塞行情请求
问题:新机器首次打开回测/寻优页时,组件挂载即触发 /security/search-index,
后端要走全量 get_security_list_all(几十次 TDX 协议往返,几十秒)。
而 AsyncTdxClient 是单连接 + _execute_lock,索引构建期间独占连接,
把 /bars 行情请求和寻优请求全部堵在后面,表现为点「一键寻优」后
等好久才出现第一根 bars 请求。

修复(三招组合):
- useStockSearch: 挂载即拉 → 改为显式 ensureLoaded(),只在首次聚焦
  输入框时才触发,确保「打开页面直接点取行情/寻优」的核心路径不被拖累
- fetchSearchIndex: 加 AbortController 15s 超时,避免长时间独占连接
- 静默失败降级:索引拉取失败只记 loadError 显示小 ⚠,不弹错不阻塞
2026-07-06 02:01:47 +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 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 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 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