Appearance
夏普比率与风险收益
为什么需要夏普比率
假设有两个策略:
- 策略A:年化收益率20%,年化波动率30%
- 策略B:年化收益率15%,年化波动率10%
哪个策略更好?如果只看收益率,A更好。但如果考虑到风险,B明显更优——B用少得多的波动换取了可观的收益。
这就是夏普比率要解决的问题:如何衡量策略的"性价比"——每承担一单位风险,能获得多少收益补偿?
在投资领域,收益和风险永远是硬币的两面。任何收益都伴随着风险,问题在于这个风险是否"值得"。夏普比率正是量化这个"值不值得"的工具。
夏普比率的定义与公式
夏普比率(Sharpe Ratio)由诺贝尔经济学奖得主威廉·夏普(William F. Sharpe)于1966年提出,是衡量风险调整后收益的最广泛使用的指标。
公式
夏普比率 = (Rp - Rf) / σp其中:
- Rp(Return of Portfolio):策略的年化收益率
- Rf(Risk-Free Rate):无风险利率
- σp(Standard Deviation of Portfolio):策略年化收益率的标准差(即年化波动率)
- Rp - Rf:超额收益(Excess Return),即策略收益中超过无风险收益的部分
分子(超额收益)衡量的是策略比"无风险投资"多赚了多少,分母(波动率)衡量的是赚这些超额收益承受了多少波动。两者相除,就得到了"每单位风险对应的超额收益"。
直观理解
你可以把夏普比率类比为生活中的"性价比":
- 买手机:花3000元买到的功能 vs 花费的钱 → 性价比
- 投资:承担的风险换来的超额收益 → 夏普比率
夏普比率高,意味着策略的"风险性价比"好——用较少的风险波动换取了较多的超额收益。
无风险利率的选择
在计算夏普比率时,需要确定无风险利率的值。不同场景下的选择:
- A股回测:通常使用一年期国债收益率,约2%左右;或使用银行间7天质押式回购利率(DR007)
- 美国市场:通常使用3个月国债收益率
- 简化计算:有时直接取0,这样夏普比率简化为 Rp / σp
python
# 计算夏普比率
import numpy as np
def sharpe_ratio(daily_values, risk_free_rate=0.02, trading_days_per_year=252):
"""
计算夏普比率
Parameters:
daily_values: 每日净值序列
risk_free_rate: 年化无风险利率(默认2%)
trading_days_per_year: 每年交易日数(默认252)
Returns:
夏普比率
"""
# 计算每日收益率
daily_returns = np.diff(daily_values) / daily_values[:-1]
# 日化无风险利率
daily_rf = (1 + risk_free_rate) ** (1 / trading_days_per_year) - 1
# 每日超额收益率
excess_returns = daily_returns - daily_rf
# 夏普比率 = 平均超额收益 / 超额收益标准差 × sqrt(252)
if excess_returns.std() == 0:
return 0.0
sharpe = excess_returns.mean() / excess_returns.std() * np.sqrt(trading_days_per_year)
return sharpe上面代码中的关键点是最后的年化处理。为什么需要乘以 sqrt(252)?
这是因为:如果我们用日频数据计算,得到的是日夏普比率。年化夏普比率的推导如下:
年化超额收益 = 日均超额收益 × 252
年化波动率 = 日波动率 × sqrt(252)
年化夏普比率 = 年化超额收益 / 年化波动率
= (日均超额收益 × 252) / (日波动率 × sqrt(252))
= (日均超额收益 / 日波动率) × sqrt(252)
= 日夏普比率 × sqrt(252)所以从日频数据计算年化夏普比率时,只需要将日夏普比率乘以 sqrt(252) 即可。
夏普比率的参考标准
以下是业界普遍接受的夏普比率评价标准:
| 夏普比率 | 评价 | 说明 |
|---|---|---|
| < 0 | 差 | 策略收益不及无风险利率,不如把钱存银行 |
| 0 ~ 0.5 | 较差 | 风险补偿不足,承担的风险没有得到足够回报 |
| 0.5 ~ 1.0 | 一般 | 可接受但不算好,很多公募基金在这个范围 |
| 1.0 ~ 2.0 | 不错 | 优秀的策略,大多数顶尖量化基金的目标 |
| 2.0 ~ 3.0 | 优秀 | 非常出色的风险调整收益 |
| > 3.0 | 卓越 | 极其优秀,但需要仔细验证是否存在过拟合 |
现实中的夏普比率
为了让你对夏普比率有更直观的感受,以下是一些现实中的参考:
- 沪深300指数:长期夏普比率大约在0.3-0.6之间(取决于计算时段)
- 优秀公募基金:长期夏普比率大约在0.5-1.0之间
- 顶尖量化私募:长期夏普比率大约在1.0-2.0之间
- 文艺复兴大奖章基金(Medallion Fund):据说长期夏普比率超过2.0
需要注意的是,上面提到的顶尖基金的夏普比率是长期实盘业绩。在回测中,如果你得到超过3.0的夏普比率,首先应该怀疑是否存在过拟合或其他问题,而不是沾沾自喜。
夏普比率的计算示例
下面通过一个完整的例子演示夏普比率的计算。
python
import numpy as np
import pandas as pd
# 模拟一个策略的每日净值(252个交易日,约1年)
np.random.seed(42)
initial_value = 1.0
daily_returns = np.random.normal(0.0008, 0.015, 252) # 日均收益0.08%,日波动率1.5%
daily_values = initial_value * np.cumprod(1 + daily_returns)
# 计算各项参数
ann_return = (daily_values[-1] / daily_values[0]) ** (252 / len(daily_values)) - 1
ann_volatility = daily_returns.std() * np.sqrt(252)
risk_free_rate = 0.02
sharpe = (ann_return - risk_free_rate) / ann_volatility
print(f"年化收益率: {ann_return:.2%}")
print(f"年化波动率: {ann_volatility:.2%}")
print(f"无风险利率: {risk_free_rate:.2%}")
print(f"夏普比率: {sharpe:.2f}")输出示例:
年化收益率: 18.25%
年化波动率: 23.81%
无风险利率: 2.00%
夏普比率: 0.68这个策略的夏普比率为0.68,处于"一般"水平——虽然跑赢了无风险收益,但考虑到23.81%的年化波动率,风险补偿不算充分。
影响夏普比率的因素
理解哪些因素会影响夏普比率,有助于你有针对性地改进策略。
因素一:提高超额收益(分子)
提高策略的平均收益率可以直接提高夏普比率。常见方法:
- 优化入场和出场时机,提高策略的胜率和盈亏比
- 加入更有效的因子或信号
- 提高资金使用效率(避免长期空仓)
因素二:降低波动率(分母)
降低收益率的波动同样可以提高夏普比率,甚至效果更明显:
- 分散投资:持有多个不相关或低相关的资产,可以有效降低组合波动率
- 仓位管理:控制单次交易的仓位大小,避免过度集中
- 动态调仓:在市场波动加大时适当降低仓位
一个简单但有效的方法是将多个相关性低的策略组合在一起:
python
# 两个策略组合后的波动率变化
strategy_a_vol = 0.20 # 策略A年化波动率20%
strategy_b_vol = 0.20 # 策略B年化波动率20%
correlation = 0.3 # 两策略收益相关性
# 组合波动率(等权重)
portfolio_vol = np.sqrt(
0.5**2 * strategy_a_vol**2 +
0.5**2 * strategy_b_vol**2 +
2 * 0.5 * 0.5 * correlation * strategy_a_vol * strategy_b_vol
)
# portfolio_vol ≈ 0.162,低于单个策略的0.20
# 如果两策略的年化收益都是15%
portfolio_return = 0.15
sharpe_a = (0.15 - 0.02) / 0.20 # = 0.65
sharpe_portfolio = (0.15 - 0.02) / 0.162 # = 0.80
print(f"单个策略夏普: {sharpe_a:.2f}")
print(f"组合后夏普: {sharpe_portfolio:.2f}")通过组合两个低相关策略,夏普比率从0.65提升到0.80。这就是分散投资的力量。
因素三:时间尺度
夏普比率的计算结果与数据频率有关。使用日频数据、周频数据或月频数据计算出的夏普比率可能略有差异。一般来说:
- 日频数据最常用,计算结果也最稳定
- 月频数据受个别异常月影响较大
- 年频数据样本量太少,统计意义不大
夏普比率的局限性
夏普比率虽然是最广泛使用的风险调整收益指标,但它有几个重要的局限性,使用者必须了解。
局限性一:假设收益服从正态分布
夏普比率用标准差来衡量风险,这隐含了一个假设——收益率服从正态分布(钟形曲线)。但实际市场中:
- 收益率分布存在尖峰厚尾(Fat Tails):极端亏损和极端盈利出现的频率比正态分布预测的要高
- 收益率分布可能是偏斜的(Skewed):某些策略(如卖出期权策略)的收益分布严重左偏
这意味着夏普比率可能会低估极端风险。一个策略的夏普比率看起来不错,但可能在极端行情下遭受远超预期的大亏损。
python
# 举例:卖出看涨期权策略的收益特征
# 大部分时间:稳定赚取期权费 → 高夏普比率
# 极端情况:偶尔巨额亏损 → 标准差无法充分反映风险
# 这类策略的夏普比率可能虚高局限性二:对上行波动和下行波动一视同仁
夏普比率的分母是总波动率,它把上涨的波动和下跌的波动同等对待。但从投资者的角度来看,上涨的波动是"好"的(赚钱了),下跌的波动才是"坏"的(亏钱了)。
一个策略如果有大量大幅上涨和小幅下跌,其波动率可能很高,导致夏普比率偏低——但这显然不是一个差策略。夏普比率无法区分"好的波动"和"坏的波动"。
局限性三:对短期数据敏感
如果回测时间很短(比如只有3个月),夏普比率的估计会非常不稳定。短时间内的市场行情可能比较特殊(比如碰上一波大牛市),导致夏普比率虚高或虚低。
一般来说,至少需要2-3年的数据,夏普比率才有一定的统计参考价值。
局限性四:不适用于非线性策略
对于包含期权等非线性衍生品的策略,或者有复杂仓位管理的策略,收益分布远非正态。此时夏普比率可能严重失真。
Sortino比率——夏普比率的改进
为了解决夏普比率"对上行和下行波动一视同仁"的问题,Sortino比率应运而生。
定义
Sortino比率(Sortino Ratio)由 Frank Sortino 提出,它只考虑下行波动(Downside Deviation)作为风险的度量。
公式
Sortino比率 = (Rp - Rf) / σd其中:
- Rp:策略的年化收益率
- Rf:无风险利率(或称为最低可接受回报 MAR, Minimum Acceptable Return)
- σd:下行波动率,只计算低于目标收益率的部分
下行波动率的计算
python
def downside_deviation(returns, target_return=0):
"""
计算下行波动率
Parameters:
returns: 收益率序列
target_return: 目标收益率(低于此值的才计入下行波动)
Returns:
下行波动率
"""
# 只取低于目标收益率的值,其余设为0
downside = np.minimum(returns - target_return, 0)
return np.sqrt(np.mean(downside ** 2))
def sortino_ratio(daily_values, risk_free_rate=0.02, trading_days_per_year=252):
"""计算Sortino比率"""
daily_returns = np.diff(daily_values) / daily_values[:-1]
# 日化无风险利率
daily_rf = (1 + risk_free_rate) ** (1 / trading_days_per_year) - 1
# 年化超额收益
excess_return = (daily_values[-1] / daily_values[0]) ** (trading_days_per_year / len(daily_values)) - 1 - risk_free_rate
# 下行波动率(年化)
dd = downside_deviation(daily_returns, daily_rf)
annualized_dd = dd * np.sqrt(trading_days_per_year)
if annualized_dd == 0:
return float('inf')
return excess_return / annualized_ddSortino比率与夏普比率的对比
python
# 对比两种策略
np.random.seed(42)
# 策略A:收益对称分布
returns_a = np.random.normal(0.001, 0.02, 252)
values_a = np.cumprod(1 + returns_a)
# 策略B:正偏分布(大涨小跌)
positive_returns = np.random.exponential(0.01, 252)
negative_returns = -np.random.exponential(0.005, 252)
mask = np.random.random(252) > 0.4
returns_b = np.where(mask, positive_returns, negative_returns)
values_b = np.cumprod(1 + returns_b)
# 计算夏普和Sortino
sharpe_a = sharpe_ratio(values_a)
sharpe_b = sharpe_ratio(values_b)
sortino_a = sortino_ratio(values_a)
sortino_b = sortino_ratio(values_b)
print(f"策略A - 夏普: {sharpe_a:.2f}, Sortino: {sortino_a:.2f}")
print(f"策略B - 夏普: {sharpe_b:.2f}, Sortino: {sortino_b:.2f}")Sortino比率通常高于夏普比率,因为它只惩罚下行波动。如果一个策略的Sortino比率显著高于夏普比率,说明该策略的上行波动占比更大——这是个好信号。
何时使用Sortino比率
- 当策略的收益分布明显偏斜时(不对称)
- 当你更关心下行风险而非总体波动时
- 当评估趋势跟踪策略时(通常正偏分布)
其他风险调整收益指标
除了夏普比率和Sortino比率,还有几个值得关注的风险调整指标。
信息比率(Information Ratio)
信息比率 = (策略收益率 - 基准收益率) / 跟踪误差信息比率衡量的是策略相对于基准的超额收益的稳定性。跟踪误差(Tracking Error)是策略收益与基准收益之间差异的标准差。
Omega比率
Omega比率考虑了收益分布的全部信息(而非只考虑均值和标准差),通过计算正收益区域的"面积"与负收益区域的"面积"之比来衡量策略质量。
卡玛比率(Calmar Ratio)
上一章已经介绍过,卡玛比率 = 年化收益率 / 最大回撤。它用最大回撤而非波动率来衡量风险,更加直观。
实战建议
如何正确使用夏普比率
- 作为筛选工具:用夏普比率初步筛选策略。低于0.5的策略可以排除,节省深入研究的时间。
- 横向比较:用夏普比率比较不同策略的风险收益效率,而非孤立地看某个策略的绝对值。
- 结合其他指标:夏普比率不应单独使用。始终结合最大回撤、胜率、盈亏比等指标综合判断。
- 关注稳定性:看夏普比率在不同时段是否稳定。如果滚动1年的夏普比率波动很大,说明策略不够稳健。
滚动夏普比率
滚动夏普比率(Rolling Sharpe Ratio)可以帮助你了解夏普比率随时间的变化情况:
python
def rolling_sharpe(daily_values, window=252, risk_free_rate=0.02):
"""计算滚动夏普比率"""
daily_returns = np.diff(daily_values) / daily_values[:-1]
daily_rf = (1 + risk_free_rate) ** (1 / 252) - 1
excess_returns = daily_returns - daily_rf
rolling_sharpe_values = []
for i in range(window, len(excess_returns)):
window_excess = excess_returns[i - window:i]
if window_excess.std() == 0:
rolling_sharpe_values.append(0)
else:
rs = window_excess.mean() / window_excess.std() * np.sqrt(252)
rolling_sharpe_values.append(rs)
return rolling_sharpe_values如果滚动夏普比率一直稳定在1.0以上,说明策略持续有效。如果某个时段突然大幅下降甚至转负,需要警惕策略可能在该市场环境下失效。
总结
夏普比率是衡量策略风险调整收益的核心工具,但不是万能的。正确的做法是:
- 理解夏普比率的计算方法和含义
- 知道它的局限性(正态分布假设、上下行波动不区分等)
- 结合Sortino比率、最大回撤、Calmar比率等指标综合评价
- 使用滚动指标观察策略表现的稳定性
- 始终将回测指标与实盘表现进行对比验证
记住:任何单一指标都无法全面评价一个策略。好的策略评价应该是多维度、多指标的。