风险管理与绩效指标 (Risk & Performance)


章节概述

收益率只是故事的一半,风险才是另一半。本章系统讲解量化策略的绩效评估体系:Sharpe 比率、Sortino 比率、最大回撤、Calmar 比率、Alpha/Beta、VaR(在险价值)。每个指标都给出数学公式和 NumPy 实现,并讨论 CAPM 模型和风险平价的基本概念。这是从”看看赚了多少”到”科学评估策略质量”的关键一步。

核心理念:金融中的”风险”不是模糊的直觉,而是可以用数学定义的量。在 C 程序员眼中,这些指标不过是对收益率序列做几个统计运算:均值、标准差、最小累计值、分位数——都是 NumPy 一行代码的事。关键是要理解每个指标背后的假设和局限性。


第一节:收益率的基本处理

1.1 简单收益率与对数收益率

python -c "
import numpy as np
import pandas as pd
 
# 模拟每日价格
np.random.seed(42)
prices = 100 + np.cumsum(np.random.randn(1000) * 2)
prices = np.maximum(prices, 10)
 
# 简单收益率
simple_ret = (prices[1:] - prices[:-1]) / prices[:-1]
 
# 对数收益率
log_ret = np.log(prices[1:] / prices[:-1])
 
# 两者在很小的时候几乎相等
print('Simple return (first 5):', simple_ret[:5])
print('Log return (first 5): ', log_ret[:5])
print()
print('Differences are tiny for daily returns')
print(f'Max diff: {np.max(np.abs(log_ret - simple_ret)):.10f}')
"

对数收益率的优势:时间可加性。N 天对数收益 = 各天对数收益之和。简单收益率则需连乘。

1.2 年化计算

python -c "
import numpy as np
 
# 假设交易日在 252 天/年
trading_days = 252
 
np.random.seed(1)
daily_returns = np.random.randn(1000) * 0.01 # 日收益率序列
 
# 年化收益率
annual_return = daily_returns.mean() * trading_days
 
# 年化波动率
annual_vol = daily_returns.std() * np.sqrt(trading_days)
 
# 累计收益率
cumulative_return = np.prod(1 + daily_returns) - 1
 
print(f'Mean daily return: {daily_returns.mean():.6f}')
print(f'Annualized return: {annual_return:.4f} ({annual_return*100:.2f}%)')
print(f'Annualized volatility:{annual_vol:.4f} ({annual_vol*100:.2f}%)')
print(f'Cumulative return: {cumulative_return:.4f} ({cumulative_return*100:.2f}%)')
"

年化公式总结:

指标日频 → 年化
收益率μ_daily * 252
波动率σ_daily * sqrt(252)
夏普比率√252 * μ_daily / σ_daily

第二节:核心绩效指标

2.1 夏普比率 (Sharpe Ratio)

衡量每单位风险(波动率)的超额收益。年化夏普比率 > 1 通常被认为不错。

python -c "
import numpy as np
 
np.random.seed(0)
returns = np.random.randn(1000) * 0.015 + 0.0005 # 模拟策略日收益
risk_free_rate = 0.03 / 252 # 日无风险利率(年化 3%)
 
# 超额收益
excess_returns = returns - risk_free_rate
 
# 夏普比率(年化)
sharpe_daily = excess_returns.mean() / excess_returns.std()
sharpe_annual = sharpe_daily * np.sqrt(252)
 
print(f'Daily Sharpe: {sharpe_daily:.4f}')
print(f'Annual Sharpe: {sharpe_annual:.4f}')
print()
print('Interpretation:')
print(' > 2.0 : Excellent')
print(' 1.0 - 2.0 : Good')
print(' 0.5 - 1.0 : Acceptable')
print(' < 0.5 : Poor')
"

2.2 Sortino 比率

夏普比率惩罚上行波动(正收益的波动),而投资者只关心下行风险。Sortino 比率用下行标准差替代总标准差:

