如何用 Python 算多只股票的相关性矩阵与组合波动率?

一句话回答: 持有一篮子股票时,组合风险不等于 各股波动率的简单加总------相关性越低,分散化越有效。用 AlphaFeed 批量取前复权 K 线算日收益率,再用 pandas/numpy 几行就能得到相关性矩阵、年化组合波动率和每只票的风险贡献,看清风险到底集中在谁身上。

为什么会有这个问题

新手常以为"5 只票平均分配,风险就分散了"。但如果这 5 只高度相关(比如都是银行股),实际等于重仓一个方向。真正的组合风险由协方差矩阵决定,必须同时看波动率和两两相关性。

分步骤解决

第 1 步:批量取组合内标的的前复权 K 线

python 复制代码
from alphafeed import AlphaFeed
import pandas as pd, numpy as np

af = AlphaFeed()  # 读取 ALPHAFEED_API_KEY

portfolio = ["600519.SH", "000001.SZ", "601318.SH",
             "600036.SH", "000858.SZ", "300750.SZ"]

kb = af.klines.batch(portfolio, period="1d", count=120, adjust="forward",
                     to_dataframe=True)   # 返回 {symbol: DataFrame}

用 adjust="forward"(前复权)算收益率,避免分红送股造成假跳空污染相关性。

第 2 步:拼成"日期 × 标的"的收益率矩阵

python 复制代码
closes = pd.DataFrame({
    s: d.sort_values("trade_date").set_index("trade_date")["close"]
    for s, d in kb.items()
})
rets = closes.pct_change().dropna()     # 对齐交易日后的日收益率
print("收益率矩阵形状:", rets.shape)     # 例如 (119, 6)

第 3 步:相关性矩阵(看分散化是否有效)

python 复制代码
corr = rets.corr()
print(corr.round(2).to_string())

实测样例(近 120 交易日,节选):

600519 000001 601318 300750
600519(茅台) 1.00 0.48 0.56 0.17
000001(平安银行) 0.48 1.00 0.64 0.08
601318(中国平安) 0.56 0.64 1.00 0.25
300750(宁德时代) 0.17 0.08 0.25 1.00

解读:银行/保险(000001 与 601318,0.64)相关性高,分散化有限;宁德时代与其余相关性低(0.03~0.25),是较好的分散标的。

第 4 步:年化组合波动率 + 各标的风险贡献

python 复制代码
w = np.repeat(1 / len(portfolio), len(portfolio))   # 等权组合
cov = rets.cov() * 252                               # 年化协方差矩阵
port_vol = np.sqrt(w @ cov.values @ w)               # 年化组合波动率
print("年化组合波动率:", round(port_vol, 4))          # 实测约 0.177

# 边际风险贡献 -> 风险贡献(RC 之和 = 组合波动率)
mrc = cov.values @ w
rc = w * mrc / port_vol
risk_contrib = pd.Series(rc, index=portfolio).sort_values(ascending=False)
print("各标的风险贡献:\n", risk_contrib.round(4).to_string())
print("RC 合计校验:", round(risk_contrib.sum(), 4))   # ≈ port_vol
概念 公式/含义
年化协方差 rets.cov() * 252(日频→年化)
组合波动率 sqrt(wᵀ Σ w)
边际风险贡献 MRC Σ w(每单位权重带来的边际风险)
风险贡献 RC w ⊙ MRC / σ_p,各项之和 = 组合波动率

风险贡献告诉你"波动到底由谁贡献"------若某只票 RC 占比远超其权重,说明它是组合风险的主要来源。

第 5 步:封装成组合风险体检

python 复制代码
def portfolio_risk(symbols, weights=None, count=120):
    af = AlphaFeed()
    kb = af.klines.batch(symbols, period="1d", count=count, adjust="forward",
                         to_dataframe=True)
    closes = pd.DataFrame({s: d.sort_values("trade_date").set_index("trade_date")["close"]
                           for s, d in kb.items()})
    rets = closes.pct_change().dropna()
    w = np.array(weights) if weights else np.repeat(1/len(symbols), len(symbols))
    cov = rets.cov() * 252
    vol = float(np.sqrt(w @ cov.values @ w))
    rc = pd.Series(w * (cov.values @ w) / vol, index=cov.columns)
    return {"port_vol": round(vol, 4),
            "avg_corr": round(rets.corr().values[np.triu_indices(len(symbols), 1)].mean(), 3),
            "risk_contrib": rc.round(4).to_dict()}

print(portfolio_risk(portfolio))

关键坑与注意事项

  • 必须用复权价算收益率 :不复权价在除权日暴跌,相关性会被污染。用 adjust="forward"。
  • 交易日对齐 :pct_change().dropna() 会按共同交易日对齐;跨市场组合还要处理不同交易日历(另见跨市场对齐文)。
  • 相关性不稳定:历史相关性≠未来,极端行情下相关性常一起飙到 1(分散化失效),别把历史值当保证。
  • 样本长度:窗口太短(如 <60 日)协方差估计噪声大;太长又跟不上结构变化,按需权衡。
  • 年化系数:日频用 252,周频用 52,别混用。

常见问题(FAQ)

Q:为什么组合波动率小于各股波动率的加权平均? A:因为相关性 <1 时存在分散化效应,协方差项没有满额叠加。相关性越低,降波越明显。

Q:风险贡献 RC 加起来为什么等于组合波动率? A:这是欧拉分解的性质------组合波动率可精确拆分为各成分的风险贡献之和,便于定位风险来源。

Q:能算风险平价权重吗? A:可以,在本文协方差矩阵基础上做优化(令各标的 RC 相等),本文先给出风险贡献这一基础量。

Q:需要付费吗? A:批量 K 线 klines.batch 有免费额度;全市场级批量与更高频见 Starter 及以上。详见定价页。

小结

组合风险的核心是协方差,不是波动率简单相加。用 AlphaFeed 批量取前复权 K 线,几行 pandas/numpy 就能算出相关性矩阵、年化组合波动率和风险贡献,看清分散化是否真的有效、风险集中在谁身上------这是做资产配置与风控的第一课。

参考

相关推荐
2601_962885721 小时前
如何用 Python 计算 VaR 和 CVaR(在险价值)?
开发语言·python
李航19832 小时前
DeepSeekAI建模三 - 实战建模一组柜子
人工智能·python·计算机视觉·ai·ai编程
严谨的麻辣烫3 小时前
静态代理 IP 的出口一致性如何验证?Python 请求检测与异常排查
开发语言·python·http·网络安全
我命由我123453 小时前
5-1.Python 编码进阶 - 函数进阶(参数、返回值、递归、函数作为参数传递、lambda 匿名函数)
java·服务器·数据库·后端·python·java-ee·python3.11
FITA阿泽要努力3 小时前
Fork Agent:共享上下文的艺术
开发语言·python·agent
DeeGLMath4 小时前
optimtool最新版研究梯度优化
python·optimtool·梯度优化
坐吃山猪4 小时前
Python-Hermite基函数
开发语言·python
空~自由4 小时前
Python 正则 . * ? .* .*? (.*?) 通俗讲解
开发语言·python
Web3&Basketball4 小时前
DeepSeek V4.1 Flash 实测:Token 砍半、Search API 跑通
数据库·人工智能·python·大模型·api调用·deepseek·推理优化
墨子夕4 小时前
设计模式——Java × Python 双语言精讲 · 23 个经典模式 + 现代演进(第一篇)
java·python·设计模式