Files
quantdingerdocs/STRATEGY_RUNTIME_ALGO_TRADING_IMPLEMENTATION_PLAN_CN.md
2026-07-11 20:14:35 +00:00

19 KiB
Raw Permalink Blame History

ScriptStrategy 复杂策略运行时改造实施方案

1. 目标

让脚本策略从“按 K 线生成买卖信号”升级为“可承载复杂状态机和算法执行”的运行时,优先支持以下用户场景:

  • 多层分仓,例如 5 个大分仓,每层 3 个子单。
  • 层内/层间价格间距可调。
  • 每个子单按马丁倍数或自定义序列放大。
  • 按实际成交均价止盈,而不是按脚本计划价止盈。
  • 只做多、只做空、多空双向 basket 独立运行。
  • 合约场景支持按杠杆后的名义价值换算下单量。
  • 后端重启、网络失败、部分成交、拒单后不重复补仓。

核心判断:现在的脚本 API 已经有基础,真正要补的是可信状态源、订单生命周期、恢复和风控。先把运行时地基打牢,再开放更复杂模板。

2. 当前代码基础

现有链路已经可复用:

  • app/services/strategy_script_runtime.py
    • StrategyScriptContext 已支持 open_long/add_long/open_short/add_short/close_long/close_short
    • ScriptPosition 已有 long/short 独立腿,并保留旧版 net position 兼容视图。
  • app/services/trading_executor.py
    • 脚本产生 ctx._orders 后转成执行信号。
    • _enqueue_pending_order() 写入 pending_orders
    • _hydrate_script_ctx_from_positions() 会从 qd_strategy_positions 恢复持仓视图。
  • app/services/pending_order_worker.py
    • PendingOrderWorker 拉取 pending_orders,执行 live/signal 模式派单。
    • 已有 live order context、client order id、成交回填等基础。
  • app/services/pending_orders/fill_records.py
    • persist_strategy_fill() 按成交更新 qd_strategy_tradesqd_strategy_positions
  • app/services/live_trading/records.py
    • apply_fill_to_local_position() 已经按成交更新均价和部分减仓。
  • migrations/init.sql
    • 已有 pending_ordersqd_strategy_positionsqd_strategy_tradesqd_grid_resting_orders 等基础表。

现有不足:

  • pending_orders 更像队列表,不是完整 order intent ledger。
  • script_runtime_state 存在 trading_config JSON 里,适合轻量参数,不适合作为复杂策略状态可信源。
  • 缺少 strategy_run_id、代码快照、参数快照和运行 epoch。
  • 缺少 basket / child order / fill / recovery event 的标准模型。
  • 多空 position API 已具备雏形,但 basket 层还没独立。
  • 订单幂等当前主要靠 (strategy_id, symbol, signal_type, signal_ts),不够表达层级分仓的 layer/order/action
  • 回测还没有完全复用 live 的 order intent、fill、fee、slippage 语义。

3. 总体架构

建议增加一层“策略运行时内核”,位于脚本和下单队列之间:

ScriptStrategy
  -> StrategyRuntimeContext
  -> BasketRuntime / RuntimeStateStore
  -> OrderIntentService
  -> ExecutionScheduler
  -> PendingOrderWorker / LiveTradingClient
  -> FillLedger / PositionLedger
  -> RecoveryService / RiskGuard

原则:

  • 脚本只表达意图和状态机,不直接操作数据库和交易所。
  • 数据库是恢复可信源,内存只做当前循环缓存。
  • 所有真实下单先落 order_intent,再提交交易所。
  • basket 均价、层级、TP、风险状态由实际 fill 驱动。
  • live、paper、backtest 尽量复用同一套 order intent 和 fill 语义。

4. 数据库改造

新增表建议:

strategy_runs
strategy_runtime_state
strategy_baskets
strategy_basket_orders
strategy_order_intents
strategy_order_fills
strategy_runtime_events
strategy_runtime_locks

