第二十三课:做市商模型验证与回测

做市商模型写好了,参数调完了,然后呢?

我见过太多人,模型回测曲线漂亮得像假的一样,一上实盘就崩。为什么?因为验证环节没做好。今天我们就来聊聊,怎么科学地验证一个做市商模型。

回测框架搭建

回测框架,说白了就是一台时光机。你把历史数据喂进去,让模型模拟交易,看看它表现如何。

我个人习惯用事件驱动的回测框架。为什么?因为做市商是高频的,逐笔撮合才能模拟真实场景。

核心组件:

  • 数据引擎:处理 tick 级行情,包括买卖盘口、成交记录
  • 订单管理器:模拟挂单、撤单、成交
  • 风险控制器:实时监控敞口、库存、资金
  • 绩效统计器:计算盈亏、夏普、最大回撤

来看一个简化版的回测框架代码:

class MarketMakerBacktest:
    def __init__(self, model, data):
        self.model = model
        self.data = data
        self.positions = []
        self.pnl = []
        
    def run(self):
        for tick in self.data:
            # 1. 更新市场状态
            self.update_market(tick)
            # 2. 模型给出报价
            bid, ask = self.model.quote(tick)
            # 3. 模拟成交
            self.simulate_fill(bid, ask)
            # 4. 记录绩效
            self.record_pnl()
            
    def simulate_fill(self, bid, ask):
        # 这里要处理部分成交、排队等问题
        pass

嗯,这里要注意一点:回测里的成交逻辑,一定要尽量贴近真实。我曾经见过有人用「最优价立即成交」的假设,结果回测年化50%,实盘直接亏到姥姥家。

避坑指南:回测时一定要考虑滑点和交易成本。做市商的成本主要是手续费和冲击成本,别小看这几毛钱,累积起来能吃掉你一半利润。

过拟合检测

过拟合,是量化交易的头号杀手。你想想看,模型把历史数据里的噪声都学进去了,到了新数据上自然就失效了。

怎么检测过拟合?我常用的方法有这几个:

  1. 参数敏感性分析:稍微改一下参数,看结果是否剧烈波动。如果参数从1.0改成1.01,夏普就从3.0掉到0.5,那基本可以断定是过拟合了。
  2. 交叉验证:把数据分成多段,轮流做训练集和测试集。做市商模型我建议用时间序列交叉验证,因为金融数据有很强的时序依赖。
  3. 夏普比率衰减测试:把回测期分成前50%和后50%,看夏普是否大幅下降。如果前段3.0,后段0.5,那模型大概率是废了。

警告:不要用「样本内拟合优度」来评估做市商模型。R²再高也没用,做市商赚的是流动性溢价,不是预测价格。我见过有人用神经网络拟合历史报价,R²达到0.99,结果实盘一塌糊涂——因为他把随机噪声都学进去了。

还有一个更专业的检测方法——Deflated Sharpe Ratio(DSR)。它考虑了多重测试的影响。你试了100个参数组合,总有一个夏普特别高,但那是运气,不是实力。DSR就是用来惩罚这种「数据挖掘」行为的。

样本外测试

样本外测试,是验证模型的最后一道防线。说白了,就是模型从未见过的数据,用它来检验模型的泛化能力。

我的做法是这样的:

  • 时间分割法:用前70%的数据做训练和调参,后30%的数据做样本外测试。注意,这30%的数据在调参阶段绝对不能碰。
  • 滚动窗口法:对于做市商模型,我更推荐这个方法。因为市场环境会变,3年前的模型可能今天就不适用了。滚动窗口可以持续验证模型的稳定性。

来看一个滚动窗口测试的示意图:

滚动窗口样本外测试示意图 训练窗口1 测试1 训练窗口2 测试2 训练窗口3 测试3 T0 T+n 每次滚动,训练窗口向前移动,测试新数据

样本外测试的评估指标,我建议重点关注这几个:

指标 含义 合格标准
夏普比率 风险调整后收益 样本外 > 样本内 × 0.7
最大回撤 账户从峰值下跌的最大幅度 样本外 < 样本内 × 1.3
胜率 盈利交易占比 样本外与样本内差异 < 10%
盈亏比 平均盈利 / 平均亏损 样本外 > 1.5

我的经验:样本外测试的夏普如果低于样本内的70%,我基本就不会上实盘了。别心疼那点调参的时间,亏钱可比亏时间疼多了。

实战中的验证流程

好了,理论讲完了,说说我在项目中实际怎么做的。

第一步,数据准备。我会把3年的tick数据分成三段:训练期(18个月)、验证期(9个月)、测试期(9个月)。验证期用来调参,测试期用来最终验证。

第二步,参数扫描。在训练期上跑1000组参数组合,选出表现最好的前10组。然后在验证期上跑这10组,看哪组最稳定。

第三步,压力测试。我会特意选几个极端行情时段做测试,比如2020年3月的流动性危机、2022年11月的FTX暴雷。如果模型在这些时段扛不住,那平时赚再多也没用。

第四步,蒙特卡洛模拟。用bootstrap方法生成1000条不同的价格路径,看模型的收益分布。如果95%的路径都是盈利的,那模型就比较靠谱了。

小技巧:做市商模型验证时,别忘了做「流动性压力测试」。把市场深度砍掉一半,看模型还能不能赚钱。我在2020年3月就吃过这个亏,平时模型跑得好好的,流动性一枯竭,报价全被吃掉,一天亏了三个月的利润。

最后,我想说一句:回测再漂亮,也只是历史。市场永远在变,模型也会老化。我建议每季度重新验证一次模型,如果样本外表现持续下滑,就该考虑迭代了。

验证不是终点,而是起点。只有经过严苛验证的模型,才配得上真金白银。


无相订单流研究社 微信Lucian808555