第二十五章:策略风险管理:风险指标、VaR与CVaR、压力测试、回撤控制、止损策略
做量化交易这么多年,我见过太多人只盯着收益率看。说实话,这是新手最容易犯的错。你策略赚了50%,但回撤40%,这有什么意义?我有个朋友,2015年股灾前策略年化80%,结果一周亏光所有利润。嗯,从那以后,风险管理就成了我所有策略的第一道防线。
今天这一章,咱们就把风险管理的几个核心模块拆开揉碎。你想想看,一个成熟的交易系统,风险控制至少占一半的权重。说白了,活得久比赚得快重要得多。
25.1 核心风险指标:你该盯哪些数字?
我个人习惯,每天开盘前先扫一眼这几个指标。它们就像汽车的仪表盘,告诉你发动机有没有过热。
| 指标 | 含义 | 我的经验阈值 |
|---|---|---|
| 最大回撤 (Max Drawdown) | 从峰值到谷底的最大跌幅 | 超过20%就要停手检查 |
| 夏普比率 (Sharpe Ratio) | 每单位风险获得的超额收益 | 低于1.0的策略我不上实盘 |
| 卡玛比率 (Calmar Ratio) | 年化收益 / 最大回撤 | 大于3才算合格 |
| 胜率 (Win Rate) | 盈利交易占比 | 别迷信高胜率,盈亏比更重要 |
| 盈亏比 (Profit/Loss Ratio) | 平均盈利 / 平均亏损 | 至少2:1,我偏好3:1以上 |
25.2 VaR与CVaR:你的钱到底有多危险?
VaR(Value at Risk)这个词,听起来高大上,其实说白了就是:在95%或99%的置信水平下,你最多会亏多少钱。举个例子,日VaR(95%) = 10万,意思是有95%的把握,明天亏损不超过10万。
但这里有个坑。我曾经吃过这个亏——VaR只告诉你「最多亏多少」,但它不告诉你「如果真的亏了,平均亏多少」。这就是CVaR(Conditional VaR)要解决的问题。CVaR计算的是「最坏那5%情况下的平均亏损」。我建议你两个指标一起看。
import numpy as np
import pandas as pd
def calculate_var_cvar(returns, confidence=0.95):
"""
计算VaR和CVaR
returns: 收益率序列
confidence: 置信水平,默认95%
"""
# 排序收益率
sorted_returns = np.sort(returns)
# VaR:第(1-confidence)分位数
var_index = int(len(sorted_returns) * (1 - confidence))
var = -sorted_returns[var_index]
# CVaR:尾部损失的均值
tail_losses = sorted_returns[:var_index]
cvar = -np.mean(tail_losses)
return var, cvar
# 模拟使用
np.random.seed(42)
daily_returns = np.random.normal(0.001, 0.02, 1000) # 模拟日收益率
var_95, cvar_95 = calculate_var_cvar(daily_returns, 0.95)
print(f"95% VaR: {var_95:.4f}")
print(f"95% CVaR: {cvar_95:.4f}")
25.3 压力测试:把策略扔进「地狱模式」
压力测试,说白了就是模拟极端行情。我习惯做三件事:
- 历史重现:把2008年金融危机、2020年新冠熔断的数据喂给策略,看它怎么死。
- 假设情景:比如「明天大盘跌10%」、「流动性枯竭」、「波动率飙升3倍」。
- 参数扰动:把策略参数随机打乱,看鲁棒性。
我记得有一次,一个看起来完美的套利策略,在压力测试下暴露了致命缺陷——当市场波动率突然上升时,滑点成本直接吃掉所有利润。嗯,还好没上实盘。
def stress_test(strategy_returns, scenarios):
"""
压力测试框架
scenarios: 字典,包含不同情景下的收益率调整
"""
results = {}
for scenario_name, shock in scenarios.items():
# 对原始收益率施加冲击
stressed_returns = strategy_returns * (1 + shock)
# 计算关键指标
sharpe = np.mean(stressed_returns) / np.std(stressed_returns) * np.sqrt(252)
max_dd = (stressed_returns.cumsum().cummax() - stressed_returns.cumsum()).max()
results[scenario_name] = {
'sharpe': sharpe,
'max_drawdown': max_dd
}
return results
# 定义压力情景
scenarios = {
'正常市场': 0.0,
'小幅下跌': -0.2,
'大幅下跌': -0.5,
'流动性危机': -0.8
}
# 假设策略日收益率
np.random.seed(42)
returns = pd.Series(np.random.normal(0.001, 0.02, 252))
stress_results = stress_test(returns, scenarios)
print(stress_results)
25.4 回撤控制:别让利润坐过山车
回撤控制,我把它分成三个层级:
- 硬止损:总资金回撤达到15%,强制清仓。这个没得商量。
- 软止损:回撤达到8%,减半仓位,进入观察模式。
- 动态调整:根据波动率自动调整仓位。波动率越高,仓位越低。
你想想看,为什么很多人赚了钱又吐回去?就是因为没有回撤控制。我见过最惨的一个案例,账户从100万做到300万,然后一个月跌回80万。为什么?因为他觉得「回撤是暂时的,会涨回来的」。结果呢?市场教他做人。
25.5 止损策略:该割肉时就割肉
止损,是量化交易里最反人性的操作。我刚开始做的时候,也经常「再等等看」。结果呢?小亏变大亏。后来我总结了几种止损方式:
| 止损类型 | 触发条件 | 适用场景 |
|---|---|---|
| 固定比例止损 | 亏损达到2% | 趋势跟踪策略 |
| 移动止损 | 从最高点回落5% | 趋势跟踪策略 |
| 时间止损 | 持仓超过5天不盈利 | 均值回归策略 |
| 波动率止损 | 亏损超过2倍ATR | 所有策略通用 |
def trailing_stop(current_price, highest_price, trail_percent=0.05):
"""
移动止损逻辑
"""
drawdown = (highest_price - current_price) / highest_price
if drawdown >= trail_percent:
return True # 触发止损
return False
def volatility_stop(current_price, entry_price, atr, multiplier=2):
"""
波动率止损
"""
loss_pct = (entry_price - current_price) / entry_price
if loss_pct > multiplier * atr / entry_price:
return True
return False
25.6 知识体系总览
下面这张图,是我做风险管理时的完整框架。你可以把它当成一个检查清单,每次上线新策略前,逐项过一遍。
嗯,这一章的内容就到这里。风险管理不是束缚,而是你策略的「安全带」。系好它,你才能在市场的惊涛骇浪里,稳稳地走下去。