第5周测试:我来复现“因子切割论”,想怎么切就怎么切!
  学满释放 17天前 129 0

一 通过自然语言构建因子

1.1 导入市值中性化模板

图片1.png

  • 注:模板可以找小助理要,市值中性化模板的python源代码我将贴在文章末尾;
  • 为什么要市值中性化
  • 市值中性化是为了在因子选股回测的时候(不属于因子挖掘时候使用)防止选到的股票集中在固定的某些股票当中。
  • 通俗的说:你以为挖到了alpha,但其实是小市值风格因子的另一种体现。

1.2 点击python代码输入,让AI生成因子

  • 问AI:你知道“因子切割论“吗,将这里的动量因子,用因子切割论的方法,使用成交量来切割。其他不变。
    图片2.png
  • 点击:确认应用+保存代码

1.3 运行工作流,查看效果。因子预测效果非常不好!

图片3.png

  • 几乎没有预测能力:IC和RankIC都约等于0,分组也不具备单调性。
  • 为什么呢

1.4 查看AI写的因子源代码,发现它理解错了!

  • AI说:你提到的“因子切割论”,通常可以理解为:
    先用一个“解释因子”(这里是成交量)去解释、“切割”目标因子(这里是动量),得到对该解释因子正交的残差部分;然后再在这个被切割后的因子上做后续处理(你原来是再对市值做正交)。 基于你给的原代码,我只做了一件核心改动:在“动量 → 市值正交”的原流程前面,插入了一步“成交量切割动量”,其他保持不变。
  • 我说这里的因子切割论,你理解错了!!!
  • 我说:因子切割论是:比如将20日内,每天涨跌幅(动量)按照成交量排序,取成交量排名前10的涨跌幅累加,再取后排名后10日涨跌幅累加。将两个累计涨跌幅相除。按照这个思路,修改代码,其他不变。
  • 让AI根据我的输入,再次修改代码。
    图片4.png

1.5 一次没跑通,报错了,AI根据日志一键智能修复。

图片6.png

1.6 跑通了,稍微有点预测能力,但是效果还是不好!

图片7.png

  • 效果不好体现在:IC、Rank IC 约等于0,就没有预测能力!
  • 我的思考:在A股,动量因子已经被广泛应用,因此不容易找到Alpha。
  • 那么如何魔改因子切割论呢? 答案是:你想怎么切就怎么切!
  • 具体表现为:可以改造切分主体、切分工具。
  • 比如:
    • 被切分的对象:可以是动量、波动率、成交量的变化率等等
    • 切分的工具:可以是成交量、上下影线对实体占比、技术指标KDJ的J值等等。

1.7 切分对象、切分工具,我都修改了。效果提升明显!

图片8.png

  • RankIC 绝对值从 0.0 => 0.05。
  • |ICIR| 绝对值超过 0.5。
  • 5分组单调性明显,t统计量绝对值超过2.0.

补充:因子挖掘需要我们广泛开动脑筋,多阅读机构研报,多去尝试!复制下方市值中性化的代码,快去试试吧!

  • AI提示词:因子切割论:比如将20日内,每天涨跌幅(动量)按照成交量排序,取成交量排名前10的涨跌幅累加,再取后排名后10日涨跌幅累加。将两个累计涨跌幅相除或相减。
import pandas as pd import numpy as np class MomentumMarketCapOrthogonalFactor(Factor): def _to_series(self, x, index, name=None): """强制把 ndarray / DataFrame / Series 转成 Series,并对齐索引""" if isinstance(x, pd.Series): s = x elif isinstance(x, pd.DataFrame): if x.shape[1] == 1: s = x.iloc[:, 0] else: s = pd.Series(x.values.ravel(), index=index) else: s = pd.Series(np.asarray(x).reshape(-1), index=index) if name is not None: s.name = name return s def calculate(self, factors): close = factors['close'] # Series, MultiIndex: (date, symbol) mcap = factors['market_cap'] # Series, MultiIndex: (date, symbol) idx = close.index # --- Step 1: 动量(20日对数收益) try: momentum_raw = RETURNS(close, 20) # 如果你有内置函数 except NameError: momentum_raw = np.log(close).groupby(level='symbol').diff(20) momentum = self._to_series(momentum_raw, index=idx, name='momentum') # --- Step 2: 截面 rank ranked_mom = momentum.groupby(level='date').rank(pct=True) ranked_mcap = (np.log(self._to_series(mcap, idx, 'mcap')) .groupby(level='date').rank(pct=True)) # --- Step 3: 去均值(消除截距) y = ranked_mom - ranked_mom.groupby(level='date').transform('mean') x = ranked_mcap - ranked_mcap.groupby(level='date').transform('mean') # --- Step 4: 每日截面 OLS 回归系数 num = (x * y).groupby(level='date').transform('sum') den = (x * x).groupby(level='date').transform('sum') beta = (num / den.replace(0, np.nan)).fillna(0.0) # --- Step 5: 残差(正交化后的动量) resid = y - beta * x resid.name = 'mom_orth_to_mcap' return resid
最后一次编辑于 15天前 0

暂无评论

推荐阅读
  18764136999   11天前   59   0   0 中频交易