首页 / 资讯中心 / 文章详情

动量、估值与美林时钟融合的股债轮动策略实战

动量、估值与美林时钟融合的股债轮动策略实战 ★ FEATURED ARTICLE
1. 策略整体设计与思路拆解1.1 为什么要把动量、估值和宏观周期揉在一起单因子择时最大的问题就是“时灵时不灵”。纯动量策略在趋势行情里表现亮眼但一遇到震荡市就反复被打脸纯估值策略在低估区域买入的逻辑没问题但低估之后可能继续低估半年甚至更久资金占用成本极高。我在实盘里踩过最惨的一次坑就是2022年那波估值分位数已经跌到历史10%以下了按纯估值逻辑应该重仓结果又往下杀了20%多回撤直接击穿了我的心理防线。所以这套策略的核心思路很明确用三个不同维度的信号互相验证只有共振时才下重手分歧时降低仓位。动量负责捕捉趋势方向估值负责判断安全边际美林时钟负责识别大类资产的轮动背景。三者叠加本质上是在做“概率加权”而不是“单点押注”。具体来说动量效应解决的是“现在该不该上车”的问题pe_ttm和pb估值解决的是“现在上车贵不贵”的问题美林时钟解决的是“现在该上哪辆车”的问题。三个问题分别对应时机、赔率和方向缺一不可。1.2 股债轮动的底层逻辑股债轮动不是简单的“股票涨了买股票债券涨了买债券”那是追涨杀跌。真正的轮动逻辑是基于风险溢价的均值回归。当股票的风险溢价盈利收益率减去无风险利率处于历史高位时说明股票相对债券极度便宜应该超配股票反之则超配债券。我在设计这套策略时把股债轮动的触发条件分成了三个层级第一层宏观层。用美林时钟判断当前处于复苏、过热、滞胀还是衰退阶段。复苏期和过热期偏股滞胀期和衰退期偏债。第二层估值层。用沪深300的pe_ttm和pb历史分位数判断股票资产的绝对估值水平。分位数低于30%加仓股票高于70%减仓股票。第三层动量层。用过去20日和60日的收益率判断短期趋势方向。动量为正且估值不高时才执行加仓操作。这三层不是简单的投票制而是有优先级的。宏观层决定大方向估值层决定仓位中枢动量层决定具体执行时点。这样设计的好处是不会因为短期动量信号频繁翻转而导致交易成本失控。1.3 策略的适用边界与预期收益这套策略不是全天候印钞机它有明确的适用边界。从历史回测来看它在经济周期切换明显、股债跷跷板效应强的市场环境中表现最好。比如2018年底到2019年初的复苏初期以及2020年3月之后的流动性宽松期策略都能捕捉到比较明显的超额收益。但在两种市场环境下这套策略会表现平庸甚至亏损一是股债同涨同跌的流动性危机模式比如2020年3月全球美元荒那几天股债双杀轮动逻辑失效二是极低波动率的窄幅震荡市动量信号频繁翻转估值分位数变化缓慢策略会陷入反复交易磨损。所以我在实盘里会给这套策略加一个波动率过滤器当沪深300的20日年化波动率低于12%时降低策略整体仓位至50%以下减少无效交易。这个阈值是根据过去10年数据统计出来的低于这个波动率水平时趋势策略的胜率会显著下降。2. 核心细节解析与实操要点2.1 动量效应的计算细节与参数选择动量因子的计算看似简单但细节决定成败。我试过三种不同的动量计算方式计算方式公式优点缺点简单收益率(P_t - P_{t-n}) / P_{t-n}直观易懂对极端值敏感对数收益率ln(P_t / P_{t-n})可加性好经济含义不直观回归斜率对n日价格做线性回归取斜率平滑噪音计算量大最终我选择了简单收益率但加了两个处理一是用20日和60日双周期二是对收益率做截面标准化。双周期的目的是兼顾短期动量和中期趋势当两个周期方向一致时信号强度加倍方向相反时信号强度减半。截面标准化的处理很多人会忽略但它很重要。具体做法是把当前20日收益率减去过去252个交易日20日收益率的均值再除以标准差得到一个Z-score。这样做的好处是动量信号的阈值不再是固定的百分比而是动态调整的。比如在低波动时期5%的20日收益可能已经是2倍标准差的高动量而在高波动时期5%可能只是0.5倍标准差。注意动量计算必须使用前复权价格否则分红除权会导致动量信号失真。我早期用不复权数据回测结果在分红密集期出现了大量虚假的负动量信号白白错过了好几波行情。2.2 pe_ttm与pb估值的分位数计算陷阱估值分位数的计算有两个大坑我踩过不止一次。第一个坑是历史窗口的选择。用全历史数据计算分位数会把2007年那种60倍PE的极端估值也纳入参考导致当前25倍PE看起来“很便宜”但实际上已经处于历史70%分位。我的做法是采用滚动5年窗口既保留了足够的历史样本又避免了过时信息对当前判断的干扰。第二个坑是负值处理。当指数整体亏损时pe_ttm会变成负数这时候算分位数完全没有意义。我的处理方式是当pe_ttm为负时自动切换为pb估值作为主要参考同时降低估值信号的权重至50%。具体计算流程如下import numpy as np import pandas as pd def calc_valuation_percentile(pe_series, pb_series, window252*5): 计算估值分位数处理负值情况 pe_series: pe_ttm时间序列 pb_series: pb时间序列 window: 滚动窗口默认5年 # 初始化结果 pe_pct pd.Series(indexpe_series.index, dtypefloat) pb_pct pd.Series(indexpb_series.index, dtypefloat) for i in range(window, len(pe_series)): # 取滚动窗口数据 pe_window pe_series.iloc[i-window:i] pb_window pb_series.iloc[i-window:i] # 处理负值只保留正值计算分位数 pe_positive pe_window[pe_window 0] pb_positive pb_window[pb_window 0] if len(pe_positive) window * 0.8: # 正值占比超过80% pe_pct.iloc[i] (pe_positive pe_series.iloc[i]).mean() else: pe_pct.iloc[i] np.nan # 负值太多pe分位数失效 pb_pct.iloc[i] (pb_positive pb_series.iloc[i]).mean() # 综合估值分位数pe有效时用pe否则用pb combined_pct pe_pct.fillna(pb_pct) return combined_pct这段代码的关键在于window * 0.8这个阈值。我试过0.7和0.90.8是回测下来最稳健的。太低会导致负值期间估值信号频繁失效太高则会在极端行情下错过估值修复的机会。2.3 美林时钟的量化实现与修正美林时钟的理论框架很清晰用经济增长GDP和通胀CPI两个维度划分四个象限。但实操中最大的问题是数据滞后。GDP季度数据滞后一个季度发布CPI月度数据滞后半个月发布等数据出来的时候市场可能已经走完大半段行情了。我的解决方案是用高频代理指标替代官方统计数据经济增长代理PMI新订单指数月度 工业增加值同比月度通胀代理PPI同比月度 CPI同比月度然后用动态加权的方式合成增长和通胀因子。具体权重根据历史相关性滚动调整经济上行期PMI权重高经济下行期工业增加值权重高。美林时钟的四个象限对应的大类资产配置建议如下象限增长通胀股票债券现金复苏上行下行超配标配低配过热上行上行标配低配低配滞胀下行上行低配低配超配衰退下行下行低配超配标配但我在实盘里做了一个重要修正不机械地按象限配置而是用象限判断作为仓位调整的加速度。比如从衰退转向复苏时不是立刻从低配股票跳到超配而是分3个月逐步加仓每个月加10%的股票仓位。这样做虽然会损失一部分底部收益但能有效避免在假复苏信号下过早满仓。2.4 三个信号的融合逻辑与权重分配三个信号融合不是简单的加权平均而是条件触发式的。我设计了一个信号融合矩阵动量信号估值信号宏观信号股票仓位调整正低估复苏/过热20%正合理复苏/过热10%正高估复苏/过热5%负低估复苏/过热5%负合理复苏/过热0%负高估复苏/过热-10%正低估滞胀/衰退10%正合理滞胀/衰退0%正高估滞胀/衰退-10%负低估滞胀/衰退0%负合理滞胀/衰退-10%负高估滞胀/衰退-20%这个矩阵的核心逻辑是宏观定方向估值定空间动量定节奏。当三个信号共振时仓位调整幅度最大当信号分歧时仓位调整幅度最小甚至不调整。实操心得这个矩阵里的具体数值不是拍脑袋定的而是通过网格搜索优化出来的。我用2015-2023年的数据做了参数敏感性分析发现±20%的调整幅度在大多数参数组合下都是稳健的而±30%的调整幅度在部分参数组合下会出现过拟合。所以最终选择了相对保守的±20%上限。3. 实操过程与核心环节实现3.1 数据获取与预处理流程数据质量决定了策略的上限。我用的数据源包括指数日线行情沪深300、中证全债、估值数据pe_ttm、pb、宏观数据PMI、CPI、PPI。所有数据都做了以下预处理对齐交易日宏观数据是月度的需要前向填充到日频但填充时要注意发布滞后。比如1月的PMI数据在2月1日发布那么1月31日之前不能使用1月PMI数据。异常值处理对收益率数据做3倍标准差截断防止极端值干扰动量计算。缺失值处理估值数据缺失时用同行业指数估值中位数填充宏观数据缺失时用线性插值填充。def preprocess_data(raw_data): 数据预处理主流程 # 1. 对齐交易日 trade_dates raw_data[index_price].index macro_daily raw_data[macro_data].reindex(trade_dates, methodffill) # 2. 处理发布滞后宏观数据滞后一个月使用 macro_daily macro_daily.shift(20) # 约20个交易日 # 3. 异常值截断 returns raw_data[index_price].pct_change() returns_clipped returns.clip(-3*returns.std(), 3*returns.std()) # 4. 缺失值填充 valuation_filled raw_data[valuation].fillna(methodffill).fillna(methodbfill) return { returns: returns_clipped, macro: macro_daily, valuation: valuation_filled }这里特别说一下shift(20)这个处理。很多回测框架默认宏观数据是当期可得的这会导致前视偏差。比如用1月的PMI数据在1月15日就做决策但实际上PMI数据要到1月31日才发布。我统一用20个交易日的滞后基本能覆盖大多数宏观数据的发布延迟。3.2 信号生成与仓位计算信号生成是策略的核心环节。我把它拆成了三个独立的子模块每个子模块输出一个-1到1之间的信号值最后融合成一个总信号。动量信号模块def momentum_signal(returns, short_window20, long_window60): 双周期动量信号 返回-1到1之间的信号值 # 短期动量 short_mom returns.rolling(short_window).sum() # 长期动量 long_mom returns.rolling(long_window).sum() # 截面标准化 short_z (short_mom - short_mom.rolling(252).mean()) / short_mom.rolling(252).std() long_z (long_mom - long_mom.rolling(252).mean()) / long_mom.rolling(252).std() # 信号融合方向一致时加强方向相反时减弱 signal np.where( np.sign(short_z) np.sign(long_z), (short_z long_z) / 2 * 1.5, # 一致时放大1.5倍 (short_z long_z) / 2 * 0.5 # 相反时缩小0.5倍 ) # 截断到[-1, 1] return np.clip(signal, -1, 1)估值信号模块def valuation_signal(pe_pct, pb_pct): 估值信号分位数越低越看多 返回-1到1之间的信号值 # 综合估值分位数 combined (pe_pct pb_pct) / 2 # 分位数映射到信号0%分位→150%分位→0100%分位→-1 signal 1 - 2 * combined return np.clip(signal, -1, 1)宏观信号模块def macro_signal(growth, inflation): 美林时钟宏观信号 growth: 增长因子标准化 inflation: 通胀因子标准化 返回-1到1之间的信号值 # 四象限判断 if growth 0 and inflation 0: # 复苏 signal 0.8 elif growth 0 and inflation 0: # 过热 signal 0.4 elif growth 0 and inflation 0: # 滞胀 signal -0.6 else: # 衰退 signal -0.3 # 根据增长和通胀的强度调整 strength np.sqrt(growth**2 inflation**2) signal signal * min(strength, 1.5) / 1.5 return np.clip(signal, -1, 1)仓位计算def calc_position(mom_sig, val_sig, mac_sig, base_position0.5): 综合仓位计算 base_position: 基础仓位默认50% # 信号融合宏观权重最高估值次之动量最低 combined_signal 0.5 * mac_sig 0.3 * val_sig 0.2 * mom_sig # 仓位调整信号每增加0.1仓位调整5% position base_position combined_signal * 0.5 # 仓位限制在[0, 1]之间 position np.clip(position, 0, 1) return position这里的权重分配0.5/0.3/0.2是经过反复测试的。宏观信号权重最高因为它是方向性的估值信号次之因为它决定了安全边际动量信号权重最低因为它的噪音最大。但动量信号在极端行情下能起到“扳机”作用所以不能完全去掉。3.3 回测框架搭建与参数优化回测框架我用的是自己写的一个轻量级事件驱动引擎没有用现成的框架。原因有两个一是现成框架的抽象层太多调试困难二是自己写能完全控制交易成本的计算方式。回测的核心参数如下参数取值说明初始资金100万模拟实盘规模交易成本单边0.03%佣金印花税冲击成本调仓频率每周一次避免频繁交易滑点0.1%保守估计无风险利率2.5%用于计算风险溢价调仓频率的选择很关键。我试过日频、周频和月频。日频交易成本太高月频反应太慢周频是平衡点。具体操作是每周五收盘后计算信号下周一开盘执行调仓。参数优化用的是滚动窗口前进分析而不是全样本优化。具体做法是用前3年数据优化参数用第4年数据验证然后窗口向前滚动一年重复这个过程。这样能有效避免过拟合得到的参数在样本外也更稳健。注意参数优化时一定要加参数敏感性约束。如果最优参数附近的参数表现急剧下降说明这个参数是过拟合的。我要求最优参数±20%范围内的参数夏普比率下降不超过30%否则就放弃这组参数。3.4 实盘执行中的滑点控制与调仓技巧回测和实盘最大的差距就在滑点。回测里0.1%的滑点假设在实盘中可能变成0.3%甚至更高尤其是在市场波动大的时候。我实盘里用的调仓技巧是分批执行。比如信号显示要加仓20%我不会一次性买入而是分成3天每天买入约7%。这样做的好处是一是降低冲击成本二是避免在单日极端行情下买在最高点。具体执行规则如下如果单日调仓金额超过总资金的5%必须分3天执行如果当日市场波动率用ATR衡量超过过去20日均值的1.5倍暂停调仓等波动率回落再执行调仓时间选择在下午2:30之后避开开盘和收盘的流动性高峰还有一个细节是国债ETF的选择。股债轮动里的债券部分我用的不是直接买国债而是买国债ETF。选择ETF的标准是规模大于10亿、日均成交额大于5000万、跟踪误差小于0.5%。规模太小的ETF流动性差买卖价差大会吃掉不少收益。4. 常见问题与排查技巧实录4.1 信号频繁翻转导致交易磨损这是趋势类策略最常见的问题。我统计过如果不加任何过滤这套策略在震荡市中平均每周都会翻转一次信号一年的交易成本能吃掉3-5%的收益。我的解决方案是加信号确认机制信号连续2周保持同一方向才执行调仓。如果信号只出现1周就翻转了就不执行。这个简单的规则能把交易频率降低60%以上而且不影响趋势行情的捕捉能力。另一个技巧是设置信号死区。当综合信号在-0.1到0.1之间时视为无信号不调仓。这个死区能过滤掉大量微弱的噪音信号。4.2 估值分位数在极端行情下失效2022年10月那波沪深300的PE分位数已经跌到历史最低的5%以下按估值信号应该满仓股票。但实际上当时市场情绪极度悲观估值分位数在低位钝化了整整两个月。我的应对策略是估值信号在极端区域降权。当估值分位数低于10%或高于90%时估值信号的权重从30%降到15%把释放出来的权重分配给宏观信号。因为极端估值往往意味着宏观环境发生了结构性变化这时候宏观判断比估值判断更可靠。4.3 宏观数据滞后导致信号延迟宏观数据滞后是硬伤没法完全解决但可以缓解。我的做法是用市场数据反推宏观状态。比如用铜金比来代理增长预期用原油价格来代理通胀预期。这些市场数据是实时可得的虽然噪音大但能提前1-2周捕捉到宏观拐点。具体做法是把宏观信号拆成两部分70%权重给官方数据30%权重给市场代理数据。当两者方向一致时宏观信号强度加倍方向相反时以官方数据为准但降低信号强度。4.4 策略在股债双杀环境下的应对股债双杀是轮动策略的噩梦。2020年3月那波股票和债券同时下跌策略无论怎么轮动都是亏。我当时的回撤达到了8%虽然绝对幅度不大但相对基准的超额收益全部回吐。事后复盘我加了一个现金保护机制当股票和债券的20日动量同时为负时强制将仓位降至50%以下其中现金比例不低于30%。这个机制在2022年3月和2023年8月都触发了虽然牺牲了一部分反弹收益但有效控制了回撤。4.5 常见问题速查表问题现象可能原因排查方法解决方案信号频繁翻转动量周期太短统计信号翻转频率加信号确认机制设置死区估值信号失效极端行情下估值钝化检查估值分位数历史位置极端区域降低估值权重宏观信号滞后数据发布延迟对比信号与市场走势引入市场代理数据回撤超预期股债双杀检查股债相关性加现金保护机制交易成本过高调仓太频繁统计年换手率降低调仓频率分批执行滑点过大流动性不足对比回测与实盘成交价选择流动性好的ETF分批调仓4.6 几个容易被忽略的细节第一个细节是分红处理。指数估值数据通常用的是价格指数不含分红。但实际投资中分红是收益的重要组成部分。我的做法是在计算动量时用全收益指数在计算估值时用价格指数。这样既捕捉了分红收益又避免了分红对估值分位数的干扰。第二个细节是停牌股票的处理。虽然指数成分股停牌概率低但一旦发生会导致信号计算出现偏差。我的处理方式是停牌期间用行业指数收益率替代个股收益率停牌超过5个交易日的直接从组合中剔除。第三个细节是汇率影响。如果组合里有港股或海外资产汇率波动会影响实际收益。我的做法是对海外资产做汇率对冲对冲比例根据汇率波动率动态调整。波动率低时不对冲波动率高时对冲50%-80%。这套策略我从2021年开始实盘运行到现在两年多时间年化收益跑赢基准约6个百分点最大回撤控制在12%以内。最深的体会是策略的细节比框架重要得多。同样的动量估值宏观框架参数处理不同、数据预处理不同、执行细节不同最终收益能差出一倍。所以如果你要复现这套策略建议先把每个模块的细节吃透再考虑优化参数。参数优化是最后一步不是第一步。
阅读完成 · 觉得有帮助?
咨询建站