3. 成交仿真入门

成交仿真入门 学习卡片

回测中最容易被低估的环节,不是策略信号,而是“信号如何变成一笔真实成交”。如果成交仿真过于理想化,再优秀的策略也会在实盘中迅速失效。本节围绕四个核心问题展开:订单有哪些类型、撮合按什么顺序发生、成交价格如何确定、滑点如何建模。

一、核心原理:从信号到成交的四个决策点

一笔回测成交的诞生,本质上要经过四个决策点:订单类型决定“以什么条件进场”,撮合逻辑决定“谁先成交”,成交价格决定“以什么价成交”,滑点决定“实际成交比预期差多少”。四者缺一不可,且相互影响。

常见的订单类型包括:

  • 市价单(Market Order):不限定价格,追求立即成交,代价是可能承受较大滑点。
  • 限价单(Limit Order):限定最高买价或最低卖价,价格优先,但可能无法成交。
  • 止损单(Stop Order):价格触发后转为市价单,常用于风控,但跳空时成交价可能远差于触发价。
  • 止损限价单(Stop-Limit):触发后转为限价单,兼顾风控与价格约束,但存在触发后不成交的风险。

撮合逻辑的核心是价格优先、时间优先。买单出价越高越优先,卖单出价越低越优先;同价则先到先得。回测中若忽略时间优先,同一根K线内的多笔订单可能被错误地全部成交。

二、方法流程:成交价格的确定方式

成交价格的确定方式直接决定回测收益的可信度。常见做法有以下几种:

  1. 收盘价成交:信号在收盘确认,以当日收盘价成交。实现简单,但存在“未来函数”风险,因为收盘价在盘中不可知。
  2. 次日开盘价成交:信号在收盘确认,次日开盘成交。更贴近实盘,但需处理隔夜跳空。
  3. 当前价成交:以触发信号时的最新价成交,适合分钟级或Tick级回测。
  4. 限价单撮合:只有当市场价格触及或穿过限价时才成交,成交价取限价或更优价格。

关键原则:成交价格必须使用“当时可获得的信息”。若用当日最高价或最低价成交,等于假设你能精准买在最低、卖在最高,这是典型的回测幻觉。

下面是一个简化的撮合伪代码,展示限价单如何按价格优先、时间优先成交:

def match_orders(orders, market_price):
    # orders: 按价格优先、时间优先排序的订单列表
    for order in orders:
        if order.side == "BUY" and market_price <= order.limit_price:
            fill_price = min(order.limit_price, market_price)
            execute(order, fill_price)
        elif order.side == "SELL" and market_price >= order.limit_price:
            fill_price = max(order.limit_price, market_price)
            execute(order, fill_price)
        else:
            order.status = "PENDING"

三、滑点:回测与实盘之间的“摩擦成本”

滑点是指预期成交价与实际成交价之间的差额。它来源于流动性不足、买卖价差、市场冲击和延迟。回测中若不建模滑点,等于假设市场无限深、零延迟,结果必然偏乐观。

滑点建模常见方式:

  • 固定滑点:每笔成交加减固定金额或固定比例,简单但不够真实。
  • 比例滑点:按成交价的一定百分比计算,适合流动性稳定的品种。
  • 基于波动率的滑点:波动越大滑点越高,更贴近真实市场。
  • 基于成交量的滑点:订单量占市场成交量比例越高,冲击成本越大。
订单类型 成交确定性 价格确定性 典型滑点 适用场景
市价单 高 低 较大 快速进出、流动性好
限价单 低 高 较小或无 震荡市、挂单等待
止损单 高 低 跳空时极大 风控止损
止损限价单 中 中 中等 兼顾风控与价格

四、案例与复盘:一个被滑点“吃掉”的策略

假设某策略在收盘价确认信号,次日开盘买入。回测中未设滑点,年化收益18%。加入0.1%比例滑点后,年化降至11%;再加入成交量冲击模型后,年化进一步降至7%。这说明滑点不是小修小补,而是决定策略生死的关键参数。

常见误区:用当日收盘价成交、忽略涨跌停无法成交、忽略停牌、忽略最小变动价位、用未来数据判断能否成交。这些都会让回测结果严重失真。

复盘建议:每次回测后,检查成交明细中是否存在“买在最低、卖在最高”的异常记录;统计滑点分布,确认其是否与品种流动性匹配;对限价单,检查未成交比例是否合理。