滑点:你按这个价下单,真成交时价格已经跑掉了
你点了一笔市价买入,屏幕上那个数字明明白白写着 10.00。成交回报弹出来,成交均价 10.03。
三分钱。你大概连眉毛都不会抬一下。可如果你一年做一百个来回,这三分钱要被收走两百次——一进一出各一次。它不出现在手续费清单上,券商不会为它给你发一张账单,也没有任何一栏对账单会告诉你「本年度滑点支出」。但它每一笔都在收,而且专挑你最急的时候多收。
回测里那条漂亮的资金曲线,和实盘账户里那个平淡的数字之间,滑点通常占了差额里相当大的一块。
先说人话:它到底是件什么事
先说个跟股票没关系的场景。
你在抢票 App 上看到一张 380 的演唱会票,点进去,付款那一秒页面刷新,这个价位没了,剩下的是 450 的区域。你没有被骗——380 那张票是真的,只不过它是刚才的价格,不是留给你的价格。屏幕上显示的数字,记录的是别人已经完成的事,不是对你的承诺。
再来一个。你去菜市场问老板排骨多少钱一斤,他说 28。你说来二十斤。他愣了一下说,28 的只剩三斤,剩下的得 32。你最后二十斤的均价,既不是 28 也不是 32,是被你自己的需求量拽上去的一个中间数。
这两件事,恰好对应滑点的两个来源:你看到的那个价格本来就不是你的价格,以及你要的量把价格顶走了。
它从哪来
来源一:报价一直在变,而且你看到的是「已经发生的」
行情软件上那个最显眼的数字,通常是最新成交价——最近一笔交易的成交价。它是过去时。
你现在真正能碰到的,是盘口:一边是别人愿意卖给你的最低价(卖一),另一边是别人愿意从你手里买的最高价(买一)。这两个价中间永远隔着一道缝,这道缝叫买卖价差。你想立刻买,就得接受卖一;想立刻卖,就得接受买一。一进一出,这道缝你要跨两次。
价差有多宽,取决于这个品种有多少人在盯着它。大盘蓝筹的价差可能只有一个最小报价单位,冷门小票的价差能宽到让你怀疑人生。这背后就是流动性——资产能不能以合理价格快速变现。流动性差不是一个抽象的形容词,它就是这道缝的宽度。
来源二:你的单子太大,一档挂单吃不下
盘口不只是两个价格,每个价格后面还挂着一个数量。卖一价上挂着 500 手,卖二价上挂着 300 手,往上依次排开。
买 100 股,卖一价那一档就吃完了,你的成交价就是卖一。买 100 万股,卖一吃完还差得远,得往上继续吃卖二、卖三、卖四……你的成交均价被自己一路推高。这一块单独有个名字叫冲击成本,是你自己把价格买贵的那部分,冲击成本:自己把价格买高了 那篇专门讲它怎么随规模非线性放大。这里只需要记住:滑点里有一部分是市场本来就有的,还有一部分是你自己造成的,后者跟你的资金量直接挂钩。
来源三:从你决定到单子落地,中间那段空白
程序在一根 K 线收盘那一刻算出信号,但这笔单最快也要到下一根 K 线才真正进入交易所。这中间可能是一秒,可能是一分钟,可能隔着一个晚上。这段时间里报价没有停下来等你。
还有一层更细的:即便单子进去了,限价单也要排队。你在 10.00 挂买单,前面可能还排着几百手别人的单。价格擦到 10.00 又弹回去了,前面的人成交了,你没有。这严格说不叫滑点,叫「没成交」,但它对结果的伤害和滑点同源——回测里默认你成交了。
什么时候它会突然放大
平静时段的滑点小到可以忽略,问题在于你的交易根本不发生在平静时段。
开盘和收盘的那几分钟。 量最大,但价格跳得也最凶。集合竞价段更极端,一笔大单能把开盘价推到一个和昨天完全脱节的位置。
消息落地那一刻。 财报、公告、政策出来的瞬间,做市和挂单的人第一反应是把单子撤掉。盘口被抽空,价差瞬间张开几倍。你恰恰在这时候最想动手。
止损被触发的时候。 这一条最该被单独记住。止损价是 9.50,触发后成交在 9.30,不是意外,是常态。因为触发你止损的那根下跌,同时也在触发别人的止损,所有人往同一个方向挤,买盘那一侧被瞬间吃穿。止损的滑点是不对称的,它永远往坏的方向偏,而且行情越糟偏得越多。
涨跌停附近。 想卖的时候卖不掉——不是滑点大,是压根没有对手盘。这时候滑点的表现形式变成了「成交不了」。
你的单子相对当天成交量的比例。 同样一笔一千万,在日成交额几十亿的品种上激不起水花,在日成交额几千万的品种上就是当天的主要买盘。当下这只票比平时活跃多少,量比 这类指标能给个粗略参照,但它只描述活跃度,不告诉你盘口每一档有多厚。
系统层:三种系统,三种态度
同一件事,三个开源量化系统的处理方式差得很远。把它们摆在一起看,比看任何一个单独的实现都更能说明滑点到底是什么。
| 系统 | 做法 | 背后的态度 |
|---|---|---|
| Vibe-Trading | 每个市场引擎自己实现滑点函数 | 承认滑点因市场而异,一个数字盖不住 |
| freqtrade 回测 | 明确声明「不模拟滑点」 | 划清边界,把它推给手续费参数近似 |
| freqtrade 模拟盘 | 真的去拉盘口逐档吃单 | 有实时数据的时候,就认真算一遍 |
| qlib | 买卖两个成交价字段 + 成交量上限 | 从价格来源和容量两头卡 |
做法一 · 把滑点做成「每个市场自己的事」
Vibe-Trading 的引擎基类里,滑点被列为一个抽象方法——和「这笔能不能成交」「手续费怎么算」「手数怎么取整」并列。抽象方法的意思是子类必须自己实现,基类不给默认答案。这个安排本身就是一句话:不同市场的滑点长得不一样,没有通用解。
A 股引擎的实现只有一行,简单到有点朴素:
def apply_slippage(self, price: float, direction: int) -> float:
"""A-share slippage (relatively small due to tick size)."""
return price * (1 + direction * self.slippage_rate)
direction 买入时是 1、卖出时是 -1,所以这个乘法保证价格永远往不利于你的方向偏。买的时候贵一点,卖的时候便宜一点,绝不会反过来给你送钱。这一点是滑点区别于普通随机误差的关键:它有方向。
外汇引擎的实现就完全不是一个形状了。它压根不收独立手续费,注释里说得很直白——成本全在点差里。它的滑点算的是「半个点差 + 一个额外的滑点点数」,而且点差要按具体货币对查表取。同一个概念,在两个市场里的公式连结构都不一样。
还有一处设计更值得琢磨:滑点不是最后在收益上减一刀,而是改成交价。改了成交价,后面所有东西跟着变——持仓市值变了、盈亏变了,还有一件事也变了:这笔到底能不能成交。
那份代码里有个函数专门算「这根 K 线上我会成交在什么价」,用的是这根 K 线的开盘价加上按方向施加的滑点,然后拿这个价格去和当天的涨跌停价带比。注释解释了为什么必须这么绕:引擎实际记账用的是滑点后的价格,如果拿原始开盘价去判断「在不在合法区间内」,就会放行一笔记账时落在价带之外的成交——一笔法律上不可能发生的交易。
这个细节很能说明问题:滑点一旦被认真对待,它就不只是一个成本项,它会反过来决定哪些交易根本不该出现在你的成交记录里。
做法二 · 干脆承认自己没模拟
freqtrade 的回测假设清单里有一条写得毫不含糊:所有订单都按请求的价格成交,没有滑点,只要那个价格落在这根 K 线的最高最低之间。
这不是偷懒,是划清边界。K 线只有开高低收四个数,K 线内部发生了什么、盘口有多厚,这些信息在日线、小时线数据里根本不存在。与其编一个模型假装知道,不如把假设写在文档第一页,让用它的人自己去补。
它给的补法在配置说明里:手续费百分比这个参数,官方说明写着「可以用来在回测里模拟滑点」。也就是说,把手续费率调高一点,当作滑点的近似。代码里还留了一处痕迹——计算某个风险调整收益比率的地方,有一行被注释掉的「每笔加 0.1% 滑点」,写了但没有启用。这行代码本身不重要,它反映的是这个问题在工程上一直悬着。
到了模拟盘(不下真单、但接实时数据的那种模式),态度完全变了。市价单不再按一个价格成交,而是真的去拉盘口,一档一档往下吃:还剩多少量没成交,就继续吃下一档,价乘量累加,最后除以总量得到成交均价。这正是前面菜市场那个二十斤排骨的算法。
同时它设了一个最坏价格封顶:买入方向不允许比参考价高出超过某个比例(当前实现里的默认值,可配置,且随版本可能调整),成交均价在「逐档算出来的」和「封顶价」之间取对你更有利的那个。
限价单还有一条特别的规则:如果你挂的限价穿过对手价超过一定幅度,它会被直接当成市价单处理。理由很实际——现实中这种单本来就会立刻成交,继续装作限价单反而失真。
同一个软件,回测里不模拟滑点,模拟盘里认真逐档模拟。这个落差本身就是本篇最想让你看到的东西。
做法三 · 从成交价来源和容量两头卡
qlib 的交易所对象允许你指定「成交价用哪个字段」,而且允许配成买卖两个不同的字段——买入用一个价格,卖出用另一个价格。这等于把买卖价差直接建进了数据层,而不是事后加一个百分比。
它还有一个成交量上限的配置,限制在一段时间内你最多能成交多少量。写法可以是「累计型」的,比如当日累计成交量的某个比例,已经成交过的要从额度里扣掉;也可以是「实时型」的,比如直接取盘口买一档、卖一档的挂单量。配了多个限制的话取最小值。
这条约束和滑点是一体两面。如果不限量,回测里你可以在一根 K 线上买走这只票当天的全部成交量,还按那个价格成交——那已经不是滑点被低估了,那是滑点被彻底删掉了。qlib 另外还把开仓成本率、平仓成本率、最低手续费分开设,成交价和成本率是两套独立的旋钮。
回测里,它怎么被系统性地低估
这一节是本篇的重点。
一个固定比例,等于同时宣布三件不成立的事。 你在配置里填一个滑点比例,就相当于声明:滑点和单子大小无关、和时段无关、和市场状态无关。三条没有一条站得住。真实滑点在平静时段远小于这个数,在你最需要成交的那些时刻远大于这个数。而策略的成交,恰恰集中在后一种时刻——这不是巧合,是策略的信号本来就来自异动。所以固定比例的净效果几乎总是高估你的成绩,而不是随机两边偏。
它按笔数收,不按持有时间收。 一笔完整来回吃两次。年化收益是按年算的,滑点是按笔算的。一套一年调仓两次的方法和一套一天调仓两次的方法,填同一个滑点参数,实际被吃掉的收益差着两个数量级。短周期方法对滑点的敏感度高得吓人——很多在无成本世界里看着漂亮的高频规则,把成本按真实量级填进去之后直接由正转负。
止损那一笔被当成了普通那一笔。 前面说过止损触发时是最坏的时刻,但绝大多数回测对止损用的还是那个固定比例。freqtrade 的假设清单里就有类似的处理:止损按止损价成交,即使这根 K 线的最低价更低。文档为这条做了一点补偿(多算一份费用),但它补不了真实的缺口。
限价单在回测里从不排队。 现实中价格碰到你的价位不等于你成交了,前面可能还排着一堆单。回测通常默认「价格到了 = 成交了」。这不是滑点,但造成的高估是同一类的,回测里的成交假设 那篇专门拆这一层。
参数优化会主动往滑点敏感的方向跑。 这一条最阴险。如果你在参数搜索阶段没有把真实成本放进去,优化器会系统性地偏好那些交易次数更多、每笔赚得更少的参数组合——因为在一个没有成本的世界里,多交易是纯赚。等你回头再把成本加上去,这批参数第一个死。所以成本假设不能等策略定型了再补,它必须一开始就在优化目标里,这一点在 回测里的交易成本假设 里有更完整的讨论。
「我把滑点设成万分之十,够保守了吧」——保守与否不取决于数字大小,取决于它会不会随场景变化。 一个恒定的比例,在你本该被狠狠惩罚的那些交易上(止损、追涨、消息面、大单)给了折扣,在你本来几乎没有成本的那些交易上(平静时段的小单)多收了钱。两边都错,只是方向相反。而由于策略的成交天然集中在前一类场景,两边并不会互相抵消,净效果是高估。想知道自己有多依赖这个假设,把它翻倍再跑一遍就知道了。手边没有回测环境也能先估个大概,滑点与手续费敏感度里填上每笔的滑点和调仓频率,就能看到成本翻倍时收益被啃掉多少。
失效边界与常见误用
把滑点和手续费当成一回事。 手续费是明码标价、事先可知、和市场状态无关的;滑点是随机的、有固定方向的(永远不利)、随规模放大的、在最坏时刻最大的。用手续费参数近似滑点是一种工程妥协,别把妥协当成等价。
只在收益上减,不在成交价上改。 这两种做法在数字上可能接近,在逻辑上差很远。只减收益的话,那些「因为滑点本来就不该成交」的单子仍然会成交——涨跌停边缘那些交易照样进了你的成交记录,而现实中它们根本不存在。
把大盘品种的假设照搬到小品种上。 同一个滑点比例,在流动性天差地别的两个品种上意味着完全不同的东西。一套在大盘股上验证过的成本假设,换到冷门品种上通常要放大很多倍,而具体放大多少,只能靠实际盘口数据估,估不出来就别做那个品种。
以为限价单就没有滑点。 限价单确实锁住了价格,但它把「价格风险」换成了「成交风险」——你可能压根成交不了,或者只成交一部分。这个成本不显示在成交价上,它显示在「那笔本该赚钱的交易你没上车」里,而回测通常算不出这一项。
把模拟盘的结果当实盘。 模拟盘再认真地拉盘口逐档模拟,你的单子也没有真的进到盘口里去,不占位置、不影响别人、不引发对手的反应。资金量小的时候差别不大,资金量上来之后这是两个世界。
发现滑点吃光了收益,就去调小滑点参数。 这个动作听起来荒唐,但在实际操作里非常常见,因为它是唯一一个能马上让曲线变好看的旋钮。回测参数不是谈判对象。
小结
- 滑点 = 你打算成交的价格和真正成交的价格之间那点差额。它有方向,永远往不利于你的一侧偏,这是它和普通随机误差的根本区别。
- 三个来源:你看到的价格本来就不是你能碰到的价格(买卖价差)、你的量把价格顶走了(冲击成本)、从决定到落地那段时间价格还在动(延迟与排队)。
- 放大的时刻高度集中:开盘收盘、消息落地、止损触发、涨跌停边缘、小品种大单。共同点是——都是你最想成交的时候。
- 三种系统三种态度:把滑点做成每个市场自己的实现并让它反过来影响「能不能成交」;直接声明回测不模拟、由手续费参数近似、到了模拟盘再逐档拉盘口;从成交价字段和成交量上限两头卡住。
- 回测低估它的机制不是「参数填小了」,而是「用一个恒定的数去描述一个随场景剧烈变化的东西」,且策略的成交恰好集中在那个数最不适用的场景里。
- 检验方法只有一个方向可靠:把滑点假设翻倍甚至更多再跑一遍。收益从正变负,说明这套方法赚的本来就是成本量级的钱。
一句话记住这篇:屏幕上那个价格,是别人刚才成交的价格,不是留给你的价格。
本文所引源码与文档均来自上述快照版本,是阅读代码得出的机制说明,不是运行结果;文中提到的默认值都可配置,且可能随版本变化,以你手上那个版本为准。回 量化与 AI 投研概念库 看其它概念各自在说什么;本站内容为投资教育,不构成任何投资建议,边界见 免责声明。
本文为投资教育整理,关键数据与结论请结合下列权威来源验证。
- freqtrade 源码快照 b3404c9(2026-08-18):docs/backtesting.md「Assumptions made by backtesting」、docs/configuration.md「Considerations for dry-run」、freqtrade/exchange/exchange.py、freqtrade/data/metrics.py、freqtrade/config_schema/config_schema.py ↗
- qlib 源码快照 79633dd(2026-07-23):qlib/backtest/exchange.py(deal_price、volume_threshold、open_cost/close_cost) ↗
- Vibe-Trading 源码快照 3a752d5(2026-08-04):agent/backtest/engines/base.py、agent/backtest/engines/china_a.py、agent/backtest/engines/forex.py ↗