Files
big-qmt/docs/trend-audit-2026-09-05.md
2026-09-05 23:36:25 +08:00

7.9 KiB
Raw Permalink Blame History

Trend 当前工作树重新审计

审计日期2026-09-05。审计基线2f93fe2,本轮读取时工作树干净。以当前磁盘文件为准,仅审计 Trend 及直接依赖;不修改策略代码,不连接交易接口。

结论

当前发现 P00 项P14 项P23 项。本报告只保留当前存在的问题与待确认行为,不保留已修复问题清单。不建议仅凭语法检查通过直接运行实盘。

  • P0紧急:无需特定边界条件即可造成全面故障或迫切严重损害,需要立即处理。本轮未发现符合此定义的问题。
  • P1优先修复:在明确场景下影响资金约束、订单权限或交易决策,建议实盘前处理。
  • P2常规修复:异常场景下影响局部执行或可靠性,需要安排修复。

工作树中 strategy/trend/state.py 已删除,Runtime 也已移除 state 字段;docs/arch/state.py 是归档,不是当前运行模块。因此此前围绕 merged_order() 的结论不作为当前策略结论。

P1-1清仓后没有清理峰值新持仓会继承旧回撤基准

  • 位置:positions.py:105:182boot.py:122 起的快照处理;libs/grid_take_profit.py:71
  • 证据:峰值键只有账户和证券代码;当前 Trend 没有调用 clear(),也没有在持仓消失时清理对应键。
  • 触发:同一进程内清仓后重新买入同一证券。
  • 影响:新持仓达到最低收益门槛后,可能按上一笔持仓的高峰立即触发止盈,而不是建立自己的峰值;补仓改变成本时也未明确重置基准。
  • 验证:真实网格组件以同一键记录 12% 峰值,再输入新持仓的 9% 盈利,返回 RETREAT;代码中没有清仓清理步骤隔离两次持仓。
  • 建议:根据后续快照确认持仓消失后清理;撤单或部分成交继续保留峰值,成本变化另行明确重置规则。

P1-2自动撤单未限定为本策略订单

  • 位置:order.py:60:68:78boot.py:57sdk/portfolio.py:9
  • 证据:将组合接口的订单列表直接传入 refresh(),只检查状态与时间,没有检查本地订单号前缀、策略归属或排除名单。
  • 触发:组合接口返回同账户其他策略或手工订单,且符合超时条件。
  • 影响:启动及每轮刷新都可能撤销非 Trend 订单,包括排除证券的订单。
  • 建议:账户级活动订单可用于防重,但主动撤单必须单独限定所有权。若业务确实授权管理整个账户,需明确记录该权限。

P1-3大盘过滤实际始终放行

  • 位置:py-client/libs/market.pymarket_allow_open()boot.py:151positions.py:67
  • 证据:真实状态比较被注释,函数直接 return True
  • 影响:下跌或未知状态仍允许开仓及补仓,外层看似存在的风险限制不生效。该问题属于 Trend 直接依赖,不是 Trend 文件内的新修改。
  • 建议:恢复真实状态判断,或以明确、可见的配置表示主动关闭过滤。
  • 验证:将本地测试状态设为 DOWN,仍返回 True;未请求网络。

P1-4买入没有统一资金预算最小整手还可能突破单笔额度

  • 位置:boot.py:146:176open.py:51positions.py:160libs/calc.py:10
  • 证据:开仓只依据本轮起始资金比例决定是否启动,不逐单扣减预算;补仓线程独立使用相同快照资金。calc_buy_volume()max(1, ...) 强制至少买一手。
  • 影响:多个开仓与补仓可能同时消耗同一份可用资金;可能跌破配置的现金安全线,或产生资金不足拒单。并非断言券商一定允许超额成交。
  • 示例:价格 100 元、单笔额度 5,000 元,计算出 100 股,即 10,000 元。
  • 建议:开仓和补仓共享本轮可预留预算;不足一手返回零;预留考虑手续费和行情变化,保留最小现金要求。

