diff --git a/api/__pycache__/QMT_API.cpython-311.pyc b/api/__pycache__/QMT_API.cpython-311.pyc index 6264d8d..44f7722 100644 Binary files a/api/__pycache__/QMT_API.cpython-311.pyc and b/api/__pycache__/QMT_API.cpython-311.pyc differ diff --git a/py-client/__pycache__/main.cpython-311.pyc b/py-client/__pycache__/main.cpython-311.pyc index bac70a4..8b399cf 100644 Binary files a/py-client/__pycache__/main.cpython-311.pyc and b/py-client/__pycache__/main.cpython-311.pyc differ diff --git a/py-client/config/__pycache__/__init__.cpython-311.pyc b/py-client/config/__pycache__/__init__.cpython-311.pyc index dbf8677..2238bba 100644 Binary files a/py-client/config/__pycache__/__init__.cpython-311.pyc and b/py-client/config/__pycache__/__init__.cpython-311.pyc differ diff --git a/py-client/libs/__pycache__/grid_take_profit.cpython-311.pyc b/py-client/libs/__pycache__/grid_take_profit.cpython-311.pyc index 7e8bb89..3e88613 100644 Binary files a/py-client/libs/__pycache__/grid_take_profit.cpython-311.pyc and b/py-client/libs/__pycache__/grid_take_profit.cpython-311.pyc differ diff --git a/py-client/libs/__pycache__/signal.cpython-311.pyc b/py-client/libs/__pycache__/signal.cpython-311.pyc index c73ccc5..61a3d53 100644 Binary files a/py-client/libs/__pycache__/signal.cpython-311.pyc and b/py-client/libs/__pycache__/signal.cpython-311.pyc differ diff --git a/py-client/sdk/__pycache__/client.cpython-311.pyc b/py-client/sdk/__pycache__/client.cpython-311.pyc index 605885d..dc358af 100644 Binary files a/py-client/sdk/__pycache__/client.cpython-311.pyc and b/py-client/sdk/__pycache__/client.cpython-311.pyc differ diff --git a/py-client/sdk/__pycache__/models.cpython-311.pyc b/py-client/sdk/__pycache__/models.cpython-311.pyc index cd06160..2330f73 100644 Binary files a/py-client/sdk/__pycache__/models.cpython-311.pyc and b/py-client/sdk/__pycache__/models.cpython-311.pyc differ diff --git a/py-client/sdk/__pycache__/trade.cpython-311.pyc b/py-client/sdk/__pycache__/trade.cpython-311.pyc index 9d01233..e521143 100644 Binary files a/py-client/sdk/__pycache__/trade.cpython-311.pyc and b/py-client/sdk/__pycache__/trade.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/boot.cpython-311.pyc b/py-client/strategy/trend/__pycache__/boot.cpython-311.pyc index b79c5af..940009a 100644 Binary files a/py-client/strategy/trend/__pycache__/boot.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/boot.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/open.cpython-311.pyc b/py-client/strategy/trend/__pycache__/open.cpython-311.pyc index a2f444c..15e7d46 100644 Binary files a/py-client/strategy/trend/__pycache__/open.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/open.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/order.cpython-311.pyc b/py-client/strategy/trend/__pycache__/order.cpython-311.pyc index 04df09c..ebcc8ab 100644 Binary files a/py-client/strategy/trend/__pycache__/order.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/order.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/positions.cpython-311.pyc b/py-client/strategy/trend/__pycache__/positions.cpython-311.pyc index b06d08c..c6c3c8c 100644 Binary files a/py-client/strategy/trend/__pycache__/positions.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/positions.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc b/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc index 71d8f32..59df6ca 100644 Binary files a/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/state.cpython-311.pyc b/py-client/strategy/trend/__pycache__/state.cpython-311.pyc index f92ba39..ec5fb14 100644 Binary files a/py-client/strategy/trend/__pycache__/state.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/state.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/watch.cpython-311.pyc b/py-client/strategy/trend/__pycache__/watch.cpython-311.pyc index 87c9838..44c031e 100644 Binary files a/py-client/strategy/trend/__pycache__/watch.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/watch.cpython-311.pyc differ diff --git a/py-client/tests/__pycache__/test_trend.cpython-311.pyc b/py-client/tests/__pycache__/test_trend.cpython-311.pyc new file mode 100644 index 0000000..bb3a3d8 Binary files /dev/null and b/py-client/tests/__pycache__/test_trend.cpython-311.pyc differ diff --git a/scripts/run.bat b/scripts/run.bat deleted file mode 100644 index 443e139..0000000 --- a/scripts/run.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -setlocal -cd /d "%~dp0.." -cd go-client - -if not defined QMT_BASE_URL set "QMT_BASE_URL=http://127.0.0.1:10086" -if not defined QMT_TOKEN set "QMT_TOKEN=QMTbyYanweidong" -if not defined QMT_ACCOUNT set "QMT_ACCOUNT=stock" - -echo QMT_BASE_URL=%QMT_BASE_URL% -echo QMT_ACCOUNT=%QMT_ACCOUNT% -echo. - -go run ./apps/cmd %* -if errorlevel 1 ( - echo. - echo 运行失败。 - pause - exit /b 1 -) diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..8ebfdfb --- /dev/null +++ b/todo.md @@ -0,0 +1,204 @@ + +### 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. 对同一账户的查询建立 100–500ms 短周期缓存。 +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. 明确刷新周期,例如每 1–5 分钟重新拉取。 +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. 验证服务端重启、超时和队列满时行为。 \ No newline at end of file