-----想定制魔改qlib因子计算和改用小型数据文件的,希望有帮助-----
今年深圳课上接触到的alphagen, 最吸引人的部分是:仅通过表达式就可以快速生成相应因子可在表达式层面高效开展因子探索。意识到引入这个部分对我当前投研数据工作会是一个很大提升。初步了解这部分内容是使用qlib实现的而qlib使用的是MongoDB数据库。初步了解MongoDB后决定暂时放弃(目前只做股票日间交易,A股全市场daily数据1GB左右目前的parquet够用)。
细化一下研究工作域边界和内容:研究qlib快速生成相应因子代码流程,修改代码将数据源替换为现有的parquet,附带学习中型项目模式设计实现代码(当时课上都是通过Cursor来修改的),再来pandaai社区分享一下。
从课上的calculate_factors脚本开始:
因子计算的主要步骤如下:
貌似StockData类就是生成和保存数据的主要对象。这一模块来自:
数据加载的部分来自另一个叫QlibDataLoader的模块
,传入的参数基本与脚本一致,也很好理解。
QlibDataLoader模块具体位置在load.py中,长下面这样:
除了能看到各种筛选切片功能,发现其继承自DLWParser( (D)ata(L)oader (W)ith §arser for features and names),其文档描述为:Extracting this class so that QlibDataLoader and other dataloaders(such as QdbDataLoader) can share the fields。所以基本上当前这条线索是对的。
发现在定义的各种load动作中,最实质的动作来自D模块的features。
D模块来自原生的qlib.data文件
class D位置不好找,遍历很久后在结尾处发现:
大致可以认为Wrapper是一个实例工具,D就是BaseProvider的实例,这里对其进行了封装和重命名后,在其他位置出现时直接拿来用就可以。
data中实现了很多复杂的用例和功能(缓存、多线程、client…此处省略几千字,应该暂时用不上)
大致就是D.featrues是通过DatasetProvider或者其继承类的ataset/dataset_processor/inst_calculator动作实现的。然后整个流程又落脚到了ExpressionProvider的表达。
那么因子数据到底是来自stockdata还是来自expression呢?到这里彻底被绕晕了。
但ExpressionProvider的expression确实是通过get_expression_instance来实现的,如下:
从名字看这个功能应该是干些实事的。最初了解alphagen可以从文字符表达式转换成算子实施计算时感觉会要用到eval函数,在这里看到了。
之前字符的表达式通过parse_field有一个转换,用到了re模块,其对字符表达式通过正则式转换的效果如下:
转换前:[‘Mul(Sub(Greater(30.0,volume),-0.5),-0.5))’]。
转换后:Operators.Mul(Operators.Sub(Operators.Greater(30.0,Operators.Feature(“close”)),-10.0),Operators.Greater(Operators.Sub(Operators.Sub(2.0,Operators.Feature(“volume”)),-0.5),-0.5))。
发现转换后的表达式多了两个东西:Operators, Feature。这是突然想起上面导入部分的最后一行:
定位到expression.py,发现Operators是一个包含很多类实例的列表容器。
自此这里来到了整个qlib因子计算最核心的部分:
将因子计算的运算步骤分解为对应包含对应动作的类,准许运算步骤的简易复用,巧妙地抽象出了UnaryOperator、BinaryOperator、RollingOperator这些中间层,对Constant,Timedelta也有很好的设计安排。
回到本次学习的初衷之一就是替换行情数据, 留意到这里表达式中的“$volume”变成了Operators.Feature(“close”)。莫非数据源加载是在这里??
上面Dataset_Provider的dataset_processor多线程中运行了一个inst_calculator提供的,而其中最终结果data由多个Expressionprovider.expression提供。
来到定义位置:这里又有get_expression_instance(其实施动作上面提到了)。eval实例化后的parsed_expression有一个load动作。
在Expression父类中可以找到对这个动作的定义
其具体由_load_internal动作在其子类Feature中实现:
至此这些完全满足我对Feature(“close”)处理的期待,行情数据确实就是从这里加载的。
这里又要回到data.py中的FeatureProvider以及其Local××provider子类。
可以看到这里出现了remote/backend以及code_to_fname功能,这些在预研mongoDB时看到过,终于找到数据源头了。
到这里qlib因子生成流程学习可以告一段落,后面有空尝试一下精简重构。
-----未完待续-----