# QuantDinger 策略运行时与算法交易升级规划 **状态**:规划建议 **适用范围**:ScriptStrategy、实盘策略运行时、订单执行、算法交易执行器 **背景**:用户开始提出更复杂的实盘策略需求,例如 5 层分仓、每层 3 个马丁子单、均价止盈、多空模式和自定义杠杆。QuantDinger 需要从“能跑策略”升级为“可靠承载复杂策略和算法执行”的量化交易基础设施。 --- ## 1. 结论 当前 ScriptStrategy 基座可以支持一类复杂策略: - 有状态的分批开仓。 - 运行时参数化。 - 多次加仓、均价止盈。 - 只做多或只做空。 - K 线/实时 tick 循环驱动。 - 回测与实盘复用同一份脚本逻辑。 但对于专业级“阶梯分仓马丁 / basket martingale / layered DCA”这类策略,当前系统还缺少一些基础设施级边界: - 独立 basket 状态持久化。 - 多空双向 basket 的独立状态。 - 重启恢复与成交回放。 - 部分成交、撤单、拒单后的确定性状态机。 - 实盘与回测在逐笔补仓、均价、手续费、滑点上的一致性校验。 - 策略级最大风险预算、爆仓距离、保证金占用和 kill switch。 因此: - **只做多 / 只做空版本**:当前基座可以写出来,适合先落地。 - **多空双开版本**:建议先补强 hedge-mode 状态模型,不建议直接用单一 `ctx.position` 硬写。 - **专业级可售卖模板**:需要配套 basket runtime、订单生命周期和恢复机制后再推广。 --- ## 2. 用户需求是否能由脚本策略表达 用户需求: ```text 5 个大分仓 每个分仓 3 个开单 单内开仓间距可调 每个子单按马丁倍数放大 均价止盈 前一个分仓没有盈利才开下一个分仓 交易品种自定义 只做多 / 只做空 / 多空双开 基础仓位是首单金额 合约按杠杆后的名义价值计算 马丁倍数可自定义 均价止盈可自定义 ``` ### 2.1 当前可直接表达的部分 ScriptStrategy 已经具备这些能力: | 需求 | 当前支持情况 | 说明 | | --- | --- | --- | | 自定义交易品种 | 支持 | 由策略创建参数和运行配置选择 symbol | | 参数可调 | 支持 | 使用 `ctx.param(...)` | | 只做多 | 支持 | `ctx.open_long` / `ctx.add_long` / `ctx.close_position` | | 只做空 | 支持 | `ctx.open_short` / `ctx.add_short` / `ctx.close_position` | | 分批加仓 | 支持 | 脚本维护层级和触发价 | | 马丁倍数 | 支持 | 通过脚本计算每次下单数量 | | 均价止盈 | 支持 | 脚本维护或读取均价后触发平仓 | | K 线或 tick 循环 | 支持 | 运行时按 bar/tick 调用脚本 | | 回测 | 支持 | 可用脚本回测面板验证 | ### 2.2 当前不建议硬写的部分 | 需求 | 风险点 | 建议 | | --- | --- | --- | | 多空双开 | 需要 long basket 和 short basket 独立状态;交易所也必须是 hedge mode | 第一版拆成两个策略实例,后续补 `ctx.long_position` / `ctx.short_position` | | 重启后继续运行 | 内存变量可能丢失,导致重复开单或错误止盈 | 必须有 basket 状态持久化和成交回放 | | 部分成交 | 均价和层级不能只按“已发单”计算 | 必须按实际成交回填 | | 拒单 / 超最小下单量 | 状态机可能误以为已开仓 | 下单确认必须驱动状态迁移 | | 合约杠杆名义价值 | 不同交易所单位、面值、张数规则不同 | 统一 order sizing 层 | | 爆仓/保证金风险 | 马丁连续补仓容易快速提高保证金占用 | 增加策略级风险预算和强制停止 | --- ## 3. 建议的脚本策略模型 这个用户需求应该抽象成 `LayeredMartingaleBasket`,不是普通网格,也不是简单 DCA。 ### 3.1 参数结构 ```text symbol direction: long | short | both base_order_value leverage layers = 5 orders_per_layer = 3 martingale_multiplier = 2.0 intra_spacing_pct = [0.5, 0.8] inter_spacing_pct = [1.2, 1.5, 1.8, 2.2] take_profit_pct = 2.0 max_total_margin_pct max_notional_pct hard_stop_pct cooldown_bars restart_after_take_profit = true ``` ### 3.2 状态结构 ```text basket_id side status: idle | opening | active | closing | closed | failed current_layer current_order_in_layer filled_orders total_qty total_notional avg_entry_price next_entry_trigger take_profit_price last_signal_ts last_order_id error_count ``` ### 3.3 执行流程 ```text if basket is idle: open layer 1 order 1 if price moves adverse by intra/inter spacing: open next child order if weighted average reaches take-profit: close basket reset state if max layer/order reached: stop adding wait for TP or trigger risk exit if hard risk limit reached: close basket or stop strategy ``` --- ## 4. ScriptStrategy 基座需要补齐的边界 ### 4.1 Basket state persistence 复杂策略不能只依赖脚本内存变量。建议新增 basket 状态表或 runtime state 标准: ```text qd_strategy_baskets qd_strategy_basket_orders ``` 核心能力: - 每个 basket 有唯一 ID。 - 每个子单有独立状态。 - 状态由成交事件驱动,而不是由“发单成功”驱动。 - 后端重启后可以恢复当前层级、均价和未完成订单。 #### 4.1.1 脚本线程与持久化边界 脚本策略通常跑在线程或任务循环里,但线程内变量只能作为运行期缓存,不能作为真实状态来源。建议把策略状态拆成三层: | 层级 | 用途 | 建议存储 | 是否可信 | | --- | --- | --- | --- | | 线程内存 | 当前循环临时计算、减少查询 | Python object | 不可信,重启即丢 | | Redis / cache | 分布式锁、短期心跳、运行中标记、限频 | Redis,可选 | 半可信,可重建 | | 数据库 | basket、订单意图、成交、风险状态、恢复检查点 | PostgreSQL / SQLite / MySQL | 可信状态源 | 原则是:脚本可以读写 `ctx.state`,但 `ctx.state` 背后必须由运行时托管并定期落库;策略代码不应该直接操作数据库连接,也不应该自己决定恢复逻辑。 建议提供运行时接口: ```python ctx.state.get("current_layer", 0) ctx.state.set("current_layer", 2) ctx.state.flush() ctx.basket("long").open_child_order(...) ctx.basket("long").checkpoint() ``` 其中: - `ctx.state` 保存轻量策略状态,例如当前层级、最近触发价格、冷却计数。 - `ctx.basket` 保存交易相关状态,例如子单、成交、均价、止盈价、未完成订单。 - `flush()` 由运行时批量落库,也可以在发单前后强制 checkpoint。 - 真实下单前必须先写入 `order_intent`,拿到幂等键,再提交交易所订单。 #### 4.1.2 数据库与 Redis 的分工 数据库是恢复源,Redis 只做加速和协调: - 数据库负责:策略实例、basket、子订单、订单意图、交易所订单 ID、成交回报、风险状态、最后 checkpoint。 - Redis 负责:策略运行锁、线程心跳、短期去重键、任务队列、行情订阅状态。 - 如果没有 Redis,单机版本也能工作,只是不能很好地做多进程调度和快速故障切换。 - 如果 Redis 数据丢失,不应该影响真实持仓恢复;最多影响 UI 的“运行中”即时状态。 推荐表结构方向: ```text strategy_runtime_state strategy_baskets strategy_basket_orders strategy_order_intents strategy_order_fills strategy_recovery_events ``` #### 4.1.3 重启与容灾流程 后端重启、线程崩溃或容器迁移后,不应该直接从脚本第一行重新开始发单。恢复流程应由运行时统一执行: 1. 标记策略实例为 `recovering`,禁止脚本继续发新单。 2. 读取数据库中的最后 checkpoint、basket、未完成 order intent。 3. 拉交易所当前持仓、未成交订单、最近成交。 4. 用交易所事实修正本地状态:成交数量、均价、未完成订单、已关闭订单。 5. 对没有交易所订单 ID 的 intent 做幂等判定:确认未提交才允许重试。 6. 重建 `ctx.state` 和 `ctx.basket`,恢复到线程内存。 7. 写入 recovery event,标记为 `running`。 8. 下一轮策略循环只允许基于恢复后的状态继续运行。 关键点是:恢复优先相信交易所事实,其次相信数据库 checkpoint,最后才相信脚本内存。 #### 4.1.4 防重复下单机制 复杂马丁策略最怕重启后重复补仓。每次下单必须有稳定幂等键: ```text strategy_id + basket_id + side + layer_index + order_index + action ``` 例如: ```text strategy-12:basket-20260629-long:L2:O3:open ``` 运行时提交订单前先创建 `order_intent`: - 如果同一幂等键已有 `submitted / accepted / partially_filled / filled`,禁止再次提交。 - 如果是 `rejected / expired`,根据策略配置决定是否重试。 - 如果是 `unknown`,必须先向交易所对账,不允许盲目重发。 这样即使线程在“写库成功、发单超时、交易所实际已接收”的中间状态崩溃,也可以通过交易所订单查询恢复,而不是重复开单。 ### 4.2 Position model upgrade 当前脚本更适合单方向持仓。后续建议提供清晰接口: ```python ctx.position ctx.long_position ctx.short_position ctx.basket(side="long") ctx.basket(side="short") ``` 这样多空双开不会挤在一个 `ctx.position` 语义里。 ### 4.3 Order lifecycle standard 复杂策略必须清楚区分: - intent created - order submitted - exchange accepted - partially filled - filled - rejected - cancelled - expired - reconciled 状态迁移必须幂等,避免重启后重复下单。 ### 4.4 Fill-driven average price 均价止盈必须基于实际成交: - 不能只按脚本计划价格。 - 不能只按信号价格。 - 必须考虑部分成交、手续费、合约面值和滑点。 ### 4.5 Restart recovery 策略恢复时应执行: 1. 读取本地 basket 状态。 2. 拉交易所持仓和未成交订单。 3. 对账本地订单与交易所订单。 4. 重建均价、层级和风险状态。 5. 再允许继续发新单。 这一节是恢复行为摘要,详细线程、数据库、Redis 与幂等设计见 `4.1.1` 到 `4.1.4`。 ### 4.6 Risk guard 马丁类策略必须内置硬风险边界: - 最大层数。 - 最大子单数。 - 最大名义价值。 - 最大保证金占用。 - 最大账户权益占比。 - 最大连续亏损次数。 - 最大浮亏。 - 距离爆仓价最小安全距离。 - 交易所错误过多自动停止。 ### 4.7 Backtest/live consistency 复杂加仓策略需要增强回测: - 支持逐层成交记录。 - 支持手续费和滑点模型。 - 支持最小下单量和价格步进。 - 支持合约张数换算。 - 支持“下根 K 成交”和“tick 触发”的差异报告。 - 支持实盘偏差报告:计划成交 vs 实际成交。 ### 4.8 Strategy run identity 每一次策略启动都必须有独立 `strategy_run_id`。不要只用 `strategy_id` 表示一次运行,因为同一策略可能多次启动、暂停、恢复、修改参数。 建议记录: ```text strategy_id strategy_run_id source_version_id parameter_snapshot account_id exchange symbol market_type position_mode started_at stopped_at stop_reason runtime_status ``` 这样可以回答: - 这笔订单属于哪一次运行。 - 当时使用的是哪一版脚本代码。 - 当时参数是什么。 - 是正常停止、用户停止、风控停止,还是异常崩溃后恢复。 ### 4.9 Single writer and lock model 同一个策略实例同一时间只能有一个运行时写状态和发单。否则多线程、多进程或容器重启时容易重复补仓。 建议: - 以 `strategy_id + account_id + symbol + side` 作为运行锁范围。 - 单机版可以用数据库锁。 - 多实例部署建议用 Redis lock + 数据库 fencing token。 - 每次写入订单状态时检查 `runtime_epoch` 或 `fencing_token`,旧线程失去锁后不能继续写库。 - UI 上的“启动策略”如果发现已有活动 run,应提示用户恢复、接管或强制停止。 ### 4.10 Event ledger 除了保存当前状态,还应该保存事件流水。当前状态用于快速恢复,事件流水用于审计和重放。 建议事件类型: ```text strategy_started signal_generated order_intent_created order_submitted order_accepted order_partially_filled order_filled order_rejected order_cancelled basket_checkpointed risk_guard_triggered recovery_started recovery_completed strategy_stopped manual_override ``` 有了 event ledger,后续才能做: - 策略事故复盘。 - 回测与实盘偏差报告。 - 用户投诉时还原执行过程。 - 版本升级后的兼容性检查。 ### 4.11 Code and parameter snapshot 实盘运行不能只引用“当前脚本代码”。用户可能在策略运行中修改脚本,如果没有快照,会出现无法复现的问题。 建议: - 每次启动实盘时固定 `source_version_id`。 - 保存代码 hash、参数快照、运行配置、交易账户、交易所模式。 - 修改代码后不影响正在运行的 run,除非用户明确点击“应用并重启”。 - 回测记录、实盘记录和市场模板都引用同一套版本快照。 ### 4.12 Manual intervention and degraded mode 恢复失败、交易所状态不一致或订单状态未知时,不应该让策略继续自动补仓。 建议增加状态: ```text running recovering paused needs_review stopping stopped failed ``` 进入 `needs_review` 时: - 禁止发新开仓单。 - 允许只减仓或撤单。 - UI 显示差异:本地 basket、交易所持仓、未成交订单。 - 用户可以选择:同步交易所状态、关闭 basket、继续观察、强制停止。 --- ## 5. 算法交易能力缺口 目前 QuantDinger 已有基础下单与策略运行能力,但还不是完整 AlgoTrading 执行平台。 ### 5.1 当前已有能力 - 市价单。 - 限价单。 - 基础取消订单。 - 策略信号触发下单。 - 止损、止盈、追踪止损等策略级风控。 - 网格 resting limit orders 的部分执行基础。 - 多交易所适配框架。 ### 5.2 需要补齐的算法交易模块 建议新增独立域: ```text app/services/algo_trading/ order_intent.py scheduler.py child_order.py execution_algorithms/ twap.py iceberg.py best_limit.py sniper.py stop.py order_state.py reconciliation.py risk.py ``` ### 5.3 第一阶段建议实现 | 算法 | 优先级 | 原因 | | --- | --- | --- | | TWAP | P0 | 最容易解释,最适合大额分批成交 | | BestLimit | P0 | 基于盘口最优价挂单,适合低滑点执行 | | Stop / StopLimit | P0 | 用户理解成本低,交易刚需 | | Iceberg | P1 | 大单隐藏,依赖交易所支持或本地拆单 | | Sniper | P1 | 需要盘口、成交量、触发条件和撤单速度 | | VWAP / POV | P2 | 需要可靠成交量曲线和市场深度数据 | --- ## 6. 算法交易必须补齐的边界 ### 6.1 Unified order intent 所有策略和手动交易都应先生成统一订单意图: ```text side symbol market_type position_side reduce_only notional quantity limit_price time_in_force execution_algo risk_budget client_order_id strategy_id basket_id ``` ### 6.2 Child order scheduler TWAP/Iceberg/Sniper 都需要子单调度: - 分片数量。 - 分片间隔。 - 每片最大数量。 - 撤单重挂。 - 超时处理。 - 最大滑点。 - 成交不足补单。 ### 6.3 Market microstructure data 算法交易不能只靠 K 线。至少需要: - best bid/ask。 - order book top levels。 - recent trades。 - spread。 - depth。 - volatility。 - exchange rate limit。 ### 6.4 Reconciliation 执行器必须定期对账: - 本地订单状态。 - 交易所订单状态。 - 实际成交。 - 当前持仓。 - 手续费。 - 平均成交价。 ### 6.5 Kill switch 算法交易需要全局和策略级开关: - 全局暂停发单。 - 单策略暂停。 - 单交易所暂停。 - 最大错误次数自动停止。 - 最大滑点自动停止。 - 仓位异常自动停止。 - API key 异常自动停止。 ### 6.6 Observability 需要能回答: - 为什么下了这笔单? - 计划成交多少? - 实际成交多少? - 滑点多少? - 哪个子单失败? - 是策略原因、交易所原因还是风控原因? 建议新增: ```text algo_order_logs algo_child_order_logs execution_trace_id strategy_run_id ``` ### 6.7 Exchange capability matrix 算法交易不能假设每个交易所能力一致。需要维护交易所能力矩阵: ```text exchange market_type supports_hedge_mode supports_reduce_only supports_post_only supports_stop_order supports_iceberg supports_client_order_id min_notional min_quantity price_tick quantity_step rate_limit order_book_depth ``` 运行时根据能力矩阵决定: - 能否启动某个策略。 - 是否需要本地模拟 stop / iceberg。 - 下单数量和价格如何 round。 - 策略模板是否适合某个交易所。 ### 6.8 Account, permission, and secret boundary 算法交易会放大账户风险,必须把账户权限纳入运行前检查: - API key 是否具备交易权限。 - 是否禁止提现权限。 - 是否支持读取订单和成交历史。 - 是否配置 IP 白名单。 - 是否允许合约交易。 - 是否设置账户级最大风险预算。 策略启动前应做 preflight check,失败时明确告诉用户缺哪个权限或配置,而不是启动后才报错。 ### 6.9 Test and certification suite 如果要把 QuantDinger 做成基础设施,需要有策略运行时验收测试: - 重启恢复测试:发单中途杀进程,恢复后不重复下单。 - 部分成交测试:均价和 TP 按真实成交更新。 - 拒单测试:最小下单量、余额不足、API 错误后不误改 basket。 - 断网测试:状态进入 `needs_review`,不继续补仓。 - 多空 hedge 测试:long basket 与 short basket 独立。 - 回测/实盘一致性测试:同一信号路径能对齐计划成交点。 - 交易所精度测试:价格 tick、数量 step、最小名义金额都正确处理。 --- ## 7. 产品分期路线 ### Phase 1:ScriptStrategy 专业化 目标:让复杂状态机策略可稳定运行。 - Basket runtime state。 - 多空独立 position/basket 接口。 - 成交驱动均价。 - 重启恢复。 - 策略运行 ID、代码版本快照和参数快照。 - 事件流水和恢复审计。 - 单写入者锁,避免重复运行。 - 复杂策略回测报告。 - Layered Martingale Basket 官方模板。 ### Phase 2:基础 AlgoTrading 目标:让用户可以选择执行算法,而不只是市价/限价。 - Unified order intent。 - TWAP。 - BestLimit。 - Stop / StopLimit。 - 子单状态表。 - 执行日志和偏差分析。 - 交易所能力矩阵。 - 账户权限 preflight check。 ### Phase 3:高级执行 目标:降低大额交易滑点,支持专业交易执行。 - Iceberg。 - Sniper。 - VWAP。 - POV。 - 盘口深度驱动。 - 智能撤单重挂。 ### Phase 4:基础设施化 目标:QuantDinger 成为策略、执行、风控、监控一体化基础设施。 - 统一策略运行 ID。 - 全链路 execution trace。 - 交易所能力矩阵。 - 多账户组合执行。 - 回测/模拟盘/实盘一致性报告。 - 策略市场模板准入审核。 - 风险沙盒和资金预算模拟器。 - 运行时验收测试套件。 --- ## 8. 推荐落地顺序 最推荐的实际落地顺序: 1. 先做 `LayeredMartingaleBasket` 官方脚本模板,只支持 `long` / `short`。 2. 增加 `strategy_run_id`、代码快照、参数快照,保证可追溯。 3. 增加 basket 状态持久化和事件流水,解决重启恢复。 4. 增加单写入者锁和幂等下单,解决重复运行和重复补仓。 5. 增加成交驱动均价,解决部分成交和真实 TP。 6. 增加 `long_basket` / `short_basket`,再开放多空双开。 7. 增加交易所能力矩阵和启动前 preflight check。 8. 增加 TWAP / BestLimit,作为算法交易第一版。 9. 再做 Iceberg / Sniper。 这样既能尽快满足用户需求,又不会把复杂风险压到脚本作者身上。 --- ## 9. 对外表述建议 当前阶段建议谨慎表述: ```text QuantDinger supports stateful ScriptStrategy workflows for scale-in, DCA, martingale-style baskets, average-cost exits, backtesting, and live execution. Advanced multi-leg basket persistence, hedge-mode basket accounting, and algorithmic execution orders such as TWAP, Iceberg, Sniper, and BestLimit are planned infrastructure upgrades. ``` 中文: ```text QuantDinger 当前支持有状态脚本策略,可实现分批加仓、DCA、马丁篮子、均价止盈、 回测和实盘执行。多腿篮子持久化、多空独立篮子核算,以及 TWAP、Iceberg、 Sniper、BestLimit 等算法订单执行能力,将作为后续基础设施升级重点。 ``` 不要过早宣传“完整算法交易平台”,应先宣传“可扩展的策略运行时 + 正在建设中的算法执行基础设施”。