第十三章:风险管理模块

做市商这行当,说白了就是在刀尖上跳舞。你赚的是流动性溢价,但一个黑天鹅事件就能让你把之前赚的全吐回去。我见过太多团队,策略回测曲线漂亮得不行,实盘一周就爆仓——问题全出在风险管理上。

这一章,咱们就聊聊怎么给做市系统装上「安全气囊」。我会结合自己踩过的坑,把最大持仓限制、VaR与CVaR、止损熔断、压力测试这几个核心模块讲透。

13.1 最大持仓限制:别把鸡蛋放在一个篮子里

做市商最忌讳什么?重仓单边。你想想看,如果某个币种你持有了总资金的30%,一旦遇到极端行情,流动性瞬间枯竭,你想平都平不掉。

我个人习惯把最大持仓限制分为三个层级:

  • 全局限制:总仓位不超过净资产的某个比例,比如80%。
  • 品种限制:单个币种不超过总资金的15%。
  • 方向限制:净多头或净空头不超过总资金的50%。

代码实现其实不复杂,核心就是每次下单前做一次检查:

class PositionManager:
    def __init__(self, total_capital, max_global_ratio=0.8, max_single_ratio=0.15):
        self.total_capital = total_capital
        self.max_global = total_capital * max_global_ratio
        self.max_single = total_capital * max_single_ratio
        self.positions = {}  # symbol -> quantity
    
    def can_open(self, symbol, quantity, price):
        """检查是否可以开仓"""
        cost = quantity * price
        current_total = sum(pos * price for pos in self.positions.values())
        
        # 全局限制
        if current_total + cost > self.max_global:
            return False, "超过全局持仓限制"
        
        # 品种限制
        current_single = self.positions.get(symbol, 0) * price
        if current_single + cost > self.max_single:
            return False, f"超过{symbol}单品种限制"
        
        return True, "允许开仓"
⚠️ 注意:我曾经犯过一个错误——只检查了名义本金,没考虑杠杆。结果某个币种用了5倍杠杆,实际风险敞口远超限制。后来我改成了用「风险敞口」来计算,也就是名义本金乘以杠杆倍数。

13.2 VaR与CVaR:量化你的「最坏情况」

VaR(Value at Risk)是个老生常谈的概念了。它回答的问题是:「在95%的置信水平下,未来一天最大可能亏损是多少?」

但VaR有个致命缺陷——它只告诉你「95%的情况不会亏超过X」,但剩下的5%呢?万一那5%发生了,亏损可能是X的10倍甚至100倍。

这就是CVaR(Conditional VaR)登场的时候了。CVaR计算的是「那最坏的5%情况下的平均亏损」。说白了,VaR是门槛,CVaR是门槛后面的深渊有多深。

我在做市策略里,通常同时监控这两个指标:

import numpy as np
from scipy.stats import norm

def calculate_var_cvar(returns, confidence=0.95):
    """
    计算VaR和CVaR
    returns: 历史收益率序列
    """
    # 排序收益率
    sorted_returns = np.sort(returns)
    
    # VaR: 第(1-confidence)分位数
    var_index = int(len(sorted_returns) * (1 - confidence))
    var = -sorted_returns[var_index]
    
    # CVaR: 尾部损失的均值
    tail_returns = sorted_returns[:var_index]
    cvar = -np.mean(tail_returns)
    
    return var, cvar

# 假设我们有某币种过去1000个tick的收益率数据
np.random.seed(42)
returns = np.random.normal(0, 0.001, 1000)  # 模拟数据

var_95, cvar_95 = calculate_var_cvar(returns, 0.95)
print(f"95% VaR: {var_95:.4f}")
print(f"95% CVaR: {cvar_95:.4f}")
💡 实战经验:我建议把VaR和CVaR做成动态的。市场波动率上升时,自动收紧头寸限制。我在一个项目中就是这么做的——当CVaR超过某个阈值时,系统自动降低最大持仓比例,相当于给策略「踩刹车」。

13.3 止损与熔断机制:活下来比什么都重要

做市商最怕什么?不是亏钱,是亏到无法继续做市。止损和熔断就是你的「保命符」。