P2-1撤单异常会中断整轮刷新和交易管理

  • 位置:order.py:78:86boot.py:122 起的快照处理。
  • 证据:撤单在遍历中直接调用,未逐单隔离;busy_keysdata 在全部遍历结束后才赋值。
  • 影响:某笔撤单失败即退出 refresh(),快照不更新;常规轮次返回而跳过全部交易管理,初始化阶段则退出启动(客户端会关闭)。持续失败的订单可能持续阻断后续轮次。
  • 建议:撤单逐项记录异常,同时发布完整活动订单快照;撤单失败的订单继续作为在途订单防重。

P2-2信号前置处理没有逐项异常隔离

  • 位置:open.py:16:55
  • 证据:异常捕获仅包围 do_open();配置、价格、数量计算、观察器调用不在逐候选保护边界中。
  • 触发:一个候选包含异常数据,例如非有限价格导致数量计算异常,或配置字段类型不符合预期。
  • 影响:该候选之后的所有开仓信号本轮不再处理;工作线程最外层只能记录整个任务失败。
  • 建议:把一个候选的完整处理放入同一异常边界,记录证券与信号键后继续;保持必要校验即可。

P2-3补仓请求结果不确定时未保留资金预算

  • 位置:positions.py:175:75order.py:119 起的异常处理。
  • 证据:place() 对请求超时等异常返回 False,保留证券方向缓存,但 handle_loss() 返回的 reserved_cash 默认为零。
  • 触发:券商已受理补仓但响应丢失,本轮继续处理后续证券。
  • 影响:方向缓存只能防同证券重复下单,不能阻止其他证券重复使用这笔可能已消耗的资金。该问题即使只启用持仓管理、不开新仓也存在,与 P1-4 的跨线程预算问题不同。
  • 验证:模拟 place() 返回 False,补仓结果 reserved_cash == 0.0
  • 建议:区分明确未提交与结果不确定;不确定时保留本轮预计金额,或停止本轮后续买入并刷新资金。

需要确认的行为(不直接判为错误)

  • positions.py:102 在盈利低于最低收益门槛时不再观察网格:若从高峰直接跌破该门槛,不会触发回撤卖出。需要确认门槛是仅用于首次激活,还是任何时候都禁止低于门槛卖出。
  • positions.py:122 向下取整到百股,可用持仓不足一手将一直跳过。是否需要支持零股清仓取决于交易标的及产品要求,本次未核验外部市场规则。
  • 当前 LOSS_TIERS = [-50.0],且 get_add_num() 依据手数/当前市值判断,不再记录真实补仓次数。若是主动放弃状态机后的新设计,应更新策略说明;不能等同于历史成交次数。
  • boot.py:84 使用 >= 15:00:00;与此前“严格大于 15:00”的表述存在边界差别。启动初始化发生在此检查之前仍可能查询或撤单。

已确认的保护与验证范围

  • busy()place() 都检查券商快照的 busy_keys 及本地 SimpleCacheplace() 在互斥区内检查并写入,不再报告旧版“下单未使用防重”的问题。
  • 开仓与持仓管理都检查排除名单;未知信号配置有独立日志后跳过。
  • place() 捕获 API、HTTP 请求和解码类异常;持仓管理有逐证券异常边界。
  • 启动生命周期有 finally,等待线程池后关闭客户端。
  • 当前 7 个 Trend 源文件全部重新通过 AST 语法检查。本轮真实网格组件的最小测试验证同一键沿用旧峰值;重新执行资金数量计算、大盘状态判断,分别得到 10,000 元买入金额和 DOWN 状态仍放行。
  • 资金预算、大盘过滤、撤单和信号异常边界本轮已重新读取;失败补仓零预算预留的实现与此前最小测试对应代码一致。
  • 未执行真实下单、撤单、完整 SDK 联调或券商回报测试;语法通过不代表策略可运行。归档文件和历史审计测试统计不计入当前验证。

优先顺序:先处理撤单权限、市场过滤、资金预算及止盈峰值生命周期,再完善异常边界。