Skip to content

Latest commit

 

History

History
419 lines (308 loc) · 27.1 KB

README_CN.md

File metadata and controls

419 lines (308 loc) · 27.1 KB

WorldQuant 101 Alpha 因子指标库

挖掘和计算 alpha 因子在量化金融领域有着重要的意义。在著名论文 101 Formulaic Alphas 中,作者给出了世界顶级量化对冲基金 WorldQuant 所使用的 101 个因子公式。为方便用户在 DolphinDB 中计算因子,本文使用 DolphinDB 脚本实现了所有101个因子的函数,并封装在 DolphinDB 模块 wq101alpha (wq101alpha.dos) 中。

该模块的实现具有三大优势:

  • 性能优越:性能远优于传统的 Python 实现方式。DolphinDB的性能中位数为Python的15.5倍,其中,27.5%的因子超100倍。
  • 批流一体:模块中定义的因子函数,既可以用于历史计算,又可以用于流式增量计算。
  • 实现简单:直接使用论文中所列公式实现,无需用户自定义函数,便于用户后续自行修改以产生定制化因子。

本教程包含的所有代码兼容 DolphinDB 2.00.9,1.30.21 及以上版本。

本教程保留了适配 2.00.8 版本的文件,并以 _2.00.8 为后缀。

本教程包含内容:

1. 函数的命名与入参规范

  • wq101alpha 模块 中的所有函数命名规则为WQAlpha + 因子序号, 如WQAlpha1,WQAlpha2。

  • 每一个因子的入参字段有所不同,具体参考附录1-因子入参一览表。所有需要用到的输入字段如下:

参数名称 / 标准字段名称 参数含义 是否为行业信息字段
tradetime 交易时间 ×
securityid 股票代码 ×
open 开盘价 ×
close 收盘价 ×
high 最高价 ×
low 最低价 ×
vol 交易量 ×
vwap 成交量加权平均价格 ×
cap 市值
indclass 行业类型
  • 在之前的版本中,由于包含行业信息的因子计算步骤与不含行业信息的因子有所不同,wq101alpha 模块中根据入参字段是否包括行业信息字段,将因子分为了两类:行业信息因子和非行业信息因子。
  • 当前版本已经统一了行业信息因子和非行业信息因子的输入输出均为矩阵类型。
因子类型 输入数据类型 输出数据类型 因子序号
行业信息因子 矩阵 矩阵 48,56,58,59,63,67,69,70,76,79,80,82,87,89,90,91,93,97,100
非行业信息因子 矩阵 矩阵 其余所有因子

2. 使用范例

本章节将通过环境配置、数据准备、计算调用方法等方面具体介绍 wq101alpha.dos 模块的具体用法。

2.1 环境配置

把附件的 wq101alpha.dos 放在 [home]/modules 目录下,[home] 目录由系统配置参数 home 决定,可以通过 getHomeDir() 函数查看。

有关模块使用的更多细节,请参见:DolphinDB 教程:模块

2.2 数据准备

如若没有数据,可参考 日频数据及分钟频数据建库建表 生成模拟的日频数据 ;对于行业信息表的模拟数据生成,可参考 行业信息数据建库建表

如若已有数据,需保证现有的数据表中的字段名与模块中字段名一致,并将日频信息和行业信息做一个表连接。为方便使用,本教程准备了一个辅助模块 prepare101.dos 来帮助统一字段名。若要使用辅助模块,请将 prepare101.dos 放在 wq101alpha 模块 同一目录下。辅助模块中的prepareData函数的作用是将数据与标准字段的名称对齐,其中,rawData为非行业信息表,infoData为行业信息表,startTime与endTime 为需要的数据的起始时间和结束时间,其余参数为现有字段名与标准字段的对应名称:

如若用户采用的数据字段名与 输入字段 中的标准字段名一致,无需调用准备函数 prepareData 。

载入模块和数据方法如下,data 即为准备好的数据:

