-
Notifications
You must be signed in to change notification settings - Fork 36
expr_codegen简易教程
仿WorldQuant Alpha101
表达式转译工具。简单!高效!开源!
量化研究员一定很羡慕WorldQuant
表达式在因子挖掘的便捷,特别是用pandas
来实现时序与截面混合的因子。到Github
上找了不少Alpha101
的代码,但都要对表达式手工翻译,工作量大易出错。
有一次忽然灵光一闪,想到用装饰器可以实现时序与截面算子的提前分组。这就是ta_cn
项目的来历。但使用了一段时间后,还是发现很大的不足。
- alpha101中有大量重复表达式,希望能化简避免重复计算
- 装饰器中有
groupby
,每个算子都分组一次,应当归集起来一起groupby
由于pandas+ta_cn
速度还是不够理想,开始转向polars
。以前的表达式翻译成polars
又是巨大的工作量。
又一次偶然想到sympy
有表达式化简功能,开始翻阅文档,发现还有cse
功能,开始验证可行性,最终推出expr_codegen
,完全解决了ta_cn
的缺点。
并将其中的算子代码独立成了polars_ta
。
一开始算子名都按Alpha101
,比如rank
,但后来发现自定义因子比较麻烦,所以约定算子有前缀。
官方源
pip install expr_codegen -i https://pypi.org/simple -U
pip install polars_ta -i https://pypi.org/simple -U
国内镜像
pip install expr_codegen -i https://mirrors.aliyun.com/pypi/simple -U
pip install polars_ta -i https://mirrors.aliyun.com/pypi/simple -U
免安装使用
常用函数只有一个codegen_exec
- 代码生成(codegen)
- output_file: 用于调试,或脱离codegen直接运行
- None: 生成的代码只存在于内存中
- sys.stdout: 打印到标准输出
- path: 保存到文件
- date/asset: 日期和资产字段名。用于分组和排序
- style: 代码风格。
- pandas: 使用
pd.DataFrame.groupby
- polars_group: 使用
pl.DataFrame.group_by
- polars_over(推荐): 使用
Expr.over
- pandas: 使用
- codes: 需转译的代码块。支持传入多块代码
- 函数块:可以利用
IDE
的智能补全,方便编写 - 字符串:可利用循环拼接表达式。支持持无法过Python语法检查的三元表达式
- 函数块:可以利用
- extra_codes: 原样复制到目标代码中的字符串
- output_file: 用于调试,或脱离codegen直接运行
- 执行生成的代码(exec)
-
df
不为None
就会exec
执行生成的代码 -
df
可以为pd.DataFrame/pl.DataFrame/pl.LazyFrame
之一- date: 日期时间。对时间粒度无要求,可以日月年,也可以秒分时。在横截面计算时用于分组,在时序计算时用于排序
- asset: 资产。在时序指标计算前用于分组。
- other: 其他特征/因子
- run_file: 可跳过转译,直接运行文件
- True: 直接运行
output_file
文件 -
out.py
: 直接运行out.py
文件 -
out
: 直接运行out
模块
- True: 直接运行
-
-
ts_xxx()
时序算子: 先groupby(asset)
,然后sort(date)
,最后调用ts_xxx
-
cs_xxx()
截面算子: 先groupby(date)
,然后调用cs_xxx
-
gp_xxx(group,)
分组算子: 先groupby(date, group)
,然后调用cs_xxx
-
xxx()
:不用groupby
直接调用的算子 -
_xxx
临时特征: 提取公共子表达式时会自动生成,参与特殊的语法调用等功能,事后会被删除 -
C?T:F
支持C语言的三元表达式。注意:仅能通过字符串输入 -
A,B,C=MACD()
。支持多输出的算子 -
CLOSE[1]
。表示昨天收盘价,底层会转换成ts_delay(CLOSE,1)
alpha003=-1 * ts_corr(cs_rank(open), cs_rank(volume), 10)
相当简单,直接复制到https://exprcodegen.streamlit.app
,然后下载到本地,导入到项目中
from output import main
# ...
df = main(df)
#...
注意:open
这是原始价?还是后复权价?
自己开发了一个算子库,如何使用?
- 保证
import
到项目中后能被Python调用成功 - 保证函数名符合
ts_/cs_
等前缀规则
- 通过代码区传入
def _code_block_1():
from my_lib import my_func as ts_func
A = ts_func(OPEN, CLOSE, 10)
df = codegen_exec(df, _code_block_1, output_file='output.py')
可以检查output.py
中的代码是否正确。之后可直接调用省去转译过程。
- 通过修改模板,备份
polars_over/template.py.j2
,修改template2.py.j2
,文件前插入from my_lib import my_func as ts_func
def _code_block_1():
A = ts_func(OPEN, CLOSE, 10)
df = codegen_exec(df, _code_block_1, output_file='output.py',template_file='template2.py.j2')
所以,polars_ta
本质只是一个符合前缀规则的算子库。用户可以开发自己的算子库
聚宽数据字段名与本工具默认设置不同
- 提前规范字段名
df = df.rename({'time': 'date', 'code': 'asset'})
- 转译时指定
df = codegen_exec(df, _code_block_1, date='time', asset='code')
除了生成因子,是否能生成机器学习的标签?
标签是未来数据,本质上只要平移即可,[-1]
最简洁
收益率_第一天开盘入场_第二天收盘出场 = CLOSE[-2]/OPEN[-1]-1
Alpha191一条条翻译成指定前缀规则的算子工作量太大了
已经提供了ast
翻译代码的示例供研究
https://github.com/wukan1986/alpha_examples/blob/main/transformer/alpha191_transformer.py
生成的代码如何下断点调试?分两步
# 生成文件
df = codegen_exec(df, _code_block_1, output_file="output1.py", run_file=False)
# 跳过转译,以模块方式调用,run_file后缀名非`.py`
df = codegen_exec(df, _code_block_1, output_file="output1.py", run_file="output1")
公式已经定型不用再改,如何最小改动跳过转译,长期使用?
# 生成文件
df = codegen_exec(df, _code_block_1, output_file="output1.py", run_file=False)
# 跳过转译,以文件方式运行
df = codegen_exec(df, _code_block_1, output_file="output1.py", run_file=True)
如何使用三元表达式?
def _code_block_1():
A = if_else(OPEN>CLOSE,1,0)
codes="""
B=OPEN>CLOSE?1:0
"""
df = codegen_exec(df, _code_block_1, codes)
以上两种方法是等价的