4.1 strategy_runs

记录每一次启动,不再只靠 strategy_id 表示运行实例。

关键字段:

  • id
  • strategy_id
  • user_id
  • source_version_id
  • code_hash
  • parameter_snapshot_json
  • exchange_id
  • credential_id
  • symbol
  • market_type
  • position_mode
  • runtime_status: running/recovering/paused/needs_review/stopping/stopped/failed
  • runtime_epoch
  • started_at
  • stopped_at
  • stop_reason

4.2 strategy_runtime_state

替代把复杂状态塞进 trading_config.script_runtime_state

关键字段:

  • strategy_run_id
  • strategy_id
  • state_key
  • state_json
  • version
  • updated_at

用途:

  • 保存轻量脚本状态:冷却计数、上次触发价、用户自定义变量。
  • 由运行时托管 ctx.state.get/set/flush()
  • 不保存成交和订单真相。

4.3 strategy_baskets

复杂分仓策略的核心状态。

关键字段:

  • basket_id
  • strategy_run_id
  • strategy_id
  • symbol
  • side: long/short
  • status: idle/opening/active/closing/closed/failed/needs_review
  • current_layer
  • current_order_in_layer
  • total_qty
  • total_notional
  • avg_entry_price
  • next_entry_trigger
  • take_profit_price
  • max_layer
  • max_orders_per_layer
  • risk_state_json
  • created_at
  • updated_at

4.4 strategy_basket_orders

记录每个子单在 basket 内的归属。

关键字段:

  • basket_order_id
  • basket_id
  • side
  • layer_index
  • order_index
  • action: open/add/reduce/close
  • planned_price
  • planned_qty
  • planned_notional
  • status: planned/intent_created/submitted/accepted/partially_filled/filled/rejected/cancelled/expired/unknown
  • order_intent_id
  • exchange_order_id
  • client_order_id
  • filled_qty
  • avg_fill_price
  • fee
  • error

唯一约束:

strategy_run_id + basket_id + side + layer_index + order_index + action

4.5 strategy_order_intents

统一订单意图。后续 TWAP、BestLimit、Iceberg 都从这里开始。

关键字段:

  • order_intent_id
  • strategy_run_id
  • strategy_id
  • basket_id
  • basket_order_id
  • idempotency_key
  • symbol
  • market_type
  • side
  • position_side
  • reduce_only
  • order_type
  • quantity
  • notional
  • limit_price
  • execution_algo: market/limit/best_limit/twap/stop/iceberg
  • status
  • client_order_id
  • exchange_order_id
  • payload_json
  • created_at
  • updated_at

4.6 strategy_order_fills

成交流水是均价、TP 和审计的真实来源。

关键字段:

  • fill_id
  • order_intent_id
  • basket_id
  • exchange_order_id
  • exchange_fill_id
  • side
  • position_side
  • price
  • quantity
  • notional
  • fee
  • fee_ccy
  • filled_at
  • raw_json

唯一约束:

exchange_id + exchange_fill_id

没有交易所 fill id 的场景,用 order_intent_id + price + quantity + filled_at 做近似去重。

5. 运行时 API 改造

保留现有 ctx.open_long() 等 API,同时新增更适合复杂策略的托管接口。

5.1 ctx.state

ctx.state.get("cooldown_bars", 0)
ctx.state.set("cooldown_bars", 3)
ctx.state.flush()

用途:

  • 轻量脚本变量。
  • 自动绑定 strategy_run_id
  • 后端定期或关键动作前 checkpoint。

5.2 ctx.basket

long_basket = ctx.basket("long")

if long_basket.is_idle():
    long_basket.open_child_order(layer=1, order=1, notional=100)

if long_basket.should_add(current_price):
    long_basket.open_child_order(layer=2, order=1, notional=200)

if long_basket.should_take_profit(current_price):
    long_basket.close_all(reason="avg_take_profit")

