风险度量指标:最大回撤、夏普比率、索提诺比率、卡玛比率
做量化交易,说白了就是在风险和收益之间找平衡。
我见过太多人,只看收益率就冲进去,结果亏得底裤都不剩。你想想看,一个策略年化50%,但最大回撤40%,你敢上仓位吗?
所以,今天咱们就来聊聊四个最常用的风险度量指标。我会用Python把它们算得明明白白。
一、最大回撤(Max Drawdown)
最大回撤,是我个人最看重的指标。它告诉你:在最倒霉的情况下,你会亏多少钱。
公式其实很简单:从净值曲线的某个高点,到后续最低点的最大跌幅。
核心公式:
最大回撤 = max( (高点 - 低点) / 高点 )
嗯,这里要注意:回撤不是从最高点到最低点那么简单。它必须是「先有高点,后有低点」的序列关系。
import numpy as np
import pandas as pd
def max_drawdown(equity_curve):
"""
计算最大回撤
equity_curve: 净值序列(pandas Series 或 list)
"""
# 计算累计最大值
rolling_max = np.maximum.accumulate(equity_curve)
# 计算回撤序列
drawdown = (equity_curve - rolling_max) / rolling_max
# 取最小值(最大回撤)
max_dd = drawdown.min()
return max_dd, drawdown
# 示例
np.random.seed(42)
equity = 100 * np.exp(np.cumsum(np.random.normal(0.001, 0.02, 252)))
max_dd, dd_series = max_drawdown(equity)
print(f"最大回撤: {max_dd:.2%}")
我的经验: 我在项目中遇到过,有些策略回撤只有5%,但持续了半年。这种「温水煮青蛙」式的回撤,比短期暴跌更可怕。所以,我还会额外看「回撤恢复时间」。
二、夏普比率(Sharpe Ratio)
夏普比率,说白了就是「每承担一单位风险,能换来多少超额收益」。
它假设收益是正态分布的,但现实嘛...你懂的,厚尾分布才是常态。
公式:
Sharpe = (Rp - Rf) / σp
其中 Rp 是策略年化收益,Rf 是无风险利率,σp 是年化波动率
def sharpe_ratio(returns, risk_free_rate=0.03, periods_per_year=252):
"""
计算夏普比率
returns: 日收益率序列
risk_free_rate: 年化无风险利率(默认3%)
"""
# 年化超额收益
excess_return = np.mean(returns) * periods_per_year - risk_free_rate
# 年化波动率
volatility = np.std(returns) * np.sqrt(periods_per_year)
sharpe = excess_return / volatility
return sharpe
# 示例
daily_returns = np.random.normal(0.001, 0.02, 252)
sr = sharpe_ratio(daily_returns)
print(f"夏普比率: {sr:.2f}")
避坑指南: 我曾经用日频数据算夏普,结果发现年化后数值特别高。后来才意识到,如果收益率序列有自相关性,直接用日频算会高估夏普。建议先用月频数据,或者做Newey-West调整。
三、索提诺比率(Sortino Ratio)
索提诺比率,是夏普比率的「升级版」。它只惩罚下行风险,不惩罚上行波动。
我个人觉得,这才是更合理的风险度量。你想想看,上涨的波动是好事啊,为什么要惩罚它?
公式:
Sortino = (Rp - Rf) / σ_down
其中 σ_down 是下行标准差(只考虑负收益)
def sortino_ratio(returns, risk_free_rate=0.03, target_return=0, periods_per_year=252):
"""
计算索提诺比率
target_return: 目标收益率(通常设为0或无风险利率)
"""
# 年化超额收益
excess_return = np.mean(returns) * periods_per_year - risk_free_rate
# 计算下行偏差
downside_returns = returns[returns < target_return]
if len(downside_returns) == 0:
return np.inf # 没有下行风险,完美!
downside_std = np.std(downside_returns) * np.sqrt(periods_per_year)
sortino = excess_return / downside_std
return sortino
# 示例
sr_sortino = sortino_ratio(daily_returns)
print(f"索提诺比率: {sr_sortino:.2f}")
我的习惯: 我会把目标收益率设成无风险利率,而不是0。因为如果策略连无风险收益都跑不赢,那还不如买国债。
四、卡玛比率(Calmar Ratio)
卡玛比率,是「收益/最大回撤」的比值。它很直观:你愿意承受多大的回撤,去换取多少收益?
这个指标在CTA策略中特别常用。为什么?因为CTA策略的回撤往往很大,但收益也高。卡玛比率能很好地衡量这种「以险换利」的效率。
公式:
Calmar = 年化收益率 / 最大回撤(绝对值)
def calmar_ratio(equity_curve, periods_per_year=252):
"""
计算卡玛比率
"""
# 年化收益率
total_return = equity_curve[-1] / equity_curve[0] - 1
years = len(equity_curve) / periods_per_year
annual_return = (1 + total_return) ** (1 / years) - 1
# 最大回撤
max_dd, _ = max_drawdown(equity_curve)
# 卡玛比率
calmar = annual_return / abs(max_dd)
return calmar
# 示例
cr = calmar_ratio(equity)
print(f"卡玛比率: {cr:.2f}")
注意: 卡玛比率对回测周期很敏感。如果回测时间短,最大回撤可能还没暴露出来。我一般要求至少3年的回测数据,卡玛比率才有参考价值。
五、四个指标对比
| 指标 | 衡量什么 | 优点 | 缺点 |
|---|---|---|---|
| 最大回撤 | 最坏情况下的亏损 | 直观、容易理解 | 只关注一个点,忽略过程 |
| 夏普比率 | 风险调整后收益 | 标准化、可比较 | 假设正态分布,不区分涨跌 |
| 索提诺比率 | 下行风险调整后收益 | 只惩罚坏波动 | 需要足够多的负收益样本 |
| 卡玛比率 | 收益/最大回撤 | 直接反映风险承受意愿 | 对回测周期敏感 |
六、知识体系图
下面这张图,帮你理清这四个指标的关系和适用场景:
七、综合实战:一键计算所有指标
最后,我把这四个指标封装成一个函数。你在实盘或回测时,直接调用就行。
def risk_metrics(equity_curve, daily_returns, risk_free_rate=0.03):
"""
综合计算四个风险指标
equity_curve: 净值序列
daily_returns: 日收益率序列
"""
metrics = {}
# 最大回撤
max_dd, _ = max_drawdown(equity_curve)
metrics['最大回撤'] = f"{max_dd:.2%}"
# 夏普比率
sr = sharpe_ratio(daily_returns, risk_free_rate)
metrics['夏普比率'] = f"{sr:.2f}"
# 索提诺比率
sortino = sortino_ratio(daily_returns, risk_free_rate)
metrics['索提诺比率'] = f"{sortino:.2f}"
# 卡玛比率
calmar = calmar_ratio(equity_curve)
metrics['卡玛比率'] = f"{calmar:.2f}"
return metrics
# 使用示例
result = risk_metrics(equity, daily_returns)
for k, v in result.items():
print(f"{k}: {v}")
最后说一句: 这些指标只是工具,不是圣杯。我见过夏普3.0的策略,实盘一个月就崩了。为什么?因为回测数据太短,或者参数过拟合。所以,永远保持怀疑,永远做压力测试。