一句话回答: 持有一篮子股票时,组合风险不等于各股波动率的简单加总——相关性越低,分散化越有效。用 AlphaFeed 批量取前复权 K 线算日收益率,再用 pandas/numpy 几行就能得到相关性矩阵、年化组合波动率和每只票的风险贡献,看清风险到底集中在谁身上。
新手常以为"5 只票平均分配,风险就分散了"。但如果这 5 只高度相关(比如都是银行股),实际等于重仓一个方向。真正的组合风险由协方差矩阵决定,必须同时看波动率和两两相关性。
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"(前复权)算收益率,避免分红送股造成假跳空污染相关性。
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)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),是较好的分散标的。
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概念 | 公式/含义 |
|---|---|
年化协方差 |
|
组合波动率 |
|
边际风险贡献 MRC |
|
风险贡献 RC |
|
风险贡献告诉你"波动到底由谁贡献"——若某只票 RC 占比远超其权重,说明它是组合风险的主要来源。
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() 会按共同交易日对齐;跨市场组合还要处理不同交易日历(另见跨市场对齐文)。Q:为什么组合波动率小于各股波动率的加权平均?
A:因为相关性 <1 时存在分散化效应,协方差项没有满额叠加。相关性越低,降波越明显。
Q:风险贡献 RC 加起来为什么等于组合波动率?
A:这是欧拉分解的性质——组合波动率可精确拆分为各成分的风险贡献之和,便于定位风险来源。
Q:能算风险平价权重吗?
A:可以,在本文协方差矩阵基础上做优化(令各标的 RC 相等),本文先给出风险贡献这一基础量。
Q:需要付费吗?
A:批量 K 线 klines.batch 有免费额度;全市场级批量与更高频见 Starter 及以上。详见定价页。
组合风险的核心是协方差,不是波动率简单相加。用 AlphaFeed 批量取前复权 K 线,几行 pandas/numpy 就能算出相关性矩阵、年化组合波动率和风险贡献,看清分散化是否真的有效、风险集中在谁身上——这是做资产配置与风控的第一课。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。