我一般把止损分为三个层次:

  1. 逐笔止损:单笔订单亏损超过某个阈值(比如0.5%)立即平仓。
  2. 日内止损:当日累计亏损超过总资金的2%,停止所有交易。
  3. 回撤止损:从历史高点回撤超过10%,清仓并进入冷静期。

熔断机制更狠——它不是基于亏损,而是基于市场异常。比如:

  • 价格在1秒内波动超过5%
  • 买卖价差突然扩大到正常水平的10倍
  • 订单簿深度骤降(挂单量减少80%以上)

遇到这些情况,系统应该立即暂停做市,等待人工确认。

class RiskController:
    def __init__(self, daily_loss_limit=0.02, drawdown_limit=0.1):
        self.daily_pnl = 0
        self.peak_capital = 1000000  # 初始资金
        self.current_capital = 1000000
        self.daily_loss_limit = daily_loss_limit
        self.drawdown_limit = drawdown_limit
        self.is_frozen = False
    
    def check_stop_loss(self, trade_pnl):
        """检查是否触发止损"""
        self.daily_pnl += trade_pnl
        self.current_capital += trade_pnl
        
        # 更新峰值
        if self.current_capital > self.peak_capital:
            self.peak_capital = self.current_capital
        
        # 日内止损
        if self.daily_pnl / self.peak_capital < -self.daily_loss_limit:
            self.is_frozen = True
            return "触发日内止损,冻结交易"
        
        # 回撤止损
        drawdown = (self.peak_capital - self.current_capital) / self.peak_capital
        if drawdown > self.drawdown_limit:
            self.is_frozen = True
            return "触发回撤止损,清仓处理"
        
        return "正常"
🔧 避坑指南:我曾经把止损设得太紧,结果市场一个正常波动就把我震出去了。后来我改用「自适应止损」——根据近期波动率动态调整止损幅度。波动率大的时候放宽,波动率小的时候收紧。这样既不会频繁触发,也不会在极端行情下亏太多。

13.4 压力测试场景:模拟最黑暗的时刻

压力测试不是预测未来,而是问自己一个问题:「如果历史上最糟糕的情况重演,我的策略能扛住吗?」

我常用的压力测试场景包括:

场景 描述 参数变化
闪电崩盘 价格在5分钟内暴跌20% 波动率×10,流动性×0.1
流动性枯竭 买卖价差扩大100倍 价差×100,深度×0.01
交易所宕机 30分钟内无法撤单 延迟×1000
黑天鹅事件 类似2020年3月12日的暴跌 价格-50%,波动率×20

代码实现上,我会把历史数据「扭曲」后回测:

def stress_test(strategy, historical_data, stress_params):
    """
    压力测试框架
    stress_params: {'vol_multiplier': 10, 'spread_multiplier': 100, ...}
    """
    # 复制历史数据
    stressed_data = historical_data.copy()
    
    # 应用压力参数
    stressed_data['price'] = apply_volatility_shock(
        stressed_data['price'], 
        stress_params['vol_multiplier']
    )
    stressed_data['spread'] = stressed_data['spread'] * stress_params['spread_multiplier']
    stressed_data['depth'] = stressed_data['depth'] * stress_params['depth_multiplier']
    
    # 运行策略
    pnl_series = strategy.backtest(stressed_data)
    
    # 分析结果
    max_drawdown = calculate_max_drawdown(pnl_series)
    var_99 = calculate_var(pnl_series, 0.99)
    
    return {
        'max_drawdown': max_drawdown,
        'var_99': var_99,
        'survived': max_drawdown < 0.5  # 回撤不超过50%算通过
    }
📊 核心逻辑图:下面这张图展示了风险管理模块的整体架构,从数据输入到最终决策的完整链路。
风险管理模块架构图 市场数据输入 最大持仓限制检查 VaR / CVaR 风险度量 止损与熔断机制 交易决策输出 全局/品种/方向 95%/99%置信水平 逐笔/日内/回撤 压力测试 闪电崩盘 流动性枯竭 黑天鹅事件

嗯,这套风险管理框架,说白了就是给做市系统上了四道保险。每一道都能独立触发,防止你在极端行情下「裸奔」。我建议你从最简单的持仓限制开始,逐步加上VaR监控,最后再部署止损熔断和压力测试。别想一口吃成胖子——风险管理这东西,宁可保守,不要激进。

无相订单流研究社 微信Lucian808555