"qlib ailabx量化投资平台" 建设第 16 篇。
这是100天小目标中的第16天。
今天的主题是手动特征工程,qlib的特征工程是基于表达式的,不像传统策略使用pandas的dataframe里的计算函数,或者Ta-lib来计算——传统方式适合因子数比较少,比如一个策略不会超过3-5个因子,而AI量化动辄上百个因子,如果一个个写那维护成本太高了(qlib自身就内置了两个handler, 一个158个因子,另一个360个因子)。
01 dataset数据集的加载方式拆解
config = {
"class": "DatasetH",
"module_path": "qlib.data.dataset",
"kwargs": {
"handler": {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": data_handler_config,
},
"segments": {
"train": ("2008-01-01", "2014-12-31"),
"valid": ("2015-01-01", "2016-12-31"),
"test": ("2017-01-01", "2020-08-01"),
},
},
}
ds = init_instance_by_config(config)
官方的例子使用了大量的init_instance_by_config,我们可以跟进代码看看细节。
主要我们需要把handler变成我们自己的。
DatasetH(带Handler的Dataset)的初始化函数如下:
def __init__(
self, handler: Union[Dict, DataHandler], segments: Dict[Text, Tuple], fetch_kwargs: Dict = {}, **kwargs
):
参数handler, segments,fetch_kwargs以及**kwargs。
一般我们只管前两个参数:handler和segments。
我们可以把handler由dict变成DataHandler来加载:
def load_dataset(data_handler):
ds = DatasetH(data_handler, segments={"train": ('20180101', '20181231'), "valid": ('20190101', '20191231')})
return ds
02 Handler的加载方式
data_handler_config = {
"start_time": "2008-01-01",
"end_time": "2020-08-01",
"fit_start_time": "2008-01-01",
"fit_end_time": "2014-12-31",
"instruments": self.market,
}
{
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": data_handler_config,
}
在路径qlib.contrib.data.handler下的Alpha158类,参数kwargs是一个dict。
Alpha158继承自DataHandlerLP(带处理器的Handler的意思)
class Alpha158(DataHandlerLP):
def __init__(
self,
instruments="csi500",
start_time=None,
end_time=None,
freq="day",
infer_processors=[],
learn_processors=_DEFAULT_LEARN_PROCESSORS,
fit_start_time=None,
fit_end_time=None,
process_type=DataHandlerLP.PTYPE_A,
filter_pipe=None,
inst_processor=None,
**kwargs,
):
在初始化的函数里,一是训练和推理的预处理器, 二是初始化了一个QlibDataLoader,传给
infer_processors = check_transform_proc(infer_processors, fit_start_time, fit_end_time)
learn_processors = check_transform_proc(learn_processors, fit_start_time, fit_end_time)
data_loader = {
"class": "QlibDataLoader",
"kwargs": {
"config": {
"feature": self.get_feature_config(),
"label": kwargs.get("label", self.get_label_config()),
},
"filter_pipe": filter_pipe,
"freq": freq,
"inst_processor": inst_processor,
},
}
super().__init__(
instruments=instruments,
start_time=start_time,
end_time=end_time,
data_loader=data_loader,
infer_processors=infer_processors,
learn_processors=learn_processors,
process_type=process_type,
)
我们简化为直接调用基类是可以的:

这段是初始化data_loader的参数:

03 QlibDataLoader的初始化

外部传入的参数如下:

04 总体代码:

加载dataset数据集是分层独立的,
dataloader加载数据,并负责特征与标注。
datahandler加上了预处理,比如数据标准化,填充不存在的值之类的。dataset用于数据集划分,比如训练集,测试集,回测集等。

05 预处理器
顾名思义,infer=推理, learn=训练。都是处理器,但使用的阶段不同而已。


MinMax正则,这是机器学习里常用的数据预处理,简单理解就是数据“归一化”,(X-Xmin) /(Xmax -Xmin),这里就与量纲无关了。
两上小问题:
为何训练阶段与测试阶段使用的处理器不同?
在官方示例里的alpha158里,默认的处理器如下:

为何正则化需要fit_start_time给定日期范围?
小结:
今天我们主要关注的是dataset如何构建,在官方完全一个config配置之余,通过代码去看背后具体发生了什么,以及这些参数的意义。
在一开始学习及后续使用,不建议用alpha158或者alpha360。
初学用这个计算量太大,太慢;
后续实战重点就是建立自己的alpha因子。
这两者作为一个benchmark是可以的。
明天要使用bigquant里的特征来建立策略。