运行时负责:

  • 检查幂等键。
  • 创建 strategy_basket_orders
  • 创建 strategy_order_intents
  • 将 intent 推进到 pending_orders 或算法调度器。
  • 成交后刷新 basket 均价、TP、层级和风险状态。

5.3 ctx.long_position / ctx.short_position

ctx.position 保持兼容,新增更明确接口:

ctx.long_position.size
ctx.long_position.avg_entry
ctx.short_position.size
ctx.short_position.avg_entry

多空双开时,脚本不能再依赖单一 net position 判断。

6. LayeredMartingaleBasket 官方模板

先内置一个官方模板,不先开放用户自由组合所有底层能力。

参数:

symbol
direction: long | short | both
base_order_value
leverage
layers = 5
orders_per_layer = 3
martingale_multiplier = 2.0
intra_spacing_pct
inter_spacing_pct
take_profit_pct
max_total_notional
max_margin_pct
hard_stop_pct
cooldown_bars
restart_after_take_profit

运行逻辑:

idle:
  创建第 1 层第 1 子单

active:
  如果价格逆向达到层内/层间触发价,创建下一个子单
  如果实际成交均价达到 TP,close basket
  如果达到最大层数,只等 TP 或风险退出
  如果触发 hard stop / margin risk,停止补仓并按配置减仓或平仓

第一版限制:

  • direction=longdirection=short 先稳定上线。
  • direction=both 内部拆成 long basketshort basket,要求交易所能力矩阵确认 hedge mode。
  • 不支持无限马丁,必须配置最大名义价值或最大保证金占用。

7. 订单生命周期改造

统一状态:

intent_created
submitted
accepted
partially_filled
filled
rejected
cancelled
expired
unknown
reconciled

现有 pending_orders.status 可以继续承载队列阶段,但不要作为专业订单状态的唯一来源。

推荐落地方式:

  1. OrderIntentService.create_intent() 先写 strategy_order_intents
  2. 根据 execution_algo
    • market/limit:直接桥接到 pending_orders
    • twap/best_limit:交给 ExecutionScheduler 拆 child order。
  3. PendingOrderWorker 执行后回写 intent 状态和 fill。
  4. persist_strategy_fill() 同步升级:除写 qd_strategy_trades/positions 外,也写 strategy_order_fills 并刷新 basket。

8. 幂等和单写入者

幂等键格式:

strategy_run_id:basket_id:side:L{layer_index}:O{order_index}:{action}

策略运行锁:

strategy_id + account_id/credential_id + symbol + side

机制:

  • 启动策略时创建 strategy_runs,拿到 runtime_epoch
  • 每个运行线程写状态和订单时必须带 runtime_epoch
  • 新线程抢锁成功后,旧线程即使还活着,也因为 epoch 不匹配不能继续写库。
  • 单机可先用数据库行锁;多实例部署再接 Redis lock + DB fencing token。

9. 重启恢复流程

启动或进程崩溃后恢复时:

  1. 将 run 标记为 recovering
  2. 读取 strategy_runsstrategy_runtime_statestrategy_baskets、未完成 strategy_order_intents
  3. 拉取交易所当前持仓、未成交订单、近期成交。
  4. 用交易所事实修正本地:
    • open order 状态。
    • filled quantity。
    • avg fill price。
    • basket avg entry。
    • pending TP/close 状态。
  5. 对没有 exchange_order_id 的 intent 做幂等判断:
    • 确认未提交才允许重试。
    • 状态未知则进入 needs_review
  6. 重建 ctx.statectx.basketctx.long_position/short_position
  7. recovery_completed 事件。
  8. 切回 running

恢复优先级:

交易所事实 > strategy_order_fills/order_intents > basket checkpoint > 内存状态

10. 风控改造

新增 StrategyRuntimeRiskGuard,在创建 intent 前检查:

  • 最大层数。
  • 最大子单数。
  • 最大名义价值。
  • 最大保证金占用。
  • 最大账户权益占比。
  • 最大浮亏。
  • 距离强平价格最小安全距离。
  • 单策略最大错误次数。
  • 单交易所/全局 kill switch。

