基础 - 数据框架
Published on
简介
Qlib的数据框架提供了友好的API用于管理与检索数据,以及高性能的数据基础设施。它是专门为量化投资设计的,例如,用户可以轻松地建立公式化alpha。
下面是使用Qlib数据工作流的一个典型的例子:
- 下载基础数据并转换为Qlib格式(后缀名为.bin)
- 使用Qlib表达式引擎建立一些基本特征,例如
Ref($close, 60) / $close
表示最近60个交易日的回报率。这一步骤通常实现在数据处理器中的数据加载器组件内 - 如果用户需要更加复杂的数据处理(如数据归一化),数据处理器模块支持用户自定义处理器。处理器可以实现表达式引擎难以支持的复杂数据处理方法
- 基于预处理的数据生成模型所需的数据集
数据预处理
Qlib格式数据
Qlib专门设计了一种数据结构用于处理金融数据,此类数据被存储于.bin文件中。
Qlib提供了两个现成的数据集:
数据集 | 美国市场 | 中国市场 |
---|---|---|
Alpha360 | √ | √ |
Alpha158 | √ | √ |
同时Qlib也提供了一个高频数据集。
Qlib格式数据集
Qlib提供脚本scripts/get_data.py
用于下载数据集。使用如下命令下载中国市场股票数据集:
# 下载日线数据
python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn
# 下载分钟线数据
python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/qlib_cn_1min --region cn --interval 1min
也可以下载美国市场股票数据集:
python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/us_data --region us
运行上述命令后,中国市场与美国市场股票数据集分别保存于~/.qlib/qlib_data/cn_data
和~/.qlib/qlib_data/us_data
目录中。
Qlib也提供一个脚本scripts/data_collector
帮助用户爬取最新数据并转换为Qlib格式。
自动更新数据
强烈建议用户先手动更新数据一次,之后设置为自动更新:
- 手动更新数据
python scripts/data_collector/yahoo/collector.py update_data_to_bin --qlib_data_1d_dir <user data dir> --trading_date <start date> --end_date <end date>
其中参数trading_date
为交易开始日期,end_date
为交易结束日期(不包含)。
- 在每个交易日自动更新数据(Linux)
执行命令crontab -e
,之后添加定时任务:
* * * * 1-5 python <script path> update_data_to_bin --qlib_data_1d_dir <user data dir>
其中脚本路径script path
为scripts/data_collector/yahoo/collector.py
。
将CSV格式转换为Qlib格式
Qlib提供脚本scripts/dump_bin.py
将CSV格式数据转换为.bin格式。假设CSV格式数据放在~/.qlib/csv_data/my_data
目录下,则可以使用如下命令进行转换:
python scripts/dump_bin.py dump_all --csv_path ~/.qlib/csv_data/my_data --qlib_dir ~/.qlib/qlib_data/my_data --include_fields open,close,high,low,volume,factor
其中–include_fields
参数指定的字段必须与CSV文件中的列名对应。
查询该命令的全部参数:
python dump_bin.py dump_all --help
CSV格式数据可以使用脚本scripts/get_data.py
下载:
# 下载日线数据
python scripts/get_data.py download_data --file_name csv_data_cn.zip --target_dir ~/.qlib/csv_data/cn_data
# 下载分钟线数据
python scripts/data_collector/yahoo/collector.py download_data --source_dir ~/.qlib/stock_data/source/cn_1min --region CN --start 2021-05-20 --end 2021-05-23 --delay 0.1 --interval 1min --limit_nums 10
也可以使用自定义CSV格式数据,但是必须满足以下条件:
- 文件名为某一标的名称,或者文件中包含一列表示标的名称,例如:
文件名为SH600000.csv或AAPL.csv等(大小写不敏感),或者文件内容为:
symbol | close |
---|---|
SH600000 | 120 |
注意在格式转换时需要指定该列名称:
python scripts/dump_bin.py dump_all ... --symbol_field_name symbol
- 文件中必需包含一列表示日期,例如:
symbol | date | close | open | volume |
---|---|---|---|---|
SH600000 | 2020-11-01 | 120 | 121 | 12300000 |
SH600000 | 2020-11-02 | 123 | 120 | 12300000 |
注意在格式转换时需要指定该列名称:
python scripts/dump_bin.py dump_all ... --date_field_name date
股票池(市场)
Qlib将股票池定义为股票列表与它们的日期范围。预定义的股票池(如csi300)可以使用如下方式导入:
python collector.py --index_name CSI300 --qlib_dir <user qlib data dir> --method parse_instruments
股票模式
目前Qlib中有两种股票模式:中国模式与美国模式,它们的不同设置如下:
模式 | 交易单位 | 涨跌阈值 |
---|---|---|
中国 | 100 | 0.099 |
美国 | 1 | 无 |
使用每种模式前均需要在代码中进行初始化:
# 初始化中国模式
from qlib.constant import REG_CN
qlib.init(provider_uri='~/.qlib/qlib_data/cn_data', region=REG_CN)
# 初始化美国模式
from qlib.config import REG_US
qlib.init(provider_uri='~/.qlib/qlib_data/us_data', region=REG_US)
数据API
数据检索
用户可以使用qlib.data
的API进行数据检索,下面是一些基本的例子:
载入指定时间范围与交易频率的交易日历:
>> from qlib.data import D
>> D.calendar(start_time='2010-01-01', end_time='2017-12-31', freq='day')[:2]
[Timestamp('2010-01-04 00:00:00'), Timestamp('2010-01-05 00:00:00')]
将市场名称解析为股票池配置:
>> from qlib.data import D
>> D.instruments(market='all')
{'market': 'all', 'filter_pipe': []}
载入指定股票池在指定时间范围内的所有标的:
>> from qlib.data import D
>> instruments = D.instruments(market='csi300')
>> D.list_instruments(instruments=instruments, start_time='2010-01-01', end_time='2017-12-31', as_list=True)[:6]
['SH600036', 'SH600110', 'SH600087', 'SH600900', 'SH600089', 'SZ000912']
载入一个市场的标的并根据名称进行过滤:
>> from qlib.data import D
>> from qlib.data.filter import NameDFilter
>> nameDFilter = NameDFilter(name_rule_re='SH[0-9]{4}55')
>> instruments = D.instruments(market='csi300', filter_pipe=[nameDFilter])
>> D.list_instruments(instruments=instruments, start_time='2015-01-01', end_time='2016-02-15', as_list=True)
['SH600655', 'SH601555']
载入一个市场的标的并根据表达式进行过滤:
>> from qlib.data import D
>> from qlib.data.filter import ExpressionDFilter
>> expressionDFilter = ExpressionDFilter(rule_expression='$close>2000')
>> instruments = D.instruments(market='csi300', filter_pipe=[expressionDFilter])
>> D.list_instruments(instruments=instruments, start_time='2015-01-01', end_time='2016-02-15', as_list=True)
['SZ000651', 'SZ000002', 'SH600655', 'SH600570']
载入指定标的在指定时间范围内的特征:
>> from qlib.data import D
>> instruments = ['SH600000']
>> fields = ['$close', '$volume', 'Ref($close, 1)', 'Mean($close, 3)', '$high-$low']
>> D.features(instruments, fields, start_time='2010-01-01', end_time='2017-12-31', freq='day').head().to_string()
' $close $volume Ref($close, 1) Mean($close, 3) $high-$low
... instrument datetime
... SH600000 2010-01-04 86.778313 16162960.0 88.825928 88.061483 2.907631
... 2010-01-05 87.433578 28117442.0 86.778313 87.679273 3.235252
... 2010-01-06 85.713585 23632884.0 87.433578 86.641825 1.720009
... 2010-01-07 83.788803 20813402.0 85.713585 85.645322 3.030487
... 2010-01-08 84.730675 16044853.0 83.788803 84.744354 2.047623'
载入指定股票池在指定时间范围内的特征:
>> from qlib.data import D
>> from qlib.data.filter import NameDFilter, ExpressionDFilter
>> nameDFilter = NameDFilter(name_rule_re='SH[0-9]{4}55')
>> expressionDFilter = ExpressionDFilter(rule_expression='$close>Ref($close,1)')
>> instruments = D.instruments(market='csi300', filter_pipe=[nameDFilter, expressionDFilter])
>> fields = ['$close', '$volume', 'Ref($close, 1)', 'Mean($close, 3)', '$high-$low']
>> D.features(instruments, fields, start_time='2010-01-01', end_time='2017-12-31', freq='day').head().to_string()
' $close $volume Ref($close, 1) Mean($close, 3) $high-$low
... instrument datetime
... SH600655 2010-01-04 2699.567383 158193.328125 2619.070312 2626.097738 124.580566
... 2010-01-08 2612.359619 77501.406250 2584.567627 2623.220133 83.373047
... 2010-01-11 2712.982422 160852.390625 2612.359619 2636.636556 146.621582
... 2010-01-12 2788.688232 164587.937500 2712.982422 2704.676758 128.413818
... 2010-01-13 2790.604004 145460.453125 2788.688232 2764.091553 128.413818'
在构建复杂表达式时,使用字符串在一行内实现整个表达式是比较困难的,例如:
>> from qlib.data import D
>> data = D.features(["sh600519"], ["(($high / $close) + ($open / $close)) * (($high / $close) + ($open / $close)) / (($high / $close) + ($open / $close))"], start_time="20200101")
此时可以不使用字符串,而使用代码实现表达式,下面是与上面例子等价的实现:
>> from qlib.data.ops import *
>> f1 = Feature("high") / Feature("close")
>> f2 = Feature("open") / Feature("close")
>> f3 = f1 + f2
>> f4 = f3 * f3 / f3
>> data = D.features(["sh600519"], [f4], start_time="20200101")
>> data.head()
特征 Feature
Qlib提供Feature
与ExpressionOps
以获取数据特征。
- Feature:从数据源加载数据时,用户可以直接获取特征, 如:
$high
、$low
、$open
、$close
等。它们需要与转换数据格式时的–include_fields
参数对应 - ExpressionOps:使用
Operator
构建特征,支持用户自定义
过滤器 Filter
Qlib提供NameDFilter
与ExpressionDFilter
用于过滤标的:
- NameDFilter:根据名称对标的进行过滤,需要一个名称的正则表达式
- ExpressionDFilter:根据表达式对标的进行过滤,需要一个表达式规则
- 基础特征过滤器:
$close/$open>5
- 横截面特征过滤器:
$rank($close)<10
- 时序特征过滤器:
$Ref($close, 3)>100
- 基础特征过滤器:
下面是在工作流的配置文件中使用过滤器的例子:
filter: &filter
filter_type: ExpressionDFilter
rule_expression: "Ref($close, -2) / Ref($close, -1) > 1"
filter_start_time: 2010-01-01
filter_end_time: 2010-01-07
keep: False
data_handler_config: &data_handler_config
start_time: 2010-01-01
end_time: 2021-01-22
fit_start_time: 2010-01-01
fit_end_time: 2015-12-31
instruments: *market
filter_pipe: [*filter]
数据加载器 Data Loader
数据加载器用于从数据源加载原始数据,它会被数据处理器模块载入并使用。Qlib提供了数据加载器的基类DataLoader
,并实现了以下数据加载器类:
- QlibDataLoader:用于从Qlib数据源加载原始数据
- StaticDataLoader:用于从文件等数据源加载原始数据
数据处理器 Data Handler
数据处理器模块用于处理被多数模型使用的通用数据处理方法,用户可以通过qrun
在自动工作流中使用数据处理器。
可学习数据处理器 DataHandlerLP
Qlib提供了可学习数据处理器类DataHandlerLP
,该类包含多个可以学习参数(如zscore归一化参数)的处理器Processor
。当新数据到来时,已训练的处理器就可以处理新数据,从而使高效地处理实时数据成为可能。
处理器 Processor
处理器模块的作用是进行数据处理,它被设计为是可训练的。Qlib提供以下处理器:
- DropnaProcessor:丢弃N/A特征
- DropnaLabel:丢弃N/A标签
- TanhProcess:使用tanh处理噪声数据
- ProcessInf:使用平均值替换无穷值
- Fillna:用0或给定数值填充N/A值
- MinMaxNorm:最大值-最小值归一化
- ZscoreNorm:zscore归一化
- RobustZScoreNorm:稳健zscore归一化
- CSZScoreNorm:横截面zscore归一化
- CSRankNorm:横截面rank归一化
- CSZFillna:横截面N/A值填充
用户可以通过继承基类Processor
以建立自己的处理器。
例子
Qlib实现了一个数据处理器Alpha158
,下面的例子展示了如何将其作为独立模块运行:
import qlib
from qlib.contrib.data.handler import Alpha158
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": "csi300",
}
if __name__ == "__main__":
qlib.init()
h = Alpha158(**data_handler_config)
# 获取数据所有列
print(h.get_cols())
# 获取所有标签
print(h.fetch(col_set="label"))
# 获取所有特征
print(h.fetch(col_set="feature"))
数据集 Dataset
数据集模块的作用是为模型训练与推理准备数据。设计该模块的动机是想要为不同的模型处理适合自己的数据提供最大的灵活性。该模块使每个模型可以使用独立的方式处理数据,例如,GBDT
等模型可以在包含nan或None的数据上正常运行,但是MLP
等神经网络模型不能在此类数据上正常运行。
如果用户的模型需要使用特殊的方式处理数据,可以继承Dataset
类以实现自己的数据集类。如果数据处理方式并不特殊,可以直接使用预置的DatasetH
类,它是带有数据处理器的数据集。
缓存 Cache
缓存是一个可选模块,它通过将常用的数据储存在缓存中以提高数据读取速度。Qlib提供了以下缓存类:
全局内存缓存 Memcache
该类由三个MemCacheUnit
实例组成,分别用于缓存日期、标的与特征。该全局变量在cache.py
中被定义为H
,用户可以使用H['c']
、H['i']
与H['f']
来获取/设置缓存。
表达式缓存 ExpressionCache
该类用于缓存表达式(如Mean($close, 5)
),用户可以通过如下步骤继承该基类以实现自己的表达式缓存机制:
- 重写
self._uri
方法以定义如何生成缓存文件路径 - 重写
self._expression
方法以定义哪些数据会被缓存以及如何缓存
目前Qlib实现了DiskExpressionCache
类,它将表达式数据存储在硬盘上。
数据集缓存 DatasetCache
该类用于缓存数据集,用户可以通过如下步骤继承该基类以实现自己的数据集缓存机制:
- 重写
self._uri
方法以定义如何生成缓存文件路径 - 重写
self._dataset
方法以定义哪些数据会被缓存以及如何缓存
目前Qlib实现了DiskDatasetCache
类,它将数据集数据存储在硬盘上。