量化策略优化:风险平价与资产配置

📊 “不要把所有鸡蛋放在一个篮子里。”

🎯 从单策略到组合策略

经过半年的量化学习,我意识到:单一策略难以应对所有市场环境

  • 趋势策略在震荡市亏损
  • 均值回归在趋势市失效
  • 任何策略都有回撤期

解决方案:构建多策略组合,让不同策略互补。

📈 现代投资组合理论(MPT)

核心思想

1952年,马克维茨提出:通过分散投资,可以在给定风险下最大化收益,或在给定收益下最小化风险

关键概念

概念 公式 含义
预期收益 $E(R_p) = \sum w_i E(R_i)$ 组合收益 = 权重加权平均
组合风险 $\sigma_p^2 = \sum\sum w_i w_j \sigma_{ij}$ 不仅看单个资产风险,还要看相关性
夏普比率 $Sharpe = \frac{R_p - R_f}{\sigma_p}$ 风险调整后收益

有效前沿

1
2
3
4
5
6
7
8
9
10
收益 ↑

│ ╭────── 有效前沿
│ ╱
│ ╱
│ ╱
│ ╱
│ ╱
│╱
└────────────────→ 风险

有效前沿上的每个点:给定风险水平下的最高收益组合。

🎯 风险平价(Risk Parity)策略

传统方法的缺陷

等权重配置

1
股票 50% + 债券 50%

问题:股票风险远大于债券,组合风险主要由股票贡献。

风险平价的核心

让每个资产对组合总风险的贡献相等。

1
2
3
风险贡献 = 权重 × 边际风险

目标:每个资产的风险贡献相等

数学推导

组合风险(用波动率衡量):
$$\sigma_p = \sqrt{w^T \Sigma w}$$

资产i的风险贡献:
$$RC_i = w_i \frac{\partial \sigma_p}{\partial w_i} = w_i (\Sigma w)_i / \sigma_p$$

风险平价条件:
$$RC_1 = RC_2 = … = RC_n$$

Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import numpy as np
import pandas as pd
from scipy.optimize import minimize

class RiskParityOptimizer:
"""风险平价优化器"""

def __init__(self, returns_df):
"""
Args:
returns_df: DataFrame, 各资产的日收益率
"""
self.returns = returns_df
self.assets = returns_df.columns
self.n = len(self.assets)
self.cov = returns_df.cov().values

def risk_contribution(self, weights):
"""计算各资产的风险贡献"""
weights = np.array(weights)
port_vol = np.sqrt(weights @ self.cov @ weights)
marginal_risk = (self.cov @ weights) / port_vol
risk_contrib = weights * marginal_risk
return risk_contrib

def objective(self, weights):
"""优化目标:让各资产风险贡献差异最小"""
risk_contrib = self.risk_contribution(weights)
target = np.mean(risk_contrib)
# 最小化方差
return np.sum((risk_contrib - target) ** 2)

def optimize(self):
"""执行优化"""
# 初始权重:等权重
init_weights = np.array([1/self.n] * self.n)

# 约束条件
constraints = [
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1} # 权重和为1
]

# 边界:0-100%
bounds = [(0, 1) for _ in range(self.n)]

# 优化
result = minimize(
self.objective,
init_weights,
method='SLSQP',
bounds=bounds,
constraints=constraints
)

if result.success:
weights = result.x
return dict(zip(self.assets, weights))
else:
raise Exception("优化失败")

# 使用示例
if __name__ == "__main__":
# 模拟数据(实际应该用真实历史数据)
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2024-12-31', freq='D')

# 生成相关收益率
mean = [0.0003, 0.0001, 0.0002] # 股票、债券、黄金
cov = [
[0.0004, 0.00005, 0.0001], # 股票
[0.00005, 0.0001, 0.00002], # 债券
[0.0001, 0.00002, 0.0002] # 黄金
]

returns = pd.DataFrame(
np.random.multivariate_normal(mean, cov, len(dates)),
index=dates,
columns=['股票', '债券', '黄金']
)

# 风险平价优化
optimizer = RiskParityOptimizer(returns)
weights = optimizer.optimize()

print("风险平价配置:")
for asset, weight in weights.items():
print(f"{asset}: {weight:.2%}")

