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()
重要提醒:模拟回测框架最怕「未来信息泄露」。比如用当天的收盘价来做开盘决策。我踩过这个坑,后来在框架里强制要求:策略只能看到当前时间之前的数据。建议在框架层面加一个时间戳检查,防止策略作弊。
好了,订单簿模拟的四个层次都讲完了。从蒙特卡洛的随机数,到零智能代理的「傻子」交易,再到事件驱动的动态模拟,最后搭一个完整的回测框架。每一步都有坑,但也都有乐趣。
我个人觉得,模拟回测框架是量化交易中最值得花时间打磨的模块。框架搭好了,后面策略开发、参数优化都会事半功倍。你想想看,与其在实盘里交学费,不如在模拟器里把问题都暴露出来。
最后一个小建议:刚开始做模拟时,别追求完美。先跑起来,再优化。我曾经花了一个月优化模拟器的性能,结果发现策略本身有问题。嗯,方向比速度重要。