买入成交价 = 信号价格 + 买入滑点
卖出成交价 = 信号价格 - 卖出滑点
如果回测系统没有加入这两项偏差,净值曲线通常只代表信号逻辑,不代表真实交易结果。手续费可以在报表中单独扣除。滑点无法这样处理。它直接改变成交价格、持仓成本、止损位置和最终收益分布。
量化策略回测表现优异,实盘却持续低于预期,滑点设置缺失或过于理想化,是最常见的原因之一。尤其是高频、短周期、高换手策略。每笔交易只损失几个点,单笔看不出差异。交易次数累积后,策略的期望值会被摩擦成本直接压缩。
滑点不是一个固定的后台参数。它来自盘口价差、订单规模、市场冲击、行情速度、网络延迟、时间戳处理和撮合规则。回测中使用一个统一的固定百分比,可以作为基准测试。但它不能自动还原实盘。
滑点的微观结构:预期价格为什么无法成交
回测中的信号价格通常是一个离散数据点。
例如:
- 当前一根分钟线收盘后生成买入信号;
- 策略以收盘价计算目标仓位;
- 回测引擎直接按照这个价格成交;
- 下一根K线从理论持仓状态开始计算收益。
真实市场不是这样运行的。信号产生时,订单需要经过策略程序、网络链路、券商柜台、交易所撮合系统。订单到达市场时,原来的报价可能已经变化。即使信号逻辑完全正确,成交价格也可能与信号价格不同。
滑点的基本定义是实际成交价格与策略预期价格之间的差异。
买入时,实际成交价高于预期价格,形成正向成本。卖出时,实际成交价低于预期价格,同样形成成本。若使用价格变动单位表达,滑点通常以点、跳或最小价格变动单位计算。若使用比例表达,则常见方式是按成交金额的一定比例扣除。
这两个表达方式不能混为一谈。
买卖价差
买卖价差是最基础的交易摩擦。
盘口中同时存在买价和卖价。主动买入需要支付卖方报价。主动卖出需要接受买方报价。中间价只是统计意义上的参考价格。策略可以按照中间价生成信号,但不能按照中间价直接完成主动成交。
当回测系统使用收盘价作为成交价时,需要先确认这个收盘价代表什么:
- 最新成交价;
- 买卖报价的中间价;
- 买一价;
- 卖一价;
- 一段时间内的聚合价格。
这几种价格对应不同的成交条件。
如果策略在回测中按照中间价买入,却没有支付买卖价差,结果已经偏离实盘。对低频策略而言,这种偏差可能被持仓收益覆盖。对短周期策略而言,价差可能直接占据单笔收益的大部分。
市场冲击
订单规模超过当前盘口可成交数量时,订单会消耗多个价位的挂单。
例如,卖一价只有一部分数量。订单先成交在卖一价,剩余数量继续向卖二价、卖三价撮合。最终成交价格是分层成交价格的加权平均值,而不是单一报价。
这就是市场冲击的基础来源。
市场冲击与订单金额、盘口深度、成交速度、标的流动性相关。相同的策略、相同的信号、相同的滑点参数,在不同标的上可能产生完全不同的结果。
固定滑点模型不能描述所有这些变化。它只能提供一种近似。更严格的回测需要至少考虑以下变量:
- 订单数量占当前盘口可成交量的比例;
- 下单时的买卖价差;
- 订单是否允许部分成交;
- 剩余订单是否继续追价;
- 订单的限价或市价属性;
- 价格快速变化时的成交顺序。
行情波动与时间延迟
行情变化越快,信号价格的有效时间越短。
在低波动环境中,几毫秒或几百毫秒的延迟可能只带来很小的价格差异。在快速波动环境中,同样的延迟可能导致订单跨越多个最小变动单位。高频交易和短周期交易尤其敏感。
延迟不只有网络传输时间。
完整链路包括:
1. 行情从交易所传输到行情服务;
2. 行情服务推送到策略程序;
3. 策略程序完成指标计算和信号判断;
4. 订单请求发送到交易接口;
5. 券商柜台接收并转发订单;
6. 交易所撮合;
7. 成交回报返回策略系统;
8. 策略更新本地持仓状态。
每一步都会产生时间差。若回测只使用K线时间,而实盘使用行情到达时间,二者并不处于同一时间轴。
滑点不是回测结果上的一个扣减项。它是从信号生成到订单完成之间,整条执行链路的价格误差。
回测中的业绩高估陷阱
滑点通常不会单独造成所有回测与实盘差异。它经常和其他偏差一起出现。
如果回测系统同时存在前视偏差、手续费遗漏、幸存者偏差和过度优化,最终净值可能远高于真实可交易水平。此时只增加一个滑点参数,无法完成校正。
前视偏差先于滑点发生
前视偏差是指策略在某个时间点使用了当时不可能获得的数据。
常见形式包括:
- 使用当前K线收盘价生成信号,并在同一收盘价完成交易;
- 使用经过未来数据修正的技术指标;
- 在复权数据中忽略复权因子发布时间;
- 计算横截面排名时使用未来成分股;
- 用整段样本期的数据选择最优参数;
- 将当天收盘后的数据用于当天收盘成交。
以收盘价交易为例。如果策略必须等收盘价确定后才能生成信号,那么订单至少应在下一时点成交。直接使用当前收盘价成交,会把未来信息提前放入成交过程。
这种错误对滑点测试影响很大。因为回测本身已经使用了不可能获得的理想价格。再加入固定滑点,也只能掩盖部分问题,不能修复时间顺序。
正确顺序应当由数据频率和执行规则共同决定:
- 用收盘信号,下一根K线执行;
- 用实时盘口信号,按照盘口时间戳执行;
- 用限价单,处理未成交、部分成交和撤单;
- 用市价单,处理成交价偏移和订单冲击。
手续费和滑点不是同一项
手续费是交易通道按照成交金额、成交数量或其他规则收取的费用。滑点是成交价格偏离预期价格的损失。
两者需要分别建模。
假设一笔买入交易的名义金额为 100 万元,回测中设置千分之一,也就是 0.1% 的基础交易手续费率假设,则手续费约为 1000 元。若同时设置万分之三,也就是 0.03% 的回测滑点,滑点成本约为 300 元。
这个例子只用于说明计算关系。实际费用还需要根据市场、品种、券商规则、开平仓方向和成交方式确定。滑点也不能简单按照固定比例永久适用。
如果只扣手续费,不加滑点,回测净值仍然可能被高估。反过来,如果只设置滑点,不扣手续费,结果也不完整。
过度优化会把滑点参数变成样本内变量
参数优化可以直接改变策略的交易频率。
例如,策略将均线周期、突破阈值、止损距离和持仓时间同时纳入搜索。系统在历史样本内寻找收益最高组合。随后再针对该组合寻找最有利的滑点参数。最终得到的不是稳定策略,而是对历史成交条件的拟合。
滑点参数不能作为提升净值的优化变量。它应当作为外部约束进行压力测试。
更合理的做法是:
1. 先根据交易品种、订单规模和执行方式设定滑点范围;
2. 使用多个滑点水平重新运行回测;
3. 观察收益、换手、最大回撤和交易次数的变化;
4. 将参数固定;
5. 再在未参与优化的数据上验证。
如果策略只有在极低滑点下盈利,而滑点稍微提高就失效,说明策略的交易边际不足。这个结果本身比一条漂亮的净值曲线更有价值。
摩擦成本建模:怎样设置滑点参数
滑点建模可以分为三个层级。
第一层是固定点数模型。每笔交易增加固定数量的最小价格变动单位。实现简单,适用于早期研究和策略结构验证。
第二层是固定比例模型。按照成交金额的一定比例扣除成本。适用于跨价格区间、跨标的组合回测,但无法直接反映不同盘口深度。
第三层是动态成交模型。根据买卖价差、波动率、订单金额、盘口数量和延迟估计实际成交价格。实现复杂,但更接近真实执行。
固定点数模型
固定点数模型可以写成:
买入成交价 = 预期买入价 + s
卖出成交价 = 预期卖出价 - s
其中,s 是每笔交易的滑点点数。
该模型的优点是简单。对于期货等有明确最小变动单位的品种,可以直接用跳数表示。研究阶段,可以先测试 1 个、2 个以及高于 2 个最小变动单位的情况。
复杂或极端交易条件下,通常建议将滑点设定在高于 2 个最小变动单位的水平,或者使用固定交易比例、固定点数进行摩擦成本建模。这不是所有策略的统一答案,而是压力测试的起点。
固定点数模型的缺点也很明确:
- 无法表达价格水平变化;
- 无法表达不同标的的流动性差异;
- 无法表达订单规模变化;
- 无法表达行情波动导致的动态冲击;
- 无法表达部分成交;
- 无法表达不同时间段的盘口差异。
因此,固定点数适合验证策略是否具有足够的收益边际,不适合直接作为最终实盘模拟。
固定比例模型
固定比例模型按成交金额扣除滑点。
设成交金额为 V,滑点比例为 r,则单边滑点成本可以表示为:
滑点成本 = V × r
研究资料中常见的示范参数包括万分之三,也就是 0.03%。部分高频或高频次策略的单笔滑点偏差可能达到 0.1% 至 0.2% 的范围。但这些数字不能脱离市场、品种、成交时段、订单规模和执行方式单独使用。
固定比例模型的主要优点是跨价格适应性较好。股票价格从 10 元变化到 100 元时,固定比例仍然保持相同的金额比例。
但它仍然不能解释盘口结构。例如,同样是 0.03%:
- 高流动性标的在正常时段可能容易实现;
- 低流动性标的可能远远不够;
- 快速波动阶段可能明显低估;
- 大额订单可能产生额外的市场冲击;
- 限价单可能不产生相同的成交成本,但会增加未成交风险。
固定比例不是市场真实滑点的替代品。它只是一个可重复的成本假设。
动态滑点模型
动态模型应尽量让滑点随交易环境变化。
可以从以下几个变量开始:
- 买卖价差;
- 最近一段时间的波动率;
- 订单金额与盘口可成交量的比例;
- 信号到订单提交之间的延迟;
- 交易时段;
- 行情方向和价格跳动速度;
- 市价单与限价单的类型。
一种简化表达方式是:
动态滑点 = 基础价差成本 + 延迟成本 + 订单冲击成本
基础价差成本可以用买一价和卖一价之间的差额表示。延迟成本可以根据历史行情中信号时间与实际提交时间之间的价格变化估计。订单冲击成本则需要结合订单金额和盘口数量计算。
在没有完整盘口数据时,不应假装具备精确的动态撮合能力。可以采用分层压力测试:
- 正常条件;
- 成本提高的条件;
- 高波动条件;
- 低流动性条件;
- 延迟增加条件。
如果策略在全部条件下都保持相近的风险收益特征,稳健性才具有一定证据基础。
不同模型的适用范围
| 建模方式 | 主要输入 | 优点 | 主要缺陷 | 适用阶段 |
|---|---|---|---|---|
| 固定点数 | 最小价格变动单位、每笔订单 | 实现简单,容易做压力测试 | 不反映价格和流动性变化 | 策略原型、期货基础回测 |
| 固定比例 | 成交金额、滑点比例 | 适合组合回测,跨价格适应性较好 | 不反映盘口深度和成交规模 | 中低频研究、统一成本估算 |
| 买卖价差模型 | 买一价、卖一价、中间价 | 能处理基础价差 | 不能单独处理市场冲击 | 有盘口数据的基础回测 |
| 动态冲击模型 | 盘口、订单量、波动率、延迟 | 更接近实际执行 | 数据和实现复杂 | 实盘前验证 |
| 影子撮合模型 | 实时行情、订单生命周期、成交规则 | 可验证订单流程和成交状态 | 需要完整交易环境 | 模拟交易、系统验收 |
高频交易的延迟挑战:时间戳比参数更重要
高频系统中的关键问题,不是把滑点从 0.03% 调整到 0.04%。首先要确认回测是否使用了正确的时间顺序。
一个策略可能使用毫秒级行情。回测中,数据按照时间戳严格排列。实盘中,行情到达顺序、订单到达时间和成交回报时间可能不一致。如果系统只记录K线时间,而没有保存原始行情时间、接收时间、计算时间和订单发送时间,就无法解释实盘偏差。
至少需要四类时间戳
较完整的交易系统通常记录:
1. 交易所行情时间;
2. 策略程序接收行情的时间;
3. 信号生成时间;
4. 订单发送、接受、成交和回报时间。
这些时间戳必须使用统一时钟体系。否则,延迟统计会出现系统性误差。
例如,行情时间为 10:00:00.100,策略接收时间为 10:00:00.120,信号生成时间为 10:00:00.123,订单发送时间为 10:00:00.126,成交回报时间为 10:00:00.150。回测若只看到 10:00:00.100 这一条行情,无法知道订单实际在何时进入市场。
这不是日志问题。它会直接改变滑点估计。
K线回测无法天然模拟订单执行
K线只包含开盘价、最高价、最低价、收盘价和成交量等聚合信息。它通常不包含:
- 价格在K线内部的变化顺序;
- 每个价位的盘口数量;
- 买卖双方的报价变化;
- 订单到达时的队列位置;
- 部分成交过程;
- 撤单与重新挂单过程。
如果策略在一根K线内部完成开仓、止损和止盈,仅凭最高价和最低价无法准确判断成交顺序。
例如,某根K线同时触及止盈价和止损价。没有更细粒度数据,就无法确认哪个价格先出现。回测引擎如果采用固定规则处理,结果可能与真实行情顺序不同。
因此,短周期策略至少需要进行更细粒度的数据验证。不能把K线收盘成交结果直接视为订单撮合结果。
限价单的滑点和市价单不同
市价单的核心风险是成交价格不确定。限价单的核心风险是无法成交。
如果回测只给限价单一个理想成交价,却不记录未成交状态,策略会得到错误结果。限价单需要建模:
- 价格是否触及;
- 触及时盘口是否有足够数量;
- 订单是否排在队列后方;
- 订单在有效期内是否被撤销;
- 部分成交后剩余数量如何处理;
- 未成交订单是否会影响后续信号。
限价单不能简单地看作没有滑点。它把一部分价格风险转换成成交概率风险。
如果回测系统只允许完全成交,通常会高估限价策略的成交率。若只允许完全不成交,又可能低估部分成交的实际效果。需要根据策略频率和市场数据精度选择模型复杂度。
从研究到实盘:构建多层级验证体系
严谨的量化系统不应只有一个回测环境。
通常需要拆分为四套环境:
- 研究环境;
- 回测环境;
- 模拟交易环境;
- 实盘环境。
每套环境的职责不同。混在一起,会导致数据、执行和风险控制相互污染。
研究环境:验证逻辑,不追求成交细节
研究环境用于分析因子、构建信号和观察统计特征。这里可以快速迭代。
但研究环境中的结果不应直接用于判断可交易性。因为研究阶段往往使用简化价格、简化成交和简化费用。它回答的是一个问题:
策略信号是否存在可重复的统计关系?
它不回答另一个问题:
订单是否能以假设价格完成成交?
这两个问题必须分开。
回测环境:加入完整成本和时间规则
回测环境需要固定以下内容:
- 数据版本;
- 复权规则;
- 成交时间规则;
- 手续费规则;
- 滑点模型;
- 订单类型;
- 仓位限制;
- 资金约束;
- 涨跌停或交易暂停规则;
- 交易日历;
- 交易容量。
回测结果至少应同时输出毛收益和净收益。还需要单独列出:
- 总交易次数;
- 单边换手;
- 平均持仓时间;
- 平均单笔收益;
- 平均单笔滑点成本;
- 手续费总额;
- 滑点总额;
- 成本占毛收益的比例;
- 最大连续亏损次数;
- 最大回撤;
- 不同时间段的收益分布。
如果系统只输出一条净值曲线,信息不足。净值曲线无法说明收益是来自信号,还是来自过于理想的成交假设。
模拟交易环境:验证真实行情和订单流程
模拟交易的价值不只是观察收益。
它应当读取真实行情,执行真实的信号流程,记录订单状态,并使用影子撮合检查:
- 行情是否完整;
- 信号是否按预期生成;
- 订单是否在正确时间发送;
- 订单状态是否正确更新;
- 部分成交是否会造成仓位偏差;
- 撤单和重试是否引入重复下单;
- 成交回报延迟是否被正确记录;
- 实际执行价格与回测假设差异多大。
模拟交易不能等价于实盘。真实资金、真实排队位置和部分交易规则可能不同。但它可以暴露大量程序错误。尤其是时间戳错位、重复下单、订单状态机错误和持仓同步失败。
实盘环境:先记录,再扩展规模
实盘初期最重要的不是扩大交易规模,而是采集执行数据。
每一笔订单都需要保留:
- 信号价格;
- 预期成交价格;
- 委托价格;
- 实际成交价格;
- 委托时间;
- 交易所或柜台接收时间;
- 成交时间;
- 成交数量;
- 撤单时间;
- 订单状态变化;
- 当时的买卖盘口;
- 当时的波动率和成交量。
实盘滑点可以按交易方向统一计算。
对于买入:
实际滑点 = 实际成交价 - 预期价格
对于卖出:
实际滑点 = 预期价格 - 实际成交价
也可以进一步除以预期价格,转换为比例:
滑点比例 = 实际滑点 ÷ 预期价格
这样才能比较不同价格区间、不同品种和不同订单规模的执行质量。
如何判断滑点参数是否合理
滑点参数不能靠单次回测确定。需要观察参数变化对策略统计特征的影响。
建议至少进行以下测试。
测试一:多档固定滑点
设置多个滑点水平,例如:
- 基准滑点;
- 基准滑点的提高版本;
- 高于 2 个最小变动单位的压力条件;
- 按固定比例计算的成本条件。
重点不是寻找最优参数,而是观察策略是否出现结构性崩溃。
如果滑点从一个较低水平提高到略高水平后,收益立刻变为负值,说明策略的单位交易优势很薄。它依赖低成本成交,不具备足够的执行容错。
测试二:按交易时段区分
同一标的在不同交易时段的盘口质量可能不同。开盘、收盘、数据发布前后和快速波动阶段,滑点不应默认相同。
可以按时间段统计:
- 平均价差;
- 成交量;
- 订单数量;
- 实际成交偏差;
- 信号到成交的延迟;
- 滑点分布的中位数和尾部。
不要只看均值。滑点分布往往不是正态分布。少量极端成交可能对策略回撤产生显著影响。
测试三:按订单规模区分
订单金额占市场成交量的比例越高,市场冲击风险通常越大。
可以把订单分为不同规模区间,分别统计实际滑点。若订单规模扩大后滑点明显增加,固定滑点模型就已经不足。此时需要引入订单冲击模型,或者直接限制策略容量。
策略容量不是一个附加指标。它决定策略能否从研究规模扩展到真实资金规模。
测试四:加入延迟扰动
对信号执行时间增加不同延迟,观察结果变化。
延迟扰动可以作用于:
- 行情接收;
- 信号计算;
- 订单发送;
- 成交确认。
如果仅增加很小的延迟,策略的收益分布就显著恶化,说明策略依赖精确时序。此类策略应减少回测中的理想化执行假设,并重点检查系统低延迟能力。
测试五:样本外验证
滑点参数确定后,不再使用同一段历史数据优化策略。
将样本划分为:
- 研究样本;
- 参数验证样本;
- 样本外测试样本;
- 模拟交易阶段。
样本外测试期间不应继续调整策略参数。否则所谓样本外只是新的样本内。
此外,需要注意幸存者偏差。只使用当前仍然存在的股票或期货合约,会让历史结果偏向存活标的。滑点模型无法修复这个问题。数据范围和标的池也必须独立审查。
一个可执行的回测成本框架
如果暂时没有完整盘口数据,可以先建立分层成本框架,而不是直接使用一个看似精确的数字。
第一层,基础模型:
- 加入手续费;
- 设置固定点数或固定比例滑点;
- 处理买入和卖出的方向差异;
- 将成交时间推迟到信号可获得之后。
第二层,压力模型:
- 将滑点提高到高于 2 个最小变动单位的水平;
- 增加固定比例成本;
- 对高波动时段使用更高成本;
- 对大订单提高冲击成本;
- 增加信号到成交之间的延迟。
第三层,执行模型:
- 引入买卖价差;
- 使用盘口数据;
- 处理部分成交;
- 处理撤单;
- 记录订单生命周期;
- 统计实际成交偏差。
第四层,实盘校准:
- 用模拟交易和实盘订单记录估计滑点分布;
- 区分品种、时段和订单规模;
- 更新成本模型;
- 在新数据上重新验证;
- 禁止用成本模型反向制造更高收益。
这个过程的目标不是得到一个绝对精确的滑点数字。市场数据和执行条件一直在变化,精确预测单笔成交并不现实。目标是确认策略在合理成本区间内仍然具有统计优势。
如果策略只有在零滑点、零延迟、完全成交的条件下盈利,它不是一个可交易策略,只是一个价格序列上的统计拟合。
常见错误及其修正方式
把滑点设为零
零滑点只能用于验证信号方向和代码逻辑。不能用于判断实盘收益。
修正方式是至少加入一个基础成本版本和一个压力测试版本。两个版本都需要保存,不能只展示收益更高的结果。
买卖方向使用同一个价格调整规则
买入和卖出不能共用同一个符号方向。买入成本应推高成交价,卖出成本应压低成交价。
如果符号写反,回测可能人为增加收益。
只按收盘价成交
收盘价是否可交易,取决于信号生成时点和订单发送规则。策略若在收盘后才完成计算,就不应按照同一收盘价成交。
修正方式是明确数据到达时间、信号时间和执行时间。
只测试平均滑点
平均值会隐藏尾部损失。极端行情中的少数大滑点,可能贡献大部分执行亏损。
修正方式是观察中位数、分位数、最大值和按时段分布。不要只记录一个平均数字。
忽略部分成交
订单数量较大或流动性较低时,完全成交假设会高估仓位调整速度。
修正方式是加入成交数量上限,或者按照盘口数量分层撮合。无法获得盘口数据时,至少降低单笔订单的可成交比例。
用实盘结果反向修改历史参数
实盘出现偏差后,直接调整滑点到一个能解释结果的数字,属于事后拟合。
修正方式是保存原始回测参数,建立版本控制,并用新数据验证修改后的模型。模型更新应有明确的统计依据。
最终判断:滑点测试要服务于策略淘汰
滑点建模的价值不是让回测收益看起来更接近预期。它的价值是尽早淘汰依赖理想成交的策略。
一个可继续研究的策略,通常应具备以下特征:
- 加入基础手续费和滑点后仍有正向期望;
- 滑点提高后,收益下降但不出现无解释的结构崩溃;
- 延迟增加后,风险指标变化可被量化;
- 交易成本占毛收益的比例处于可接受范围;
- 不同时间段、不同标的和不同市场状态下具有一定稳定性;
- 样本外结果没有明显偏离研究样本;
- 模拟交易中的订单流程与回测假设一致;
- 实盘成交偏差可以被日志和数据解释。
不能断言设置固定百分比滑点,就能完全消除实盘与回测之间的差距。前视偏差、幸存者偏差、过度拟合、数据质量和订单系统错误,都可能独立造成回测失真。
参数优化也应保持克制。先固定数据和执行规则,再测试策略参数。先验证滑点分布,再决定是否需要动态冲击模型。先记录实盘执行数据,再调整回测假设。
量化交易的核心不是得到一条平滑净值曲线。核心是让每一个收益单位都能追溯到数据、信号、订单和成交。滑点是其中最容易被简化、也最容易破坏结果的环节。忽略它,回测只是理论价格上的演算。建模它,才能开始讨论策略是否具备真实执行条件。
相关阅读: 交易员决策疲劳应对策略:减少日内无效交易的系统化方案 、 全职交易员的社交孤立:享受孤独还是走向心理失衡?.
