欢迎光临
我们一直在努力

多因子轮动系列(二):构建因子池——动量、低波、成交量与趋势强度

摘要: 本文是“多因子轮动”系列的第二篇。上一篇我们剖析了单因子动量的三个核心局限,今天我们将正式动手,为策略引入更多维度的信息。我们将逐一构建四个候选因子:价格动量(保留)、低波动、成交量相对强弱、以及趋势强度。对每个因子,我们都会解释其逻辑、给出计算公式,并用Python代码实现。最后,我们会讨论因子间的相关性,避免选取冗余因子。读完本文,你将拥有一个基础因子库,为下一篇文章的因子合成与策略构建做好准备。


大家好,我是你们的老朋友。

上一篇我们坦诚地聊了单因子动量的“天花板”:它分不清稳健上涨和颠簸上涨,在动量值接近时缺乏分辨力,还完全忽视成交量的验证。这些局限的根源就是信息来源太单一。

从这一篇开始,我们要给策略装上“复眼”——用多个维度的信息综合判断。今天的目标很明确:选定几个候选因子,把它们的计算逻辑和代码都搞扎实。 我们不会急着构建策略,而是先把每一个因子研究透彻,确保它确实能提供动量之外的有效信息。

一、候选因子概览

我们选择因子的原则有三个:逻辑清晰、计算简单、与动量因子相关性低。基于这些原则,首期候选因子池包括四个因子:

  • 价格动量因子(保留):过去N日累计涨跌幅。
  • 低波动因子(新增):波动率越低,得分越高。
  • 成交量因子(新增):近期成交量相对放大的程度。
  • 趋势强度因子(新增):价格相对于均线的位置和均线发散程度。
  • 这四个因子从不同角度刻画ETF的状态:动量看“涨了多少”,低波动看“涨得稳不稳”,成交量看“有没有资金支持”,趋势强度看“趋势健不健康”。组合起来,就能描绘出一幅更立体的图像。

    二、因子一:价格动量因子

    这个因子我们已经用了很久,这里简单回顾一下,并统一参数和计算口径。

    逻辑:过去一段时间涨幅越大的ETF,未来继续上涨的概率越高。这是动量效应的核心假设。

    计算方式:过去N个交易日的累计涨跌幅。我们沿用经典的20日周期。

    # 计算20日动量
    momentum_20 = price_df.pct_change(periods=20)

    # 删除前20日无法计算的行
    momentum_20.dropna(inplace=True)

    pct_change(periods=20) 一步到位,它等价于 (今日收盘价 / 20个交易日前收盘价 – 1)。返回的DataFrame中,每一行是日期,每一列是ETF代码,值就是该ETF当天的20日涨跌幅。

    因子方向:数值越大越好。这是我们的基准因子,后续所有新增因子都要与它配合。

    三、因子二:低波动因子

    这是针对“颠簸上涨”问题引入的因子。

    逻辑:在同等收益水平下,波动率更低的ETF通常趋势更稳健,回撤风险更小,持有体验更好。我们给低波动ETF更高的分数。

    计算方式:先计算过去20个交易日的年化波动率,然后取负值(因为波动率越低我们越喜欢,所以用负值来统一“越大越好”的方向)。

    # 计算每日收益率
    daily_returns = price_df.pct_change()

    # 计算20日滚动年化波动率
    hist_vol = daily_returns.rolling(20).std() * np.sqrt(252)

    # 低波动因子:取负值,即波动率越小,因子值越大
    low_vol_factor = hist_vol

    这里有两个细节需要注意。第一,rolling(20).std() 计算的是滚动标准差,乘以 np.sqrt(252) 转换为年化值,方便理解和比较。第二,取负值是一个简单但关键的技巧——它让低波动因子与动量因子的方向一致,后续合成时不会出现逻辑混淆。

    因子方向:数值越大越好(即波动率越低越好)。

    四、因子三:成交量因子

    成交量因子用来验证价格动量的可靠性。

    逻辑:“量在价先”,放量上涨通常意味着资金在积极介入,趋势更可靠;缩量上涨可能只是被动拉升或技术性反弹。我们给近期成交量相对放大的ETF更高的分数。

    计算方式:用近5日的平均成交量除以近20日的平均成交量,再减1。这样得到的是一个相对变化率,正值表示近期放量,负值表示缩量。

    # 假设 volume_df 是每日成交量DataFrame,列是ETF代码
    # 计算近5日和近20日的均量
    vol_ma5 = volume_df.rolling(5).mean()
    vol_ma20 = volume_df.rolling(20).mean()

    # 成交量相对变化率
    volume_factor = vol_ma5 / vol_ma20 1

    选择5日和20日作为短期和长期窗口,是为了与我们20日动量周期相匹配。volume_factor 是一个围绕0波动的值,正数表示近期成交量高于近期平均水平,负数则相反。

    因子方向:数值越大越好(近期放量越明显越好)。

    数据获取补充:要计算成交量因子,我们需要在数据获取阶段同时拉取成交量数据。如果使用 pytdx,get_security_bars 返回的数据中已经包含 volume 字段;如果用其他数据源,确保能拿到 volume 列即可。

    # 数据获取阶段,保留volume列
    def get_etf_daily_data(hq, code, market, start_date='20180101', end_date='20251231'):
    # … 前面的代码不变 …
    df = df[['date', 'close', 'volume']].set_index('date').sort_index()
    return df

    # 分别构建价格DataFrame和成交量DataFrame
    price_df = pd.concat(...) # 收盘价
    volume_df = pd.concat(...) # 成交量

    五、因子四:趋势强度因子

    这个因子刻画价格相对于均线的位置,以及短期均线与长期均线的发散程度。

    逻辑:处于上升趋势的ETF,其价格应该在均线之上运行,且短期均线高于长期均线。价格在均线上方越多、均线发散越明显,趋势越强。

    计算方式:用两个子指标合成。第一个是价格相对于20日均线的偏离度;第二个是5日均线与20日均线的相对差值。我们将两者相加,得到一个综合的趋势强度度量。

    # 计算均线
    ma5 = price_df.rolling(5).mean()
    ma20 = price_df.rolling(20).mean()

    # 价格偏离度:收盘价相对于20日均线的位置
    price_deviation = (price_df ma20) / ma20

    # 均线发散度:5日均线相对于20日均线的位置
    ma_divergence = (ma5 ma20) / ma20

    # 趋势强度因子:两者之和
    trend_factor = price_deviation + ma_divergence

    price_deviation 捕捉的是价格本身的趋势位置,ma_divergence 捕捉的是短期趋势相对于中期趋势的强度。当价格在20日均线上方运行,且5日均线也在20日均线上方时,trend_factor 为正值;两者都在下方时,为负值;一上一下时,数值在零附近。

    因子方向:数值越大越好(趋势越强越好)。

    六、因子相关性分析:避免冗余

    选好了四个因子,我们还需要确认它们之间没有太高的相关性。如果两个因子高度相关(比如相关系数超过0.8),意味着它们提供的信息高度重叠,保留一个就够了。冗余因子不仅不会提升策略,还会增加过拟合风险。

    我们可以计算四个因子在历史上的平均截面相关系数。以下是一个简化的分析框架:

    # 将四个因子在某一天的值取出,计算相关系数矩阵
    import pandas as pd
    import numpy as np

    # 假设我们有一个日期列表,每个日期计算因子间的相关系数
    corr_list = []
    for date in common_dates:
    # 取出当天所有ETF的四个因子值
    data = pd.DataFrame({
    'momentum': momentum_20.loc[date],
    'lowvol': low_vol_factor.loc[date],
    'volume': volume_factor.loc[date],
    'trend': trend_factor.loc[date]
    })
    corr_matrix = data.corr()
    corr_list.append(corr_matrix)

    # 计算平均相关系数矩阵
    avg_corr = sum(corr_list) / len(corr_list)
    print(avg_corr)

    根据历史经验,动量因子和趋势强度因子之间可能存在中等偏高的相关性(因为两者都基于价格趋势),但通常不会超过0.7。如果确实超过0.8,可以考虑只保留其中一个,或者将两者合并为一个“复合趋势因子”。在首期策略中,我们暂时保留全部四个因子,实际回测时再根据表现调整。

    七、本篇总结

    今天我们完成了多因子轮动的基础工作——构建了一个包含四个因子的候选池:

    因子名称计算方式方向解决的问题
    价格动量 pct_change(20) 越大越好 捕捉趋势延续
    低波动 -rolling(20).std() * sqrt(252) 越大越好 过滤颠簸上涨
    成交量 MA5 / MA20 – 1 越大越好 验证资金支持
    趋势强度 (close-MA20)/MA20 + (MA5-MA20)/MA20 越大越好 评估趋势健康度

    这四个因子各有侧重,组合起来能够从“涨了多少、涨得稳不稳、有没有量、趋势强不强”四个维度综合评判一只ETF。在下一篇文章中,我们将对这些因子进行标准化处理,并合成一个综合分数,嵌入到现有的轮动策略框架中。

    下一篇预告:多因子轮动系列(三)——因子标准化与合成:从多个分数到一个排名

    我们离完整的多因子轮动策略只差最后一步了。准备好将理论变成代码了吗?咱们下篇见!

    赞(0)
    未经允许不得转载:171主机测评 » 多因子轮动系列(二):构建因子池——动量、低波、成交量与趋势强度
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址