↓ 跳过正文
  1. Posts/

为什么预测对了价格方向,交易却可能亏钱?——从 Bid/Ask 到 Adverse Selection

之前写 order book 和价格形成那篇时,花了不少篇幅搞清楚 bid/ask、maker/taker、以及成交量和价格之间的关系。写完之后我觉得这些概念已经理顺了,但最近在看加密货币量化执行的材料时,碰到了一个更尖锐的问题:

假如我有一个预测模型,它能准确预测未来 10 秒的价格方向。这个模型能赚钱吗?

直觉上,当然能——方向对了,买涨卖跌就行了。但真正开始算交易细节时,我发现事情远没有这么直接。

Bid、Ask 和 Mid Price
#

先快速回顾一下。Bid 是买方愿意出的最高价(出价),Ask 是卖方愿意接受的最低价(要价)。主动买入通常与 Ask 撮合,主动卖出通常与 Bid 撮合。这些在之前的文章里已经写过,这里不再重复撮合流程。

用 BTC 举例。假设当前盘口:

1Best Bid = 99,990 USDT
2Best Ask = 100,010 USDT

那"BTC 的价格"到底是多少?

行情软件里通常显示的是 Last Traded Price——最近一笔实际成交的价格。如果上一笔是在 Ask 成交的,显示 100,010;如果是在 Bid 成交的,显示 99,990。但盘口本身没有变化,两边的报价还是 99,990 和 100,010。

这里就会出现一个现象:即使 Bid 和 Ask 都纹丝不动,Last Traded Price 可以在 99,990 和 100,010 之间来回跳。一笔主动买入把价格打到 100,010,紧接着一笔主动卖出又打回 99,990。这种由买卖方向交替引起的价格跳动,叫 Bid-Ask Bounce。

这对短周期的价格序列有一个不太直观的影响。如果用 Last Traded Price 来计算收益率,这些 bounce 会引入额外的微观结构噪声——价格看起来在动,但其实只是买卖方向在交替,市场对 BTC 的估值没有发生实质变化。

所以在短周期模型中,经常用另一个价格作为参考:

$$ \text{Mid Price} = \frac{\text{Best Bid} + \text{Best Ask}}{2} $$

在上面的例子里:

$$ \text{Mid Price} = \frac{99{,}990 + 100{,}010}{2} = 100{,}000 \text{ USDT} $$

Mid Price 把 Bid-Ask Bounce 的影响平滑掉了。只要两边报价不变,Mid Price 就不变,不管最近一笔交易是买还是卖。

不过要注意一件事:Mid Price 是一个计算出来的参考价格,不是你能直接成交的价格。你不能在 100,000 这个位置买入或卖出——当前能买到的最低价是 100,010(Ask),能卖出的最高价是 99,990(Bid)。Mid Price 是 Bid 和 Ask 的中点,但它本身并不对应任何一方的报价。

Bid、Mid、Ask 在价格轴上的位置关系

也不能说 Mid Price 就是"真实的公允价值"。它只是一种常用的近似——在 Bid 和 Ask 两侧挂单数量差异不大时,Mid Price 是一个合理的参考点。但当买卖两侧的深度严重不对称时,Mid Price 可能并不是最好的估计。后面会一直用 Mid Price 作为参考基准来分析执行成本,需要记住它始终是一个简化。

Spread 到底意味着多大的成本
#

Bid 和 Ask 之间的差值,就是 Bid-Ask Spread:

$$ \text{Spread} = \text{Best Ask} - \text{Best Bid} $$

在上面的例子中,Spread = 100,010 - 99,990 = 20 USDT。

20 USDT 看起来是个绝对数字,不同价格的资产之间不太好直接比较。所以还有一个相对的版本:

$$ \text{Relative Spread} = \frac{\text{Spread}}{\text{Mid Price}} $$

这个比值通常用 Basis Point(简称 bp,复数 bps)来表达。1 bp = 0.01% = 0.0001。

