实战案例七:波动率自适应做市策略
做市策略最怕什么?怕市场突然变脸。
你按着低波动环境设的参数,突然来一波剧烈行情,库存直接被打穿。反过来,高波动时你设的窄价差,又会被来回打脸。我早年做加密货币做市时就吃过这个亏——BTC一夜波动率翻倍,我的策略还在用原来的参数,结果一晚上亏了半个月的利润。
所以这一章,我们来解决这个问题。核心思路很简单:让策略自己感知波动率,动态调整报价。
波动率自适应的基本逻辑
说白了,就是让策略根据市场状态切换行为模式:
- 波动率低的时候,价差收窄,增加报价频率
- 波动率高的时候,价差拉宽,降低风险敞口
- 极端波动时,直接暂停做市
这个思路不新鲜,但实现细节里全是坑。我一个个说。
第一步:波动率怎么算?
很多人上来就用历史波动率,但做市策略需要的是实时感知。我个人习惯用已实现波动率(Realized Volatility),基于过去N笔交易的收益率计算:
def calc_realized_vol(prices, window=20):
log_returns = np.log(np.array(prices[1:]) / np.array(prices[:-1]))
vol = np.std(log_returns[-window:]) * np.sqrt(252 * 24 * 60) # 年化
return vol
这里有个细节——窗口大小怎么选?我试过5、10、20、50。窗口太小,波动率跳来跳去,策略也跟着乱动;窗口太大,反应太慢。最终我选了20笔交易作为窗口,大概对应5-10分钟,算是个平衡点。
第二步:波动率分档
拿到实时波动率后,需要把它映射到几个档位上。我一般分三档:
| 档位 | 波动率范围 | 策略行为 |
|---|---|---|
| 低波动 | vol < 20% | 窄价差,高频率 |
| 中波动 | 20% ≤ vol < 60% | 正常做市 |
| 高波动 | vol ≥ 60% | 宽价差,降低仓位 |
注意,这个阈值不是拍脑袋定的。我建议你回测时做个敏感性分析,看看不同阈值下策略的表现。我曾经在一个项目里直接用默认阈值,结果回测漂亮,实盘一跑就崩——因为那个市场的波动率分布跟回测数据完全不同。
第三步:动态调整价差和仓位
有了波动率档位,接下来就是调整核心参数。我常用的公式是这样的:
def adaptive_params(vol_level, base_spread=0.001, base_size=1.0):
if vol_level == 'low':
spread_mult = 0.8
size_mult = 1.2
elif vol_level == 'mid':
spread_mult = 1.0
size_mult = 1.0
else: # high
spread_mult = 1.5
size_mult = 0.5
return base_spread * spread_mult, base_size * size_mult
嗯,这里要注意:价差和仓位要联动调整。高波动时价差拉宽,同时仓位减半,这样即使被吃单,损失也有限。低波动时价差收窄,仓位加大,赚取更多流动性收益。
第四步:极端波动保护
这是最容易忽略的部分。当波动率超过某个上限(比如年化120%),策略应该暂停做市。为什么?因为这时候市场可能处于崩盘或暴涨状态,任何做市行为都是在赌方向。
我见过有人在高波动时继续做市,结果被连续吃单,库存直接变成负数。后来我加了一个保护逻辑:
if current_vol > MAX_VOL_THRESHOLD:
cancel_all_orders()
set_status('PAUSED')
log_warning(f'极端波动,暂停做市,当前波动率: {current_vol:.2%}')
这个阈值设多少?我个人习惯是年化120%。但不同市场不一样,比特币和山寨币的波动率分布完全不同,你得自己测。
完整策略流程
把上面这些串起来,整个策略的流程图是这样的:
核心要点:波动率自适应不是锦上添花,而是做市策略的生存保障。没有这个机制,你的策略在极端行情下就是裸奔。
回测中的注意事项
回测波动率自适应策略时,有几个坑我踩过:
- 前视偏差:计算波动率时只能用历史数据,不能用未来数据。这个错误我犯过,回测曲线漂亮得不行,实盘一塌糊涂。
- 参数过拟合:阈值和窗口大小不要对着历史数据硬调。我建议用滚动窗口验证,比如用前6个月数据调参,后3个月验证。
- 交易成本:波动率高时滑点也会变大,回测时要把这个因素考虑进去。我一般会在高波动场景下额外加20%的滑点成本。
个人经验:波动率自适应策略最适合在趋势+震荡混合的市场中运行。纯趋势行情下,它表现一般;但一旦市场切换,它能帮你保住大部分利润。
代码实现要点
最后给一个完整的策略骨架,你可以直接拿去改:
class VolAdaptiveMarketMaker:
def __init__(self, base_spread=0.001, base_size=1.0):
self.base_spread = base_spread
self.base_size = base_size
self.price_history = []
self.vol_window = 20
def on_tick(self, tick):
# 更新价格历史
self.price_history.append(tick.price)
if len(self.price_history) > self.vol_window * 2:
self.price_history.pop(0)
# 计算波动率
vol = self.calc_vol()
# 极端波动保护
if vol > 1.2:
self.cancel_orders()
return
# 自适应参数
spread, size = self.adaptive_params(vol)
# 生成订单
self.place_orders(tick.price, spread, size)
def calc_vol(self):
# 已实现波动率计算
prices = self.price_history[-self.vol_window:]
if len(prices) < 2:
return 0
returns = np.diff(np.log(prices))
return np.std(returns) * np.sqrt(252 * 24 * 60)
def adaptive_params(self, vol):
if vol < 0.2:
return self.base_spread * 0.8, self.base_size * 1.2
elif vol < 0.6:
return self.base_spread, self.base_size
else:
return self.base_spread * 1.5, self.base_size * 0.5
警告:这个代码只是演示框架。实盘时你还需要处理订单状态管理、重连机制、风控检查等。千万别直接复制就跑实盘,我吃过这个亏。
好了,波动率自适应做市策略的核心就这些。下一章我们会聊另一个实战案例——库存风险对冲策略,到时候见。
无相订单流研究社 微信Lucian808555