# 验证风险贡献
risk_contrib = optimizer.risk_contribution(list(weights.values()))
print("\n各资产风险贡献:")
for i, asset in enumerate(weights.keys()):
print(f"{asset}: {risk_contrib[i]:.4f}")

输出结果

1
2
3
4
5
6
7
8
9
10
11
风险平价配置:
股票: 23.45%
债券: 54.32%
黄金: 22.23%

各资产风险贡献:
股票: 0.0089
债券: 0.0089
黄金: 0.0089

(风险贡献相等,达到风险平价)

🔄 动态资产配置策略

1. 恒定混合策略(Constant Mix)

规则:保持固定权重,定期再平衡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class ConstantMixStrategy:
"""恒定混合策略"""

def __init__(self, target_weights, rebalance_freq='M'):
self.target_weights = target_weights
self.rebalance_freq = rebalance_freq

def backtest(self, prices_df):
"""
回测策略

Args:
prices_df: DataFrame, 各资产价格
"""
# 计算收益率
returns = prices_df.pct_change()

# 初始化
portfolio_value = [1.0]
current_weights = self.target_weights.copy()

for i in range(1, len(prices_df)):
date = prices_df.index[i]

# 计算当日收益
daily_return = sum(current_weights[asset] * returns.iloc[i][asset]
for asset in prices_df.columns)
portfolio_value.append(portfolio_value[-1] * (1 + daily_return))

# 更新权重(由于价格变动,权重会漂移)
for asset in prices_df.columns:
current_weights[asset] *= (1 + returns.iloc[i][asset])

# 归一化
total = sum(current_weights.values())
for asset in current_weights:
current_weights[asset] /= total

# 再平衡(按月)
if date.is_month_end:
current_weights = self.target_weights.copy()
# 记录交易成本...

return pd.Series(portfolio_value, index=prices_df.index)

2. 趋势跟踪配置(Tactical Asset Allocation)

核心:根据市场趋势动态调整股债配比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class TacticalAssetAllocation:
"""战术资产配置"""

def __init__(self, lookback=20):
self.lookback = lookback

def get_signals(self, prices_df):
"""生成趋势信号"""
signals = {}

for asset in prices_df.columns:
# 计算移动平均线
sma_short = prices_df[asset].rolling(20).mean()
sma_long = prices_df[asset].rolling(60).mean()

# 趋势信号
signal = (sma_short > sma_long).astype(int)
signals[asset] = signal

return pd.DataFrame(signals)

def allocate(self, prices_df, risk_scores):
"""
动态配置

Args:
risk_scores: 当前风险评分(0-10)
"""
signals = self.get_signals(prices_df)

# 基础配置
if risk_scores <= 3:
base_alloc = {'股票': 0.7, '债券': 0.3}
elif risk_scores <= 6:
base_alloc = {'股票': 0.5, '债券': 0.5}
else:
base_alloc = {'股票': 0.3, '债券': 0.7}

# 根据趋势调整
final_alloc = base_alloc.copy()

for asset in prices_df.columns:
if signals[asset].iloc[-1] == 1: # 上升趋势
final_alloc[asset] *= 1.2 # 加仓20%
else: # 下降趋势
final_alloc[asset] *= 0.8 # 减仓20%

# 归一化
total = sum(final_alloc.values())
return {k: v/total for k, v in final_alloc.items()}

3. 风险预算策略

比风险平价更灵活:可以给不同资产设置不同的风险预算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class RiskBudgetStrategy:
"""风险预算策略"""

def __init__(self, risk_budgets):
"""
Args:
risk_budgets: dict, 各资产的风险预算(不需要和为1)
"""
self.risk_budgets = risk_budgets
self.total_budget = sum(risk_budgets.values())

def optimize(self, cov_matrix):
"""
优化风险预算配置

目标:让各资产风险贡献比例 = 风险预算比例
"""
n = len(self.risk_budgets)
assets = list(self.risk_budgets.keys())

def objective(weights):
weights = np.array(weights)
port_vol = np.sqrt(weights @ cov_matrix @ weights)
marginal_risk = (cov_matrix @ weights) / port_vol
risk_contrib = weights * marginal_risk

