回撤控制到底该放在信号层还是仓位层
两种做法在同一份历史上的对比,以及为什么波动率目标法更容易过拟合。
两种放法
信号层的做法是:在市场状态不好时直接让信号失效,比如加一个「指数在 200 日均线之下就不开仓」的门。仓位层的做法是:信号照常产生,但整体仓位按波动率或当前回撤幅度缩放。
两者在净值曲线上可能长得很像,但性质完全不同。信号层的过滤会改变持仓结构,仓位层的缩放只改变杠杆。
# 仓位层:波动率目标 target_vol = 0.12 port_ret = (w * ret).sum(axis=1) realized = port_ret.ewm(halflife=20).std() * (252 ** 0.5) lev = (target_vol / realized).clip(upper=2.0).shift(1) w_scaled = w.mul(lev, axis=0) # 信号层:状态过滤 regime = index_close > index_close.rolling(200).mean() w_filtered = w.where(regime.shift(1), 0.0)
为什么要 shift(1)
波动率和状态判断都用到了当日收盘信息,直接乘上去等于用今天的收盘决定今天的仓位。所有派生自价格的控制变量都要滞后一期,这是回撤控制里最常见的未来函数来源。
同一份历史上的对比
在 2015 到 2025 的 A 股宽基上做同样的动量策略,两种控制方式的差别集中在极端行情:
-11.6%
波动率目标·最大回撤
-9.2%
状态过滤·最大回撤
1.32
波动率目标·夏普
1.08
状态过滤·夏普
状态过滤把最大回撤压得更低,但夏普也掉了两成——因为它在每次假信号上都完整错过了反弹的前两周。波动率目标法保留了大部分收益,代价是回撤发生得更频繁但更浅。
为什么波动率目标更容易过拟合
它有三个自由参数:目标波动率、估计窗口、杠杆上限。三个参数在历史上做网格搜索,几乎一定能找到一组把 2015 和 2022 两次大跌都躲过去的组合,而这组参数没有任何理由在下一次生效。
务实的做法是把目标波动率定成一个和你的资金约束相关的外生数字(比如你能承受的年化波动就是 12%),然后只检查估计窗口在 10 到 60 天之间结果是否稳定,不做优化。
下一篇专门拆波动率目标法的三个陷阱,以及每个陷阱对应的修法。
回撤仓位管理风控