← 返回文章列表 风控

波动率目标法的三个陷阱,以及怎么把它调稳

估计窗口、杠杆上限和调仓频率,任何一个没设好都会让它在极端行情里放大亏损。

陷阱一:估计窗口太短

用 10 日滚动标准差估计波动率,响应确实快,但它对单日大跌的反应是「明天把仓位砍一半」。而市场恰恰经常在大跌后的两三天内反弹,于是你系统性地在最低点减仓、在反弹后加仓,每次波动都被收一道税。

# 用半衰期而不是等权窗口,并给波动率估计设下限
vol_fast = port_ret.ewm(halflife=10).std() * (252 ** 0.5)
vol_slow = port_ret.ewm(halflife=60).std() * (252 ** 0.5)

# 取两者较大值:波动上升时快速反应,下降时缓慢恢复
vol = pd.concat([vol_fast, vol_slow], axis=1).max(axis=1)
vol = vol.clip(lower=0.04)   # 防止低波动期算出天量杠杆

非对称响应是有道理的

波动率的经验规律是「上升快、回落慢」。取快慢两个估计的较大值,等于在风险上升时立刻降杠杆、在风险回落时谨慎加回来,这个不对称性和市场本身的性质一致,不算过拟合。

陷阱二:没有杠杆上限

目标波动 12%、实际波动 3% 的时候,公式会告诉你上 4 倍杠杆。低波动期往往正是风险积累期,这时候顶格加杠杆是最糟糕的时点选择。上限必须存在,而且要低于你直觉的数字。

lev = (target_vol / vol).clip(upper=1.5).shift(1)

# 再加一层:单日杠杆变化不超过 20%,避免仓位跳变带来的换手
lev = lev.ewm(alpha=0.2).mean()

陷阱三:调仓过于频繁

每天按最新波动率调整杠杆,会产生大量纯粹由风控引起的换手,而这部分换手不带任何 alpha。实测下来,加一个死区(杠杆变化小于 10% 就不动)能省下三分之一的风控换手,而对回撤控制效果几乎没有影响。

def apply_deadband(lev, band=0.10):
    """死区过滤:杠杆变化不足 band 时保持上期值"""
    out, cur = [], lev.iloc[0]
    for x in lev:
        if abs(x / cur - 1) > band:
            cur = x
        out.append(cur)
    return pd.Series(out, index=lev.index)

调稳之后

三个陷阱都处理完,波动率目标法的价值就回到了它本该有的位置:它不制造 alpha,只是让你的净值曲线在不同市场状态下有可比的风险水平。判断它有没有起作用,看的是实现波动率是否稳定在目标附近,而不是看收益有没有变高。

波动率仓位管理杠杆
下一篇

TWAP 与 VWAP 之外:把执行拆成时间片和价格带