触发后写事件:

risk_guard_triggered

并进入以下之一:

  • paused: 暂停新增开仓。
  • needs_review: 状态不确定,需要人工确认。
  • stopping: 自动减仓或平仓中。
  • failed: 无法安全处理。

11. 算法交易第一版

新增目录:

app/services/algo_trading/
  order_intent.py
  scheduler.py
  child_order.py
  execution_algorithms/
    twap.py
    best_limit.py
    stop.py
  order_state.py
  reconciliation.py
  risk.py

P0 支持:

  • MarketIntent: 现有 market order 的标准化入口。
  • LimitIntent: 限价单标准化入口。
  • BestLimit: 使用 best bid/ask 挂单,超时撤单重挂。
  • TWAP: 按时间切片拆成多个 child order。
  • Stop/StopLimit: 交易所支持则原生,不支持则本地触发。

P1 再做:

  • Iceberg。
  • Sniper。
  • 更细的 order book 驱动。

12. 交易所能力矩阵

现有 app/services/live_trading/capabilities.py 只有市场类型能力,需要扩展为:

supports_hedge_mode
supports_reduce_only
supports_post_only
supports_stop_order
supports_iceberg
supports_client_order_id
supports_order_query_by_client_id
supports_fills_query
min_notional
min_quantity
price_tick
quantity_step
rate_limit

策略启动前做 preflight

  • 交易所是否支持该 market type。
  • 是否支持 hedge mode。
  • API key 是否有交易、订单查询、成交查询权限。
  • 合约杠杆和保证金模式是否可配置。
  • symbol 精度和最小下单量是否满足模板参数。

失败时不启动策略,给用户明确错误。

13. 回测一致性

复杂策略不能只靠布尔信号回测。建议新增运行时回测模式:

ScriptStrategy -> RuntimeContext(backtest) -> OrderIntentService(backtest) -> SimulatedFillEngine -> BasketRuntime

模拟能力:

  • 最小下单量。
  • 数量步进。
  • 价格 tick。
  • 手续费。
  • 滑点。
  • 部分成交。
  • 下根 K 成交 / 当前 tick 成交差异。
  • 子单级成交记录。

验收目标:

  • 同一份 LayeredMartingaleBasket 模板在 backtest/paper/live 中的子单路径一致。
  • 回测报告能展示每层、每个子单、均价、TP、费用和滑点。

14. 前端需要配合的能力

第一版 UI 不要让用户直接编辑底层 runtime 表,而是提供模板化表单:

  • 方向:只做多 / 只做空 / 多空双向。
  • 分仓层数。
  • 每层子单数。
  • 层内间距。
  • 层间间距。
  • 马丁倍数或自定义序列。
  • 基础下单金额。
  • 杠杆。
  • 均价止盈。
  • 最大名义价值。
  • 最大保证金占用。
  • 硬止损。
  • 启动前预估最大风险。

策略详情页增加:

  • 当前 run id。
  • 当前 basket 状态。
  • 子单列表。
  • 未完成订单。
  • 最近成交。
  • 恢复事件。
  • 风控状态。
  • needs_review 人工处理面板。

15. 分期落地

Phase 0:补齐可追踪运行身份

目标:先让每次运行可追溯。

任务:

  • 新增 strategy_runs
  • 启动策略时生成 strategy_run_id
  • 保存代码 hash、参数快照、交易所配置摘要。
  • pending_orders.payload_json 增加 strategy_run_id
  • qd_strategy_tradesqd_strategy_positions 增加 strategy_run_id 可选字段。

验收:

  • 任意一笔订单能追到哪次运行、哪版代码、哪组参数。

Phase 1Basket runtime MVP

目标:支持单向 LayeredMartingaleBasket 稳定运行。