其中

python -c "
import numpy as np
 
np.random.seed(1)
returns = np.random.randn(1000) * 0.015 + 0.0003
 
risk_free_daily = 0.03 / 252
excess = returns - risk_free_daily
 
# 下行标准差
downside_returns = np.minimum(returns, 0)
downside_std = np.sqrt(np.mean(downside_returns ** 2))
 
sortino_daily = excess.mean() / downside_std
sortino_annual = sortino_daily * np.sqrt(252)
 
print(f'Downside std: {downside_std:.6f}')
print(f'Total std: {returns.std():.6f}')
print(f'Sortino (annual):{sortino_annual:.4f}')
print(f'Sharpe (annual): {excess.mean() / returns.std() * np.sqrt(252):.4f}')
print()
print('Sortino > Sharpe → most volatility is upside')
"

2.3 最大回撤 (Max Drawdown)

回撤是从历史最高点到当前点的跌幅。最大回撤是期间最严重的损失。

python -c "
import numpy as np
 
np.random.seed(0)
returns = np.random.randn(500) * 0.015 + 0.0003
equity_curve = 100 * np.cumprod(1 + returns) # 净值曲线
 
# 计算最大回撤
rolling_max = np.maximum.accumulate(equity_curve)
drawdown = (equity_curve - rolling_max) / rolling_max
max_drawdown = np.min(drawdown)
max_dd_idx = np.argmin(drawdown)
 
print(f'Max drawdown: {max_drawdown:.2%}')
print(f'Occurred at index: {max_dd_idx}')
print(f'Drawdown end: {drawdown[-1]:.4%}')
 
# 回撤持续期(drawdown duration)
is_in_drawdown = drawdown < 0
# 寻找最长回撤持续期
def max_drawdown_duration(drawdown):
 """计算最长水下持续期(bar 数)"""
 dd_series = drawdown < 0
 duration = 0
 current = 0
 for d in dd_series:
 if d:
 current += 1
 duration = max(duration, current)
 else:
 current = 0
 return duration
 
print(f'Max drawdown duration: {max_drawdown_duration(drawdown)} days')
"

图解回撤:

净值曲线
 /\
 / \ /\
 / \ / \ 最高点(watermark)
 / \/ \/\
/ ← 当前点
|-------| 回撤(跌幅)

2.4 Calmar 比率

python -c "
import numpy as np
 
np.random.seed(0)
returns = np.random.randn(500) * 0.015 + 0.0005
equity = 100 * np.cumprod(1 + returns)
 
# 年化收益
annual_ret = returns.mean() * 252
 
# 最大回撤
roll_max = np.maximum.accumulate(equity)
max_dd = np.min((equity - roll_max) / roll_max)
 
calmar = annual_ret / abs(max_dd)
 
print(f'Annual return: {annual_ret:.2%}')
print(f'Max drawdown: {max_dd:.2%}')
print(f'Calmar ratio: {calmar:.4f}')
print('Interpretation: Calmar > 1 means annual return exceeds max drawdown')
"

第三节:Alpha 与 Beta —— CAPM 模型

3.1 CAPM 基础

资本资产定价模型 (CAPM) 将股票/策略的收益分解为:

  • Alpha ():独立于市场波动的超额收益(策略真正的”能力”)
  • Beta ():策略收益对市场收益的敏感度
  • :随机误差

3.2 NumPy 实现

python -c "
import numpy as np
 
np.random.seed(42)
n = 500
 
# 模拟市场收益和策略收益
market_returns = np.random.randn(n) * 0.01 + 0.0003
# 策略收益 = alpha + beta * 市场收益 + 噪声
true_alpha = 0.0002
true_beta = 1.2
noise = np.random.randn(n) * 0.008
strategy_returns = true_alpha + true_beta * market_returns + noise
 
