10、订单簿模拟:蒙特卡洛模拟、零智能代理模型、事件驱动模拟、模拟回测框架

订单簿模拟,说白了就是给市场拍一部「纪录片」。

你想想看,真实的订单簿数据多贵啊,而且就那么几天的。但做策略回测,我们需要成千上万种市场场景。怎么办?自己造!

我个人习惯把订单簿模拟分成四个层次:从最简单的随机数生成,到带一点点智能的代理模型,再到完全事件驱动的微观模拟,最后搭一个完整的回测框架。今天咱们一层层拆开聊。

10.1 蒙特卡洛模拟:用随机数「猜」市场

蒙特卡洛模拟,名字听着高大上,其实就是「大量随机试验」。用在订单簿上,就是随机生成订单的到达时间、价格、数量。

核心假设:订单到达服从泊松过程,价格变动服从某种分布。

关键公式

订单到达间隔时间 ~ Exp(λ),其中 λ 是平均到达率

价格变动 ~ N(0, σ²),σ 是波动率

我在项目中遇到过一个问题:直接用正态分布模拟价格,结果出现了负价格。嗯,这显然不合理。后来我改用对数正态分布,或者加一个价格下限约束。

import numpy as np

def monte_carlo_orderbook(lam=10, sigma=0.01, T=100):
    """
    蒙特卡洛模拟订单簿
    lam: 每秒平均订单数
    sigma: 价格波动率
    T: 模拟时长(秒)
    """
    # 生成订单到达时间(泊松过程)
    inter_arrival = np.random.exponential(1/lam, int(lam * T * 2))
    arrival_times = np.cumsum(inter_arrival)
    arrival_times = arrival_times[arrival_times < T]
    
    # 生成价格变动
    n_orders = len(arrival_times)
    price_changes = np.random.normal(0, sigma, n_orders)
    
    # 生成订单数量(对数正态分布)
    volumes = np.random.lognormal(mean=5, sigma=1, size=n_orders)
    
    return arrival_times, price_changes, volumes

# 使用示例
times, prices, vols = monte_carlo_orderbook(lam=50, sigma=0.005, T=60)
print(f"生成了 {len(times)} 个订单,模拟了 60 秒的市场")

我的小技巧:蒙特卡洛模拟适合做「压力测试」。比如把 λ 设成正常值的 10 倍,看看你的策略扛不扛得住。我曾经用这个方法发现了一个订单簿深度不足的问题,避免了实盘中的大坑。

10.2 零智能代理模型:让「傻子」来交易

零智能代理(Zero Intelligence Agent),名字有点损,但意思很明确:代理没有任何策略,随机买卖。

你可能会问:「这能模拟真实市场吗?」

答案是:不能完全模拟,但能模拟出一些基本特征。比如订单簿的形状、价差的分布。Gode & Sunder 在 1993 年就证明了,即使代理是零智能的,市场依然能产生类似真实市场的价格发现过程。

零智能代理的核心规则

  • 每个代理随机决定是买单还是卖单
  • 价格在合理范围内随机选取
  • 数量也随机生成
  • 没有记忆,没有策略,纯粹随机
class ZeroIntelligenceAgent:
    def __init__(self, agent_id, price_range=(90, 110)):
        self.id = agent_id
        self.price_range = price_range
        
    def generate_order(self):
        """生成一个随机订单"""
        side = np.random.choice(['buy', 'sell'])
        price = np.random.uniform(self.price_range[0], self.price_range[1])
        volume = np.random.randint(1, 100)
        
        return {
            'agent_id': self.id,
            'side': side,
            'price': round(price, 2),
            'volume': volume,
            'timestamp': None  # 由模拟器填充
        }

# 创建一群「傻子」代理
agents = [ZeroIntelligenceAgent(i) for i in range(100)]
# 每个代理生成一个订单
orders = [agent.generate_order() for agent in agents]

避坑指南:我曾经用零智能模型做策略回测,结果策略表现特别好。后来发现是因为代理太「傻」了,我的策略随便捡漏。真实市场里可没这种好事。所以零智能模型适合做基准测试,不适合做策略验证。

10.3 事件驱动模拟:让市场「动」起来

蒙特卡洛和零智能模型都是「静态」的——订单生成后就不管了。但真实市场是动态的:订单会成交、会撤单、会修改。

事件驱动模拟,就是模拟这些「事件」的发生和响应。

核心事件类型