use wq101alpha
use prepare101
login('admin', '123456')
rawData = loadTable("dfs://k_day_level", "k_day")
infoData = select * from loadTable("dfs://info", "info_data")
startTime = timestamp(2010.01.01)
endTime = timestamp(2010.01.31)
data = prepareData(rawData=rawData, startTime=startTime, endTime=endTime, securityidName="securityid", tradetimeName="tradetime", openName="open", closeName="close", highName="high", lowName="low", volumeName="vol", vwapName="vwap", capName="cap", indclassName="indclass", infoData=infoData, infoSecurityidName="securityid")

2.3 矩阵入参计算行业因子与非行业因子

在当前版本的 wq101alpha 模块中,全部因子都会运用到横向与纵向的计算。用户需先准备矩阵,再调用对应的 WQAlpha# 函数,返回的结果也同为矩阵。由于不同因子计算时用到的参数不同,用户需通过查询 附录1-因子入参一览表 来确定所需的参数。

计算方法如下:

use wq101alpha
input1 = exec close from data where tradetime between startTime : endTime pivot by tradetime, securityid
res1 = WQAlpha1(input1)

input2 = dict(`vol`close`open, panel(data.tradetime, data.securityid, [data.vol, data.close, data.open]))
res2 = WQAlpha2(input2.vol, input2.close, input2.open)

为了更加便于用户计算,省去查询参数这一步骤,因子计算准备函数模块 prepare101.dos提供了所需的矩阵准备函数 prepare# 和计算函数 calAlpha#,用户可将其作为模块导入。prepare#的作用是调用对应模块计算函数所需要的字段,calAlpha#的作用是将prepare#以及wqAlpha#封装。

以world quant alpha 第 1 号因子为例,辅助准备模块内的函数如下:

def prepare1(data, startTime, endTime){
    p = exec close from data where tradetime between startTime : endTime pivot by tradetime, securityid
    return p
}

def calAlpha1(data, startTime, endTime){
    input = prepare1(data, startTime, endTime)
    return WQAlpha1(input)
}
//调用方法如下:
use prepare101

res = calAlpha1(data, startTime, endTime)

非行业因子中的41、54、101号因子除了可以用矩阵入参之外,也可以向量入参用 SQL 调用,如101号因子也可以这样调用:

use wq101alpha 
res = select tradetime, securityid, `alpha101 as factorname, WQAlpha101(close, open, high, low) as val from data where tradetime between startTime : endTime

用户可以按需灵活选取调用方法。

对于行业信息因子,论文中用了多种行业分类,如IndClass.subindustry,IndClass.industry, IndClass.sector等,为了简便起见,本模块统一只用IndClass这一个字段做行业中性化。

3. 因子的存储

因子的存储可以参考因子最佳实践中的因子存储章节。wq101alpha 模块计算的因子返回的数据均为矩阵格式,本章节将以宽表形式存储因子为例,完整代码可参考Alpha101计算存储全流程代码汇总

存储面板数据,需要先将面板数据转换为表,而后进行存储。以第 1 号因子为例,计算并存储因子,示例代码如下:

// 计算alpha 1号因子,得到的矩阵存储在res中
res = calAlpha1(data, startTime, endTime)

// 将res转换成表并存储在因子宽表中
writePanelInWideTable(res, `alpha1)

其中 writePanelInWideTable 的实现可见 Alpha101计算存储全流程代码汇总

4. 性能对比

本章节将用 DolphinDB 实现的 wq101alpha 模块与 python 中用 pandas, numpy 模块实现的 WorldQuant alpha 因子分别对比计算性能。

本小节测试显示,DolphinDB 的 wq101alpha 模块的性能显著优于使用 python 的 pandas 与 numpy 模块的实现。

  • 测试设备

CPU:Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz

操作系统: 64 位 CentOS Linux 7 (Core)

  • 使用数据

本节测试使用一年模拟日频数据进行性能对比

4.1 DolphinDB 与 Python Pandas 性能对比

本节用一年模拟日频数据对比了 wq101alpha 模块和 101因子的Pandas实现 计算101个因子的性能。

wq101alpha 模块中的函数计算并计时,核心代码如下,完整的脚本可参考 wq101alpha 模块性能测试

times = array(INT, 0)
defs()
for (i in 1:102){
    if (i in passList) times.append!(NULL)
    else{
        print(i)
        alphaName = exec name from defs() where name = "wq101alpha::WQAlpha"+string(i)
        alphaSyntax = exec syntax from defs() where name = "wq101alpha::WQAlpha"+string(i)
        function = alphaName + alphaSyntax
        t1 = time(now())
        res = parseExpr(function[0]).eval()
        t2 = time(now())
        times.append!(t2 - t1)
    }
}

用 Python 脚本计算并计时,核心代码如下,完整的脚本可参考 Python alpha 101 性能测试

times = []

nofunc = [48, 56, 58, 59, 63, 67, 69, 70, 76, 79, 80, 82, 87, 89, 90, 91, 93, 97, 100]

for i in range(1, 102):
    if i in nofunc:
        times.append('no function')
        continue
    else:
        factor = getattr(Alphas, "alpha{:03d}".format(i))
    try:
        t1 = time.time()
        res = factor(stock)
        t2 = time.time()
        times.append(t2 - t1)
    except Exception:
        times.append('error')

通过两个性能测试脚本,可得到 wq101alpha 模块和 Python 脚本计算101个因子的运行耗时,完整结果可见 性能对比结果。筛除 Python 脚本中未实现的因子和实现有误的因子后,可供比较的因子共 69 个。对比结果见下表,单位为毫秒 ms。

因子ID# DolphinDB Python 耗时比(py/ddb) ddbWin 因子ID# DolphinDB Python 耗时比(py/ddb) ddbWin
1 86 68,837 800.43 true 38 91 89,487 983.38 true
2 229 2,117 9.24 true 40 73 2,379 32.59 true
3 140 2,018 14.41 true 41 84 11 0.13 false
4 75 89,440 1,192.53 true 42 97 218 2.25 true
5 120 600 5. true 43 91 165,954 1,823.67 true
6 21 1,765 84.05 true 44 82 1,918 23.39 true
7 148 72,001 486.49 true 45 170 4,853 28.55 true
8 112 1,513 13.51 true 46 35 57 1.62 true
9 50 714 14.27 true 47 252 1,156 4.59 true
10 128 808 6.31 true 49 33 37 1.13 true
11 145 898 6.2 true 50 235 2,475 10.53 true
12 15 10 0.69 false 51 36 38 1.05 true
13 213 1,784 8.38 true 52 131 91,360 697.4 true
14 113 1,987 17.59 true 53 29 28 0.97 false
15 147 2,572 17.49 true 54 175 178 1.02 true
16 208 1,776 8.54 true 55 216 2,997 13.88 true
17 177 174,055 983.36 true 60 154 72,081 468.06 true
18 104 2,417 23.24 true 61 147 2,614 17.78 true
19 72 440 6.11 true 62 354 3,204 9.05 true
20 262 439 1.68 true 64 191 2,956 15.48 true
21 78 2,296 29.43 true 65 181 2,968 16.4 true
22 97 2,358 24.31 true 68 247 81,582 330.29 true
24 64 686 10.72 true 74 380 4,761 12.53 true
25 96 500 5.21 true 75 279 4,461 15.99 true
26 61 182,340 2,989.18 true 78 384 5,204 13.55 true
27 226 2,573 11.38 true 81 519 61,954 119.37 true
28 45 2,155 47.9 true 83 209 1,107 5.3 true
29 406 89,515 220.48 true 84 152 80,908 532.29 true
30 82 832 10.15 true 85 303 184,645 609.39 true
32 53 2,146 40.5 true 86 123 75,681 615.3 true
33 88 148 1.68 true 94 169 221,036 1,307.91 true
34 254 1,382 5.44 true 95 287 67,899 236.58 true
35 131 249,748 1,906.47 true 99 203 4,758 23.44 true
36 388 92,303 237.89 true 101 9 20 2.2 true
37 148 1,953 13.2 true

从上表结果可以看出,使用 DolphinDB 实现的 wq101alpha 模块的计算效率远远高于 Python 的实现。DolphinDB 的性能中位数为 Python 的15.5倍,其中,27.5%的因子超100倍。

其中因子41的实现,DolphinDB 慢于 Python。原因是 pow 函数的实现有些许不同。DolphinDB 会在后续版本中提升这个内置函数的性能。

4.2 DolphinDB 与 Python numpy 的性能对比

考虑到用 python pandas Dataframe 实现因子的计算性能效率可能不及 numpy ,为公平起见,本节抽取了11个 pandas 实现耗时特别久的因子,重新用 numpy 实现,具体代码见部分101因子的numpy实现。 DolphinDB 与 numpy 在实现这些因子的性能比较如下:

因子ID# ddb pandas numpy 耗时比(pandas/ddb) 耗时比(numpy/ddb)
1 86 68,837 418 800.4 4.9
4 75 89,440 54,417 1,192.5 725.6
5 120 600 218 5.0 1.8
7 148 72,001 39,472 486.5 266.7
8 112 1,513 265 13.5 2.4
9 50 714 152 14.3 3.0
17 177 174,055 93,704 983.4 529.4
29 406 89,515 47,100 220.5 116.0
38 91 89,487 46,257 983.4 508.3
52 131 91,360 46,715 697.4 356.6
83 209 1,107 464 5.3 2.2

从结果上来看, numpy 的实现确实比 pandas dataframe 实现快一些,但是由于窗口函数 numpy 中是用 numpy.lib.stride_tricks.sliding_window_view 实现的,对于窗口计算没有优化,故性能并没有显著提升。

综合来看,DolphinDB 在实现 WorldQuant 101 alpha 因子上有非常大的优势,性能卓越。

5. 正确性验证

目前 python 101个alpha因子不存在统一的实现方式。本文基于 101因子的Python实现 中的实现方式,对部分报错代码进行调整(详情见 python模块代码),与 wq101alpha 模块进行对比,部分验证了 wq101alpha 模块的正确性。由于在验证过程中发现诸多问题,该小节中将分类验证并探讨该模块实现方式与 Python 实现方式的不同。

验证所用的数据为一年模拟日频数据中的前十支股票。用 DolphinDB 验证脚本 在 DolphinDB 及 Python 验证脚本 在 python 中跑完因子后,截取第一支股票的数据做对比。

最后的验证步骤可参考正确性验证脚本

5.1 非行业因子验证

结果显示50%的因子DolphinDB与pandas计算结果一致。其余因子通过研究,发现 wq101alpha 模块与 Python pandas 实现有以下差异:

  • 空值处理:wq101alpha 模块中,对一个滚动 / 累积窗口长度为 k 的函数,每组最初的 (k-1) 个位置的结果均为空;Python 实现中,某些因子会默认将空值取0,某些因子不作改动。

  • Rank实现差异:如 alpha 4 号因子,在 DolphinDB 中的结果与 python 中的结果相差1,其原因是Rank的实现在 DolphinDB 中是从0开始排序,而在 python 中是从1开始排序。

  • 结果不一致:经人工检验,Python 实现中存在一定错误。例如 Python 脚本计算 alpha 27 号因子的结果全为1,经检验为 Python 实现有错误。

5.2 行业因子验证

该类因子目前少见 Python 实现,且因该模块中考虑到实际使用的便利性,已调整论文中的实现方式,故不作比较。

6. 实时流计算实现

World Quant 101 alpha 因子中大多数因子的实现方式都较为复杂,在流计算中,需要创建多个引擎进行流水线处理来完成。DolphinDB 提供了一个解析引擎 streamEngineParser 来代替人工创建并串联多个引擎,大大提高效率。

此功能从 DolphinDB 1.30.20 和 2.00.8 开始支持。

使用 wq101alpha 模块进行流计算时,因子的定义无需修改,直接在流引擎 streamEngineParser 中调用即可。

完整World Quant 101 alpha流计算流程代码可查看 WQ101alpha流计算全流程 。这里以 WQAlpha 1 为例,演示如何调用 wq101alpha 模块,实现流计算:

首先定义输入输出的表结构:

inputSchemaT = table(1:0, ["SecurityID","TradeTime","close"], [SYMBOL,TIMESTAMP,DOUBLE])
resultStream = table(10000:0, ["SecurityID","TradeTime", "factor"], [SYMBOL,TIMESTAMP, DOUBLE])

调用 wq101alpha 模块,并在 streamEngineParser 中使用 WQAlpha1 函数:

use wq101alpha
metrics = <[WQAlpha1(close)]>
streamEngine = streamEngineParser(name="WQAlpha1Parser", metrics=metrics, dummyTable=inputSchemaT, outputTable=resultStream, keyColumn="SecurityID", timeColumn=`tradetime, triggeringPattern='perBatch', triggeringInterval=4000)

部分因子可能会创建多个引擎,可以调用 getStreamEngineStat() 查看总共串联了哪些引擎:

getStreamEngineStat()

#output
ReactiveStreamEngine->
name            user        status lastErrMsg numGroups ...
-------------   ----------- ------ ---------- --------- ...
WQAlpha1Parser0 admin OK                0         0       
WQAlpha1Parser2 admin OK                0         0       

CrossSectionalEngine->
name            user  status lastErrMsg numRows ...
--------------- ----- ------ ---------- ------- ...
WQAlpha1Parser1 admin OK                0       2          

将数据注入引擎,即可在 resultStream 输出表中查看结果:

streamEngine.append!(data)
//check the result
res = exec factor from resultStream pivot by TradeTime, SecurityID

7. 小结

本教程详细介绍了 wq101alpha 模块的实现原理和用法。该模块用 DolphinDB 内置函数实现了 WorldQuant 101 alpha 因子,具有简单准确、高效快速、批流一体的特点。

8. 附录

8.1 附录1-因子入参一览表

  • 非行业信息因子
因子序号 所需参数 因子序号 所需参数 因子序号 所需参数
1, 9, 10, 19, 24, 29, 34, 46, 49, 51 close 23 high 71 vwap, vol, open, close, low
2, 14 vol, open, close 25, 47, 74 vwap, vol, close, high 72, 77 vwap, vol, high, low
3, 6 vol, open 27, 50, 61, 81 vwap, vol 73 vwap, open, low
4 low 28, 35, 55, 60, 68, 85 vol, high, low, close 75, 78 vwap, vol, low
5 vwap, open, close 31, 52 vol, close, low 83 vwap, vol, close, high, low
7, 12, 13, 17, 21, 30, 39, 43, 45 vol, close 32, 42, 57, 84 vwap, close 88, 92, 94 vol, open, close, high, low
8, 18, 33, 37, 38 open, close 36, 86 vwap, vol, open, close 95 vol, open, high, low
11, 96 vwap, vol, close 41 vwap, high, low 65, 98 vwap, vol, open
15, 16, 26, 40, 44 vol, high 53 close, high, low 99 vol, high, low
20, 54, 101 open, close, high, low 62, 64 vwap, vol, open, high, low
22 vol, high, close 66 vwap, open, high, low
  • 行业信息因子
因子序号 所需参数 因子序号 所需参数
48 close, indclass 76, 89 vwap, vol, low, indclass
56 close, cap 80 vol, open, high, indclass
58, 59 vwap, vol, indclass 82 vol, open, indclass
63, 79 vwap, vol, open, close, indclass 90 vol, close, indclass
67 vwap, vol, high, indclass 97 vwap, vol, low, indclass
69, 70, 87, 91, 93 vwap, vol, close, indclass 100 vol, close, high, low, indclass

8.2 附录2