任务:

  • 新增 basket 表、basket order 表、runtime event 表。
  • 实现 ctx.statectx.basket(side)
  • 实现 BasketRuntime.open_child_order/close_all/checkpoint
  • 实现幂等键和 DB 级唯一约束。
  • 官方模板先支持 longshort

验收:

  • 5 层、每层 3 单能按配置触发。
  • 重启后不会重复开同一个 layer/order。
  • 均价止盈基于实际成交均价。

Phase 2Order intent 与成交驱动

目标:把 pending_orders 从唯一状态源降级为执行队列。

任务:

  • 新增 strategy_order_intentsstrategy_order_fills
  • TradingExecutor._enqueue_pending_order() 前置创建 intent。
  • PendingOrderWorker 执行后回写 intent 和 fill。
  • persist_strategy_fill() 刷新 basket 均价、TP 和状态。
  • 部分成交不再误判为完整开仓。

验收:

  • 部分成交后 basket 数量、均价、TP 正确。
  • 拒单不修改 basket 为已开仓。
  • 同一幂等键不会重复提交。

Phase 3:恢复与 needs_review

目标:重启、网络失败、未知订单状态可控。

任务:

  • 实现 RecoveryService
  • 启动策略前自动恢复 active run。
  • 未知状态进入 needs_review
  • UI 提供同步交易所状态、继续观察、关闭 basket、强制停止。

验收:

  • 发单后模拟进程崩溃,恢复后不重复下单。
  • 交易所和本地不一致时不会继续补仓。

Phase 4:多空双 basket

目标:安全开放 direction=both

任务:

  • ctx.long_position/short_position 正式化。
  • ctx.basket("long")ctx.basket("short") 独立持久化。
  • preflight 检查交易所 hedge mode。
  • 单写入锁按 strategy_id + credential_id + symbol + side 切分。

验收:

  • long basket 和 short basket 可同时存在,互不覆盖均价和层级。
  • 单向交易所不允许启动 both 模式。

Phase 5:基础 AlgoTrading

目标:把复杂策略的“如何成交”从策略逻辑里抽出来。

任务:

  • 实现 BestLimit
  • 实现 TWAP
  • 实现 Stop/StopLimit
  • 子单调度器支持撤单、重挂、超时和最大滑点。

验收:

  • 同一个 basket 子单可选择 market、best_limit、twap 执行。
  • 执行报告展示计划成交和实际成交差异。

16. 测试清单

必须新增的测试:

  • test_strategy_run_identity.py
  • test_basket_runtime_state.py
  • test_basket_order_idempotency.py
  • test_basket_partial_fill_avg_price.py
  • test_basket_rejected_order_state.py
  • test_basket_restart_recovery.py
  • test_basket_long_short_independent.py
  • test_order_intent_lifecycle.py
  • test_algo_twap_scheduler.py
  • test_algo_best_limit.py
  • test_exchange_capability_preflight.py
  • test_backtest_live_basket_consistency.py

重点场景:

  • 同一根 K 线重复触发,不重复开同一子单。
  • 写库成功但交易所提交超时,恢复时不盲目重发。
  • 部分成交后按成交数量更新均价。
  • 拒单后 basket order 为 rejected,不增加层级。
  • 交易所最小下单量不足,策略进入 needs_review 或拒绝启动。
  • 多空双开时 long/short basket 独立恢复。

17. 最小可交付版本

建议最小版本不要一口气做完整算法交易平台,先交付:

  • strategy_run_id
  • ctx.state
  • ctx.basket("long"|"short")
  • strategy_baskets / strategy_basket_orders
  • order intent 幂等键。
  • fill 驱动 basket 均价。
  • LayeredMartingaleBasket 单向模板。
  • 重启后不重复下单。

这一步完成后,已经可以覆盖用户最核心的“多层分仓 + 马丁 + 均价止盈 + 可恢复”诉求。随后再扩展多空双向和 TWAP/BestLimit,会稳很多。