darkeril

V5.4 执行层状态机设计

V5.4 Execution State Machine

本文档描述 V5.4 策略的执行层状态机设计,重点是 maker/taker 组合策略,确保在不牺牲入场/出场时效性的前提下,最大程度降低手续费与滑点摩擦。

设计目标:

  • 将"信号质量"和"执行质量"解耦,执行层只负责:更便宜、更稳定地实现既定 TP/SL/flip/timeout 规则。
  • 入场阶段在不丢失大行情的前提下尽量使用 maker;
  • 出场阶段 TP 强制 maker 主路径,同时用 taker 做安全兜底;
  • SL 始终使用 taker,优先保证风控。

1. 入场状态机(Entry State Machine)

1.1 状态定义

  • IDLE:无持仓、无挂单,等待信号。
  • ENTRY_PENDING_MAKER:已下入场限价挂单(post-only),等待成交或超时。
  • ENTRY_FILL:入场成交完成(全仓或部分)。
  • ENTRY_FALLBACK_TAKER:超时后使用 taker 市价单补齐未成交部分。

1.2 关键参数

  • entry_price_signal:信号引擎给出的入场参考价(通常为最新价或中间价 mid)。
  • tick_size:交易所最小价格步长。
  • entry_offset_ticks:maker 入场挂单相对盘口的偏移(通常为 1–2 个 tick)。
  • entry_timeout_ms:入场 maker 挂单最大等待时间(如 3000–5000ms)。
  • entry_fallback_slippage_bps:fallback taker 允许的最大滑点(基础保护,超出则放弃补仓或缩小仓位)。

1.3 状态机伪代码

state = IDLE

on_signal_open(signal):
  if state != IDLE:
    return  // 避免重复入场

  // 计算 maker 挂单价格
  side = signal.side  // LONG or SHORT
  ref_price = best_bid_ask_mid()

  if side == LONG:
    entry_price_maker = min(ref_price, best_bid() + entry_offset_ticks * tick_size)
  else:  // SHORT
    entry_price_maker = max(ref_price, best_ask() - entry_offset_ticks * tick_size)

  // 下 post-only 入场挂单
  order_id = place_limit_post_only(side, entry_price_maker, target_size)
  entry_start_ts = now()
  state = ENTRY_PENDING_MAKER


on_timer():
  if state == ENTRY_PENDING_MAKER:
    if order_filled(order_id):
      filled_size = get_filled_size(order_id)
      if filled_size >= min_fill_ratio * target_size:
        state = ENTRY_FILL
        return

    if now() - entry_start_ts >= entry_timeout_ms:
      // 超时,取消剩余挂单
      cancel_order(order_id)
      remaining_size = target_size - get_filled_size(order_id)

      if remaining_size <= 0:
        state = ENTRY_FILL
        return

      // 兜底:按容忍滑点发市价单
      mkt_price = best_bid_ask_mid()
      theoretical_price = ref_price_at_signal
      slippage_bps = abs(mkt_price - theoretical_price) / theoretical_price * 10000

      if slippage_bps <= entry_fallback_slippage_bps:
        place_market_order(side, remaining_size)
        state = ENTRY_FILL
      else:
        // 滑点过大,放弃补仓或缩减仓位
        state = ENTRY_FILL  // 仅保留已成交部分

2. 出场状态机(TP/SL/Flip/Timeout)

出场分为四类:TP(止盈)、SL(止损)、flip(信号翻转)、timeout(超时退出)。

2.1 通用状态

  • POSITION_OPEN:持仓打开,已根据策略下好 TP/SL 限价单。
  • TP_PENDING_MAKER:TP 限价挂单等待成交。
  • TP_FALLBACK_TAKER:TP 越价未成交时,撤单+市价平仓兜底。
  • SL_PENDING:止损触发,直接发送 taker 单。
  • FLIP_PENDING:翻转触发,先平仓再反向开仓(可复用入场状态机)。
  • TIMEOUT_PENDING:超时触发,按策略规则离场(可偏 maker)。

2.2 关键参数

  • tp1_r, tp2_r:TP1/TP2 的目标 R 距离(如 1.0R / 2.0R)。
  • sl_r:止损距离(如 -1.0R)。
  • tp_timeout_ms:价格越过 TP 水平后,TP 限价未成交的允许时间窗口。
  • flip_threshold:翻转触发条件(score + OBI + VWAP 等综合判断)。
  • timeout_seconds:最大持仓时间,用于 timeout 出场。

2.3 TP 状态机(maker 主路径 + taker 兜底)

on_position_open(pos):
  // 开仓后立即挂 TP1 限价单(maker)
  tp1_price = pos.entry_price + pos.side * tp1_r * pos.risk_distance
  tp2_price = pos.entry_price + pos.side * tp2_r * pos.risk_distance

  // 半仓挂 TP1,半仓挂 TP2
  tp1_id = place_limit_post_only(exit_side(pos.side), tp1_price, pos.size * 0.5)
  tp2_id = place_limit_post_only(exit_side(pos.side), tp2_price, pos.size * 0.5)
  pos.state = POSITION_OPEN


