【历史文档】策略-BigQuant AI策略详解
由ypyu创建,最终由small_q 被浏览 1120 用户
更新
本文内容对应旧版平台与旧版资源,其内容不再适合最新版平台,请查看新版平台的使用说明
新版量化开发IDE(AIStudio):
https://bigquant.com/wiki/doc/aistudio-aiide-NzAjgKapzW
新版模版策略:
https://bigquant.com/wiki/doc/demos-ecdRvuM1TU
新版数据平台:
https://bigquant.com/data/home
https://bigquant.com/wiki/doc/dai-PLSbc1SbZX
新版表达式算子:
https://bigquant.com/wiki/doc/dai-sql-Rceb2JQBdS
新版因子平台:
https://bigquant.com/wiki/doc/bigalpha-EOVmVtJMS5
\
导语
AI策略和传统的量化策略有很大差异,本文根据策略背后代码详细讲解AI策略,了解AI策略运行机制。
前面我们通过可视化策略模板或者策略生成器,可以快速开发出一个AI策略,我们来详细解读一下策略的代码。
看看下面这个简单的AI策略抽象流程示意图,将帮助我们来理解AI策略代码。
基础配置
这些配置将在后续用到:
- start_date 和 end_date 确定了我们要用数据段 其中 [start_date, split_date) 区间的数据,用于模型训练 [split_date, end_date] 区间的数据,用户模型回测
- instruments:股票池,D.instruments(start_date, split_date) 取A股给定时间段内所有出现过的股票。
- label_expr,用户标注的函数,更多说明见下面关于 M.advanced_auto_labeler的介绍。
- hold_days:持仓时间,用于数据标注和回测,具体见下面相关模块使用
- features:特征 (因子),具体见下面相关模块使用
class conf:
start_date = '2014-01-01'
end_date='2017-07-17'
split_date = '2015-01-01'
instruments = D.instruments(start_date, end_date)
hold_days = 5
features = [
'close_5/close_0', # 5日收益
'close_10/close_0', # 10日收益
'close_20/close_0', # 20日收益
'avg_amount_0/avg_amount_5', # 当日/5日平均交易额
'avg_amount_5/avg_amount_20', # 5日/20日平均交易额
'rank_avg_amount_0/rank_avg_amount_5', # 当日/5日平均交易额排名
'rank_avg_amount_5/rank_avg_amount_10', # 5日/10日平均交易额排名
'rank_return_0', # 当日收益
'rank_return_5', # 5日收益
'rank_return_10', # 10日收益
'rank_return_0/rank_return_5', # 当日/5日收益排名
'rank_return_5/rank_return_10', # 5日/10日收益排名
'pe_ttm_0', # 市盈率TTM
]
# 数据标注
label_expr = [
# 计算未来一段时间(hold_days)的相对收益
'shift(close, -5) / shift(open, -1) - shift(benchmark_close, -5) / shift(benchmark_open, -1)',
# 极值处理:用1%和99%分位的值做clip
'clip(label, all_quantile(label, 0.01), all_quantile(label, 0.99))',
# 将分数映射到分类,这里使用20个分类,这里采取等宽离散化
'all_wbins(label, 20)',
# 过滤掉一字涨停的情况 (设置label为NaN,在后续处理和训练中会忽略NaN的label)
'where(shift(high, -1) == shift(low, -1), NaN, label)'
]
示例解读:
- shift(close, -5) / shift(open, -1) - shift(benchmark_close, -5) / shift(benchmark_open, -1):未来5天的相对收益率(股票收益率减去基准收益率),其中shift(close,-5)为未来5天的收盘价,shift(open,-1)为明天的开盘价,基准同理。
- clip:clip用于极值处理,上面的例子就是将1%分位数和99%分位数以外的数据进行裁剪
- all_wbins:对连续性的标注数据进行离散化,上面的例子是将标注数据分为20类。详情请参考:表达式引擎
- where(shift(high, -1) == shift(low, -1), NaN, label):过滤掉一字涨停的情形
数据标注
之前采取的是 M.fast_auto_labeler 进行数据标注,但如果要使用表达式引擎构建因子、数据标注,建议使用功能更为强大的M.advanced_auto_labeler进行数据标注。
# 给数据做标注:给每一行数据(样本)打分,一般分数越高表示越好
m1 = M.advanced_auto_labeler.v1(
instruments=conf.instruments, start_date=conf.start_date, end_date=conf.split_date,
label_expr=conf.label_expr, benchmark='000300.SHA', cast_label_int=True)
示例解读:
- label_expr:表示通过conf类中的标注语句进行数据标注
- benchmark:对数据进行标注的时候,偶尔会用到基准数据,因此需要制定基准
- cast_label_int:标注结果是否转换为整数;默认值是True
\
基础特征抽取
机器学习算法很大程度上依赖于特征工程,AI策略同样如此,特征抽取地好,对收益率的预测将更加准确。 有些因子的基础因子,直接可以抽取。
m2 = M.general_feature_extractor.v5(
instruments=conf.instruments, start_date=conf.start_date, end_date=conf.end_date,
features=conf.features)
详情请参考文档:基础特征抽取。
\
衍生特征抽取
很多AI策略的特征并不是简单的基础特征,而是由基础特征衍生计算出来的衍生特征。
# 计算衍生特征
m2_1 = M.derived_feature_extractor.v1(data=m2.data, features=conf.features)
详情请参考文档:衍生特征抽取。
\
数据转换
如果你采用的模型是StockRanker,该算法需要对输入的特征作相应转换。如果你使用的是随机森林、线性SGD模型等,这一步可以省略。
m3 = M.transform.v2(data=m2_1.data, transforms=None, drop_null=True)
当然,在之前我们的AI策略是采取的 M.fast_auto_labeler进行数据标注,详解请点击展开。
基础配置
这些配置将在后续用到:
- start_date 和 end_date 确定了我们要用数据段 其中 [start_date, split_date) 区间的数据,用于模型训练 [split_date, end_date] 区间的数据,用户模型回测
- instruments:股票池,D.instruments(start_date, split_date) 取A股给定时间段内所有出现过的股票。
- label_expr,用户标注的函数,更多说明见下面关于 M.fast_auto_labeler的介绍。
- hold_days:持仓时间,用于数据标注和回测,具体见下面相关模块使用
- features:特征 (因子),具体见下面相关模块使用
class conf:
start_date = '2010-01-01'
end_date='2017-01-01'
# split_date 之前的数据用于训练,之后的数据用作效果评估
split_date = '2015-01-01'
# D.instruments: https://bigquant.com/docs/data_instruments.html
instruments = D.instruments(start_date, split_date)
# 机器学习目标标注函数
# 如下标注函数等价于 min(max((持有期间的收益 * 100), -20), 20) + 20 (后面的M.fast_auto_labeler会做取整操作)
# 说明:max/min这里将标注分数限定在区间[-20, 20],+20将分数变为非负数 (StockRanker要求标注分数非负整数)
label_expr = ['return * 100', 'where(label > {0}, {0}, where(label < -{0}, -{0}, label)) + {0}'.format(20)]
# 持有天数,用于计算label_expr中的return值(收益)
hold_days = 5
# 特征 https://bigquant.com/docs/#/datasource?id=%E5%9B%A0%E5%AD%90%E5%BA%93,你可以通过表达式构造任何特征
features = [
'close_5/close_0', # 5日收益
'close_10/close_0', # 10日收益
'close_20/close_0', # 20日收益
'avg_amount_0/avg_amount_5', # 当日/5日平均交易额
'avg_amount_5/avg_amount_20', # 5日/20日平均交易额
'rank_avg_amount_0/rank_avg_amount_5', # 当日/5日平均交易额排名
'rank_avg_amount_5/rank_avg_amount_10', # 5日/10日平均交易额排名
'rank_return_0', # 当日收益
'rank_return_5', # 5日收益
'rank_return_10', # 10日收益
'rank_return_0/rank_return_5', # 当日/5日收益排名
'rank_return_5/rank_return_10', # 5日/10日收益排名
'pe_ttm_0', # 市盈率TTM
]
数据标注
为了使用有监督的机器学习算法,我们需要对数据做标注。M.fast_auto_labeler 是一个自动标注模块,具体使用说明见文档。
# 给数据做标注:给每一行数据(样本)打分,一般分数越高表示越好
m1 = M.fast_auto_labeler.v8(
instruments=conf.instruments, start_date=conf.start_date, end_date=conf.split_date,
label_expr=conf.label_expr, hold_days=conf.hold_days,
benchmark='000300.SHA', sell_at='open', buy_at='open')
示例解读:
- return * 100:表示将收益 * 100,比如某只股票,今天的收盘价是10元,5天后的收盘价是11.32元,则return = 13.2%,return * 100 = 13.2
- 'where(label > {0}, {0}, where(label < -{0}, -{0}, label)) + {0}'.format(20):这个表达式,20带入之后是
where(label > 20, 20, where(label < -20, -20, label)) + 20
第一部分:where(label > 20, 20, where(label < -20, -20, label)),将数据裁剪到[-20, 20],即小于-20的用-20替换,大于20的用20替换 第二部分:+20,则将整个值的范围变换到[0, 40]
label_expr = ['return * 100', 'where(label > {0}, {0}, where(label < -{0}, -{0}, label)) + {0}'.format(20)]
特征抽取
机器学习算法很大程度上依赖于特征工程,AI策略同样如此,特征构建地好,对收益率的预测将更加准确。
# 计算特征数据
m2 = M.general_feature_extractor.v5(
instruments=conf.instruments, start_date=conf.start_date, end_date=conf.split_date,
features=conf.features)
示例解读:
- 特征抽取也可以称作因子抽取或者特征数据计算,接口的介绍参考 模块和API概览:特征抽取。
- instruments=conf.instruments:表示计算哪些股票的特征数据。
- start_date=conf.start_date, end_date=conf.split_date :表示对什么时间段的股票数据计算特征。该时间段和训练集的时间段是一致的。
- features=conf.features:表示抽取哪些特征。
数据预处理
从上一步 特征抽取中,我们可以将特征数据抽取出来,但是计算出来的特征数据不一定满足机器学习算法的需要。StockRanker算法要求数据为正整数,因此需要对数据进行预处理。
m3 = M.transform.v2(
data=m2.data, transforms=T.get_stock_ranker_default_transforms(),
drop_null=True, astype='int32', except_columns=['date', 'instrument'],
clip_lower=0, clip_upper=200000000)
示例解读:
- 不同的机器学习算法可能在数据预处理模块有所差异,StockRanker算法的接口的介绍参考 模块和API概览:特征转化。
- data=m2.data:表示对什么数据进行数据预处理,一般为计算完成的特征数据。
- transforms=T.get_stock_ranker_default_transforms():表示进行怎样的数据预处理,具体的数据变换可以通过T.get_stock_ranker_default_transform()接口进行查询,transform是由正则表达式类型的变换函数组成的列表,对于输入数据的每一列,从transforms里依序寻找到匹配的表达式,用对应的变换函数对列数据做处理。
- 该接口其他的参数一般采用默认即可,详情请参考 [模块和API概览:特征转化]。(https://bigquant.com/docs/#/develop?id=数据变换)。
[/details]
\
合并数据
通过数据标注和计算特征数据,我们获得了两个数据,只有同时包含这两部分数据的训练集才能完整地训练一个AI模型,因此需要进行数据合并。
# 合并标注和特征数据
m4 = M.join.v2(data1=m1.data, data2=m3.data, on=['date', 'instrument'], sort=True)
示例解读:
- 数据合并也成为数据连接,详情请参考 模块和API:数据连接。
- data1=m1.data 表示:第一个需要连接的数据,例如标注数据。
- data2=m3.data 表示:第二个需要连接的数据,例如计算完成的特征数据。
- on=['date', 'instrument'] 表示:数据合并时使用的主要列。一般使用日期和股票代码就可以对数据进行合并。
- sort=True 表示:是否对合并数据的结果按on指定的列进行排序。
\
模型训练
当我们将标注数据和经过数据预处理的特征数据合并以后,此时可以通过机器学习算法训练出一个AI模型。
# StockRanker机器学习训练
m5=M.stock_ranker_train.v5(training_ds=m4.data, features=conf.features)
示例解读:
- 机器学习模型训练是必不可少的一步,训练时间依赖于数据量,如果是全市场股票多年数据,时间大概需要3-10分钟。详情请参考 模块和API概览:模型训练。
- training_ds=m4.data 表示:训练模型时应以什么数据进行输入,输入的数据为上一步合并的数据。
- features=conf.features 表示:训练模型时以什么特征或因子参与模型进行训练。
- M.stock_ranker_train接口的其他参数一般采用默认值。
\
训练结果
通过上一步的训练模型,我们已经产生出了一个在训练集上表现不错的模型。我们可以这样查询训练结果:
print('模型ID:', m5.model_id)
print('模型因子得分:', m5.feature_gains)
print('模型可视化:', m5.plot_model())
示例解读:
- m5.model_id 表示:唯一的模型ID。
- m5.feature_gains 表示: 各个特征的得分情况,可以借此判断特征重要性程度。由于输出类型为DataSource,因此可以通过read_df方法查看——m5.feature_gains.read_df()。
- m5.plot_model() 表示:可视化查看模型结果,这样就能打开AI算法的‘黑箱’,可以查看算法的每个细节。
\
模型预测
此时,我们已经产生出了一个在训练集上表现不错的模型。现在我们根据该模型来获取在测试集上的预测结果。
# 计算基础数据
n2 = M.general_feature_extractor.v5(
instruments=conf.instruments, start_date=conf.start_date, end_date=conf.end_date,
features=conf.features)
# 计算衍生特征
n2_1 = M.derived_feature_extractor.v1(data=n2.data, features=conf.features)
# 将特征数据转换机器学习算法能够接受的数据类型(只有StockRanker算法需要)
n3 = M.transform.v2(data=n2_1.data, transforms=None, drop_null=True)
# 进行预测
n4 = M.stock_ranker_predict.v5(model=m6.model, data=n3.data)
# 查看预测数据
prediction = n4.predictions.read_df()
示例解读:
- n1和n2和之前的特征抽取、特征转换完全一样,只是现在传入的时间是测试集的时间段。
- 机器学习算法通过模型和特征数据就可以进行预测,因此并不需要标注数据、合并数据。比如当你获得了一个回归模型后,此时传入新的自变量就可以带入模型获得因变量。
- n3是模型预测,详情可参看 模块和API概览:模型预测
- model_id=context.options['model_id'] 表示:用哪个模型进行预测就传入哪个模型ID。
- data=n2.data 表示:在什么数据上进行预测就传入什么数据。一般为测试集的特征数据。
- n3.predictions.read_df() 表示:模型在测试集上的预测结果为n3.predictions,类型为DataSource,因此需要通过read_df方法查看。
\
策略回测
当我们获得测试集上的预测结果以后,我们就可以通过BigQuant回测机制进行策略回测,验证该策略是否有效。策略回测相关内容请参考 BigQuant回测机制。
小结:AI策略其实主要包含训练和预测,然后基于预测的结果开发交易策略来进行验证。本文因为涉及到机器学习算法为监督式学习算法,因此有数据标注这一步。希望通过本文介绍,大家能有更加深入的认识。
\