# 因子研究
# 因子研究简介
米筐的因子研究集成了量化多因子研究相关的各类投研工具,为您提供完善的因子创建、因子检验、因子审核管理等功能,方便协同办公,帮助您实现高效的团队协作和多因子研究。
- 在功能方面,米筐支持如下功能:
功能 说明 备注 因子创建 可利用米筐因子及丰富工具函数创建自定义因子 因子检验 对自定义因子进行多维度的检验,包括 IC 分析、收益率分析 因子发布 通过一定标准筛选因子,并最终发布到产品库 量化企业版支持 因子预计算 对产品库的因子进行预计算,因子值每日定时自动更新 量化企业版支持 因子跟踪 选择产品库的因子进行收益表现跟踪 量化企业版支持 因子引用 用户可引用产品库中因子进行新的自定义因子的研究 量化企业版支持
- 在管理方面,米筐为企业版用户提供一套完善的审核机制,因子从创建到发布可由工作空间管理人员统一设定标准,进行严格把关,做到因子产品库的质量保证。
# 因子开发指南
在股票投资中,我们会经常使用某种指标或者多种指标来对股票池进行筛选,这些用于选股的指标一般被称为因子。在米筐提供的因子系统中,目前仅支持日频率的因子。具体来说,一个因子在其定义的股票池中,对于池中的上市股票,每个交易日每只股票只会计算出一个值。
因子可分为基础因子和复合因子两种:
- 基础因子:不依赖于其他因子的因子。如基本的行情因子、财务因子,这些因子的值直接来源于财务报表或交易所行情数据;
- 复合因子:基础因子经过各种变换、组合之后得到的因子;
复合因子又可以分为两种:
- 横截面因子:典型的比如沪深 300 成分股根据当前的 pe_ratio 的值大小进行排序,序号作为因子值;此时一个股票的因子值不再只取决于股票本身,而是与整个股票池中所有其他股票有关;对于横截面因子,一个给定的股票在不同的股票池中计算得到的因子值一般是不同的;
- 非横截面因子
在实现上,基础因子是 rqfactor.interface.LeafFactor
的一个实例。米筐提供的公共因子可以用 Factor(factor_name)
来引用,如 Factor('open')
表示开盘价这个因子。当一个自定义因子进入产品库之后,也可以作为基础因子进行引用,此时需要给因子名加上 private.
前缀,如 Factor('private.abc')
表示引用产品库中名为 abc
的因子。
# 一个简单的因子
In[]:
from rqfactor import *
f = (Factor('close') - Factor('open')) / (Factor('high') - Factor('low'))
f
Out[]:
<rqfactor.interface.BinaryCombinedFactor at 0x126916b00>
在上面的代码中,我们定义了一个简单的因子f
,它表示股票当天的收盘价与开盘价的差和最高价与最低价差的比值;可以看到这个定义是非常直观的。显然f
是一个非横截面类型的复合因子。我们来看看这个因子的依赖:
In[]:
f.dependencies
Out[]:
[Factor('close'), Factor('open'), Factor('high'), Factor('low')]
这个因子具体应该如何计算?
In[]:
f.expr
Out[]:
(<ufunc 'true_divide'>,
((<ufunc 'subtract'>, (Factor('close'), Factor('open'))),
(<ufunc 'subtract'>, (Factor('high'), Factor('low')))))
expr
属性返回了一个前缀表达式树;因子计算引擎正是根据这棵树来计算因子值的。
# 算子
在前面的因子定义中,Factor('close') - Factor('open')
中减法是怎么回事呢?从业务层面看,非常简单,两个因子相减,生成了一个新的因子;从实现层面看,两个LeafFactor
相减?我们来检验一下:
In[]:
Factor('close') - Factor('open')
Out[]:
<rqfactor.interface.BinaryCombinedFactor at 0x10a7d5c88>
与业务层面看起来一样,两个因子相减,确实生成了一个新的因子。对一个或多个 因子进行组合、变换,生成一个新的因子,这样的函数我们称为算子。在上面的例子中,-
正是我们预先定义的一个算子。一个算子封装一个对输入的因子进行变换的函数,-
这个算子对应的是numpy.ufunc.subtract
;这个函数由因子计算引擎在计算因子值时调用。
在本系统中,算子除+
, -
, *
, /
, **
, //
, <
, >
, >=
, <=
, &
, |
, ~
, !=
这些内置的操作符外,都以全大写命名,如MIN
, MA
, STD
。
与复合因子类似,算子可以分为两类,横截面算子和非横截面算子。一个因子,如果在表达式中使用了横截面算子,就成为了一个横截面因子。一般情况下,横截面因子命名以CS_
(cross sectional)为前缀,如CS_ZSCORE
;非横截面算子一般不带前缀,或以TS_
(time series)为前缀,以和同功能的横截面因子区分。
非横截面算子封装的函数,其输入是一个或多个一维的numpy.ndarray
; 横截面算子封装的函数,其输入则是一个或多个pandas.DataFrame
。
系统提供的算子可以参考文档,不赘述。
# 一些你应该了解的细节
复权
我们来看一个简单的因子:
In[]:f2 = Factor('close') / REF(Factor('close'), 1) - 1
在这个因子定义中,REF
用来对因子在时间上进行调整,REF(Factor('close'), 1)
表示上一个交易日的收盘价。f2
这个因子是相对于上一个交易日的涨幅,也就是当日收益率。
不过,如果股票在当天进行了分红或者拆分,其收盘价与上一个交易日的收盘价是不可以直接比较的;需要对价格序列进行复权处理。在本系统中,Factor('open')
, Factor('close')
等价格序列是后复权价格,另外提供了Factor('open_unadjusted')
, Factor('close_unadjusted')
等带有后缀_unadjusted
的不复权价格数据。
我们建议,所有试用价格数据的因子,其最终输出的应该是一个无量纲的数字,避免使用价格的绝对数值。
停牌处理
对于很多使用了均线的技术指标来说,在计算时需要过滤掉停牌期间的数据,否则结果会不符合预期。
因此,因子计算引擎在计算因子值时,会过滤掉停牌期间的数据;在计算完成后,将停牌日期的因子值填充为 NaN。
NaN 及 Inf 处理
在系统提供的横截面算子中,Inf 与 NaN 处理方式相同。
# 因子列表
用户可创建自定义因子,并对自己创建的所有因子进行相应的管理。
我的因子列表部分包含以下的字段:
- 因子 ID:因子的标识码,由系统自动生成
- 因子名称:用于识别自定义因子,因子名称不允许重复
- 分类:用于区分因子的风格
- 最新修改时间:展示该因子最近被修改的时间
- 申请发布(企业版):当用户希望将自定义因子发布到产品库时,可发起申请(因子申请发布之前,需要进行因子检验)
- 删除:支持作者对自定义因子进行删除操作
我的因子界面如下图所示:
点击新建因子输入因子名称后即可进入因子编辑页面
# 因子编写
因子编写界面主要包含三个部分,分别为因子详情、设置参数、因子代码编辑。
- 对于因子详情部分,您需选择因子分类,并在因子描述部分记录下因子的思路。
- 对于设置参数,可根据自己设定的场景,在此处设置进行检验时的各个参数。
- 对于因子代码编辑部分,您可按照特定的语法自定义因子表达式。 其界面如下
在因子详情部分,您可以选择因子的分类、添加因子的描述:
因子分类:
- 价值:通常来源于财报信息和行情价格信息的整合,如 BP, EP 等
- 成长:通常是同一指标不同时期的纵向比较,如同比增长率(净利润、营业收入等)
- 质量:通常是单份财报的数字整合,如 EPS, 营业利润/营业收入,ROE 等
- 量价:通常是行情数据中的价格、成交量和换手率的各种组合,不仅包括动量、反转和各种 TA-Lib 下的技术指标,同时也是市场情绪指标的构建基础
- 基础:财务报表中各种常见的财务数据,通常能够直接从三大报表中获取,无需合成,比如总负债,每股资本公积,流通市值等
- 混合:通常源于不同指标、因子之间的相互合成,如量价合成的 alpha101,因子合成的多因子等
- 分析师预期:券商分析师对股票的一致预期数据,但目前 Ricequant 平台还没有相关数据,期待数据引入
- 舆情及其他:通过挖掘与市场相关的新闻而构建的舆情指标可以用来辅助交易的执行,其他指标则包括了各种指标创建者的脑洞想法
因子的描述:您可以在把因子的创建思路及分析写进因子的描述部分。
在设置参数部分,主要是在检验之前,设定各种场景,包括股票池的选择、基准的选择、检验时间、调仓周期等等参数。
编辑因子的逻辑为创建自定义因子的核心部分,为满足用户对因子要求的多样性,米筐支持用户使用类似于通达信公式的方式计算自定义因子。在该部分,具体使用如下:
- 对所有因子:编写前,用户需导入米筐因子的相关信息:
from rqfactor import *
- 对技术指标:若需调用技术类指标,需导入该指标的信息。例如 KDJ,需导入如下。
from rqfactor.indicators import KDJ
公式计算符:公式计算符将函数连接为新的公式,计算符分为算术计算符和逻辑计算符,米筐自定义因子部分支持以下符号:
算术计算符:包括(+、-、*、/)等,它们分别对计算符两边的数据进行加减乘除等计算,与 python 支持的算术计算符一样;
逻辑计算符:如下表,如果条件成立,结果返回 1,否则结果返回 0。
计算符 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 模 |
** | 幂 |
// | 整除 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
& | 与 |
| | 或 |
~ | 非 |
!= | 不等于 |
支持引用米筐提供的行情因子、财务因子、技术类因子,可引用的财务因子可见 财务指标文档。
支持使用某些工具函数,如 MAX、MIN、HHV、LLV 等,可见工具函数。
自定义因子中引用行情相关的因子
from rqfactor import * def compute(): return Factor('open') + Factor('close')
自定义因子中引用财务类的因子。
from rqfactor import * def compute(): return Factor('pe_ratio')
自定义因子中引用技术类因子。
from rqfactor import * from rqfactor.indicators import KDJ def compute(): return KDJ(9, 3, 3).K
自定义财务因子。
from rqfactor import * def compute(): return 1/Factor('pe_ratio') + 1/Factor('pb_ratio') + Factor('return_on_equity')
因子引用
用户在编写新的因子时,可在表达式中引用一些常见的行情因子、财务因子和技术因子。可引用的因子分为三部分: 行情因子
, 财务因子
以及技术指标
。
行情因子
行情因子的引用方式: Factor('factor')
其中 因子 包含以下七个因子:
因子 | 类型 | 说明 |
---|---|---|
open | float | 开盘价 |
close | float | 收盘价 |
high | float | 最高价 |
low | float | 最低价 |
total_turnover | float | 总成交额 |
volume | float | 总成交量 |
num_trades | float | 成交笔数 |
财务因子
alpha101 因子
- alpha101 因子的调用方式: Factor('factor'),具体因子详情可见alpha101 因子
技术指标
技术函数的调用方式:函数(参数).返回值
当不进行参数的设定时,返回的是默认的参数,如 MACD( ).DIFF,实际返回的是 MACD(12,26,9).DIFF
其中函数及其对应的参数及返回值如下:
函数中文名 函数 参数 返回值及说明 指数平滑移动平均线 MACD SHORT:快速移动平均线(短线),默认值 12;
LONG:慢速移动平均线(长线),默认值 26;
M:天数,默认值为 9返回值:DIFF, DEA, HIST;
DIFF = EMA(CLOSE, SHORT) - EMA(CLOSE, LONG);
DEA = EMA(DIFF, M);
HIST = (DIFF - DEA) * 2随机波动指标 KDJ N:快速确认线,默认值为 9;
M1:慢速主干线,默认值为 3;
M2:方向敏感线,默认值为 3返回值:K, D, J;
K = EMA(RSV _ 100, (M1 _ 2 - 1));
RSV = (CLOSE - LLV(LOW, N)) / (HHV(HIGH, N) - LLV(LOW, N)) _ 100;
D = EMA(K, (M2 _ 2 - 1)); J = K _ 3 - D _ 2趋向指标 DMI M1:默认值为 14;
M2:默认值为 6返回值:DI1, DI2, ADX, ADXR;
DI1 = DMP _ 100 / TR,
DMP = SUM(IF((HD > 0) & (HD > LD), HD, 0), M1);
DI2 = DMM _ 100 / TR,
DMM = SUM(IF((LD > 0) & (LD > HD), LD, 0), M1);
ADX = MA(ABS(DI2 - DI1) / (DI1 + DI2) * 100, M2);
ADXR = (ADX + REF(ADX, M2)) / 2相对强弱指标 RSI N1:默认值为 6;
N2:默认值为 12;
N3:默认值为 24返回值:RSI1, RSI2, RSI3;
RSI1 = MA(MAX(CLOSE - LC, 0), N1) / MA(ABS(CLOSE - LC), N1) _ 100,
LC = REF(CLOSE, 1);
RSI2 = MA(MAX(CLOSE - LC, 0), N2) / MA(ABS(CLOSE - LC), N2) _ 100,
LC = REF(CLOSE, 1);
RSI3 = MA(MAX(CLOSE - LC, 0), N3) / MA(ABS(CLOSE - LC), N3) * 100,
LC = REF(CLOSE, 1)布林带 BOLL N:时间长度,默认值为 20;
P:上轨及下轨的标准差系数,默认值为 2返回值:MID, UPPER, LOWER;
MID = MA(CLOSE, N);
UPPER = MID + STD(CLOSE, N) _ P;
LOWER = MID - STD(CLOSE, N) _ P威廉指标 WR N:默认值为 10;
N1:默认值为 6返回值:WR1, WR2;
WR1 = (HHV(HIGH, N) - CLOSE) / (HHV(HIGH, N) - LLV(LOW, N)) _ 100;
WR2 = (HHV(HIGH, N1) - CLOSE) / (HHV(HIGH, N1) - LLV(LOW, N1)) _ 100乖离率 BIAS L1:默认值为 5;
L4:默认值为 3;
L5:默认值为 10返回值:BIAS1, BIAS2, BIAS3;
BIAS1 = (CLOSE - MA(CLOSE, L1)) / MA(CLOSE, L1) _ 100;
BIAS2 = (CLOSE - MA(CLOSE, L4)) / MA(CLOSE, L4) _ 100;
BIAS3 = (CLOSE - MA(CLOSE, L5)) / MA(CLOSE, L5) * 100震动升降指标 ASI M1:默认值为 26;
M2:默认值为 10返回值:ASI, ASIT;
LC = REF(CLOSE, 1);
AA = ABS(HIGH - LC);
BB = ABS(LOW - LC);
CC = ABS(HIGH - REF(LOW, 1));
DD = ABS(LC - REF(OPEN, 1));
R = IF((AA > BB) & (AA > CC), AA + BB / 2 + DD / 4, IF((BB > CC) & (BB > AA), BB + AA / 2 + DD / 4, CC + DD / 4));
X = (CLOSE - LC + (CLOSE - OPEN) / 2 + LC - REF(OPEN, 1));
SI = X _ 16 / R _ MAX(AA, BB);
ASI = SUM(SI, M1);
ASIT = MA(ASI, M2)容量比例 VR M1:默认值为 26 返回值:VR;
VR = SUM(IF(CLOSE > LC, VOL, 0), M1) / SUM(IF(CLOSE <= LC, VOL, 0), M1) * 100,
LC = REF(CLOSE, 1)人气意愿指标 ARBR M1:默认值为 26 返回值:AR, BR;
AR = SUM(HIGH - OPEN, M1) / SUM(OPEN - LOW, M1) _ 100;
BR = SUM(MAX(0, HIGH - REF(CLOSE, 1)), M1) / SUM(MAX(0, REF(CLOSE, 1) - LOW), M1) _ 100区间震荡线 DPO M1:默认值为 20;
M2:默认值为 10;
M3:默认值为 6返回值:DPO, MADPO;
DPO = CLOSE - REF(MA(CLOSE, M1), M2);
MADPO = MA(DPO, M3)三重指数平均移动平均 TRIX M1:短线,默认值为 12;
M2:长线,默认值为 20返回值:TRIX, TRMA;
TRIX = (TR - REF(TR, 1)) / REF(TR, 1) * 100;
TR = EMA(EMA(EMA(CLOSE, M1), M1), M1);
TRMA = MA(TRIX, M2)
# 工具函数
为了进一步提高用户编写因子的效率,米筐提供了两类工具函数,分别为:
- 时间序列工具函数
- 横截面工具函数
对于 时间序列工具函数 ,米筐集成了以下常见的运算操作:
函数 说明 用法 样例 ABS(X) 求绝对值 ABS(X)返回变量 X 的绝对值 ABS(Factor('close')-Factor('open'))返回收盘价与开盘价差值的绝对值 LOG(X) 求对数 LOG(X)以 10 为底,返回 X 的对数 LOG(Factor('close')) 表示返回收盘价 10 为底的对数 EXP(X) 求幂 EXP(X)返回 e 的 X 次幂 EXP(Factor('close')) 表示 e 的 X 次幂 SIGN(X) 求符号 SIGN(X)返回 X 的符号 如果 X>0,SIGN(X)就返回 1,如果 X<0,则返回-1,如果 X=0,则返回 0 SIGNEDPOWER(X, T) 将差异放大 保持 X 的正负特性,将其进行 T 次幂处理使其差异放大 其中式子为 SIGN(X) * (ABS(X) ** T),Sign(X)为符号函数,表示:如果 X>0,就返回 1,如果 X<0,则返回-1,如果 X=0,则返回 0 MAX(A, B) 求最大值 MAX(A, B)返回 A 和 B 中的较大值 MAX(Factor('close') - Factor('open'), 0)表示若收盘价大于开盘价返回它们的差值,否则返回 0 MIN(A, B) 求最小值 MIN(A, B)返回 A 和 B 中的较小值 MIN(Factor('close'), Factor('open'))返回开盘价和收盘价中的较小值 FMAX(A, B) 求最大值,忽略 NAN MAX(A, B)返回 A 和 B 中的较大值,忽略 NAN FMAX(Factor('close') - Factor('open'), 0)表示若收盘价大于开盘价返回它们的差值,否则返回 0,忽略 NAN FMIN(A, B) 求最小值,忽略 NAN MIN(A, B)返回 A 和 B 中的较小值,忽略 NAN FMIN(Factor('close'), Factor('open'))返回开盘价和收盘价中的较小值,忽略 NAN CROSS(A, B) 交叉函数 A:变量或常量,判断交叉的第一条线;B:变量或常量,判断交叉的第二条线 CROSS( MA(Factor('close'), 5), MA(Factor('close'), 10)):返回 5 日均线与 10 日均线金叉;CROSS( Factor('close'), 10):价格由下向上突破 10 元 IF(X, A, B) 根据条件求不同的值 IF(X, A, B)若 X 为 TRUE 则返回 A,否则返回 B IF(Factor('close') > Factor('open'), Factor('close'), Factor('open')) 表示如果收盘价大于开盘价,则返回收盘价,否则返回开盘价 REF(X, N) 返回一定交易日前的数据 REF(X, N)返回 N 个交易日前的 X 的值 REF(Factor('close'), 10)返回 10 个交易日前的收盘价 MA(X, N) 简单移动平均 MA(X, N)返回 X 的 N 个交易日移动平均值 MA(Factor('close'), 10)返回收盘价 10 个交易日的均价 EMA(X, N) 指数平滑移动平均 EMA(X, N)返回 X 的 N 个交易日的指数平滑移动平均 EMA(Factor('close'), 10)返回收盘价 10 个交易日的指数平滑移动平均,算法可见计算方法说明 WMA(X, N) 线性加权移动平均 WMA(X, N)返回 X 的 N 个交易日的线性加权移动平均 WMA(Factor('close'), 10)返回收盘价 10 个交易日的线性加权移动平均,算法可见计算方法说明 STD(X, N) 算标准差 STD(X, N)表示 X 的 N 个交易日的标准差 STD(Factor('close'), 10)表示收盘价 10 个交易日的标准差 SUM(X, N) 求总和 SUM(X, N)统计 N 个交易日内 X 的总和 SUM(Factor('volume'), 10):统计 10 个交易日成交量的总和 COUNT(X, N) 求频次 统计 N 日内出现 X 情况的频次 COUNT(Factor('close')>100, 5):统计过去 5 个交易日收盘价大于 100 的频次 DELAY(X, N) 求 N 天前的值 返回 X 在 N 个交易日前的值 DELAY(Factor('close'), 10) :返回收盘价在 10 个交易日之前的值 DELTA(X, N) 求差分 返回 X 今天与 N 个交易日前值的差值 DELTA(Factor('close'), 10) :返回今日收盘价与 10 个交易日之前的收盘价之间的差值 HHV(X, N) 求最高值 HHV(X, N)求 N 个交易日内 X 的最高值 HHV(Factor('close'), 10)表示 10 个交易日内收盘价的最大值 LLV(X, N) 求最低值 LLV(X, N)求 N 个交易日内 X 的最低值 LLV(Factor('close'), 10)表示 10 个交易日内收盘价的最小值 EVERY(X, N) 一直存在 EVERY(X, N)判断在 N 个交易日内是否在每个交易日; 条件 X 都成立,若成立则返回 True,否则返回 False EVERY(Factor('close') > Factor('open'), 10) 表示若 10 个交易日内一直是阳线,则返回 True,否则返回 False VAR(X, N) 方差 VAR(X, N)返回过去 N 个交易日 X 的方差 VAR(Factor('close'), 10) 表示收盘价在过去 10 个交易日内的方差 COV(X, Y, N) 协方差 COV(X, Y, N)返回过去 N 个交易日 X 与 Y 的协方差 COV(Factor('open'), Factor('close'), 66) 表示过去 66 个交易日收盘价与开盘价的协方差 CORR (X, Y, N) 相关系数 CORR(X, Y, N)返回过去 N 个交易日 X 与 Y 的相关系数 CORR(Factor('close'), Factor('open'), 66) 表示过去 66 个交易日收盘价与开盘价的相关系数 PRODUCT(X, N) 求移动平均求积 PRODUCT(X, N)返回过去 N 个交易日 X 的移动平均求积 PRODUCT(Factor('close'), 10) 表示过去 10 个交易日收盘价的移动平均求积 PCT_CHANGE(X, N) 求变化率 PCT_CHANGE(X, N)返回 X 在 N 个交易日内的变化率 PCT_CHANGE(Factor('close'), 10) 表示收盘价在 10 个交易日内的变化率 TS_MAX(X, N) 求序列中最大值 返回 N 个交易日中 X 序列的最大值 TS_MAX(Factor('close'), 5):返回过去 5 个交易日中收盘价的最大值 TS_MIN(X, N) 求序列中最小值 返回 N 个交易日中 X 序列的最小值 TS_MIN(Factor('close'), 5):返回过去 5 个交易日中收盘价的最小值 TS_ARGMAX(X, N) 求序列中最大值所在索引 返回 N 个交易日 X 序列的最大值的索引 TS_ARGMAX(Factor('close'), 5):返回过去 5 个交易日中收盘价的最大值的索引 TS_ARGMIN(X, N) 求序列中最小值所在索引 返回 N 个交易日 X 序列的最小值的索引 TS_ARGMIN(Factor('close'), 5):返回过去 5 个交易日中收盘价的最小值的索引 TS_SKEW(X, N) 求序列的偏度 返回序列 X 在过去 N 个交易日的偏度 TS_SKEW(Factor('close'), 60) :返回收盘价过去 60 个交易日的偏度 TS_KURT(X, N) 求序列的峰度 返回序列 X 在过去 N 个交易日的峰度 TS_KURT(Factor('close'), 60) :返回收盘价过去 60 个交易日的峰度 TS_RANK(X, N) 求时间序列的排序占比 返回今天 X 在过去 N 个交易日的排序占比 如 N=5,今天的 X 排序为 3,则返回 0.6 TS_REGRESSION(Y, X, N) 求序列回归参数 返回因变量为 Y,自变量为 X 在 N 个交易日的回归参数 TS_REGRESSION(Factor('close'),Factor('open'),60) :返回收盘价为因变量,开盘价为自变量在过去 60 个交易日的回归参数 TS_ZSCORE (X, N) 标准化 TS_ZSCORE (X, N)以 X 在过去 N 个交易日的取值作为样本,返回当前交易日 X 对应的标准分数 TS_ZSCORE (Factor('close'), 10) 表示收盘价在过去 10 个交易日的取值作为样本,返回当前交易日收盘价对应的标准分数 TS_FILLNA(X, nv = N, method = 'value') 时间序列缺失值填充 S_FILLNA(X, N, method = 'value'),其中 method 可取:
'value': N 表示数值,以数值 N 替换 X 中的缺失值;
'MA': N 表示交易日,以 X 最近 N 个交易日的移动平均值替换当前交易日 X 中的缺失值;
'forward': N 表示交易日,以 X 最近一个非缺失值替换当前交易日 X 中的缺失值TS_FILLNA(Factor('close'), 10, 'value'):
用 10 替换收盘价中的缺失值;
TS_FILLNA(Factor('close'), 10, 'MA'):用过去 10 个交易日的移动平均值替换当前交易日收盘价的缺失值 ;
TS_FILLNA(Factor('close'), 10, 'forward'):在过去 10 天内用最近一个非缺失值替换当前收盘价的缺失值,若过去 10 天中,某一股票的 X 全为缺失值,则不进行替换
对于 横截面工具函数,米筐集成了以下常见的运算操作:
函数 说明 用法 样例 DEMEAN(X) 均值消减 DEMEAN(X) 表示选定股票池,将个股指标 X 减去股票池中 X 的均值 DEMEAN(Factor('close')) 给定股票池,返回每只股票的收盘价减去收盘价的均值 CS_ZSCORE(X) 标准化 CS_ZSCORE (X) 表示选定股票池,将指标 X 进行标准化处理 CS_ZSCORE(Factor('close')) 给定股票池,返回收盘价在选定股票池的标准化值 SCALE(X, A) 量级缩放 SCALE(X, A) 表示调整 X 的数量级,使得返回的调整后 X'的绝对值之和等于 A SCALE(Factor('close'), 10) 给定股票池,将股票池内收盘价量级调整进行调整,使绝对值之和等于 10 CS_REGRESSION_RESIDUAL(Y, X1, …, XN) 返回横截面回归的残差 CS_REGRESSION_RESIDUAL(Y, X1, …, XN)返回多元回归的"残差+截距" CS_REGRESSION_RESIDUAL (Factor('close'), Factor('open'), Factor('high')) FIX(X, order_book_id) 返回某个标的的 X 基于股票池,返回某个标的的 X FIX(Factor('close'), '000300.XSHG'):令股票池中每只标的都返回沪深 300 的收盘价,相当于固定某个标的,可用于求股票池中每只标的对某个股票标的的 beta 值 RANK(X, method = 'first', ascending = True) 横截面排序占比 RANK(X, method = 'first', ascending = True),其中 method 可取:
'first': 按照它们出现在数组中的顺序进行排序;
'average': 组内平均排序;
'max': 组内最大等级排序;
'min': 组内最小等级排序;
'dense': 组内RANK(Factor('close'), 'first', True) 返回今天收盘价在过去股票池内的升序排序占比,若多项值相同,则按它们出现在数组中的顺序进行排序;
RANK(X, 'average', True) 表示升序排序占比,若多项值相同,则其排序为平均排序;
RANK(X,'max', True) 表示升序排序占比,若多项值相同,则按组内最大等级排序数赋值;
RANK(X,'min', True) 表示升序排序占比,若多项值相同,则按组内最小等级排序数赋值;
RANK(X,'dense', True) 表示升序排序占比,若多项值相同,则按组内最小等级排序数赋值,后面的排序紧接该项元素,如多项并列排序为 3,则后续排序还是从 4 开始TOP(X, threshold=N, method='ordinal') 取最大的 N 个值为 1 返回最大的 N 个值为 1,其余为 0 TOP(Factor('close'), threshold=50, method='ordinal'):在股票池中排名前 50 的返回 1,其余为 0 BOTTOM(X, threshold=N, method='ordinal') 取最小的 N 个值为 1 返回最小的 N 个值为 1,其余为 0 BOTTOM(Factor('close'), threshold=50, method='ordinal'):在股票池中排名最后 50 的返回 1,其余为 0 QUANTILE(X, bins, ascending = True) 分组 QUANTILE(X, bins = 5, ascending = True)表示对 X 按升序分为 5 组 QUANTILE(Factor('close'), bins = 5, ascending = True)表示对收盘价按升序分为 5 组 INDUSTRY_NEUTRALIZE(X) 行业中性化处理 INDUSTRY_NEUTRALIZE(X) 对 X 进行行业中性化处理,其中行业分类采用申万一级行业分类标准 INDUSTRY_NEUTRALIZE(Factor('close')) 选定股票池,根据申万行业分类对收盘价进行行业中性化处理 CS_FILLNA(X) 替换缺失值 以同行业均值对缺失值进行替换 这里以申万一级 28 个行业为分类标准
# 因子分析
# 参数设置
在对因子进行分析前,可设置 股票池
, 基准
, 起止日期
, 调仓周期
, 因子方向
, 分组
, 离群值处理
以及 IC计算方式
等参数,参数的可选范围及相关说明如下:
参数 范围 说明 股票池 沪深 300、上证 50、中证 50、中证 800、自定义(可输入指数代码) 因子检验所在的股票池。在不同股票池中因子的表现不尽相同,用户可根据因子风格选取目标股票池 基准 沪深 300、上证 50、中证 50、中证 800 因子表现的对照基准 起止日期 2007 年 1 月至今 因子检验的开始结束日期 因子方向 升序、降序 可根据因子方向将股票池中股票因子值进行排序,为因子分组做准备。升序:即因子值越小越好,如市值、估值类(市盈率、市净率等); 降序:即因子值越大越好,如 ROE、利润等 调仓周期 可输入交易日天数,默认值为 5 个交易日 调仓系统给定在收盘后进行相应的调仓 分组 默认为 5 组,可填写>1 的整数型 基于因子值,根据因子方向将股票进行分组,可用于观察各组表现的差异性 IC 计算方式 RankIC、NormalIC 默认 IC 计算方法为 NormalIC,具体计算方法请参考计算方法说明 离群值处理 不处理、MAD、3σ、百分位法 离群值值处理是调整因子值中的离群值至上下限(Winsorization 处理),使因子值在一个合理的范围内 行业中性化处理 选择与否 行业中性化处理是使因子对行业没有偏向,算法可参考计算方法说明 ST 股 选择与否 选择是否包含 ST 股,默认不勾选 新股 选择与否 选择是否包含新股,默认不勾选
# 检验结果
因子测试结果目前主要包括 IC分析
以及 收益率分析
这两个方面,分别就因子的 IC 值、以及因子的收益率方面进行多维度分析。
IC 分析
在因子分析的结果中,IC 分析可呈现每个调仓日股票池对应 IC 分析。IC(Information Coefficient)又称为信息系数。在对因子检验之前可对 IC 的计算方式进行选择,现提供两种计算方式,分别为 RankIC、NormalIC。
- IC 统计指标:根据测试期内用户选定的股票池,在每个调仓日所对应的 IC 值得到的各种统计指标。
统计指标 解释 IR 值 多期 IC 值的均值与标准差的比值 均值 多期 IC 值的均值 标准差 多期 IC 值的标准差 正值次数 IC 值取值为正的期数 负值次数 IC 值取值为负的期数 显著比例 IC 值显著的比例(即相关系数的 P 值小于 0.01) 正显著比例 IC 值为正且显著的比例(即相关系数的 P 值小于 0.01) 负显著比例 IC 值为负且显著的比例(即相关系数的 P 值小于 0.01) T 统计量 多期 IC 值分布作 T 检验所得到的 T 统计量 P 值 多期 IC 值分布作 T 检验所得到的 P 值 偏度 多期 IC 值分布的偏度 峰度 多期 IC 值分布的峰度
IC 变化图:反映因子预测能力如何时间变化。因子信息系数随时间波动越小,方向(取值正负)越稳定,因子预测效果越好。
IC 衰减率:反映因子值与滞后 N 期(N 为横坐标)的收益率的信息系数,以评估因子预测能力的稳定性。因子衰减速率越低,因子的预测周期越长。
股票池 IC 行业分布:反映因子在不同行业的预测能力差异。用户可参考分析结果,以评估该因子适用于在哪些行业进行选股。
收益率分析
因子分组累积收益率:呈现了给定股票池及分组后,各组股票在测试期内的累积收益率的变化。
换手率分析:呈现了给定股票池及分组后,各个分组在测试期内换手率的变化。
因子检验结果如下图所示
因子检验函数
factor_analysis(
f, period, start_date=None, end_date=None, universe=None, shift=1,
rank_ic=True, quantile=5, ascending=True, winzorization='mad',
normalization=True, neutralization='none',
include_st=True, include_new=True, benchmark='000300.XSHG'
)
对因子进行检验分析,并返回分析结果
参数
参数 类型 说明 f dataframe 因子值或因子定义,如 Factor('close')
。当传入因子值时,类型为pd.DataFrame
,index
为交易日,columns
为order_book_id
。当传入因子定义时,需要同时指定start_date
及end_date
,可通过universe
参数设置需要计算的股票范围period int 调仓周期,以交易日为单位。如 period=5
表示调仓周期为 5 个交易日start_date str , datetime.date, datetime.datetime, pd.Timestamp 计算因子值时的开始日期。当传入因子值时,这个参数无效 end_date str ,datetime.date, datetime.datetime, pd.Timestamp 计算因子值时的结束日期。当传入因子值时,这个参数无效 universe str 计算因子值时股票的范围。可传入指数代码,如 000300.XSHG
,表示计算沪深 300 指数成分的因子值shift_days int 因子值延后的期数。一般来说, T
日的因子值只能在收盘后获得,用于指导T+1
日的调仓,此时设置shift
为 1 即可。默认为 1rank_ic bool 是否使用因子值的排名计算 ic
。为False
时,使用因子值计算ic
quantile int 分组。对分析时组别进行指定,默认 quantile = 5
ascending int 因子值排序方向,默认为 True
,表示从小到大排序;False
则反向排序winzorization str 离群值处理方式。默认为 none
,可选择mad
(绝对值差中位数法),std
(标准差法),percentile
(百分位法) 及none
(不进行离群值处理)normalization bool 标准化处理,默认 True
neutralization str 中性化处理,默认 none
,不处理 -none
,行业中性化 -industry
include_st bool 是否包含 st 股,默认 False
,即不包含include_new bool 是否包含新股,默认 False
,即不包含benchmark str 基准,默认为 000300.XSHG
(沪深 300 指数)
返回
- pandas DataFrame
对象 类型 说明 ic_summary DataFrame IC 统计指标 ic_series DataFrame IC 序列 ic_decay DataFrame IC 衰减率 ic_industrial_distribution DataFrame IC 行业分布 quantile_factor_returns DataFrame 因子分组累积收益率 quantile_turnover DataFrame 分组换手率
execute_factor 函数
execute_factor(factor, order_book_ids, start_date, end_date)
- 参数
参数 类型 说明 factor str factor 既可以是平台提供的基础因子,也可以传入因子表达式类似 Factor('pe_ratio') + Factor('close') 这样的经过一系列变换后得到的因子; order_book_ids str list 合约代码,可传入 order_book_id list start_date str, datetime.date, datetime.datetime, pandasTimestamp 开始日期 end_date str, datetime.date, datetime.datetime, pandasTimestamp 结束日期
- 返回:pandas DataFrame
范例
- 基于沪深 300 股票池,对收盘价因子在 20170101-20170601 进行调仓周期为 5 的检验分析
[In]
from rqfactor import *
from rqfactor.notebook import *
f = Factor('close')
df = execute_factor(f, index_components('000300.XSHG', '20170101'), '20170101', '20170601')
result = factor_analysis(df, 5)
result.ic
[Out]
2017-01-04 -0.269962
2017-01-11 -0.163985
2017-01-18 0.103013
2017-01-25 0.096184
2017-02-08 -0.297142
2017-02-15 0.101017
2017-02-22 0.079381
2017-03-01 0.269773
2017-03-08 0.203213
2017-03-15 0.136387
2017-03-22 -0.218898
2017-03-29 -0.167801
2017-04-07 -0.067472
2017-04-14 0.190367
2017-04-21 0.070248
2017-04-28 -0.049433
2017-05-08 0.121866
2017-05-15 0.228014
2017-05-22 -0.193295
2017-05-31 0.292108
dtype: float64
- 对上例的检验结果进行图形化展示
[In]
result.show()
提示: 如上面的示例,您可以在投资研究引入rqfactor
包进行因子编写和因子检验分析。
# 因子发布与审核管理
申请发布因子
当您在创建完因子,并对其进行充分的检验分析后,您可申请发布因子,让您的因子进入产品库。您可以在因子列表页面点击 申请,或者在因子检验完成的页面点击 申请发布 ,工作空间的管理员会对您的因子进行审核。
- 您发起申请时,需进一步确认因子名称、因子分类、因子描述(注:描述不可为空);
- 若您的因子为横截面因子,需先选定股票池,才可进一步进行申请发布;
- 因子从申请发布开始,因子名称、因子分类、因子描述等均不可再进行修改;
- 您发布到产品库的因子包含:因子的名称、因子分类、因子描述、因子代码、因子检验分析的默认参数;
申请发布的弹窗如下:
我提交的因子
- 该页面主要列出用户自己已提交申请发布到产品库的因子,可从该页面查看因子的审核进度以及相应的状态。
- 在我提交的因子列表中主要包含因子 ID、因子名称、因子类型、状态、发布时间以及申请时间,因子的状态有审核中、已发布、已下架几个内容。
- 我发布的因子界面如下:
因子审核
当您申请发布您的因子后,您的因子会进入该工作空间管理员的 因子审核 列表。工作空间管理员对您的因子始终拥有最高权限(代码可见),故可多方面对您的因子进行审核检验分析。 若审核通过,您的因子将会被发布,进入产品库;若审核未通过,您的因子将会被驳回,用户可检查编辑因子后重新申请发布。
因子审核列表的界面如下:
因子的审核窗口界面如下:
# 因子产品库
因子发布
当因子发布后,会进入到产品库中,此时工作空间中的成员都可看到该因子,且可以引入到新创建的自定义因子中,但只有作者和管理员可以看到具体代码。
产品库列表中展示的内容为“已发布”因子或发布后被下架的因子。在该界面,用户可以勾选因子创建因子跟踪。
因子下架
工作空间的管理员可对已发布的因子进行下架处理。在因子下架处理时,需要注意以下的点:
- 当审核管理人对已发布因子进行下架处理后,该因子在产品库、因子作者的我提交的因子 界面中的状态将变成已下架。
- 因子下架后,工作空间管理员可看到产品库中的因子增加了删除按钮,可对该因子产品进行删除,删除后因子会从产品库和因子作者的我提交的因子页面中消失。
- 因子下架后,不再对该因子进行预计算,对该因子创建的因子跟踪会自动删除。
- 【注意】:如果有其他因子依赖于该因子,底层被依赖因子下架时,上层因子也会一同下架。
产品库其界面如下:
因子相关性
勾选两个及以上因子即可进行因子相关性系数的计算。可对选择因子的股票池和数据处理,点击计算后生成因子相关性矩阵。
- 分析期小等于六个月时,使用每周最后一个交易日的数据计算相关系数,并取平均值;
- 分析期大于六个月时,使用每月最后一个交易日的数据计算相关系数,并取平均值。
# 因子预计算
对于产品库已发布的因子,米筐支持因子预计算,让因子实现每日自动增量更新。
因子的预计算状态分为以下几类:
因子预计算状态 说明 等待调度 指当前正在等待调度使用资源 调度中,等待计算 资源已就位,正在等待计算 计算中 因子的数据当前正在计算 计算成功 当前计算任务已完成 计算失败 指因子数据在更新过程中报错,可 hover 查看报错日志
因子预计算的注意事项:
- 因子一旦提交到产品库,便会触发历史全量计算,默认从 2007 年 1 月 4 日开始计算;
- 每日在 21:00 后触发增量计算
- 每日早上 6:00,若前一交易日的因子值未预计算完,会自动触发报警邮件发给空间管理员。
因子引用
对于产品库中历史全量计算完成的因子,工作空间的所有用户可以把该因子作为基础因子进行引用,此时需要给因子名加上 private. 前缀,如 Factor('private.abc') 表示引用产品库中名为 abc 的因子。同时工作空间的所有用户可以在投资研究、代码策略、模拟交易中调取因子,如 get_factor(order_book_ids, 'private.abc',……)表示引用产品库中名为 abc 的因子。
因子重算
因子预计算完成后,若发现因子值有问题,或者依赖的基础因子数据有变化,空间管理员可以点击重算操作,对因子进行重新计算。
【注意】:如果有其他因子依赖于该因子,则该因子重算的同时,依赖该因子的因子也会自动重算。
# 因子跟踪
基于产品库的因子,可设置参数创建因子跟踪记录。当因子每日增量计算结束后,会触发因子跟踪的相关分析结果更新。在因子跟踪的面板,可查看因子的重要指标,包括昨日收益率、近一周收益率、累积收益率、昨日 IC、平均 IC、平均 IR等信息。
可点击查看每个因子分析结果的详情。当勾选完产品库因子后,可点击创建因子跟踪的按钮进行创建,界面如下:
其中涉及两个创建方式:
创建方式 说明 设置参数 对于所勾选的因子,可设置统一参数进行收益跟踪,需指定分组名称(唯一性) 原始参数 对于所勾选的因子,可根据各因子所带的原始参数进行收益跟踪
由于两种模式不同,需对「因子跟踪」界面做以下点做区分:
- 因子跟踪记录是以「因子 ID」为唯一性;
- 当「分组参数」为「原始参数」时,则代表该跟踪记录用的是因子本身所带的参数;
- 当「分组参数」为其他时,则为创建跟踪时所设置的参数。
因子跟踪列表的界面如下:
# 因子的生命周期
因子从开始创建到最终进入到产品库会经历一个周期,这叫做因子的生命周期。
- 因子整个生命周期会经历的状态如下:
- 创建中:当您有一个想法时,便可以创建一个新的因子。可调整不同的参数进行因子分析检验。从创建因子开始,此时因子处于 创建中
- 审核中:当您觉得因子已经完成开发,希望因子能进入到产品库中时,便可申请发布。一旦发起申请,到因子被发布前,此时因子会处于 审核中
- 已发布:当您的因子被工作空间管理员检验通过后,便会被发布到线上。此时因子的状态为 已发布
- 未通过:当您的因子被审核管理人驳回后,因子状态变为 未通过
- 已下架:当您的因子已经上线后,再被管理员进行下架操作,因子状态变为 已下架
- 不同状态的因子的可操作性如下:
状态 所处列表 作者可进行的操作 空间管理员可进行的操作 空间中其他用户可进行的操作 创建中 因子列表 可编辑,可检验,可删除,可申请发布 无 无 审核中 我提交的因子列表 代码可见,可检验 代码可见,可检验,可审核 无 已发布 产品库 代码可见,可检验,可引用 可下架,代码可见,可检验,可引用用 可引用,可检验 未通过 因子列表 可编辑,可删除,可申请发布 无 无 已下架 产品库 代码可见,可检验 代码可见,可检验,可删除(删除后从产品库和我提交的因子列表删除) 无
其整个状态的变动流程如下图:
在因子的整个周期中,作者和管理员可对因子进行如下的操作:
- 创建自定义因子
- 申请发布因子
- 审核因子(仅限管理员)
- 下架因子(仅限管理员)
- 删除产品库中已下架的因子(仅限管理员)
# 因子开发进阶功能
使用系统提供的基础因子和算子,你已经可以写出很多因子了,比如著名的alpha101 (opens new window)。不过,有时候你需要的功能系统内置的算子不能实现,比如你需要一种不一样的均线计算方式。这时候你就需要用到自定义算子。
# 自定义算子
我们以一个对时间序列进行指数加权的算子为例,说明如何定义一个算子。这个算子实现如下功能:
- 半衰期为 22 个交易日;
- 时间窗口长度可设置;
- 输出值为加权平均值;
我们先看一下这个自定义算子的代码:
In[]:
import numpy as np
from rqfactor.utils import rolling_window
from rqfactor.rolling import RollingWindowFactor
def my_ema(series, window):
# series: np.ndarray, 一维数组
# window: int, 窗口大小
q = 0.5 ** (1 / 22)
weight = np.array(list(reversed([q ** i for i in range(window)])))
r = rolling_window(series, window)
return np.dot(r, weight) / window
def MY_EMA(f, window):
return RollingWindowFactor(my_ema, window, f)
我们来逐行看一下这个代码:
import numpy as np
这一行引入了numpy
这个包。
from rqfactor.utils import rolling_window
rolling_window
是定义在rqfactor.utils
中的一个辅助函数,其实现可参考 这里 (opens new window)
我们看一下这个函数的具体功能:
In[]:
a = np.arange(100)
rolling_window(a, 20)
Out[]:
array([[ 0, 1, 2, ..., 17, 18, 19],
[ 1, 2, 3, ..., 18, 19, 20],
[ 2, 3, 4, ..., 19, 20, 21],
...,
[78, 79, 80, ..., 95, 96, 97],
[79, 80, 81, ..., 96, 97, 98],
[80, 81, 82, ..., 97, 98, 99]])
from rqfactor.rolling import RollingWindowFactor
我们实现这个算子是一个滑动窗口算子,RollingWindowFactor
中封装了相应的细节。
def my_ema(series, window):
# series: np.ndarray, 一维数组
# window: int, 窗口大小
q = 0.5 ** (1 / 22)
weight = np.array(list(reversed([q ** i for i in range(window)])))
r = rolling_window(series, window)
return np.dot(r, weight) / window
这是实际的运算逻辑,weight
是对应的权重。
def MY_EMA(f, window):
return RollingWindowFactor(my_ema, f, window)
这里我们定义了算子MY_EMA
,它有两个参数,f
是输入因子,window
是窗口大小。这个函数返回一个RollingWindowFactor
对象。RollingWindowFactor
类接受三个参数,第一个是实际执行变换的函数,在这个例子里是 my_ema
,第二个参数是窗口大小,第三个参数是待变换的因子。
我们来试试这个刚定义的算子:
In[]:
f3 = MY_EMA(Factor('close'), 60)
execute_factor(f3, ['000001.XSHE', '600000.XSHG'], '20180101', '20180201')
Out[]:
000001.XSHE 600000.XSHG
2018-01-02 5.944022 5.827988
2018-01-03 5.952963 5.825259
2018-01-04 5.960143 5.822716
2018-01-05 5.967883 5.820776
2018-01-08 5.970194 5.819007
2018-01-09 5.973855 5.817374
2018-01-10 5.984104 5.821251
2018-01-11 5.992415 5.823474
2018-01-12 6.003120 5.824976
2018-01-15 6.024703 5.828567
2018-01-16 6.046347 5.830474
2018-01-17 6.067316 5.835606
2018-01-18 6.096103 5.842811
2018-01-19 6.125558 5.849993
2018-01-22 6.147142 5.849045
2018-01-23 6.171557 5.850669
2018-01-24 6.195099 5.863637
2018-01-25 6.210929 5.877712
2018-01-26 6.223416 5.887375
2018-01-29 6.230726 5.897447
2018-01-30 6.236587 5.901649
2018-01-31 6.247322 5.906545
2018-02-01 6.256862 5.913355
execute_factor
会调用因子计算引擎来计算因子值。
自定义横截面算子
上面我们定义了一个非横截面类型的算子,下面我们看看如何定义一个横截面算子。系统提供了一个行业中性化的算子,INDUSTRY_NEUTRALIZE
,这个算子采用的是申万一级行业分类;现在希望使用中信行业分类,为此我们需要定义一个算子:
import pandas as pd
import rqdatac
from rqfactor.cross_sectional import UnaryCrossSectionalFactor
def zx_industry_neutralize(df):
# 横截面算子在计算时,输入是一个 pd.DataFrame,其 index 为 trading date,columns 为 order_book_id
latest_day = df.index[-1]
# 事实上我们需要每个交易日获取行业分类,这样是最准确的。不过这里我们简化处理,直接用最后一个交易日的行业分类
# 无需担心 rqdatac 的初始化问题,在因子计算引擎中已经有适当的初始化,因此这里可以直接调用
industry_tag = rqdatac.zx_instrument_industry(df.columns, date=latest_day)['first_industry_name']
# 在处理时,对 inf 当做 null 处理,避免一个 inf 的影响扩大
with pd.option_context('mode.use_inf_as_null', True):
# 每个股票的因子值减去行业均值
result = df.T.groupby(industry_tag).apply(lambda g: g - g.mean()).T
# reindex 确保输出的 DataFrame 含有输入的所有股票
return result.reindex(columns=df.columns)
def ZX_INDUSTRY_NEUTRAILIZE(f):
return UnaryCrossSectionalFactor(zx_industry_neutralize, f)
UnaryCrossSectionalFactor
封装了横截面算子的一些细节,其原型如下:
UnaryCrossSectionalFactor(func, factor, *args, **kwargs)
其中 args
, kwargs
是func
除df
外的其他参数,计算引擎在调用func
时,会一并传入。
我们来试试这个新的算子:
In[]:
f4 = ZX_INDUSTRY_NEUTRAILIZE(Factor('pb_ratio'))
execute_factor(f4, index_components('000300.XSHG', '20180201'), '20180101', '20180201')
Out[]:
002508.XSHE 601727.XSHG 600362.XSHG
2018-01-02 4.772367 -1.187943 -2.622838
2018-01-02 4.772367 -1.187943 -2.622838
......
自定义算子方面我们就介绍到这里;更详细的信息可以参考附录中信息,详细列出了rqfactor
提供的相关工具函数。
# 自定义基础因子
自定义算子解决的是自定义转换方法的问题,自定义基础因子解决的则是材料问题。我们来看一个实际的例子:股票的日内波动率,也就是计算每个交易日分钟线的收盘价的波动率。我们来看一下代码:
import numpy as np
import rqdatac
# 所有自定义基础因子都是 UserDefinedLeafFactor 的实例
from rqfactor.interface import UserDefinedLeafFactor
# 计算因子值
def get_factor_value(order_book_id, start_date, end_date):
"""
@param order_book_id: 股票/指数代码,如 000001.XSHE
@param start_date: 开始日期,pd.Timestamp 类型
@param end_date: 结束日期,pd.Timestamp 类型
@return 一维的 np.ndarray, [start_date, end_date] 的因子值;注意,仅包含交易日
"""
data = rqdatac.get_price(order_book_id, start_date, end_date, fields='close', frequency='1m')
if data is None or data.empty:
return None
result = data.groupby(lambda d: (d.year, d.month, d.day)).apply(lambda g: g.pct_change().std())
return result.values
f5 = UserDefinedLeafFactor('day_volatility', get_factor_value)
UserDefinedLeafFactor
的原型如下:
UserDefiendLeafFactor(name, func)
其中,参数name
是因子名称,func
则是因子值的计算方法,其原型如上面代码中注释所示。
我们来使用一下这个因子:
In[]:
execute_factor(f5, ['000001.XSHE', '600000.XSHG'], '20180101', '20180201')
Out[]:
000001.XSHE 600000.XSHG
2018-01-02 0.001672 0.000872
2018-01-03 0.001680 0.000772
2018-01-04 0.001232 0.000767
2018-01-05 0.000830 0.000639
2018-01-08 0.000943 0.000619
2018-01-09 0.000999 0.000585
2018-01-10 0.001251 0.001110
2018-01-11 0.001203 0.000852
2018-01-12 0.001065 0.000694
2018-01-15 0.001562 0.000817
2018-01-16 0.001791 0.000909
2018-01-17 0.002437 0.001630
2018-01-18 0.001841 0.001025
2018-01-19 0.001785 0.001460
2018-01-22 0.001730 0.001036
2018-01-23 0.001777 0.001054
2018-01-24 0.002149 0.002010
2018-01-25 0.001493 0.001465
2018-01-26 0.001483 0.001591
2018-01-29 0.001488 0.001163
2018-01-30 0.001303 0.001158
2018-01-31 0.001268 0.001162
2018-02-01 0.002066 0.001315
这时候,f5
作为一个自定义因子,已经可以如其他基础因子一样使用了:
In[]:
execute_factor(f5 * Factor('pb_ratio'), ['000001.XSHE', '600000.XSHG'], '20180101', '20180201')
Out[]:
000001.XSHE 600000.XSHG
2018-01-02 0.001771 0.000765
2018-01-03 0.001732 0.000674
2018-01-04 0.001263 0.000670
2018-01-05 0.000853 0.000559
2018-01-08 0.000945 0.000542
2018-01-09 0.001010 0.000512
2018-01-10 0.001303 0.000998
2018-01-11 0.001247 0.000760
2018-01-12 0.001116 0.000619
2018-01-15 0.001715 0.000734
2018-01-16 0.001966 0.000809
2018-01-17 0.002682 0.001473
2018-01-18 0.002096 0.000936
2018-01-19 0.002043 0.001333
2018-01-22 0.001932 0.000913
2018-01-23 0.002013 0.000938
2018-01-24 0.002433 0.001882
2018-01-25 0.001639 0.001380
2018-01-26 0.001611 0.001472
2018-01-29 0.001581 0.001080
2018-01-30 0.001375 0.001047
2018-01-31 0.001377 0.001056
2018-02-01 0.002241 0.001207
# 附录-自定义算子参考
非横截面算子
非横截面算子又可以分为两种,一种算子计算的结果只与输入因子的当期值有关,这种算子输出的因子值长度与输入因子值相同,这种我们称为简单算子,如LOG
, MAX
;另一种则是根据输入因子的一个时间序列进行计算,如最近 20 个交易日的均值,这种因子我们称为滑动窗口算子。
对于上面两种算子,我们提供了一些预定义的类:
简单算子
CombinedFactor(func, *factors)
: 定义在rqfactor.interface
中;其接受的func
原型为func(*series)
;
滑动窗口算子
RollingWindowFactor(func, window, factor)
: 定义在rqfactor.rolling
中;func
函数原型为def func(series, window)
;CombinedRollingWindowFactor(func, window, *factors)
: 定义在rqfactor.rolling
中,接受多个因子作为输入,func
函数原型为def func(window, *series)
.
滑动窗口算子中有一种特殊情况:封装talib
中的函数形成的算子。talib
函数返回的数组长度与输入一致,数组前几项为nan
:
In[]:
import talib
talib.MA(np.full(100, 1.0), 10)
Out[]:
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1.])
我们需要将开头的nan
除去。为此我们提供了专门针对talib
的类:
TALibFactor(func, factor, window)
: func
为talib
中函数,window
为窗口大小。
横截面算子
对于横截面算子,我们提供了以下预定义的类:
UnaryCrossSectionalFactor(func, factor, *args, **kwargs)
: 定义在rqfactor.cross_sectional
中,其中func
的原型为func(df, *args, **kwargs)
;CombinedCrossSectionalFactors(func, *factors)
: 定义在rqfactor.cross_sectional
中,其中func
的原型为func(*dfs)
.
# 计算方法说明
# 工具函数计算
-:是指数平滑移动平均,若,则 ,其中表示本周期值,表示上一周期值。
-:是指线性加权移动平均,若,则
其中表示本周期值,表示上一周期值。
# 离群值处理
离群值处理是调整因子值中的离群值至上下限(Winsorzation 处理),其中上下限由离群值判断的标准给出,从而减小离群值的影响力。离群值的判断标准有三种,分别为、、百分位法。
- 法:又称为绝对值差中位数法(Median Absolute Deviation)。是一种采用计算先需计算所有因子与平均值之间的距离总和来检测离群值的方法。处理的逻辑是:第一步,找出所有因子的中位数;第二步,得到每个因子与中位数的绝对偏差值;第三步,得到绝对偏差值的中位数;最后,确定参数,从而确定合理的范围为,并针对超出合理范围的因子值做如下的调整:
- 法:又称为标准差法。标准差本身可以体现因子的离散程度,是基于因子的平均值而定的。在离群值处理过程中,可通过用来衡量因子与平均值的距离。标准差法处理的逻辑与 MAD 法类似,首先计算出因子的平均值与标准差,其次确认参数 n(这里选定 n = 3),从而确认因子值的合理范围为,并对因子值作如下的调整:
- 百分位法:计算的逻辑是将因子值进行升序的排序,对排位百分位高于 97.5%或排位百分位低于 2.5%的因子值,进行类似于
MAD
、3σ
的方法进行调整。
# 标准化处理
标准化处理是一个去除量纲的方法,能够使得股票池中不同股票在每期的因子值的横截面数据都能有可比性,这里用到了 Z-score
的方法,将因子值的均值调整为 0,标准差调整为 1。
标准化处理基于原始数据的均值和标准差,处理的逻辑是因子值减去均值后,再除以标准差。公式如下:
公式中,为因子的均值,为因子的标准差。
# 行业中性化处理
行业中性化处理具体做法是在每个时间截面上用所有股票的数据做横截面回归方程:
其中,为股票的 alpha 因子,为行业虚拟变量,即如果股票属于行业则暴露度为 1,否则为 0,而且每个股票仅属于一个行业,以上述回归方程的残差项作为原因子在中性化后的新因子。
# IC 计算方法
IC(Information Coefficient)为信息系数。IC 值能够很好的反映因子对下期收益率的预测能力,IC 值越高,就表明该因子在该期对股票收益的预测能力越强。IC 的计算方法有两种,如下:
-,又称为皮尔逊相关系数,是指在某个时点上,给定股票池,股票池中所有股票的因子暴露值与下一期因子回报的截面相关系数。
公式中,为期的因子值,为期的股票收益率。
-,又称为斯皮尔曼相关系数,是指在某个时点上,给定股票池,股票池中所有股票的因子暴露值排名与其下期回报排名的截面相关系数。
公式中,为期的因子暴露值排名,为期的因子收益率排名。
两者的区别在于,更常见,其显著性检验假设两组随机变量近似服从正态分布;对于两组随机变量的分布没有作出假设,且受离群值影响较小。