毛利哥
8bf4ffb322
Merge pull request #20 from handsomejustin/feat/auto-failover
...
feat: v1.20.0 服务器失败自动 ping 切换(无需手动 easy-tdx ping)
2026-07-08 18:13:03 +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
e8b088bca1
fix(ci): ci.yml test/mypy job 先构建前端再 pip install
...
pyproject.toml 的 force-include 要求 web-ui/dist 存在,否则 pip install -e .
报 FileNotFoundError。ci.yml 的 test 和 mypy job 此前没有构建前端步骤,
导致全部 CI 失败。
修复:test(6 矩阵)和 mypy job 在 pip install 前加 setup-node + npm ci +
npm run build。frontend job 本来就有,不变。
同时清理 pyproject.toml:恢复 force-include(之前误改成 build-hook 但
hatchling 自定义 hook 需要插件注册,过于复杂)。
2026-07-07 22:14:03 +08:00
Justin Gu
9ab093a3c7
fix(build): hatchling artifacts 让 sdist 也含 web-ui/dist
...
python -m build 先构建 sdist 再从 sdist 构建 wheel。sdist 默认尊重 .gitignore
不含 web-ui/dist(构建产物),导致 wheel 阶段 force-include 找不到源报
FileNotFoundError。用 [tool.hatch.build] artifacts 强制 sdist 和 wheel
都包含 web-ui/dist。
本地验证:sdist 含 4 个 dist 文件,wheel 含 4 个 web/dist 文件。
2026-07-07 20:50:48 +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
6b51487477
test: 修复 test_task_runner_lru_eviction 的竞态 flaky
...
根因:测试用 max_workers=1 串行执行 5 个任务,断言「前 2 个被淘汰」。
但 LRU 淘汰发生在 submit() 时,淘汰的是「当时最旧的非 running 任务」。
快机器上 t0 还在 running(被跳过),实际淘汰 t1/t2;慢机器(CI windows
3.12)上 t0 已完成,淘汰 t0/t1——取决于提交速度 vs 执行速度的竞态。
修复:不断言特定 task_id 被淘汰,改为验证不变量:
(1) 最后提交的任务一定存活(LRU 最近)
(2) 至少淘汰 2 个(5 - max_results 3)
(3) 存活任务数 ≤ max_results
本地跑 10 次全过,不再 flaky。
2026-07-07 15:47:13 +08:00
Justin Gu
27a8ab1c76
style: ruff format security_bars.py(修复 ci.yml format 检查)
...
合并 e3e8dd4 时采用远程版本,但其 _log.warning 字符串换行未跑
ruff format,导致 ci.yml 的 format 检查失败。纯格式调整,无逻辑变化。
2026-07-07 15:27:20 +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
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
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
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
dfbafd5ad4
docs(readme): 补充拼音声母搜索的升级提示(v1.19.0 需重装 [web] extra)
...
- 前置条件代码块注释加 pypinyin
- 新增 ⚠️ 升级注意:旧版本升级必须重跑 pip install -e ".[web]" 否则搜索端点 500
- 单标的回测取行情描述补上拼音声母搜索能力说明
2026-07-06 01:49:49 +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
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
5aec4996c6
feat(web-ui): 策略库组合回测结果加组合评级 + 市场前缀纠正
...
- StrategiesView: 组合回测结果区新增「组合评级」详情块,复用
gradePortfolio(与 /portfolio 页和单标的回测页同口径的 5 维度评分)
- StrategiesView: 新增 normalizeSymbol,发请求前用 detectMarket 重算
市场前缀,纠正历史保存策略的错配(如 SZ:515030 → SH:515030)
- BacktestView: fullSymbol 改用 market.ts 的 detectMarket,修复 5 开头
沪市基金/ETF 被错判为深市的问题(与 SymbolPicker/StocksPicker 统一)
2026-07-06 00:59:13 +08:00
Justin Gu
6f121dc156
fix(web-ui): 参数寻优「查看」用寻优时的标的快照,避免输入框/路由切换污染 URL
...
- OptimizeView「查看」拼 URL 前从输入框实时值取 code/startDate/endDate/category,
导致两个问题:(1) 寻优后改输入框代码再点查看,URL 指向没回测过的标的;
(2) 切走再回 /optimize,组件 ref 丢失,URL 里 symbol/startDate/endDate 全空。
- 将标的上下文快照(取行情成功那一刻冻结)下沉到 Pinia store 的 optimizeContext,
与 optimizeResult/optimizeAllResult 同生命周期持久化,跨路由保留。
- buildBacktestQuery 改读 store.optimizeContext,输入框改动与组件卸载都不再污染。
2026-07-05 18:03:12 +08:00
Justin Gu
173069d518
docs(example): 异动示例改为分页拉取全部(单次 600 协议上限)
...
get_unusual 的 count 实际被 min(count,600) 截断,写 1000 也只能拿到 600。
改用 start 翻页 + pd.concat 合并,直到某页不足 600 视为到尾。
2026-07-05 18:02:38 +08:00
Justin Gu
5c520c513d
docs: 上手手册更新到 v1.18.0(策略组合保存+一键看今日信号+Tab 分类)
...
新增「第十章补充」完整章节,详细写:
- 如何保存策略组合(💾 保存为组合按钮 + 弹窗)
- 如何载入组合(↻ 重跑到今天,自动用今天重跑)
- 持仓三态徽章含义(持有/持有·浮亏/空仓·等买点)
- 三处警示(过拟合/模型仓位水印/载入确认)
- 完整日常使用流程(每天收盘后重跑看信号变化)
- 组合的局限(模型仓位≠真实账户、资金均分、过拟合风险)
补充更新:
- 第四章 4.4 加投资大师名言轮播说明(100 条/3 秒轮播)
- 第六章 6.2 加策略库 Tab 分类说明(单标的/组合)
- 封面 + footer 版本号 v1.17.14 → v1.18.0
- 目录加第十章补充链接
保持原作者口语化新手教学风格,沿用原文半角标点惯例。
2026-07-05 03:06:40 +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
15af7a92ee
feat(web-ui): 一键寻优按钮改橙色 + 等待期间投资大师名言轮播
...
v1.17.15
一键寻优所有策略按钮改为暖橙渐变(#f59e0b → #ea580c),比 primary
蓝更醒目,hover 加亮上移、运行中保留暗橙识别度。
寻优运行中右侧不再空白:展示 100 条全球投资大师名言(巴菲特/芒格/
格雷厄姆/林奇/索罗斯/利弗莫尔/塔勒布/达利欧等),3 秒随机轮播一条,
Fisher-Yates 洗牌避免短期重复,让等待不枯燥、还能学到东西。
视觉细节:
- 卡片顶部 3 秒线性进度条(暗示后台在跑)
- fade + slide-up 350ms 过渡动画
- 底部脉动橙点 + '后台寻优进行中' 提示
纯前端 TypeScript + 原生 CSS,零后端改动,零依赖新增。
- 新增 web-ui/src/data/investment-quotes.ts(100 条名言)
- 新增 web-ui/src/components/QuoteCarousel.vue(轮播组件)
- 改 web-ui/src/views/OptimizeView.vue(按钮样式 + 引入轮播)
- bump pyproject.toml 1.17.14 → 1.17.15
2026-07-05 02:17:22 +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
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