ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

量化因子挖掘实战:从分钟收益序列到滚动权重与因子治理

量化因子挖掘实战:从分钟收益序列到滚动权重与因子治理 做量化交易这些年我把大部分研究时间都花在了一个环节上因子。不是下单接口不是回测框架而是“到底用什么特征去预测下一段行情”。尤其在ptrade这种券商端的策略交易平台上很多人已经跑通了几套基础策略但收益曲线一直不稳定换参数就换结果核心痛点往往不是代码而是因子没有逻辑、没有区分度。第166期我专门把三个实用的因子挖掘思路拿出来拆一遍日内分钟收益率的时序特征提炼、滚动单因子的自适应权重以及用因子分析法和因子图优化来治理因子池的冗余问题。这期内容适合两类人。一类是刚开始接触ptrade、已经会写简单均线策略但想往量化研究深水区走的新手另一类是因子池越来越大、写了一大堆信号但不知道怎么组合和取舍的研究员。我会把每个思路的适用场景、构造方式、落地代码片段和踩坑代价都讲清楚尽量做到你看完就能在自己的研究框架里试一版。1. 三个因子挖掘思路的底层逻辑——在ptrade上做研究先找到“抓手”1.1 ptrade的本质策略试验台不是研究终端先说清楚ptrade的定位。它是恒生电子推出的策略交易终端运行的是Python环境提供行情、回测、模拟交易和实盘交易接口最大的特点是和券商柜台系统直接打通。这意味着你在ptrade里写好的策略可以比较顺畅地从回测切到实盘尤其适合做日内、中低频的股票和期货策略。但我和很多同行交流下来大家对ptrade最容易产生一个误解想在ptrade里完成所有研究环节。实际上ptrade更像是“策略试验台”它的强项是行情获取、信号触发和订单执行而不是专业级的因子研究。你可以在里面做快速验证但要深挖因子、做复杂的统计检验还是得靠外部的研究框架。所以我在做因子挖掘时通常把流程拆成两段先用Python生态pandas、numpy、statsmodels这些工具做研究研究出有稳定逻辑的因子后再到ptrade里写成策略做回测和实盘落地。这个习惯非常重要因为如果你一开始就把因子研究绑死在ptrade的API上你会发现自己被接口限制得很厉害。比如有些统计方法在ptrade里实现起来很别扭数据预览、缺失值处理、因子间的相关性分析都不如自己搭建研究环境来得好用。1.2 因子研究的三种基本切面时序、截面与动态权重回到因子挖掘本身。我个人习惯把因子分成三个切面去思考这也是本期三个思路的底层框架时序切面只看单个资产过去一段时间的数据比如过去N分钟收益率的特征、波动率聚集特征、量价背离特征。这类因子解决的是“这个资产自身接下来会不会涨”的问题。截面切面在某个时间截面比较所有资产之间的相对关系比如PE、PB、动量排名、分析师预期差。这类因子的逻辑是“如果同类资产中A比B更便宜/更强那么A接下来的收益大概率超过B”。动态权重切面不是设计一个新的预测信号而是思考“手上现有的几个因子在不同市场环境下应该如何分配权重”。比如行情趋势明显时多给动量因子权重震荡行情中多给反转因子权重。对应到本期内容上思路一属于时序切面思路三属于截面切面和因子治理思路二则是动态权重切面。三者不是互相替代的关系而是可以串联成一个完整的因子研究闭环。这也是我为什么要把它们放在一期里讲——很多新手研究因子是一个一个孤立地看挖一个、测一个、丢掉一个最后什么也没积累下来。真正高效的做法是先建立这个三层框架再往里面填充具体的因子。1.3 先搞清楚你要挖的是哪一类“因子”还有一个容易被忽视的问题你挖的“因子”到底是要用于横截面选股还是用于时序择时这两者虽然都叫因子但验证方式完全不一样。横截面因子的检验核心是Rank IC排序相关系数和多空组合收益。你要看的是每个时点按因子值排序后排名靠前的股票是不是系统性跑赢排名靠后的股票。时序因子的检验核心是IC和时间序列收益。你要看的是因子值和标的未来收益率之间的相关性是否稳定是否在某些市场环境下突然失效。ptrade里很多策略属于时序择时类比如均线策略、突破策略那你就应该重点用时序的思路去挖因子。如果你做的是股票池轮动、多因子选股那就要用截面因子检验的方法。这两个方向搞混了后面全都会乱套。2. 思路一日内分钟收益率的时序特征——从盘口噪音里提炼Alpha这个思路一开始是受到开源金工那篇关于“日内分钟收益率的时序特征逻辑讨论与因子增强”的报告启发后来我在自己的策略里反复验证发现这确实是散户可以研究、且很容易被忽视的一块领域。2.1 为什么选择分钟线而不是日线很多刚做量化的朋友会问为什么非要挖分钟收益率的时序特征日线数据不是更稳定吗这里有个关键认知日线级别的信息已经被市场充分定价了。当你看日线时你看到的是当天所有交易者博弈后的最终结果大量微观信息被“平均”掉了。而分钟线上的收益率序列保留了市场参与者一天之内情绪的变化过程比如高开后回落、尾盘拉升、盘中突然放量这些短期行为背后往往隐藏着可以统计的规律。我并不是说日线不能挖因子而是说在ptrade这类平台做中低频策略日线因子的拥挤度已经很高了类似动量、反转、均线偏离这类信号大家用同一套数据、同一种算法研究出来的东西高度同质化交易对手也会越来越聪明。相比之下分钟收益率时序特征的门槛更高处理不当会有噪音但一旦找到稳定规律Alpha的持续性会好不少。2.2 具体怎么构造动量、反转与收益分布的形态刻画先把最容易上手的构造方法说清楚。假设你取一只股票日内每分钟的收益率序列按照每5分钟聚合一次可以构造三类特征第一类是日内动量/反转特征。比如开盘后前30分钟的累计收益率和前一日尾盘30分钟的累计收益率。经验规律是某些市场环境下开盘半小时的强势会在全天延续动量某些环境下开盘半小时的过度反应会在全天被修正反转。关键是你不能凭感觉判断今天该用动量还是反转你要通过滚动窗口去统计哪种效应在当前市场阶段更占优。第二类是波动率聚集特征。日内分钟收益率有一个很典型的现象大波动之后跟着大波动小波动之后跟着小波动。你可以计算过去60分钟内收益率的方差、平均绝对偏差、以及高波动持续的时间比例这类特征对接下来一段时间的收益预测有一定帮助因为它们本质上刻画了市场的参与热情。第三类是收益率分布的形态特征。把过去N个分钟收益率做一个分布统计看它的偏度、峰度、以及大于某个阈值比如2倍标准差的次数。这类特征可以捕捉到“温和上涨”和“剧烈拉升”之间的区别——同样是涨温和放量、缓慢推升和突然拉升所预示的后续走势完全不同。我实际用下来最有区分度的特征组合通常是早期动量 尾盘反转 波动率状态变化。但请注意不同股票、不同板块、不同市值区间里这些特征的显著性差异非常大一定不能拿全市场的结论直接套到某个小票上。2.3 ptrade上的落地片段先跑通“特征计算”这层再想信号在ptrade里做实验时我推荐先把特征计算封装成独立的函数先用历史数据离线验证再放到handle_data里做实时信号。下面是一个简化示例展示如何计算“开盘30分钟累计收益”和“盘中波动率状态”两个特征# ptrade风格的关键代码示意 def init(context): context.symbol 600000.SS context.minute_window 30 context.vol_lookback 12 # 12个5分钟k线 def handle_data(context, data): # 获取当日分钟线这里使用ptrade的get_price传入频率参数 bars get_price(context.symbol, count48, frequency1m, fqpre) if len(bars) 48: return # 计算开盘后第30分钟的累计收益 first30_return bars[close][29] / bars[close][0] - 1 # 计算过去60分钟收益率的波动率 minute_returns bars[close].pct_change().dropna() recent_vol minute_returns[-12:].std() # 根据阈值把波动率状态映射为0/1特征 vol_state 1 if recent_vol minute_returns.std() else 0 # 这里只是把特征打印出来验证实际策略中会结合其他信息生成信号 log.info(first30_return%s, vol_state%s, first30_return, vol_state)这段代码我在真实跑的时候发现一个坑ptrade返回的K线数据里close序列的开头位置要特别注意第0根K线是当天第一分钟还是昨天最后一分钟取决于你取数的起始时间和right参数。如果不仔细核对你计算出来的“开盘30分钟收益”实际上是包含前一日尾盘的信号就会偏差很厉害。我建议不管用什么接口第一步永远是把拿到的K线前几根的时间戳打印出来确认对齐方式。2.4 实操心得分钟数据的清洗决定了这个思路的生死分钟收益率时序特征的挖掘真正的大头不在算法而在数据清洗。我遇到过的情况包括涨跌停导致某段时间分钟收益率全是0如果直接把0当作正常值统计波动率特征会被严重低估停牌复牌后的断点会把前后两天连接成一根长的K线导致计算出的收益出现虚假的极端值某些小盘股一天只有很少几笔成交分钟收益率非0跳变很大分布特征会骗人。我的处理习惯是先做停牌剔除再对涨跌停时段的分钟收益率做标记不纳入统计或单独设置虚拟收益最后对极端值做缩尾处理。这些清洗步骤看起来琐碎但在回测中影响非常大不做清洗的因子回测结果往往是乐观且不可复现的。3. 思路二滚动单因子的自适应权重——让因子“跟着行情走”第二个思路和热搜词“滚动单因子”直接相关。你可能在研究报告里见过这个词但它通常有两种理解一种是计算因子值时用滚动窗口一种是因子的权重用滚动方式动态调整。这里我重点讲后者因为它是把多因子策略从“死板”变“灵活”的关键。3.1 固定权重的最大盲区因子有效性会漂移很多人在ptrade里做多因子策略时用的是最简单的方式选三到五个因子每个因子取等权合成一个总分然后按总分排序选股。这种做法的第一个问题是因子的有效性不是一成不变的。某些季度动量因子有效某些季度反转因子有效如果你固定等权表现差的因子会持续拖累整个组合。一句话总结固定权重等于假设因子在未来和过去一样有效。这个假设在大多数市场状态下都不成立。因子有效性是会漂移的权重不能从一而终。3.2 滚动窗口怎么选本质是平衡“信息量”和“时效性”滚动单因子自适应的基本思路是在每个调仓时点回看过去一段时间比如说20个交易日因子的表现用这个历史表现来决定下一期每个因子的权重。表现好的因子权重上调表现差的因子权重下调。这里最关键的参数是滚动窗口长度。窗口太短比如5天权重会对最近几天的噪音过度敏感今天这个因子好就猛加权明天它一回调就立刻被减掉窗口太长比如120天权重切换又太慢行情风格已经切换两周了你还在给旧风格赋值高权重。我的经验是滚动窗口的选择要和因子本身的换手周期匹配。如果你的因子是做月度调仓的滚动窗口至少要用过去60个交易日来估计因子IC大约一个季度如果你的因子是做周度调仓的窗口可以缩短到20到40个交易日。另外窗口长度还有一个次要因素要考虑你的样本量够不够。因子IC的估计需要足够的截面样本如果池子里只有30只股票20天窗口算出来的IC均值几乎没有统计显著性。3.3 滚动IC均值加权一个可以直接抄作业的框架整个滚动加权的过程在ptrade里可以用模块化的函数来实现。核心步骤如下# 伪代码滚动IC均值加权框架 def calc_rolling_ic(hist_factors, hist_returns, window40): hist_factors: {因子名: 历史因子值DataFrame} hist_returns: 未来N日收益DataFrame ic_dict {} for factor_name, factor_df in hist_factors.items(): # 每个调仓截面计算一次Spearman相关系数 ic_series factor_df.apply(lambda row: spearmanr(row, hist_returns.loc[row.name])[0], axis1) ic_series ic_series.replace([np.inf, -np.inf], np.nan).dropna() rolling_ic ic_series.rolling(window).mean().iloc[-1] ic_dict[factor_name] rolling_ic return ic_dict def assign_weights(ic_dict, decay0.8): 将IC映射为权重, 可按IC大小线性加权, 也可以使用衰减系数 ic_values pd.Series(ic_dict) ic_values ic_values[ic_values 0] # 抛弃近期IC为负的因子 if ic_values.empty: return None # 使用softmax风格或归一化, 让正IC因子按比例分配权重 weights (ic_values ** decay) / (ic_values ** decay).sum() return weights代码里我做了两件很实用的事。第一把近一期IC小于等于0的因子直接剔掉它不但没有正向信息还在拖累组合。第二用(IC ** decay)做权重变换decay小于1时可以压缩大小IC之间的差距避免单期极端IC导致的权重暴涨暴跌。这些细节才是决定策略稳定性的关键。3.4 防止过拟合自适应权重不是“追涨杀跌”滚动自适应权重有一个容易走偏的地方它本质上是在用滚动IC去学习因子的风格切换规律但如果你让这个切换速度太快它就会变成“追涨杀跌”。举个例子。动量因子过去5天表现很好你立刻把它的权重提到80%结果第6天市场风格突变动量因子大跌权重已经来不及调整了组合损失惨重。所以我在实际设计中会加两个保护措施权重上限约束。单个因子权重不超过40%防止单一因子主导组合切换阈值约束。只有当一个因子的滚动IC连续两周跑赢另一个因子超过一定幅度时才允许大幅调整权重否则保持原有权重。这两个保护措施的本质是承认我们观测到的因子表现有噪音不要因为短期的表现就大幅修改模型。自适应权重的目标不是追求每次调仓的最优而是避免权重长期处于错误状态。4. 思路三因子分析法与因子图优化——治理因子池的冗余第三个思路来自热搜词里的“因子分析法”和“因子图优化”。如果你做量化到了一定阶段手里可能攒了十几个甚至几十个因子这时候最重要的问题已经不是“找新因子”而是“把你已有的因子组合干净”。4.1 因子池膨胀之后的三个典型症状因子太多第一个症状是共线性。动量因子和均线偏离因子看起来很不一样实际上和过去收益高度重叠价值因子里的账面市值比、盈利收益率、股息率互相之间相关性极高。如果不处理等权合成时相当于给同一个底层信息重复加了权重。第二个症状是拥挤风险。当很多交易者使用相似的因子因子的收益会逐渐被收窄。这时候单纯靠堆因子数量是没用的你的因子池里表面上有30个因子真正的有效信息可能是5到6个维度。第三个症状是归因困难。因子多了以后单笔亏损根本说不清楚到底是哪个因子在亏钱、哪个因子在赚钱。做归因时一团乱麻策略优化也就无从谈起。4.2 因子分析法怎么用降维但不等于丢弃可解释性因子分析法Factor Analysis是一种降维方法它在量化中的应用方式和主成分分析PCA类似但有区别主成分分析更强调提取方差最大的成分因子分析更强调找到能解释变量之间相关关系的潜在结构。对初学者来说我不建议一上来就用复杂的因子分析模型。我推荐的做法是先用主成分分析做快速体检计算全部因子的相关矩阵对相关矩阵做特征分解看前几个特征值的累计贡献率如果前3个主成分已经解释了80%以上的方差说明真正独立的因子维度很少你的因子池严重冗余。然后在此基础上可以用因子旋转比如Varimax旋转来让每个主成分更容易被解释看看它们分别对应哪个风格方向这样你就知道这个因子池里到底有几个“不可合并的分组”。这里顺带提醒一下网上的热搜词“劈因子法解方程”是数学里解多项式方程的一种手算技巧和量化交易里的“因子”不是一回事大家在搜索的时候不要被带偏了。4.3 因子图优化把因子关系当成“社交网络”看待因子图的思路是我最近用得比较顺手的一个方法。它把每个因子看成一个节点因子之间的相关性看成连边相关性超过某个阈值的两个因子之间就画一条边然后通过社区发现算法来划分“因子族群”。具体操作如下我用因子历史时间序列两两计算Spearman相关系数相关系数绝对值大于0.6的保留一条边阈值太高会丢掉有效关联太低会把所有因子连成一个巨大的连通图我建议从0.6开始调试对生成的图跑一遍社区发现算法可以简单用Louvain算法或标签传播算法把同一个社区内的因子看作一组每组只保留一个代表因子通常选择与组内其他因子平均相关性最高、同时历史IC表现最好的那一个。这个方法的好处是可以直观地看出因子池里有几个“帮派”而且它对待相关性的方式比单看相关矩阵更结构化。4.4 一个实操中的独立性筛选流程我在自己的研究流程里目前形成了这样一套“因子治理”的固定动作把候选因子统一做标准化和缺失值处理确保所有因子都在同一个度量尺度上计算相关矩阵剔除冗余度高但信息贡献低的因子用主成分或因子分析看剩余因子的有效维度用因子图做社区划分确认哪些因子可以被归为一个风格组每个风格组保留一个代表性因子进入最终的多因子模型最后用滚动IC再加权的方式处理组间权重。这个过程运行下来通常一个50个因子的池子会被压缩到10到15个有效因子组合的夏普比率不一定有巨大提升但回撤形态会平稳不少归因也会变得清晰。5. 在ptrade上落地三个思路时踩过的坑前面讲的都是研究思路但最后总要落到ptrade的代码和回测里。这几个思路在落地时我踩过的坑比较集中拿出来逐个说。5.1 数据API能用和好用是两回事热搜词里有“量化交易用的数据api最好的是什么”这个问题。很多新手会到处找所谓“最好的”数据API但我在ptrade上实践下来真正的答案不是“哪个API最好”而是“你手上的API的数据口径是什么、能不能满足因子的复现要求”。在我最早做分钟收益率特征的时候就遇到过一个问题ptrade的get_price接口在没有传入fq参数时返回的是不复权价格而股票的除权除息会导致价格出现跳跃。如果某只股票在你回测窗口内除权了分钟收益率序列里会出现一个巨大的假负收益。后来我做所有涉及分钟数据的因子都强制传入前复权或后复权参数并且在因子计算时打上标记。不同数据源的复权方式也有差异有的默认前复权有的默认后复权有的甚至不做复权。不能用同一个因子测试脚本今天在A平台算出IC是0.06明天在ptrade里算出0.02就断言因子失效了。先检查数据口径是不是一致这个排查顺序永远放在第一位。5.2 回测中的前视偏差你以为你没用未来数据其实用了回测里最隐蔽的问题就是前视偏差。我在实现滚动IC加权时第一次回测的战绩特别漂亮年化收益率高得离谱。当时我觉得自己发现了一个圣杯后来仔细一查才发现我在计算滚动IC的时候用了当期的收益数据来估计权重然后在同一个交易时段里去下单等于在调仓那一刻偷看了一点点未来。纠正方法其实很朴素所有因子IC的计算只能用到调仓之前的干净数据调仓信号必须延迟到次日开盘才执行。也就是说收盘后计算因子次日开盘下单这是最安全的时序安排。还有一个容易忽略的点财务类因子具有发布滞后性你在“当下”能拿到的财报数据实际上在现实中是几个月前就截止统计的。如果你直接用最新财报数据生成因子即便代码顺序没问题也隐含了前视偏差。正确的做法是把每个财务数据的实际发布时间录进去用发布日之后才可用的数据来计算因子。5.3 因子库管理接口返回的类型和单位坑你没商量ptrade和外部数据源有一个让我每次迁移都要排查的问题数值类型和单位。我遇到过的情况包括涨跌幅接口返回的是百分数比如1.5表示涨1.5%但某个其他数据源返回的是小数0.015如果你没做统一转换相关性计算、回归系数全都会对不上市值单位有的是万元有的是元有的是百万元比较截面数据时单位不统一会直接影响排名停牌股票在行情接口里可能返回NaN、0、或者昨天收盘价取决于具体接口的设计需要专门清洗。所以我强烈建议在整个研究环境中加一层“字段标准化”封装把所有外源数据的字段统一成同一个口径之后再进入因子计算。这层封装看起来不性感但能帮你节省大量排查bug的时间。6. 三个思路如何串成一个完整的研究闭环最后把三个思路串起来形成一个可以反复迭代的流程。6.1 从原始数据到最终策略的六个阶段第一步确定标的池和研究周期。比如沪深300成分股过去3年的日线和分钟线数据。第二步多维度构造候选因子。用思路一的方法从日内分钟收益率的时序特征里挖出日内动量、波动率聚集等因子再从基本面或量价数据里构造截面因子。第三步对候选因子做截面独立性筛选。用思路三的因子分析法和因子图优化把高冗余的因子剔除掉。第四步用滚动IC分析每个因子的有效周期。观察各因子在不同行情阶段的IC均值、IC胜率、换手情况判断哪些因子适合做长期底仓、哪些因子适合做短期轮动。第五步用思路二的滚动自适应权重方法为最终入选的因子分配动态权重。第六步在ptrade里做完整的回测包括交易成本、滑点、涨跌停限制然后逐步过渡到模拟盘最后再进入实盘。6.2 这套流程越到后面越会发现“逻辑比参数重要”说实话这套流程刚跑通的时候我并没有挖到某个单因子特别惊艳的案例。更多的是当我把日内分钟收益率的时序特征、滚动IC加权动态权重、以及因子图降维筛选放在一起之后策略的整体稳定性提升了。原因也不难理解单因子负责“发现局部规律”动态权重负责“跟随市场风格”因子治理负责“不去重复押注同样的风险”。很多人做量化到这里会陷入一个追求极致参数的心态试图把每一段回测的收益都调到最高。但我的体会是回测曲线越完美越要警惕。真正的稳健策略往往在单段回测里不是最优的但它面对不同行情切片的适应力更强。因子研究这件事讲究的是持续迭代不是毕其功于一役。6.3 给正在走这条路的朋友一句实在话如果你现在还在纠结“ptrade里到底怎么写代码才能跑出高收益”我建议你先把代码量放一边把精力先放在两件事上第一把你手上的数据口径搞清楚前复权、后复权、停牌处理、单位统一这些都做到位第二先跑通一个最朴素的因子检验流程哪怕只用一个因子也要把IC计算、因子分组、回测闭环完整地走一遍。我自己就是从这个过程里走过来的。这个行业不缺聪明策略缺的是能把每一个环节都做得扎实的人。希望这篇第166期的内容能让你在因子挖掘这条路上少走一点弯路。
返回列表