Files
big-qmt/docs/todo.md
2026-08-29 01:50:09 +08:00

228 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### 2.4 开仓和补仓没有共享同一个本轮资金预算
位置:
- `py-client/strategy/trend/boot.py:133-140`
- `py-client/strategy/trend/boot.py:182-187`
- `py-client/strategy/trend/open.py:15-53`
- `py-client/strategy/trend/positions.py:44-84`
现状:开仓前只检查一次现金比例,`open_signal()` 可对多个信号分别按 `buy_value` 下单,但不扣减本轮可用资金。随后 `manage_positions()` 又以最初的 `assets.available` 作为补仓预算。
影响:同一轮多笔开仓与多笔补仓的总金额可能超过真实可用资金,导致集中拒单或资金计划失控。
解决方案:
1. `RunOnce()` 创建唯一的本轮 `remaining_cash`
2. 开仓、补仓共享该预算对象。
3. 每笔订单提交成功后立即预留预计金额。
4. 预算应保留 `min_cash_ratio` 对应的安全现金,不能把全部 available 用完。
5. 下单结果未知时也应暂时占用预算,直到对账明确失败。
验收标准:模拟多个开仓和补仓信号时,全部订单预计金额与保留现金之和不超过本轮资产快照的可用资金。
### 2.8 服务端与客户端可能把失败下单当成成功
位置:
- 服务端:`api/QMT_API.py:652-669`
- 客户端:`py-client/strategy/trend/order.py:90-102`
现状:服务端在 `order_ref` 为空时仍返回 `status=success``order_ref=unknown`;客户端不检查响应,直接返回 `True`
影响:真实订单未提交,本地状态却进入 `ING`,后续可能长期锁仓或重复判断错误。
解决方案:
1. 服务端只有在获得有效订单引用或明确成功码时返回成功。
2. 无订单引用时返回非 2xx或返回 `status=failed` 并包含错误原因。
3. SDK 将下单响应解析成明确的 `OrderResult` dataclass。
4. `OrderBook.place()` 验证 `status``order_ref` 后才能写入锁并返回成功。
5. 下单异常不能更新 `StateItem`
验收标准:
- `order_ref=None`、空字符串、`unknown` 均被识别为失败。
- 失败时本地订单锁和状态文件均不发生变化。
- 成功时保存服务端返回的真实订单引用。
### 3.1 同步 QMT 调用阻塞 Tornado 主线程
位置:所有同步 Handler例如
- `api/QMT_API.py:235`,行情查询
- `api/QMT_API.py:279`FullTick
- `api/QMT_API.py:1095`,持仓查询
- `api/QMT_API.py:1124`,资产查询
- `api/QMT_API.py:1547-1553`,单 IOLoop 启动
影响:任意一个慢请求都会阻塞其他资产、行情和交易请求。
解决方案:
1. 首先确认 QMT API 是否允许跨线程调用,以及是否要求在策略主线程执行。
2. 如果 QMT 要求固定线程:建立单一 QMT Worker 和任务队列HTTP Handler 异步等待任务结果。
3. 如果部分查询允许跨线程:仅将线程安全的查询放到受控线程池。
4. 下单、撤单等有顺序要求的操作仍通过单一串行交易队列执行。
5. 给每类任务设置超时、最大队列长度和请求标识。
6. 不允许无限堆积;队列满时返回明确的 503。
推荐结构:
```text
HTTP Handler
-> Query Worker Pool线程安全的只读查询
-> Trade Command Queue串行下单/撤单)
-> Short TTL Snapshot Cache
```
验收标准:
- 一个耗时 2 秒的历史行情请求不会阻塞资产接口。
- 下单和撤单仍保持提交顺序。
- 压测期间队列长度和超时可观测。
### 3.2 资产、持仓和订单被重复查询
影响:客户端每轮会分别查询订单、资产、持仓和行情,产生多次 HTTP 与 QMT 往返。
解决方案:
1. 增加账户快照接口,一次返回资产、持仓和活动订单。
2. 对同一账户的查询建立 100500ms 短周期缓存。
3. 交易命令执行后主动使相关缓存失效。
4. 缓存只用于查询,不能缓存下单和撤单结果。
5. 快照中返回统一的 `snapshot_time`,客户端可以判断数据新鲜度。
### 3.4 大行情响应在主线程转换和编码
位置:`api/QMT_API.py:235-276`
解决方案:
1. 限制股票数量、字段数量、日期跨度和最大响应体。
2. 使用明确的 DataFrame 转换方向和紧凑 JSON 格式。
3. 大结果支持分页、分批或文件下载。
4. 启用 gzip/br 压缩,但要衡量 QMT 机器 CPU。
5. 移除生产接口中的泛化 `default=str`,避免无意返回巨型对象字符串。
6. 将允许异步处理的转换和 JSON 编码移出 IOLoop。
验收标准:
- 超出范围的请求快速返回 400不拖垮服务。
- 大行情接口有响应大小和耗时指标。
- 资产、下单等小请求的 P95 不受大查询明显影响。
### 4.4 信号配置被硬编码且运行期间不刷新
位置:`py-client/strategy/trend/boot.py:83-84`
解决方案:
1. 使用账户配置中的 `signal_allow`,不要硬编码信号名。
2. 明确刷新周期,例如每 15 分钟重新拉取。
3. 拉取失败时保留最近一次成功快照,并记录快照时间。
4. 信号按 `(signal_key, code)` 去重。
5. 过期信号必须根据服务端 `updated` 或有效期淘汰。
验收标准:修改 YAML 后重启即可生效,长时间运行能获取新信号且不会重复下单。
### 5.1 服务端 Handler 重复代码过多
现状:每个接口重复执行 JSON 解码、默认值转换、异常捕获和 JSON 编码。
解决方案:
1. `BaseHandler` 增加 `read_json()`、参数校验和 `write_json()`
2. 使用 dataclass 或轻量 schema 定义请求参数。
3. 统一异常映射:参数错误 400、认证错误 401、业务冲突 409、服务不可用 503、未知错误 500。
4. 抽取固定字段对象转换函数。
5. 不要让 `safe_call()` 把所有错误统一变成 `None`
收益:减少接口行为差异,降低维护成本,并使性能监控更容易统一接入。
### 5.2 客户端 SDK 过度使用单行函数和动态字典
解决方案:
1. 高频账户、持仓、订单和交易接口优先使用明确 dataclass。
2. 长单行函数拆成可读的请求构造、发送和响应解析步骤。
3.`Client` 增加统一响应校验。
4. 区分查询异常、业务失败、订单结果未知和明确拒单。
5. 给所有交易方法增加输入校验:代码、方向、整手数量和金额。
### 5.3 止盈逻辑存在两套状态实现
现状:项目同时存在 `GridTrailingTracker``Runtime.peak_grids` 的设计痕迹。
解决方案:保留 `GridTrailingTracker` 作为唯一实现,将其放入 `Runtime`;删除旧字典逻辑和重复函数。
### 5.4 入口和配置使用全局可变状态
解决方案:
1. `config.load()` 返回配置后,由 `main()` 显式传给策略启动器。
2. `StartTrend(global_cfg, account_cfg)` 不直接读取模块全局变量。
3. 测试时可注入临时配置和模拟客户端。
### 5.5 缺少正式自动化测试
当前 `test.py` 是人工连通性脚本,不是完整测试套件。
建议至少建立:
- SDK 请求载荷和响应解析测试。
- `Position``Tick``StateItem` 模型测试。
- 时间段和交易时间测试。
- 开仓去重测试。
- 下单失败不更新状态测试。
- 过期撤单测试。
- 网格止盈状态机测试。
- 补仓次数和预算测试。
- 崩溃重启后的订单对账测试。
- 服务端账户快照和固定字段序列化测试。
### 阶段 1建立基线
1. 为每个 Handler 记录请求总耗时、QMT 调用耗时、序列化耗时和响应大小。
2. 记录并发请求数、任务队列长度、超时数和错误率。
3. 分别测量资产、持仓、订单、FullTick、历史行情接口的 P50/P95/P99。
### 阶段 2低风险优化
1. 固定字段序列化,删除 `dir()` 反射。
2. 合并账户快照接口。
3. 添加短 TTL 查询缓存。
4. 限制大查询范围和响应大小。
5. 客户端启用连接池。
### 阶段 3并发模型优化
1. 先验证 QMT 的线程安全和线程亲和性。
2. 建立查询 Worker 或单 QMT Worker 队列。
3. 下单、撤单保持串行和幂等保护。
4. 对大数据转换使用独立执行资源。
### 阶段 4压力验证
1. 同时执行慢历史行情与高频资产查询。
2. 在压力期间提交模拟下单和撤单。
3. 验证交易请求延迟不会因数据查询无限增长。
4. 验证服务端重启、超时和队列满时行为。