之前写 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 的中点,但它本身并不对应任何一方的报价。
也不能说 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,010Mid Price 从 100,000 涨到 100,010,上涨了 10 USDT,正好 1 bp。模型预测完全正确。
但我的交易呢?
- 开仓:在初始 Ask 以 100,010 主动买入(Taker)
- 平仓:10 秒后在 Bid 以 100,000 主动卖出(Taker)
每个 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 面对的是两种不同的 trade-off。Taker 的优势是确定性高——主动去成交,几乎一定能拿到头寸(前提是对手盘有量)。代价是每次都要穿过 Spread。Maker 的优势是成交价更好,但代价是成交不确定——你只能等别人来和你交易,等不到就空手。
到这里如果只是"Maker 可能成交不了",问题还不算太严重——大不了有些信号没抓到,抓到的那些不是照样赚吗?
但接下来的问题是:抓到的那些,真的照样赚吗?
成交的那些订单,表现还一样吗#
这一步是我在整个学习过程中被触动最大的地方,因为它直接和我熟悉的模型评估联系上了。
假设模型产生了 100 个买入信号。事后来看,其中 60 个方向预测正确(价格确实涨了),40 个预测错误(价格跌了)。原始的方向预测准确率是 60%。
现在假设我用 Maker 限价买单来执行这些信号。由于成交概率的问题,100 个信号中只有 40 个实际成交了。
问题是:这 40 个成交的信号,预测准确率还是 60% 吗?
我一开始想当然地觉得应该差不多——成交了 40%,没成交 60%,如果成交是随机的,那成交的那 40 个里大概应该有 24 个预测正确、16 个预测错误,准确率还是 60%。
但仔细想想,成交不太可能是随机的。
考虑这样的情形:当价格真的要涨的时候,你挂在 Bid 上的买单不太容易成交——因为卖方没有动力主动压低价格来打到你的 Bid。但当价格要跌的时候,有人急着卖出,主动卖单会打到你的 Bid,你的限价买单反而容易成交。
也就是说:预测正确的信号(价格要涨)更不容易成交,预测错误的信号(价格要跌)更容易成交。
用一个示意性的数字来说明(这些数字是为了解释概念构造的,不是真实策略的经验统计):
| 预测正确 | 预测错误 | 合计 | |
|---|---|---|---|
| 原始信号 | 60 | 40 | 100 |
| 实际成交 | 10 | 30 | 40 |
| 未成交 | 50 | 10 | 60 |
原始预测准确率 = 60 / 100 = 60%。
成交样本中的预测准确率 = 10 / 40 = 25%。
准确率从 60% 掉到了 25%。
这里需要说几个边界。第一,模型的原始准确率 60% 是纯粹的方向预测指标——它衡量的是"模型说涨,后来确实涨了"的比例,本身并没有假设一定成交。第二,如果离线做策略回测的时候默认所有信号都能在某个参考价格成交,那个 60% 其实已经隐含了"理想成交"的假设。第三,成交样本中的准确率 25% 也不能直接等同于交易盈利概率——实际 PnL 还取决于价格变化的幅度、成交价格和手续费。
但核心的 insight 不变:成交不是一个与预测结果无关的随机事件。成交概率本身和未来价格方向之间可能存在系统性的关联。
用条件概率的语言来说:
$$ P(\text{Correct} \mid \text{Fill}) \neq P(\text{Correct}) $$如果成交和预测结果是统计独立的,那不管成交率多低,成交样本的期望准确率都应该等于原始准确率。准确率之所以下降,不是因为成交的少了,而是因为成交和未来价格之间不独立。
从做 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 到可实现的收益#
回过头来看,这篇文章其实在追一条链路:一个预测模型说"价格要涨",最终我的账户里有没有多出钱来。
从信号到收益,中间至少要过这些关:
- 模型产生信号:预测未来 Mid Price 上涨。
- 下单:选择 Taker 还是 Maker,决定在什么价位、多大的量。
- 成交与否:Taker 几乎确定成交但价格差;Maker 价格好但可能不成交。
- 成交价格:不一定等于 Mid Price,也不一定等于挂单价——可能有滑点。
- 交易成本:Spread、手续费、市场冲击。
- 实际 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 在量化策略中的影响