on_timer():
  if pos.state == POSITION_OPEN:
    current_price = best_bid_ask_mid()

    // 检查 TP1 越价兜底
    tp1_crossed = (pos.side == LONG and current_price >= tp1_price) or
                  (pos.side == SHORT and current_price <= tp1_price)
    if tp1_crossed and not pos.tp1_cross_ts:
      pos.tp1_cross_ts = now()

    if pos.tp1_cross_ts:
      if order_filled(tp1_id):
        pos.tp1_cross_ts = None  // 成交,清除计时
      elif now() - pos.tp1_cross_ts >= tp_timeout_ms:
        cancel_order(tp1_id)
        remaining = size_tp1 - get_filled_size(tp1_id)
        if remaining > 0:
          place_market_order(exit_side(pos.side), remaining)

    // 检查 TP2 越价兜底
    tp2_crossed = (pos.side == LONG and current_price >= tp2_price) or
                  (pos.side == SHORT and current_price <= tp2_price)
    if tp2_crossed and not pos.tp2_cross_ts:
      pos.tp2_cross_ts = now()

    if pos.tp2_cross_ts:
      if order_filled(tp2_id):
        pos.tp2_cross_ts = None
      elif now() - pos.tp2_cross_ts >= tp_timeout_ms:
        cancel_order(tp2_id)
        remaining = size_tp2 - get_filled_size(tp2_id)
        if remaining > 0:
          place_market_order(exit_side(pos.side), remaining)

  // 检查是否已全部平仓
  if pos_size(pos) <= 0:
    pos.state = CLOSED

2.4 SL 状态机(纯 taker)

on_sl_trigger(pos, sl_price):
  // 触发条件可以来自价格监控或止损订单触发
  // 这里策略层只关心:一旦触发,立即使用 taker
  close_size = pos_size(pos)
  if close_size > 0:
    place_market_order(exit_side(pos.side), close_size)
    pos.state = CLOSED

SL 不做 maker 逻辑,避免在极端行情下挂单无法成交。

2.5 Flip 状态机(平旧仓 + 新开仓)

on_flip_signal(pos, new_side, flip_context):
  if not flip_condition_met(flip_context):
    return

  // flip 条件:score < 85 AND OBI 翻转 AND 价格跌破 VWAP(三条件同时满足)
  // flip_condition_met 由信号引擎判断

  // 1) 先平旧仓(按 SL 逻辑,优先 taker)
  close_size = pos_size(pos)
  if close_size > 0:
    place_market_order(exit_side(pos.side), close_size)

  pos.state = CLOSED

  // 2) 再按入场状态机开新仓
  new_signal = build_signal_from_flip(new_side, flip_context)
  entry_state_machine.on_signal_open(new_signal)

flip 的关键是:门槛更高(如 score < 85 且 OBI 翻转且价格跌破 VWAP),尽量减少在震荡行情中来回打脸。

2.6 Timeout 状态机(超时出场)

on_timer():
  if pos.state == POSITION_OPEN and now() - pos.open_ts >= timeout_seconds:
    // 可以偏 maker:先挂限价平仓,超时再 taker
    timeout_price = best_bid_ask_mid()
    size = pos_size(pos)

    oid = place_limit_post_only(exit_side(pos.side), timeout_price, size)
    pos.timeout_order_id = oid
    pos.timeout_start_ts = now()
    pos.state = TIMEOUT_PENDING

  if pos.state == TIMEOUT_PENDING:
    if order_filled(pos.timeout_order_id):
      pos.state = CLOSED
    elif now() - pos.timeout_start_ts >= timeout_grace_ms:
      cancel_order(pos.timeout_order_id)
      remaining = pos_size(pos)
      if remaining > 0:
        place_market_order(exit_side(pos.side), remaining)
      pos.state = CLOSED

3. 监控指标(执行层 KPI)

指标说明目标
maker_ratio_entry入场成交中 maker 比例≥ 50%
maker_ratio_tpTP 成交中 maker 比例≥ 80%
avg_friction_cost_r每笔平均摩擦成本(手续费+滑点,以 R 计)≤ 0.15R
entry_timeout_rate入场超时触发 taker 兜底比例≤ 30%
tp_overshoot_rateTP 越价后兜底比例≤ 20%
flip_frequency每笔持仓中 flip 次数≤ 1次/持仓

4. 设计原则汇总

场景主路径兜底
入场limit post-only(盘口内侧 1-2 tick)超时 → taker(滑点容忍内)
TP1 / TP2limit post-only(预挂)越价 X ms 未成交 → 撤单 + taker
SL纯 taker,立即执行
Flip平仓用 taker,新开仓复用入场逻辑
Timeoutlimit post-onlygrace period 后 → taker

标签#EXECUTION-MAKER-TAKER
状态:V5.4 设计文档,待 3/11 A/B test 结束后进入实现阶段
作者:小范(xiaofan)+ 露露(lulu)
日期:2026-03-06

On this page