事件类型 触发条件 处理逻辑
新订单到达 随机时间间隔 尝试撮合,否则加入订单簿
订单成交 买卖价格匹配 从订单簿移除,更新持仓
订单撤单 随机概率或超时 从订单簿移除
订单修改 随机概率 更新价格或数量
class EventDrivenSimulator:
    def __init__(self):
        self.orderbook = {'bids': [], 'asks': []}
        self.events = []
        self.current_time = 0
        
    def process_order(self, order):
        """处理一个订单事件"""
        if order['side'] == 'buy':
            # 尝试与卖单撮合
            if self.orderbook['asks'] and order['price'] >= self.orderbook['asks'][0]['price']:
                # 成交!
                self.execute_trade(order, self.orderbook['asks'][0])
            else:
                # 加入买单队列
                self.orderbook['bids'].append(order)
                self.orderbook['bids'].sort(key=lambda x: -x['price'])
        else:
            # 类似处理卖单
            if self.orderbook['bids'] and order['price'] <= self.orderbook['bids'][0]['price']:
                self.execute_trade(order, self.orderbook['bids'][0])
            else:
                self.orderbook['asks'].append(order)
                self.orderbook['asks'].sort(key=lambda x: x['price'])
    
    def execute_trade(self, buy_order, sell_order):
        """执行成交"""
        trade_price = (buy_order['price'] + sell_order['price']) / 2
        trade_volume = min(buy_order['volume'], sell_order['volume'])
        print(f"时间 {self.current_time}: 成交 {trade_volume} 股 @ {trade_price}")
        # 更新剩余数量...

我的经验:事件驱动模拟最考验的是「事件队列」的设计。我建议用优先队列(heapq)来管理事件,按时间排序。这样模拟效率高,而且逻辑清晰。我曾经用普通列表排序,结果模拟 1 小时的市场数据跑了 10 分钟,换成堆之后秒级完成。

10.4 模拟回测框架:把一切串起来

前面三个模块是零件,现在我们要把它们组装成一台完整的「回测机器」。

一个合格的模拟回测框架,至少需要:

  • 市场模拟器:生成订单流,维护订单簿
  • 策略模块:接收市场数据,做出交易决策
  • 风控模块:检查仓位、资金是否合规
  • 绩效统计:记录每笔交易,计算收益、夏普等指标
模拟回测框架架构 市场模拟器 订单簿维护 策略模块 交易决策 风控模块 仓位/资金检查 绩效统计 收益/夏普/最大回撤 订单流 交易信号 执行/拒绝 成交数据 决策记录

我个人习惯用「事件循环」来驱动整个框架。每一轮循环处理一个事件,更新市场状态,通知策略,检查风控,记录绩效。

class BacktestFramework:
    def __init__(self, market_simulator, strategy, risk_manager):
        self.market = market_simulator
        self.strategy = strategy
        self.risk = risk_manager
        self.performance = PerformanceTracker()
        
    def run(self, duration_seconds=3600):
        """运行回测"""
        while self.market.current_time < duration_seconds:
            # 1. 获取下一个市场事件
            event = self.market.next_event()
            
            # 2. 更新策略
            signal = self.strategy.on_market_data(event)
            
            # 3. 风控检查
            if signal and self.risk.check(signal):
                # 4. 执行交易
                trade = self.market.execute(signal)
                # 5. 记录绩效
                self.performance.record(trade)
            
            # 6. 推进时间
            self.market.advance_time()
        
        return self.performance.summary()

重要提醒:模拟回测框架最怕「未来信息泄露」。比如用当天的收盘价来做开盘决策。我踩过这个坑,后来在框架里强制要求:策略只能看到当前时间之前的数据。建议在框架层面加一个时间戳检查,防止策略作弊。

好了,订单簿模拟的四个层次都讲完了。从蒙特卡洛的随机数,到零智能代理的「傻子」交易,再到事件驱动的动态模拟,最后搭一个完整的回测框架。每一步都有坑,但也都有乐趣。

我个人觉得,模拟回测框架是量化交易中最值得花时间打磨的模块。框架搭好了,后面策略开发、参数优化都会事半功倍。你想想看,与其在实盘里交学费,不如在模拟器里把问题都暴露出来。

最后一个小建议:刚开始做模拟时,别追求完美。先跑起来,再优化。我曾经花了一个月优化模拟器的性能,结果发现策略本身有问题。嗯,方向比速度重要。

无相订单流研究社 微信Lucian808555