3. 回测中未成交情形的典型表现

限价单回测最容易出错的地方,往往不是策略逻辑,而是“成交假设”。在真实市场中,限价单能否成交取决于价格是否触及、排队位置、盘口深度和撤单时机;而在回测中,如果只用一根K线的最高价、最低价去判断“价格碰到限价就算成交”,就会系统性地高估策略表现。本节围绕四类典型表现展开:信号触发但无成交、部分成交、延迟成交,以及由此引发的回测与实盘偏差案例。

一、信号触发但无成交:最隐蔽的乐观偏差

信号触发但无成交,指的是策略在某一时刻产生了挂单意图,但回测引擎判定该限价单最终没有成交。常见成因有三类:价格未真正触及、触及但排队未轮到、挂单在成交前被撤销或到期。

第一类最容易理解:买单限价 10.00 元,而该K线最低价为 10.01 元,价格从未进入可成交区间,自然不成交。第二类更隐蔽:K线最低价正好等于 10.00 元,但这一价位上已有大量排队买单,你的单子排在队尾,实际成交量可能为零。第三类则与策略自身的挂单有效期有关,例如“当日有效、收盘撤单”,若价格在尾盘才触及,撤单逻辑可能先于成交触发。

在实现层面,一个常见的错误写法是:

# 错误示例:只要最低价触及限价就判定买入成交
if bar.low <= limit_price:
    fill_price = limit_price
    position += qty

这段代码忽略了排队与成交量约束。更合理的做法是引入“可成交比例”或“成交量上限”参数,例如假设触及价位时最多成交该K线成交量的某一比例,或要求价格严格穿越限价(最低价 < 限价)才视为成交。两种假设各有适用场景,但必须在回测报告中明确声明,否则结果无法与实盘对照。

二、部分成交:成交量约束下的真实常态

部分成交指挂单数量只有一部分被撮合。它在大单、低流动性标的或涨跌停附近尤为常见。回测中若把部分成交简化为“全成或全不成”,会同时扭曲收益与风险:一方面高估了建仓速度,另一方面低估了未成交残量带来的敞口不确定性。

处理部分成交,核心是维护“剩余数量”状态。以下是一个可参考的撮合伪代码,假设每根K线在限价价位上可提供的成交量为 available_vol:

remaining = order.qty
fillable = min(remaining, available_vol * participation_rate)
if bar.low <= order.limit_price and fillable > 0:
    order.filled += fillable
    remaining -= fillable
    if remaining == 0:
        order.status = "FILLED"
    else:
        order.status = "PARTIAL"
        # 残量可选择继续挂单、撤单或按规则顺延

其中 participation_rate 是参与率假设,例如 10% 表示只吃掉该价位可用量的十分之一。这个参数没有普适值,需要结合标的日均成交额、下单规模和历史盘口数据校准。残量的后续处理也必须写清楚:是继续挂原价、改价追单,还是当日撤单,不同选择会显著改变回测曲线。

三、延迟成交:时间维度上的错位

延迟成交指挂单并非在信号当根K线成交,而是在后续若干根K线才被撮合。它带来的直接后果是成交价与信号价脱节,进而影响持仓成本、止盈止损基准和资金占用时长。

回测引擎需要为每笔挂单记录“挂单时间戳”,并在后续K线中持续检查成交条件,而不是只在信号K线上判断一次。若策略依赖“当日信号当日成交”的假设,延迟成交会打破这一前提,导致回测中的持仓区间与实盘不一致。实践中应统计延迟成交的分布:多少比例在1根K线内成交、多少在3根以上、最长延迟多久。这个分布本身就是评估策略可执行性的重要指标。

限价单未成交情形:从信号到回测偏差 策略信号 产生限价挂单意图 ① 信号触发但无成交 价格未触及 / 排队未轮到 挂单到期或提前撤销 ② 部分成交 成交量约束 / 参与率限制 残量需明确后续处理 ③ 延迟成交 跨K线撮合 / 成交价脱节 资金占用时间被低估 回测与实盘偏差 成交率被高估 持仓成本被低估 收益曲线偏乐观 风险敞口被隐藏 校准手段 参与率参数 · 排队假设 · 延迟分布统计 成交率与实盘对账

四、回测与实盘偏差案例

下面用一个教学示例说明偏差如何累积。假设某策略在 20 个交易日内产生 40 次买入信号,限价均为信号价下方 0.5%。回测引擎采用“最低价触及即成交”的乐观假设,结果显示 40 次全部成交,累计收益 +6.2%。

改用更保守的撮合规则后:要求最低价严格低于限价才成交,并引入 15% 参与率与“当日未成交则撤单”。结果变为:完全成交 26 次、部分成交 7 次、未成交 7 次。由于未成交的 7 次恰好集中在几次快速反弹行情中,策略错过了部分上涨,累计收益降至 +3.8%。这 2.4 个百分点的差距,并非策略失效,而是成交假设从乐观切换到接近真实所致。

这个案例的启示是:回测偏差往往不是均匀分布的,而是集中在特定行情中。快速单边行情里价格触及限价后立即反弹,排队靠后的单子最容易落空;而震荡行情中价格反复穿越,成交反而容易。因此,评估未成交影响时,不能只看总成交率,还要看未成交样本的行情分布。

五、落地检查要点

为了让回测中的未成交处理可复现、可对账,建议在引擎中显式维护以下状态与参数:挂单时间戳、限价、剩余数量、订单状态(挂单中/部分成交/完全成交/已撤销/已过期)、参与率假设、撤单规则。回测报告中应至少披露:总信号数、完全成交数、部分成交数、未成交数、平均成交延迟K线数,以及不同成交假设下的收益对比。

判断撮合假设是否合理,一个实用方法是做敏感性测试:把参与率从 5% 调到 30%,观察收益曲线的变化幅度。若结果对参数极度敏感,说明策略高度依赖成交假设,实盘可执行性存疑,应优先优化挂单价格或下单方式,而不是继续调参。

归根结底,未成交不是回测的“异常”,而是限价单交易的常态。把无成交、部分成交、延迟成交当作一等公民来建模,回测结果才具备与实盘对话的资格。