一开始接触 bp 的时候,我花了一点时间理清它到底是什么。bp 就是一个固定的比例单位,它和"百分比"的关系是:

  • 1 bp = 0.01%
  • 10 bps = 0.1%
  • 100 bps = 1%

相同的 bp 数对应的绝对价格变化,取决于价格基准本身。用 BTC = 100,000 USDT 的例子:

  • 1 bp = 100,000 × 0.0001 = 10 USDT
  • 2 bps = 100,000 × 0.0002 = 20 USDT
  • 10 bps = 100,000 × 0.001 = 100 USDT

顺便提一句,在利率的语境下,bp 也常用来描述利率变化。比如利率从 4.00% 上升到 4.25%,上升了 25 bps(即 0.25 个百分点)。这里需要注意区分:利率上升 25 bps 是说绝对变化了 0.25 个百分点,不是相对变化了 25%。

回到交易成本。上面的盘口里:

  • Spread = 20 USDT
  • Relative Spread = 20 / 100,000 = 0.0002 = 2 bps

如果我用 Taker 方式在 Ask 主动买入,成交价 100,010,相对 Mid Price 100,000 多付了 10 USDT,也就是 1 bp。这 1 bp 是 Half-Spread——从 Mid 到 Ask 的距离。

如果我想立刻平仓,只能在 Bid 99,990 主动卖出。卖出价相对 Mid 又少收了 1 bp。

一买一卖的往返,在报价不变的情况下,损失的就是完整的 Spread:2 bps。这还不包括交易所收取的手续费。

这里有几个简化假设需要说清楚。第一,上面的计算假设最佳档位有足够的挂单量来容纳你的订单,不会出现一笔单吃穿好几档的情况。第二,假设你买入和卖出时 Spread 没有变化。第三,没有考虑市场冲击——你自己的订单可能影响后续报价。在真实交易中,实际的执行成本往往比 Spread 大,但 Spread 是最直接、最容易量化的那一层。

Spread 不等于交易的全部成本,但它是短周期交易中最基本的成本下界。

模型对了,交易为什么亏了
#

到这里就可以回到开头的问题了。

假设初始盘口:

1Best Bid = 99,990
2Best Ask = 100,010
3Mid Price = 100,000

我的模型预测:未来 10 秒,Mid Price 将上涨 1 bp,也就是 10 USDT。

10 秒后,市场确实按模型预测的方向移动了:

1Best Bid = 100,000
2Best Ask = 100,020
3Mid Price = 100,010

Mid Price 从 100,000 涨到 100,010,上涨了 10 USDT,正好 1 bp。模型预测完全正确。

但我的交易呢?

  • 开仓:在初始 Ask 以 100,010 主动买入(Taker)
  • 平仓:10 秒后在 Bid 以 100,000 主动卖出(Taker)
$$ \text{PnL} = 100{,}000 - 100{,}010 = -10 \text{ USDT} $$

每个 BTC 亏了 10 USDT。还没算手续费。

模型方向完全正确,Mid Price 确实涨了 1 bp。但这 1 bp 的预测收益全部被执行成本吃掉了——开仓时为 Ask 多付了 1 bp,平仓时在 Bid 少收了 1 bp,总执行成本 2 bps,而预测收益只有 1 bp。

这个例子的关键不是"1 bp 太小",而是一个结构性的问题:Taker 的每次执行都要穿过 Half-Spread。只要预测收益不够大,再准确的方向预测也无法覆盖执行成本。

用公式来表达这笔交易:

$$ \text{Net PnL} = \text{Exit Price} - \text{Entry Price} - \text{Fees} $$

在 Spread 不变、单次完整 Taker 往返、无额外冲击的简化假设下,这可以改写成:

$$ \text{Net Return} \approx \text{Mid Price Return} - \text{Relative Spread} - \text{Fees} $$

在上面的例子里:Net Return ≈ 1 bp - 2 bps - Fees < 0。

