如何用 Python 做波动率目标仓位(Volatility Targeting)回测?让组合波动稳定在目标
如何用 Python 做波动率目标仓位(Volatility Targeting)回测?
一句话回答: 波动率目标仓位(Volatility Targeting)= 每天把仓位设为 目标波动率 ÷ 近期已实现波动率,于是波动放大时自动减仓、波动收敛时自动加仓,让组合的实际波动稳定在目标附近。用 AlphaFeed 取前复权日线算滚动已实现波动率,仓位滞后一天(T+1)作用于收益即可完成无未来函数回测——它改变的是风险暴露,不保证提高收益。
为什么会有这个问题 / 传统做法的痛点
满仓买入持有的问题是:市场平静和剧烈时你承担的风险完全不同,暴跌往往发生在高波动期,而满仓正好在那时风险最大。波动率目标的理念来自风险管理:与其固定资金,不如固定风险。痛点在实现细节:① 已实现波动率怎么估(窗口、年化);② 避免未来函数——今天的仓位只能用到昨天为止的信息;③ 要诚实对比「波动是否真的被压到目标附近」,而不是只看收益。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线,算日收益与滚动已实现波动率
from alphafeed import AlphaFeed
import numpy as np, pandas as pd
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=800,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
ret = df["close"].pct_change()
rv = ret.rolling(20).std() * np.sqrt(252) # 20 日已实现波动率,年化
第 2 步:按目标波动率定仓位,T+1 作用避免未来函数
target_vol, cap = 0.15, 1.5 # 目标年化波动 15%,仓位上限 1.5 倍
weight = (target_vol / rv).clip(upper=cap) # 波动高→仓位低;波动低→仓位高
w_lag = weight.shift(1) # 关键:用昨天的波动定今天仓位
strat_ret = (w_lag * ret).dropna() # 策略收益
bh_ret = ret.dropna() # 买入持有基准
weight.shift(1) 是避免未来函数的核心——今天开盘前只知道截至昨天的波动,不能用今天的。
第 3 步:对比收益与「波动是否被压到目标」
def total(x): return (1 + x).prod() - 1
def ann_vol(x): return x.std() * np.sqrt(252)
print("策略累计 %.2f%% / 买入持有 %.2f%%" % (total(strat_ret)*100, total(bh_ret)*100))
print("策略年化波动 %.2f%% / 买入持有 %.2f%% (目标 15%%)" % (ann_vol(strat_ret)*100, ann_vol(bh_ret)*100))
print("平均仓位", round(w_lag.mean(), 3))
实测(
tools/verify_new_2026-10-10.py,SDK 0.1.4):贵州茅台近 800 日,策略累计 −15.01% vs 买入持有 −18.15%;策略年化波动 17.48% vs 买入持有 22.95%(更贴近 15% 目标);平均仓位 0.806。本例波动被明显压低、亏损也略收窄,但这是单标的单区间结果,不可外推为”一定更好”。
参数与字段说明
| 参数/概念 | 含义 | 备注 |
|---|---|---|
adjust="forward" | 前复权 | 必须,除权缺口会伪造波动 |
rolling(20) | 波动率估计窗口 | 窗口越短越灵敏、越长越稳 |
* np.sqrt(252) | 年化 | 日波动 → 年化 |
target_vol | 目标年化波动率 | 示例 15%,自定 |
cap | 仓位上限 | 防止低波动时过度加杠杆 |
weight.shift(1) | 仓位滞后一天 | 避免未来函数的关键 |
关键坑与注意事项
- 避免未来函数:仓位必须用
shift(1)——用今天的波动定今天仓位是经典前视偏差,会让回测虚高。 - 改风险不改方向:波动率目标只调整暴露,趋势向下时它只是”少亏/晚亏”,不会把亏损变盈利。本例就是少亏而非反转。
- 杠杆与现实约束:低波动期
target/rv可能 >1,意味着加杠杆;A 股个股难加杠杆,须用cap限制或理解为资金利用率,别当真上杠杆。 - 波动估计有滞后:已实现波动是回看的,剧烈拐点处仓位调整会慢半拍(波动跳升后才减仓)。
- 交易成本未计:频繁调仓有换手成本,示例未计费,实盘要加入手续费与冲击成本。
- 单标的演示:真正的波动率目标常用于组合层面;个股示例仅演示机制。
常见问题(FAQ)
Q:波动率目标和风险平价是一回事吗? A:相关但不同。波动率目标是把单个组合的总波动压到目标;风险平价是让多个资产对组合的风险贡献相等。两者都以”管理风险”为核心,常结合使用。
Q:目标波动率设多少合适? A:没有标准答案,取决于风险偏好与品种。常见股票组合 10%~20%。关键是回测里检查实际波动是否真的收敛到目标附近(本例 17.48% vs 目标 15%,窗口/上限会影响贴合度)。
Q:需要付费吗?
A:单只/几只日线在免费额度内即可;若要对组合或全市场批量做波动率目标,需要 klines.batch + universes(Starter 及以上),详见官网定价页。
小结
波动率目标仓位的核心是「固定风险而非固定资金」:用目标波动率除以近期已实现波动率定仓位,波动高减、波动低加。用 AlphaFeed 的前复权日线即可完成 T+1 无未来函数的回测——记住它改的是风险暴露不是方向、仓位必须滞后一天、低波动期的”加仓”隐含杠杆假设。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。