# 用线性回归估计 alpha 和 beta
# y = alpha + beta * x → 用 np.polyfit 或公式
beta = np.cov(strategy_returns, market_returns)[0, 1] / np.var(market_returns)
alpha = strategy_returns.mean() - beta * market_returns.mean()
 
print(f'True Alpha: {true_alpha * 252:.4f} Estimated: {alpha * 252:.4f}')
print(f'True Beta: {true_beta:.4f} Estimated: {beta:.4f}')
print(f'Correlation: {np.corrcoef(strategy_returns, market_returns)[0, 1]:.4f}')
 
# 年化 Alpha(Jensen Alpha)
annual_alpha = alpha * 252
print(f'Annual Alpha: {annual_alpha:.2%}')
"

3.3 信息比率 (Information Ratio)

Information Ratio 衡量每单位跟踪误差(相对于基准的偏离)的超额收益:

python -c "
import numpy as np
 
np.random.seed(1)
n = 500
benchmark = np.random.randn(n) * 0.01 + 0.0003
tracking_error = np.random.randn(n) * 0.008
strategy = benchmark + tracking_error + 0.0002
 
excess = strategy - benchmark
ir = excess.mean() / excess.std() * np.sqrt(252)
 
print(f'Tracking error (annual): {excess.std() * np.sqrt(252):.4f}')
print(f'Information Ratio: {ir:.4f}')
print('IR > 0.5 : Good active manager')
print('IR > 1.0 : Excellent')
"

第四节:Value at Risk (VaR) 与 Conditional VaR

4.1 VaR 的定义

VaR(在险价值):在给定置信水平下,一定时期内的最大预期损失。

  • VaR 95%:有 95% 的概率损失不超过 VaR(即 5% 概率损失超过 VaR)
python -c "
import numpy as np
 
np.random.seed(0)
returns = np.random.randn(1000) * 0.015 + 0.0003
 
# 历史 VaR(非参数法)
confidence = 0.95
var_95 = -np.percentile(returns, 100 * (1 - confidence))
var_99 = -np.percentile(returns, 100 * (1 - 0.99))
 
print(f'VaR 95% (daily): {var_95:.4f} ({var_95*100:.2f}%)')
print(f'VaR 99% (daily): {var_99:.4f} ({var_99*100:.2f}%)')
print(f'Meaning: 5% chance of losing > {var_95*100:.2f}% in a single day')
print()
 
# 参数法(假设正态分布)
from scipy.stats import norm
mu, sigma = returns.mean(), returns.std()
var_95_param = -(mu + sigma * norm.ppf(1 - 0.95))
print(f'VaR 95% (parametric): {var_95_param:.4f} ({var_95_param*100:.2f}%)')
"

4.2 Conditional VaR (CVaR / Expected Shortfall)

VaR 只告诉你”阈值”,不告诉你阈值之外的损失有多大。CVaR 是超过 VaR 的所有损失的均值。

python -c "
import numpy as np
 
np.random.seed(1)
returns = np.random.randn(500) * 0.015 + 0.0002
 
# 历史 CVaR
confidence = 0.95
var_threshold = np.percentile(returns, 100 * (1 - confidence))
cvar = -np.mean(returns[returns <= var_threshold])
 
print(f'VaR 95% threshold: {var_threshold:.4f}')
print(f'CVaR 95% (expected shortfall): {cvar:.4f} ({cvar*100:.2f}%)')
print(f'Meaning: When things go wrong (worst 5%), avg loss is {cvar*100:.2f}%')
"

所有指标汇总:

指标公式用途
Sharpe风险调整收益(对称风险)
Sortino风险调整收益(仅下行风险)
Max DD最差情况损失
Calmar$R_{\text{annual}} /MDD
Alpha回归截距独立于市场的收益
Beta回归斜率市场敏感度
VaR分位数阈值损失
CVaR阈值外均值尾部期望损失


练习

以下题目用于验证本章所学内容:

题号题目链接涉及知识点
本章无对应力扣题请用动手练习题自检