这不是说 Taker 策略一定不赚钱。如果模型预测的收益足够大——比如预测 Mid Price 会涨 10 bps,而 Spread 只有 2 bps——那扣掉执行成本之后仍然有利润。问题出在预测收益和执行成本之间的大小关系。

那全部挂 Maker 单不就好了?
#

写到这里的时候,我脑子里的第一个反应是:既然 Taker 每次执行都要付 Spread,那我不主动吃单就行了——在 Bid 上挂限价买单,等别人来和我成交。这样不仅省了 Spread,很多交易所的 Maker 手续费还比 Taker 低,甚至有返佣。

这个直觉在成本端是对的。如果你在 Bid 99,990 挂单买入并成交了,你的成交价比 Mid Price 还低了 1 bp,相当于获得了"负的 Spread 成本"。

但有一个前提:你的单必须成交。

回到刚才的场景。当前 Bid = 99,990,Ask = 100,010。模型预测价格会涨。你在 Bid 99,990 挂了一笔限价买单。

如果模型对了,价格确实快速上涨到 100,050——那你的 99,990 买单大概率根本没有成交。市场直接从你头顶飞过去了。

即使某个价位在你挂单期间发生过成交,也不代表你的订单一定成交了。同一个价位上可能有很多笔挂单在排队,按先到先得的顺序逐笔匹配。如果在你之前排了大量的买单,价格在你的价位短暂停留又离开,排在后面的你可能根本轮不到。

这就是 Fill Probability(成交概率)的问题。预测对了方向,但没有获得任何头寸——利润为零。

Taker 和 Maker 两条执行路径的对比

Taker 和 Maker 面对的是两种不同的 trade-off。Taker 的优势是确定性高——主动去成交,几乎一定能拿到头寸(前提是对手盘有量)。代价是每次都要穿过 Spread。Maker 的优势是成交价更好,但代价是成交不确定——你只能等别人来和你交易,等不到就空手。

到这里如果只是"Maker 可能成交不了",问题还不算太严重——大不了有些信号没抓到,抓到的那些不是照样赚吗?

但接下来的问题是:抓到的那些,真的照样赚吗?

成交的那些订单,表现还一样吗
#

这一步是我在整个学习过程中被触动最大的地方,因为它直接和我熟悉的模型评估联系上了。

假设模型产生了 100 个买入信号。事后来看,其中 60 个方向预测正确(价格确实涨了),40 个预测错误(价格跌了)。原始的方向预测准确率是 60%。

现在假设我用 Maker 限价买单来执行这些信号。由于成交概率的问题,100 个信号中只有 40 个实际成交了。

问题是:这 40 个成交的信号,预测准确率还是 60% 吗?

我一开始想当然地觉得应该差不多——成交了 40%,没成交 60%,如果成交是随机的,那成交的那 40 个里大概应该有 24 个预测正确、16 个预测错误,准确率还是 60%。

但仔细想想,成交不太可能是随机的。

考虑这样的情形:当价格真的要涨的时候,你挂在 Bid 上的买单不太容易成交——因为卖方没有动力主动压低价格来打到你的 Bid。但当价格要跌的时候,有人急着卖出,主动卖单会打到你的 Bid,你的限价买单反而容易成交。

也就是说:预测正确的信号(价格要涨)更不容易成交,预测错误的信号(价格要跌)更容易成交。

用一个示意性的数字来说明(这些数字是为了解释概念构造的,不是真实策略的经验统计):

预测正确预测错误合计
原始信号6040100
实际成交103040
未成交501060

原始预测准确率 = 60 / 100 = 60%。

成交样本中的预测准确率 = 10 / 40 = 25%。

准确率从 60% 掉到了 25%。

这里需要说几个边界。第一,模型的原始准确率 60% 是纯粹的方向预测指标——它衡量的是"模型说涨,后来确实涨了"的比例,本身并没有假设一定成交。第二,如果离线做策略回测的时候默认所有信号都能在某个参考价格成交,那个 60% 其实已经隐含了"理想成交"的假设。第三,成交样本中的准确率 25% 也不能直接等同于交易盈利概率——实际 PnL 还取决于价格变化的幅度、成交价格和手续费。