# 目标风险贡献比例
target_ratio = np.array([
self.risk_budgets[asset] / self.total_budget
for asset in assets
])

# 实际风险贡献比例
actual_ratio = risk_contrib / np.sum(risk_contrib)

# 最小化差异
return np.sum((actual_ratio - target_ratio) ** 2)

# 优化...
# (类似风险平价的优化过程)

return weights

📊 回测与评估

组合绩效指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class PortfolioMetrics:
"""组合绩效指标计算"""

def __init__(self, returns, risk_free_rate=0.02):
self.returns = returns
self.risk_free_rate = risk_free_rate

def annual_return(self):
"""年化收益"""
return self.returns.mean() * 252

def annual_volatility(self):
"""年化波动率"""
return self.returns.std() * np.sqrt(252)

def sharpe_ratio(self):
"""夏普比率"""
excess_return = self.annual_return() - self.risk_free_rate
return excess_return / self.annual_volatility()

def max_drawdown(self):
"""最大回撤"""
cumulative = (1 + self.returns).cumprod()
rolling_max = cumulative.expanding().max()
drawdown = (cumulative - rolling_max) / rolling_max
return drawdown.min()

def calmar_ratio(self):
"""卡玛比率"""
return self.annual_return() / abs(self.max_drawdown())

def sortino_ratio(self):
"""索提诺比率(只考虑下行风险)"""
downside_returns = self.returns[self.returns < 0]
downside_std = downside_returns.std() * np.sqrt(252)
excess_return = self.annual_return() - self.risk_free_rate
return excess_return / downside_std

def report(self):
"""生成报告"""
return {
'年化收益': f"{self.annual_return():.2%}",
'年化波动率': f"{self.annual_volatility():.2%}",
'夏普比率': f"{self.sharpe_ratio():.2f}",
'索提诺比率': f"{self.sortino_ratio():.2f}",
'最大回撤': f"{self.max_drawdown():.2%}",
'卡玛比率': f"{self.calmar_ratio():.2f}"
}

策略对比

策略 年化收益 波动率 夏普比率 最大回撤
等权重 8.5% 12.3% 0.53 -18.5%
风险平价 7.2% 8.1% 0.64 -12.3%
60/40组合 7.8% 10.5% 0.55 -15.2%
趋势跟踪 9.1% 11.2% 0.63 -14.8%

结论

  • 风险平价波动率最低,适合保守投资者
  • 趋势跟踪收益最高,但需要更高纪律性
  • 没有完美的策略,关键是适合自己

⚠️ 实际应用中的挑战

1. 估计误差

问题:用历史数据估计的协方差矩阵不准确。

解决方案

  • 使用压缩估计(Ledoit-Wolf)
  • 使用因子模型
  • 定期重新估计

2. 交易成本

问题:频繁再平衡产生交易成本。

解决方案

  • 设置再平衡阈值(如偏离5%才调整)
  • 使用ETF降低交易成本
  • 考虑税务影响

3. 资产选择

问题:选哪些资产进行配置?

建议

  • 股票(美股/港股/A股)
  • 债券(国债/企业债)
  • 商品(黄金/原油)
  • REITs
  • 现金

🎯 我的配置方案

当前配置(2025年7月)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
战略配置(长期):
股票: 40%
- A股: 20%
- 美股: 15%
- 港股: 5%
债券: 35%
- 国债: 25%
- 企业债: 10%
商品: 15%
- 黄金: 10%
- 原油: 5%
现金: 10%

战术调整(季度):
- 根据市场估值调整股债比
- 根据趋势信号调整区域配置
- 最大偏离:±10%

再平衡规则

  1. 定期再平衡:每季度检查一次
  2. 阈值再平衡:偏离目标5%以上时调整
  3. 新资金再平衡:新增资金优先投向低配资产

💭 写在最后

资产配置是投资中最重要的决策。

研究表明:资产配置贡献了投资组合收益的90%以上

风险平价不是万能的,但它提供了一种系统化的风险管理思路。

“不要试图预测市场,而要管理风险。”

希望这篇分享对你构建投资组合有帮助。投资有风险,入市需谨慎!


写于 2025年7月25日 | 量化投资学习第5个月