01-组合构建理论 (Portfolio Construction)
预计学习时间:3 小时
难度:⭐⭐⭐⭐
核心问题:怎么把一堆信号变成一个具体的持仓组合?
从一个直觉出发
假设你的量化模型对 500 只股票分别给出了预期收益率预测:
贵州茅台: 预期涨 5%
宁德时代: 预期涨 8%
中国平安: 预期涨 3%
...
ST 某某: 预期涨 15% ← 预期收益最高,但风险极大
你怎么分配资金?
最直觉的方案:把钱全押在预期收益最高的那只上。
但问题来了:
- 你的预测可能是错的
- 高预期收益往往伴随高风险
- 如果所有高预期收益的股票都是同一行业,遇到行业黑天鹅就全完了
你需要一个系统化的框架来回答”每只股票买多少”这个问题。
这个框架就是组合理论。
一、从单个信号到组合:为什么需要组合理论
1.1 单个信号的局限
一个 Alpha 信号告诉你”某只股票看多”或”看空”。但信号不是确定性知识,它只是一种概率判断。
单个信号 → 仓位 → 收益
问题:
1. 信号可能错(误判风险)
2. 单个仓位波动太大(集中度风险)
3. 多个信号之间可能有相关性(风险重叠)
4. 资金有限(预算约束)
1.2 分散化的力量
分散化是金融学中最接近”免费午餐”的概念。核心直觉:
如果你有 10 个独立且期望为正的赌局,同时下注 10 个比单独下注 1 个的风险要小得多。
当资产之间的相关性小于 1 时,组合波动率小于各资产波动率的加权平均。这就是分散化的数学基础。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# 演示分散化效应
n_simulations = 10000
n_days = 252
# 单只股票:年化收益 10%,年化波动率 30%
mu = 0.10 / 252
sigma = 0.30 / np.sqrt(252)
# 模拟单只股票的一年收益
single_stock = np.exp(np.cumsum(np.random.normal(mu, sigma, (n_simulations, n_days)), axis=1))
single_returns = single_stock[:, -1] - 1
# 模拟等权组合,资产数量从 1 到 50
portfolio_sizes = [1, 2, 5, 10, 20, 50]
results = {'size': [], 'mean': [], 'std': [], 'sharpe': []}
for n in portfolio_sizes:
# 模拟 n 只独立股票
stocks = np.exp(np.cumsum(
np.random.normal(mu, sigma, (n_simulations, n_days, n)),
axis=1
))
# 等权组合
equal_weight = 1.0 / n
portfolio = np.sum(stocks[:, -1, :] * equal_weight, axis=1) - 1
results['size'].append(n)
results['mean'].append(np.mean(portfolio))
results['std'].append(np.std(portfolio))
results['sharpe'].append(np.mean(portfolio) / np.std(portfolio))
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].bar([str(s) for s in results['size']], results['std'],
color='steelblue', alpha=0.7)
axes[0].set_xlabel('组合中股票数量')
axes[0].set_ylabel('年化波动率')
axes[0].set_title('分散化效应:股票越多,波动率越低')
axes[0].grid(True, alpha=0.3)
# 理论值:sigma_p = sigma / sqrt(n)(独立资产)
theoretical_std = 0.30 / np.sqrt(np.array(portfolio_sizes))
axes[0].plot(range(len(portfolio_sizes)), theoretical_std,
'ro-', label='理论值 (独立资产)')
axes[0].legend()
axes[1].bar([str(s) for s in results['size']], results['sharpe'],
color='darkgreen', alpha=0.7)
axes[1].set_xlabel('组合中股票数量')
axes[1].set_ylabel('Sharpe 比率')
axes[1].set_title('分散化效应:Sharpe 比率提升')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()关键洞察:分散化不能提高预期收益,但可以大幅降低风险,从而提高风险调整后收益(Sharpe 比率)。
二、Markowitz 均值方差模型
2.1 核心思想
Harry Markowitz 在 1952 年提出:投资者应该在给定风险下最大化预期收益,或在给定预期收益下最小化风险。
其中:
- :权重向量()
- :协方差矩阵()
- :预期收益向量()
- :目标预期收益
- :全 1 向量
2.2 数学推导(拉格朗日方法)
引入拉格朗日乘子 :
对 求偏导并令其为零:
这就是 Markowitz 模型的解析解。它告诉我们:最优权重是协方差矩阵的逆与预期收益的线性组合的乘积。
2.3 有效前沿
对于不同的目标收益 ,求解上述优化问题,得到的 的轨迹就是有效前沿。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
np.random.seed(42)
# ============================================================
# Markowitz 均值方差模型
# ============================================================
def simulate_assets(n_assets, n_days=252, mu_base=0.08, sigma_base=0.25):
"""模拟 n 只资产的一年日度数据"""
returns = np.zeros((n_days, n_assets))
for i in range(n_assets):
mu = mu_base + np.random.normal(0, 0.05)
sigma = sigma_base + np.random.normal(0, 0.08)
sigma = max(sigma, 0.10) # 确保波动率为正
returns[:, i] = np.random.normal(mu / 252, sigma / np.sqrt(252), n_days)
return returns
# 模拟 10 只资产
n_assets = 10
returns = simulate_assets(n_assets)
# 估计参数
mu = returns.mean(axis=0) * 252 # 年化预期收益
Sigma = np.cov(returns.T) * 252 # 年化协方差矩阵
def portfolio_variance(w, Sigma):
"""组合方差"""
return w @ Sigma @ w
def portfolio_return(w, mu):
"""组合预期收益"""
return w @ mu
def markowitz_optimize(mu_target, mu, Sigma, short_allowed=False):
"""
Markowitz 均值方差优化
参数:
mu_target: 目标预期收益
mu: 预期收益向量
Sigma: 协方差矩阵
short_allowed: 是否允许做空
"""
n = len(mu)
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 权重之和 = 1
{'type': 'eq', 'fun': lambda w: w @ mu - mu_target} # 达到目标收益
]
bounds = None if short_allowed else [(0, 1) for _ in range(n)]
# 初始值:等权
w0 = np.ones(n) / n
result = minimize(
portfolio_variance, w0,
args=(Sigma,),
method='SLSQP',
bounds=bounds,
constraints=constraints,
options={'maxiter': 1000, 'ftol': 1e-12}
)
return result.x if result.success else None
# ============================================================
# 计算有效前沿
# ============================================================
n_points = 50
mu_min = mu.min()
mu_max = mu.max()
mu_targets = np.linspace(mu_min + 0.01, mu_max - 0.01, n_points)
efficient_portfolios = []
for target in mu_targets:
w = markowitz_optimize(target, mu, Sigma, short_allowed=False)
if w is not None:
port_ret = portfolio_return(w, mu)
port_vol = np.sqrt(portfolio_variance(w, Sigma))
efficient_portfolios.append((port_vol, port_ret, w))
efficient_portfolios = np.array(efficient_portfolios)
# 全局最小方差组合(GMV)
def gmv_portfolio(Sigma):
"""全局最小方差组合"""
n = len(Sigma)
constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]
bounds = [(0, 1) for _ in range(n)]
w0 = np.ones(n) / n
result = minimize(
portfolio_variance, w0,
args=(Sigma,),
method='SLSQP',
bounds=bounds,
constraints=constraints
)
return result.x
w_gmv = gmv_portfolio(Sigma)
gmv_ret = portfolio_return(w_gmv, mu)
gmv_vol = np.sqrt(portfolio_variance(w_gmv, Sigma))
# 可视化
fig, ax = plt.subplots(figsize=(12, 7))
# 有效前沿
ax.plot(efficient_portfolios[:, 0], efficient_portfolios[:, 1],
'b-', linewidth=2, label='有效前沿')
# GMV 点
ax.scatter(gmv_vol, gmv_ret, c='red', s=100, zorder=5,
label=f'GMV (vol={gmv_vol:.2%}, ret={gmv_ret:.2%})')
# 等权组合
w_eq = np.ones(n_assets) / n_assets
eq_ret = portfolio_return(w_eq, mu)
eq_vol = np.sqrt(portfolio_variance(w_eq, Sigma))
ax.scatter(eq_vol, eq_ret, c='green', s=100, zorder=5,
label=f'等权 (vol={eq_vol:.2%}, ret={eq_ret:.2%})')
# 单个资产
ax.scatter(np.sqrt(np.diag(Sigma)), mu, c='gray', s=30, alpha=0.5,
label='单个资产')
# 资本市场线(从无风险利率到最大 Sharpe 比率组合)
rf = 0.02 # 无风险利率
sharpe_ratios = (efficient_portfolios[:, 1] - rf) / efficient_portfolios[:, 0]
tangent_idx = np.argmax(sharpe_ratios)
tangent_vol = efficient_portfolios[tangent_idx, 0]
tangent_ret = efficient_portfolios[tangent_idx, 1]
cml_x = np.linspace(0, tangent_vol * 1.5, 50)
cml_y = rf + (tangent_ret - rf) / tangent_vol * cml_x
ax.plot(cml_x, cml_y, 'r--', linewidth=1.5,
label=f'资本市场线 (Sharpe={sharpe_ratios[tangent_idx]:.2f})')
ax.set_xlabel('年化波动率')
ax.set_ylabel('年化预期收益')
ax.set_title('Markowitz 有效前沿')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"=== 组合对比 ===")
print(f"等权组合: 波动率={eq_vol:.2%}, 收益={eq_ret:.2%}, Sharpe={sharpe_ratios.mean():.2f}")
print(f"GMV组合: 波动率={gmv_vol:.2%}, 收益={gmv_ret:.2%}")
print(f"最大Sharpe: 波动率={tangent_vol:.2%}, 收益={tangent_ret:.2%}, "
f"Sharpe={sharpe_ratios[tangent_idx]:.2f}")2.4 均值方差模型的致命问题
Markowitz 模型在理论上很优美,但在实践中有一个致命缺陷:对输入参数极度敏感。
均值方差模型的问题:
1. 估计误差放大效应
→ Sigma^{-1} 的逆运算会放大估计误差
→ 微小的协方差估计变化 → 巨大的权重变化
2. 收益率估计极度不准确
→ 年化预期收益的估计误差通常大于预期收益本身
→ 输入垃圾,输出垃圾(GIGO)
3. 极端权重
→ 优化器倾向于给出 90% 某只股票、-50% 另一只股票的极端权重
→ 在实际中不可执行
4. 集中在少数资产
→ 有效前沿上的最优组合往往只持有少数几只股票
→ 与"分散化"的初衷相悖
经验法则:在 只股票上做 Markowitz 优化,你需要至少 个独立数据点才能可靠估计协方差矩阵。但金融市场的不平稳性意味着历史数据不一定代表未来。
三、Black-Litterman 模型
3.1 核心思想
Black & Litterman (1992) 提出的问题是:与其直接估计每只股票的预期收益(这几乎不可能),不如从”市场均衡”出发,然后把你的”观点”融合进去。
Markowitz 的做法:
估计每只股票的 mu → 输入优化器 → 得到权重
问题:mu 的估计极度不准确
Black-Litterman 的做法:
从市场均衡出发 → 加入投资者观点 → 得到"后验"预期收益 → 输入优化器
优势:后验收益比直接估计更稳定
3.2 数学框架
第一步:市场均衡收益
从 CAPM 出发,如果所有投资者持有市场组合,那么资产的均衡预期收益为:
其中:
- :隐含均衡收益向量
- :风险厌恶系数(通常取 )
- :市场权重(市值加权)
- :协方差矩阵
第二步:融入投资者观点
投资者有 个观点,用矩阵 和向量 表示:
其中:
- 是 的”观点矩阵”(每行是一个观点,指出涉及哪些资产)
- 是 的观点收益向量(每个观点的具体收益预期)
- 是观点的不确定性
第三步:贝叶斯更新
后验预期收益:
其中 是衡量均衡收益不确定性的标量(通常取 0.025-0.05), 是观点不确定性矩阵。
import numpy as np
np.random.seed(42)
# ============================================================
# Black-Litterman 模型
# ============================================================
def black_litterman(Sigma, w_mkt, P, Q, Omega, delta=2.5, tau=0.05):
"""
Black-Litterman 模型
参数:
Sigma: 协方差矩阵 (n x n)
w_mkt: 市场权重 (n,)
P: 观点矩阵 (K x n)
Q: 观点收益 (K,)
Omega: 观点不确定性 (K x K)
delta: 风险厌恶系数
tau: 均衡收益不确定性标量
返回:
mu_bl: 后验预期收益
Sigma_bl: 后验协方差矩阵
"""
n = len(w_mkt)
# 第一步:隐含均衡收益
Pi = delta * Sigma @ w_mkt
# 第二步:贝叶斯更新
tau_Sigma_inv = np.linalg.inv(tau * Sigma)
Omega_inv = np.linalg.inv(Omega)
# 后验协方差
M = np.linalg.inv(tau_Sigma_inv + P.T @ Omega_inv @ P)
Sigma_bl = Sigma + M # 后验协方差
# 后验预期收益
mu_bl = M @ (tau_Sigma_inv @ Pi + P.T @ Omega_inv @ Q)
return mu_bl, Sigma_bl
# 模拟:5 只资产
n_assets = 5
Sigma = np.array([
[0.0400, 0.0060, 0.0020, 0.0010, 0.0005],
[0.0060, 0.0300, 0.0040, 0.0020, 0.0010],
[0.0020, 0.0040, 0.0500, 0.0030, 0.0015],
[0.0010, 0.0020, 0.0030, 0.0600, 0.0040],
[0.0005, 0.0010, 0.0015, 0.0040, 0.0700],
])
# 市场权重(市值加权)
w_mkt = np.array([0.30, 0.25, 0.20, 0.15, 0.10])
# 投资者观点
# 观点 1:资产 1 比市场预期多涨 3%
# 观点 2:资产 3 比资产 2 多涨 2%(相对观点)
P = np.array([
[1, 0, 0, 0, 0], # 绝对观点:资产 1
[0, -1, 1, 0, 0], # 相对观点:资产 3 - 资产 2
])
Q = np.array([0.03, 0.02])
# 观点不确定性(对角矩阵,值越小表示越确信)
Omega = np.diag([0.01, 0.01])
# 计算 Black-Litterman 后验收益
mu_bl, Sigma_bl = black_litterman(Sigma, w_mkt, P, Q, Omega)
# 对比
Pi = 2.5 * Sigma @ w_mkt # 均衡收益
print("=== 隐含均衡收益 vs Black-Litterman 后验收益 ===")
for i in range(n_assets):
print(f"资产 {i+1}: 均衡={Pi[i]:.4f}, BL后验={mu_bl[i]:.4f}, "
f"差异={mu_bl[i]-Pi[i]:.4f}")=== 隐含均衡收益 vs Black-Litterman 后验收益 ===
资产 1: 均衡=0.0385, BL后验=0.0643, 差异=0.0258
资产 2: 均衡=0.0280, BL后验=0.0249, 差异=-0.0031
资产 3: 均衡=0.0325, BL后验=0.0426, 差异=0.0101
资产 4: 均衡=0.0330, BL后验=0.0330, 差异=0.0000
资产 5: 均衡=0.0365, BL后验=0.0365, 差异=0.0000
3.3 Black-Litterman 的优势
| 维度 | Markowitz | Black-Litterman |
|---|---|---|
| 收益估计 | 直接估计(极度不准确) | 市场均衡 + 观点修正(更稳定) |
| 输入敏感度 | 极度敏感 | 相对稳健 |
| 权重分布 | 常出现极端权重 | 更合理的权重分布 |
| 允许观点 | 不明确 | 可以融入投资者的具体观点 |
| 直觉性 | 低 | 高(“我认为 X 比 Y 多涨 2%“) |
四、风险平价 (Risk Parity)
4.1 核心思想
风险平价的思想来自一个非常简单的观察:
在传统 60/40 股债组合中,虽然资金是 60:40,但风险的贡献是 80:20(甚至更极端)。
为什么?因为股票的波动率远高于债券。
风险平价的目标:让每个资产对组合总风险的贡献相等。
传统组合(60% 股 / 40% 债):
资金配比: 股 60% | 债 40%
风险贡献: 股 85% | 债 15% ← 不均衡!
风险平价组合:
资金配比: 股 30% | 债 70%
风险贡献: 股 50% | 债 50% ← 每个资产风险贡献相等
4.2 数学表达
资产 对组合总风险的边际风险贡献:
资产 对组合总风险的风险贡献(Risk Contribution):
风险平价目标:
等价于:
import numpy as np
from scipy.optimize import minimize
np.random.seed(42)
# ============================================================
# 风险平价模型
# ============================================================
def risk_contribution(w, Sigma):
"""
计算每个资产的风险贡献
返回:
rc: 各资产的风险贡献 (n,)
total_risk: 组合总风险(波动率)
"""
port_var = w @ Sigma @ w
port_vol = np.sqrt(port_var)
marginal = Sigma @ w / port_vol
rc = w * marginal # 风险贡献 = 权重 x 边际贡献
return rc, port_vol
def risk_parity_objective(w, Sigma):
"""风险平价目标函数:使各资产风险贡献的差异最小化"""
rc, _ = risk_contribution(w, Sigma)
n = len(w)
# 目标:所有 rc_i 相等
# 用 rc_i / port_var 来归一化
target = 1.0 / n # 每个资产贡献 1/n 的风险
rc_pct = rc / np.sum(rc) # 百分比形式
return np.sum((rc_pct - target) ** 2)
def solve_risk_parity(Sigma, long_only=True):
"""求解风险平价组合"""
n = len(Sigma)
constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]
bounds = [(0, 1) for _ in range(n)] if long_only else None
w0 = np.ones(n) / n
result = minimize(
risk_parity_objective, w0,
args=(Sigma,),
method='SLSQP',
bounds=bounds,
constraints=constraints,
options={'maxiter': 1000, 'ftol': 1e-15}
)
return result.x
# 模拟股债组合
Sigma_2 = np.array([
[0.04, 0.001], # 股票协方差矩阵
[0.001, 0.01], # 债券协方差矩阵
])
w_rp = solve_risk_parity(Sigma_2)
w_6040 = np.array([0.6, 0.4])
rc_rp, vol_rp = risk_contribution(w_rp, Sigma_2)
rc_6040, vol_6040 = risk_contribution(w_6040, Sigma_2)
print("=== 60/40 组合 vs 风险平价组合 ===")
print(f"60/40 组合: 权重=[{w_6040[0]:.1%}, {w_6040[1]:.1%}]")
print(f" 风险贡献: [{rc_6040[0]/sum(rc_6040):.1%}, {rc_6040[1]/sum(rc_6040):.1%}]")
print(f" 组合波动率: {vol_6040:.2%}")
print(f"\n风险平价组合: 权重=[{w_rp[0]:.1%}, {w_rp[1]:.1%}]")
print(f" 风险贡献: [{rc_rp[0]/sum(rc_rp):.1%}, {rc_rp[1]/sum(rc_rp):.1%}]")
print(f" 组合波动率: {vol_rp:.2%}")
# 多资产风险平价
n_assets = 5
np.random.seed(42)
Sigma_5 = np.random.uniform(0.01, 0.06, (n_assets, n_assets))
Sigma_5 = (Sigma_5 + Sigma_5.T) / 2
np.fill_diagonal(Sigma_5, np.random.uniform(0.03, 0.08, n_assets))
w_rp_5 = solve_risk_parity(Sigma_5)
rc_rp_5, vol_rp_5 = risk_contribution(w_rp_5, Sigma_5)
print(f"\n=== 多资产风险平价 ===")
for i in range(n_assets):
print(f"资产 {i+1}: 权重={w_rp_5[i]:.2%}, "
f"风险贡献={rc_rp_5[i]/sum(rc_rp_5):.2%}")4.3 风险平价 vs 等权 vs Markowitz
| 维度 | 等权组合 | Markowitz | 风险平价 |
|---|---|---|---|
| 核心逻辑 | 每个资产分配相同资金 | 最大化风险调整后收益 | 每个资产贡献相同风险 |
| 需要收益估计 | 否 | 是(极不准确) | 否 |
| 需要协方差矩阵 | 否 | 是 | 是 |
| 权重稳定性 | 高 | 低 | 高 |
| 极端权重 | 无 | 常见 | 无 |
| 分散化质量 | 中 | 理论最优(但不可靠) | 高 |
关键认知:风险平价是 Markowitz 模型的一个实用替代方案。它不需要估计预期收益(这是最难的部分),只需要协方差矩阵,而且产生的权重更稳定。
五、全局最小方差 (GMV)
5.1 核心思想
GMV 是最”保守”的组合构建方法:不关心收益,只关心风险。
直觉:如果我不知道哪只股票会涨,但我能估计它们之间的相关性,那我至少可以找到波动率最小的组合。
5.2 解析解
GMV 有解析解:
这就是有效前沿最左边的那一点。
5.3 GMV 的优缺点
优点:
- 不需要估计预期收益(只依赖协方差矩阵)
- 产生的权重比 Markowitz 更稳定
- 在实证中表现往往优于均值方差优化
缺点:
- 可能过度集中在低波动资产(如大量持有债券)
- 完全忽略了收益信息
- 在低利率环境中,低波动组合的绝对收益可能不够
def gmv_analytical(Sigma):
"""GMV 解析解"""
Sigma_inv = np.linalg.inv(Sigma)
ones = np.ones(len(Sigma))
w = Sigma_inv @ ones / (ones @ Sigma_inv @ ones)
return w
# 对比
w_gmv_analytical = gmv_analytical(Sigma_5)
w_gmv_numerical = gmv_portfolio(Sigma_5)
print("=== GMV 解析解 vs 数值解 ===")
print(f"解析解: {w_gmv_analytical}")
print(f"数值解: {w_gmv_numerical}")
print(f"差异: {np.max(np.abs(w_gmv_analytical - w_gmv_numerical)):.8f}")六、约束优化
6.1 为什么需要约束
在实务中,组合优化几乎不会在”无约束”条件下进行。原因:
实务约束:
1. 行业中性:不对任何行业过度暴露
→ Σ(某行业权重) ≤ 20%
2. 个股权重上限:防止过度集中
→ w_i ≤ 5%
3. 换手率约束:控制交易成本
→ Σ|w_new - w_old| ≤ 20%
4. 做空限制:许多机构不能做空
→ w_i ≥ 0
5. 因子暴露约束:控制风格偏移
→ |Σ w_i * factor_i - target| ≤ threshold
6.2 带约束的组合优化
import numpy as np
from scipy.optimize import minimize
def constrained_portfolio_optimize(mu, Sigma, w_old=None,
max_weight=0.05,
max_turnover=0.2,
industry_exposure=None,
industry_limits=None):
"""
带约束的组合优化
参数:
mu: 预期收益
Sigma: 协方差矩阵
w_old: 当前持仓(用于换手率约束)
max_weight: 个股权重上限
max_turnover: 最大换手率
industry_exposure: 行业暴露矩阵 (n_assets x n_industries)
industry_limits: 行业权重上限 (n_industries,)
"""
n = len(mu)
w0 = np.ones(n) / n
# 目标函数:最大化风险调整后收益(等价于最小化负的)
def neg_sharpe(w):
port_ret = w @ mu
port_vol = np.sqrt(w @ Sigma @ w)
return -port_ret / port_vol if port_vol > 1e-10 else 0
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1} # 权重之和 = 1
]
# 换手率约束
if w_old is not None:
constraints.append({
'type': 'ineq',
'fun': lambda w: max_turnover - np.sum(np.abs(w - w_old))
})
# 行业暴露约束
if industry_exposure is not None and industry_limits is not None:
n_ind = industry_limits.shape[0]
for j in range(n_ind):
constraints.append({
'type': 'ineq',
'fun': lambda w, j=j: industry_limits[j] - w @ industry_exposure[:, j]
})
bounds = [(0, max_weight) for _ in range(n)]
result = minimize(
neg_sharpe, w0,
method='SLSQP',
bounds=bounds,
constraints=constraints,
options={'maxiter': 2000, 'ftol': 1e-12}
)
return result.x if result.success else None
# 模拟:10 只股票,3 个行业
np.random.seed(42)
n = 10
mu_sim = np.random.uniform(0.05, 0.20, n)
Sigma_sim = np.eye(n) * 0.04 + np.random.uniform(0.005, 0.02, (n, n)) * 0.5
Sigma_sim = (Sigma_sim + Sigma_sim.T) / 2
# 行业分类
industry_matrix = np.zeros((n, 3))
industry_matrix[:4, 0] = 1 # 行业 1:资产 1-4
industry_matrix[4:7, 1] = 1 # 行业 2:资产 5-7
industry_matrix[7:, 2] = 1 # 行业 3:资产 8-10
industry_limits = np.array([0.40, 0.40, 0.40]) # 每个行业最多 40%
# 当前持仓(用于换手率约束)
w_current = np.ones(n) / n
w_new = constrained_portfolio_optimize(
mu_sim, Sigma_sim,
w_old=w_current,
max_weight=0.10,
max_turnover=0.30,
industry_exposure=industry_matrix,
industry_limits=industry_limits
)
if w_new is not None:
print("=== 约束优化结果 ===")
for i in range(n):
ind = np.argmax(industry_matrix[i])
print(f"资产 {i+1} (行业{ind+1}): 权重={w_new[i]:.2%}")
print(f"\n行业暴露:")
for j in range(3):
print(f"行业 {j+1}: {w_new @ industry_matrix[:, j]:.2%} "
f"(上限={industry_limits[j]:.0%})")
turnover = np.sum(np.abs(w_new - w_current))
print(f"\n换手率: {turnover:.2%}")6.3 各组合构建方法对比
| 方法 | 需要收益估计 | 需要协方差 | 权重稳定性 | 分散化 | 实务适用性 |
|---|---|---|---|---|---|
| 等权 | 否 | 否 | 极高 | 中 | 基准对比 |
| Markowitz | 是 | 是 | 低 | 高(理论) | 低(估计误差大) |
| Black-Litterman | 部分 | 是 | 高 | 中-高 | 中-高 |
| GMV | 否 | 是 | 中 | 高 | 中 |
| 风险平价 | 否 | 是 | 高 | 高 | 高 |
实务建议:
- 如果你对收益估计有信心(有强大的因子模型),用 Black-Litterman
- 如果你对收益估计没信心,用风险平价或 GMV
- 永远加约束(权重上限、换手率限制、行业中性)
- 用协方差矩阵的稳健估计(下一章的内容)
七、小结
| 概念 | 要点 |
|---|---|
| 分散化 | 核心原理:相关性小于 1 的资产组合可以降低风险 |
| Markowitz | 在给定风险下最大化收益。理论上优美,实务中因估计误差而难以使用 |
| Black-Litterman | 从市场均衡出发,融入投资者观点。比 Markowitz 更实用 |
| 风险平价 | 每个资产贡献相等风险。不需要收益估计,权重稳定 |
| GMV | 只关注最小风险。不需要收益估计,解析解可用 |
| 约束优化 | 实务必需。权重上限、换手率约束、行业中性 |
一句话总结:组合构建的核心矛盾是”你想要高收益但预测不准”。Markowitz 告诉你最优是什么样子,但它的前提(准确的预期收益)几乎不可能满足。Black-Litterman、风险平价、GMV 分别从不同角度回避了这个问题,但都需要一个准确的协方差矩阵——这就是下一章的主题。
参考阅读:Markowitz (1952), “Portfolio Selection”; Black & Litterman (1992), “Global Portfolio Optimization”; Maillard, Roncalli & Teiletche (2010), “The Properties of Equally Weighted Risk Contribution Portfolios”
→ 下一章:02-协方差矩阵估计 — 组合理论的基础设施