但核心的 insight 不变:成交不是一个与预测结果无关的随机事件。成交概率本身和未来价格方向之间可能存在系统性的关联。

用条件概率的语言来说:

$$ P(\text{Correct} \mid \text{Fill}) \neq P(\text{Correct}) $$

如果成交和预测结果是统计独立的,那不管成交率多低,成交样本的期望准确率都应该等于原始准确率。准确率之所以下降,不是因为成交的少了,而是因为成交和未来价格之间不独立。

全部信号 vs 实际成交信号中预测正确/错误的分布变化

从做 ML 的角度来看,这个问题有一个熟悉的名字:Selection Bias(选择偏差)。你在全量样本上评估模型得到的指标,不能直接代表你在某个非随机子集上的实际表现。在推荐系统里,这对应的是 offline evaluation 和 online performance 的差异——离线评估是在已有的曝光数据上做的,但线上推荐会面对不同的用户反馈分布。

在交易中也一样。离线回测里所有信号都"成交"了,但实盘中只有一部分信号能成交,而且成交的那部分可能系统性地偏向于表现差的信号。

Adverse Selection——为什么成交可能是坏消息
#

上面说的 Selection Bias 描述了一个统计现象:成交样本不是原始信号的随机子集。Adverse Selection(逆向选择)则更进一步,指出了这种非随机性背后的一个具体机制。

Adverse Selection 本来是经济学中的概念——在信息不对称的交易中,拥有信息劣势的一方更容易在不利条件下成交。经典例子是二手车市场:卖方比买方更了解车况,好车的卖方不愿意以平均价出售,留在市场上的更可能是质量差的车。

在金融市场里,Maker 面临的情况有类似的结构。

还是用 BTC 举例。我在 Bid = 100,000 挂了一笔限价买单。

情景 A:BTC 即将上涨。市场上的买方在推高价格,没有人愿意在 100,000 主动卖出。我的买单安静地挂在那里,没有人理它。价格涨到 100,050,我一股没买到。

情景 B:BTC 即将下跌。有人掌握了某个利空信息,或者有人需要紧急平仓,主动在 Bid 抛售。他的卖单打到了我的 100,000 买单,成交了。然后价格继续跌到 99,950。我买在了 100,000,现在浮亏 50 USDT。

这两个情景揭示了 Maker 的一个结构性困境:成交价格虽然好(在 Bid 买入,比 Mid 低),但成交本身可能携带了一个不利的信号——愿意主动卖给你的人,可能知道一些你不知道的事情。

不过有几件事要说清楚。

第一,不是所有 Taker 都有信息优势。很多 Taker 只是普通散户在执行常规交易,或者是被动型基金在做再平衡。Maker 成交并不意味着一定遇到了 Informed Trader。

第二,不是所有 Maker 成交都遭遇 Adverse Selection。如果价格在你成交后横盘甚至反弹,你的 Maker 买入可能是一笔很好的交易。

第三,并非所有 Selection Bias 都是 Adverse Selection。Selection Bias 是一个更宽泛的统计概念——任何导致成交样本非随机的因素都算。Adverse Selection 特指的是:成交与后续价格向不利方向运动之间存在系统性的联系。关键词是"系统性"——不是某一次亏了就叫 Adverse Selection,而是在大量交易中,成交的平均后果偏向不利。

在做市(market making)领域,评估一笔成交是否遭遇了 Adverse Selection 的一种常见方法是 Markout——看成交之后一段时间(比如 1 秒、5 秒、30 秒),Mid Price 相对于你成交价的变化。如果你在 Bid 买入后,Mid Price 系统性地下跌,说明你的成交倾向于发生在价格即将下行的时刻。这里不展开 Markout 的具体计算,只是提一句:它提供了一种量化 Adverse Selection 程度的思路。

