第25章:冲击成本与风险管理:VaR、CVaR、压力测试

做量化交易这么多年,我越来越觉得一个道理:赚钱靠策略,活下来靠风控。冲击成本这东西,平时看着不起眼,一旦市场剧烈波动,它能把你的利润瞬间吃掉,甚至让你爆仓。

这一章,我们就来聊聊怎么把冲击成本纳入风险管理体系。说白了,就是回答三个问题:

  • 最坏情况下我会亏多少?(VaR)
  • 亏起来有多惨?(CVaR)
  • 极端行情下扛得住吗?(压力测试)

25.1 为什么冲击成本会影响VaR?

先讲个我自己的教训。几年前我做了一个高频策略,回测夏普比高达3.5,实盘却亏得稀里哗啦。后来一查,问题出在VaR模型里——我根本没考虑冲击成本。

传统VaR的计算,通常假设你能按当前市场价格成交。但真实情况是:大单子一进去,价格就跑了。你想想看,如果市场深度只有100手,你非要砸1000手,那成交价格会滑到哪里去?

所以,考虑冲击成本后的VaR,才是真正的风险暴露。我习惯把它叫做「有效VaR」。

核心公式:

有效VaR = 传统VaR + 冲击成本调整项

其中冲击成本调整项 = f(订单规模, 市场深度, 波动率)

25.2 冲击成本调整后的VaR模型

具体怎么做呢?我给大家一个实操框架。

第一步:计算传统VaR

假设我们采用历史模拟法,取过去250个交易日的收益率数据,排序后取第5%分位数。这是最基础的做法。

import numpy as np
import pandas as pd

def calculate_var(returns, confidence_level=0.95):
    """
    计算传统VaR
    """
    sorted_returns = np.sort(returns)
    index = int((1 - confidence_level) * len(sorted_returns))
    var = -sorted_returns[index]
    return var

# 示例:假设日收益率序列
np.random.seed(42)
returns = np.random.normal(0, 0.02, 1000)
var_95 = calculate_var(returns, 0.95)
print(f"95% VaR: {var_95:.4f}")

第二步:叠加冲击成本

这里有个关键点:冲击成本不是固定的,它随订单规模非线性增长。我一般用平方根模型来近似:

def impact_adjusted_var(order_size, market_depth, volatility, var_base):
    """
    考虑冲击成本后的VaR调整
    """
    # 平方根冲击模型
    impact_cost = 0.01 * volatility * np.sqrt(order_size / market_depth)
    
    # 调整后的VaR
    var_adjusted = var_base + impact_cost
    
    return var_adjusted

# 示例
order_size = 10000  # 订单股数
market_depth = 5000  # 市场深度
volatility = 0.02    # 日波动率
var_base = 0.05      # 基础VaR

var_effective = impact_adjusted_var(order_size, market_depth, volatility, var_base)
print(f"有效VaR: {var_effective:.4f}")

我的经验:当订单规模超过市场深度的20%时,冲击成本会急剧上升。这时候,要么拆单,要么放弃交易。

25.3 CVaR:比VaR更狠的风险度量

VaR有个毛病:它只告诉你「最坏情况下的最小损失」,但没告诉你「一旦亏起来,到底有多惨」。举个例子,两个策略的95% VaR都是5%,但一个策略亏损超过5%后平均亏6%,另一个平均亏15%。你觉得哪个更危险?

这就是CVaR(条件风险价值)要解决的问题。它计算的是尾部损失的期望值

def calculate_cvar(returns, confidence_level=0.95):
    """
    计算CVaR
    """
    sorted_returns = np.sort(returns)
    index = int((1 - confidence_level) * len(sorted_returns))
    tail_returns = sorted_returns[:index]
    cvar = -np.mean(tail_returns)
    return cvar

# 对比VaR和CVaR
var_95 = calculate_var(returns, 0.95)
cvar_95 = calculate_cvar(returns, 0.95)
print(f"95% VaR: {var_95:.4f}")
print(f"95% CVaR: {cvar_95:.4f}")

你看,CVaR通常比VaR大不少。我个人的风控标准是:CVaR不能超过账户净值的3%。超过这个线,不管策略多诱人,我都会砍掉。

25.4 压力测试:模拟极端行情

VaR和CVaR都是基于历史数据,但历史不会简单重复。2008年金融危机、2020年疫情熔断,这些极端事件在历史数据里可能只出现一次,甚至没出现过。

所以,压力测试是必须的。我一般做三种场景:

  1. 历史重现场景:比如模拟2015年股灾时的流动性枯竭
  2. 假设场景:比如某只重仓股突然跌停,且无法卖出
  3. 极端冲击场景:比如市场深度瞬间缩水90%
def stress_test_scenario(portfolio, scenario_params):
    """
    压力测试场景模拟
    """
    # 场景参数:价格冲击、流动性衰减、波动率飙升
    price_shock = scenario_params['price_shock']
    liquidity_decay = scenario_params['liquidity_decay']
    vol_multiplier = scenario_params['vol_multiplier']
    
    # 计算冲击成本
    impact = price_shock * (1 + liquidity_decay) * vol_multiplier
    
    # 组合损失
    portfolio_loss = portfolio * impact
    
    return portfolio_loss

# 示例:模拟极端场景
scenario = {
    'price_shock': -0.10,      # 价格下跌10%
    'liquidity_decay': 0.50,   # 流动性衰减50%
    'vol_multiplier': 3.0      # 波动率放大3倍
}

portfolio_value = 1000000  # 100万组合
loss = stress_test_scenario(portfolio_value, scenario)
print(f"极端场景下预计损失: {loss:.2f}")

警告:压力测试不是走过场。我曾经见过一个团队,压力测试结果显示会亏30%,但他们觉得「不可能发生」,结果2020年3月真的发生了。嗯,那之后团队就解散了。

25.5 把冲击成本纳入风控体系

说了这么多,怎么落地呢?我给大家画一张流程图,看看完整的风险管理框架长什么样。

冲击成本风险管理框架 订单与市场数据 冲击成本模型(平方根/线性) VaR计算 CVaR计算 压力测试 风险阈值判断 通过/拒绝/调整订单

25.6 实战中的避坑指南

最后,分享几个我踩过的坑:

  • 别用日频数据算VaR:高频交易里,日VaR基本没用。我习惯用5分钟级别的VaR,甚至1分钟级别的。
  • 冲击成本参数要动态更新:市场深度是变化的,熊市里流动性差,冲击成本会翻倍。我每周重新校准一次模型参数。
  • 压力测试要「过激」:别只测试「合理」的场景。我一般会测试流动性下降90%、波动率放大10倍这种极端情况。虽然概率低,但一旦发生,就是灭顶之灾。

一句话总结:不考虑冲击成本的风险管理,就是纸上谈兵。把冲击成本纳入VaR、CVaR和压力测试,你才算真正理解了「风险」二字。


无相订单流研究社 微信Lucian808555