从 Accuracy 到可实现的收益
#

回过头来看,这篇文章其实在追一条链路:一个预测模型说"价格要涨",最终我的账户里有没有多出钱来。

从信号到收益,中间至少要过这些关:

  1. 模型产生信号:预测未来 Mid Price 上涨。
  2. 下单:选择 Taker 还是 Maker,决定在什么价位、多大的量。
  3. 成交与否:Taker 几乎确定成交但价格差;Maker 价格好但可能不成交。
  4. 成交价格:不一定等于 Mid Price,也不一定等于挂单价——可能有滑点。
  5. 交易成本:Spread、手续费、市场冲击。
  6. 实际 PnL:Exit Price - Entry Price - Fees。

每一步都在过滤和修改信号的价值。模型解决的是第一步——未来价格可能往哪里走。但从第二步开始,Execution 决定了哪些信号最终能变成头寸,以什么价格成交,付出多少成本。

这篇文章里反复出现的几个 gap:

  • 只关注模型的 Accuracy 或 IC 不够。预测方向正确,即使达到 100%,也不能保证净盈利——微小的预测收益可能不足以覆盖执行成本。
  • 只比较 Maker 和 Taker 的手续费率也不够。Maker 手续费低,但成交概率和 Adverse Selection 的风险也是成本的一部分,只是不那么容易直接量化。
  • 离线回测中的策略表现不等于实盘表现。回测里的"成交"可能隐含了在真实市场中无法实现的假设。

在短周期交易中,预测到价格变化只是第一步。真正有价值的 Alpha,必须能够通过现实中的订单执行转化为净收益。

参考资料
#

  • Larry Harris,Trading and Exchanges: Market Microstructure for Practitioners(Oxford University Press,2003)——关于 bid-ask spread、adverse selection 和执行成本的标准参考
  • Marcos López de Prado,Advances in Financial Machine Learning(Wiley,2018)——关于微观结构特征和 selection bias 在量化策略中的影响

相关文章

从价格发现到流动性螺旋:股票市场为什么需要“国家队”?

在很长一段时间里,我对股票市场的理解非常机械,甚至带着一种理所当然的工程偏见。 作为一个长期和分布式系统、性能优化与反馈控制打交道的工程师,我习惯把撮合引擎看作一个纯粹的分布式信息处理系统:全市场的参与者带着各自的信息、算力和风险偏好,用真金白银在订单簿上挂单出清。价格上涨,说明对未来的盈利预期转好;价格下跌,说明基本面恶化或风险溢价上升。

大量主动卖出,股价为什么不跌?——从 SOXL 成交统计理解流动性与价格冲击

10月16日,美东时间上午,我在看 SOXL 的成交统计时看到了一组让我反复盯着屏幕的数字。 SOXL 是 3 倍做多半导体 ETF,那天跌了超过 6%。我打开成交统计,本来预期应该看到一个主动卖出量远超买入量的页面——毕竟已经跌成这样了。结果是这样的:

从成交量到资金净流入:重新理解股票价格是如何形成的

之前写量化交易系统那篇时提到过 execution 层——每笔 order 多大、对手盘的流动性够不够、是挂盘口还是主动吃价差。当时一笔带过,没有认真想 order book 这一层到底怎么运转。 最近第一次认真看市场微观结构(market microstructure)的一些材料,想搞清楚几个一直模模糊糊的问题。结果发现,不是模糊,是错的。

期权入门:为什么看对涨跌方向,仍然可能亏钱?

最近在学股票的波段交易(Swing Trading),平时看行情主要依赖趋势形态、动量和成交量。前段时间在琢磨做空交易时,遇到过一个很实际的场景:我看空某只股票,但我不想直接融券做空——融券需要借入股票,要付融券利息,还有被券商提前召回的可能;更棘手的是,如果遇到逼空(Short Squeeze),理论上的亏损幅度是上不封顶的,账户随时面临追加保证金(Margin Call)的压力。