手把手教你,利用机器学习模型,构建量化择时策略(附全流程代码)

手把手教你,利用机器学习模型,构建量化择时策略(附全流程代码)

歌神演唱会人脸识别抓逃犯,阿尔法狗战胜人类围棋手,AI绘图《太空歌剧院》惊艳艺术博览会~~~这些震撼成果的背后,都是人工智能在蓬勃发力。

既然人工智能/机器学习这么厉害,在其他领域都取得了丰硕的成果和巨大的成功,那么是不是可以让计算机帮咱预测市场大盘、选择和买卖股票/期货/外汇/大饼,解放双手,丰衣足食,岂不美哉?

已经有非常多的机构开展这方面的研究和实践了,今儿个咱也来探索一下,机器学习在量化投资当中的应用,从零开始走通基于机器学习的量化指数择时模型的开发全流程。


零. 机器学习,浅尝一下

人类发现规律一般采用的是归纳演绎法,对观察到的大量现象进行归纳,在心中形成规律,然后再遇到类似的情况,就可以迅速做出预测和判断,比如说“朝霞不出门,晚霞行千里”、“瑞雪兆丰年”、“狗打喷嚏天要晴”,这都是老祖先归纳演绎法的结晶,可能不是每回都灵验。

机器学习和人类的思考过程非常相似,把历史数据输入到模型当中,训练出一个能完成特定任务的数学模型,等到有新的数据出现时,就把新数据输入到训练好的模型当中,这就会输出一个预测的结果,特别地,机器学习在识别非线性规律方面,相较于人脑更有优势。

具体结合咱这次的目标进行说明,咱这次的任务就是利用机器学习模型,预测沪深300指数第二天的涨跌情况,输入的数据就是沪深300指数的开高低收行情数据,将这些数据输入到支持向量机(Support Vector Machine,SVM)当中进行模型训练,待SVM训练好之后,输入今天的行情数据,就能输出对第二天行情的预判。

为什么选择SVM呢?因为咱这次使用的数据集是沪深300指数的日线行情数据,它自2005年上市以来,拢共才四千多个交易日,换句话说,也才四千多个样本点,相对于几百万上千万的“大数据”来说,这充其量才算一个“小样本”,这无疑是一个非常适合SVM的应用场景。

因为在深度学习(一般需要大数据“投喂”)尚未全面兴起时,SVM由于其较高的小样本预测准确率,并且能够解决非线性分类问题,属于当时机器学习方法潮流圈中的扛把子。

SVM很能打,但本身并不是非常复杂,今儿个不抠数学细节,大白话讲讲SVM是什么有什么用,以免没开始就把大伙儿劝退了。

SVM最初的设计是用来解决二分类问题,后来扩展到多分类问题,“预测沪深300指数涨跌”就属于典型的二分类问题,“涨”是一个类别,“跌”就是另一个类别。

它通过寻找一个最大间隔超平面(上图黑斜线)将两类样本线性区分开来,并且保证两侧样本的最近边缘点到这个平面的距离是最大的,由于最大间隔超平面仅取决于两个类别的边缘点,例如上图中被红线和蓝线穿过的红点和蓝点,这些点就被称为支持向量,这就是“支持向量机”名称的由来。

但现实世界很奇妙,有线性可分的数据集,就有非线性可分的数据集,那遇到非线性可分的情况怎么办?

那也有办法,SVM引入了核函数,可以将低维不可分的数据映射到高维线性可分,如上图,二维不可分就映射到三维,常用的核函数有线性核、多项式核、高斯核(RBF核)和Sigmoid核。

但在现实当中,由于噪声和极端样本点的存在,数据集无论在低纬和高维都可能出现线性不可分的情况,于是乎,SVM当中引入了松弛变量的概念,允许了最大间隔超平面不用完美区分两个类别,允许错误分类的存在,SVM通过惩罚系数C控制这些错误分类的容忍程度,C值越高分类准确率越高,但数值过高容易导致过拟合,C值过低则会导致准确率受损。

SVM唠完了,咱来说说一般机器学习建模的流程,一般分为6步走,按照先后顺序分别是收集数据、准备数据、选择/建立模型、训练模型、测试模型和调节参数。量化,没有白走的路,每一步都算数,下文咱一步一步地走一遍~

一. 收集数据

巧妇难为无米之炊,第一步咱要获取到最原始的建模数据,对于指数的日线数据而言,有非常多的免费获取渠道,只要能获取到指数的日期(date)、开盘价(open)、最高价(high)、最低价(low)和收盘价(close)就可以了。

在此处以tushare为例,获取沪深300指数自2005年4月8日上市以来的全部行情数据,它是一个免费、开源的python财经数据接口包,网址是:tushare.org/

导入tushare包之后,使用它的get_k_data函数,就可以获得沪深300指数的历史K线数据,返回的数据格式是dataframe,咱只选取开高低收数据,并将日期(字符串格式)设置为索引。

import numpy as np
import pandas as pd
import talib
import warnings
warnings.filterwarnings('ignore')
import tushare as ts

data = ts.get_k_data(code='hs300', start='2005-04-08', end='2022-11-08', ktype='D')
data = data.set_index('date')
data = data[['open', 'high', 'low', 'close']]
print('样本数目:%d' %data.shape[0])
print(data.head(10))
print(45*'-')
print(data.tail(10))


二. 数据准备

有了原始数据之后,咱还要进一步进行加工和处理,主要工作是变量选择、确认标签和数据清洗。

变量选择在量化投资当中叫“因子选择”,就是用哪些因子(factor)进行选股择时那些,在机器学习领域一般叫“特征选择”,指定用哪些特征(feature)来作为算法模型的输入。

在这里咱用到因子分别是EMA值(ema)、价格波动率(stddev)、价格斜率(slope)、RSI值(rsi)和威廉指标值(wr),此处咱利用talib包丝滑地完成计算,它是一款量化圈驰名、彪悍强大的第三方技术分析指标计算包。

data['ema'] = talib.EMA(data['close'].values, timeperiod=20)
data['stddev']= talib.STDDEV(data['close'].values, timeperiod=20, nbdev=1)
data['slope'] = talib.LINEARREG_SLOPE(data['close'].values, timeperiod=5)
data['rsi'] = talib.RSI(data['close'].values, timeperiod = 14)
data['wr'] = talib.WILLR(data['high'].values, data['low'].values, data['close'].values, timeperiod=7)
data.tail(10)

确认标签呢,就是给这个样本点打上类别标签,由于咱是预测指数第二天的涨跌情况,于是,咱先计算出每个样本第二天的涨幅(pct),如果第二天上涨,则设置标签(rise)为1,反之为0。

由于指数数据一般异常情况不是很多,如果有空值,咱把空值删除就好了。

data['pct'] = data['close'].shift(-1) / data['close'] - 1.0
data['rise'] = data['pct'].apply(lambda x: 1 if x>0 else 0)
#删除缺失值
data = data.dropna()
data.tail(10)


三. 选择/建立模型

选择/建立模型就是需要确定自己这次使用哪种机器学习模型,是支持向量机SVM呢,还是神经网络NN呢,亦或是随机森林RF呢,或者其他的模型。

在之前已经说过了,咱是用SVM模型,原因和原理不再赘述,为了方(tou)便(lan)实现和建立模型,咱可以直接从Scikit-learn(简称sklearn)中导入,它是非常流行的Python免费机器学习库 ,具有各种分类、回归和聚类算法,一般配合numpy数据格式使用。


四. 训练模型

在这里,咱需要把整个数据集分拆为训练集和测试集,因为除了训练模型之外,咱还要留出一部分数据来验证训练出来模型的优劣。

一般来说,将完整数据集80%的样本作为训练集,剩余20%的样本作为测试集,要注意的是,这里要将dataframe中的因子数据转换成numpy的ndarray数组格式,因为这种数据类型更适配sklearn。

# 划分训练集和测试集
num_train = round(len(data)*0.8)
data_train = data.iloc[:num_train, :]
data_test = data.iloc[num_train:, :]
# 训练集数据和标签
X_train = data_train[['ema', 'stddev', 'slope', 'rsi', 'wr']].values
y_train = data_train['rise']
# 测试集数据和标签
X_test = data_test[['ema', 'stddev', 'slope', 'rsi', 'wr']].values
y_test = data_test['rise']
print(X_train[:10])
print(45*'-')
print(X_test[:10])

在划分数据集之后,还有非常重要的一步,那就是对数据进行标准化处理,这是因为每个因子的数值量纲差别太大,例如指数EMA的均值是2919.6,而RSI的均值是52.7,这样会造成SVM对某些因子的“偏心”。

在此处,采用“(原始值 - 均值) / 标准差”的方法对数据进行标准化处理,处理过后,每个因子的均值都会变为0,标准差变为1.0。

from sklearn.preprocessing import StandardScaler

print('---标准化之前---')
print('训练集的均值:')
print(X_train.mean(axis=0))
print('训练集的标准差:')
print(X_train.std(axis=0))

# 对数据进行标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

print('---标准化之后---')
print('训练集的均值:')
print(X_train.mean(axis=0))
print('训练集的标准差:')
print(X_train.std(axis=0))

数据标准化处理之后,就可以将训练集数据输入SVM当中,代码实现很简单,从sklearn的svm模块当中导入SVM分类器SVC,创建实例对象后,将训练集因子数据和对应标签塞进fit函数就行了,SVM模型的惩罚系数使用默认值1.0,核函数也用默认的RBF核函数,训练过程非常快,不用一个东的时间,就把SVM分类器训练好了。

from sklearn.svm import SVC

classifier = SVC(C=1.0, kernel='rbf')
classifier.fit(X_train, y_train)
print(classifier)

五. 测试模型

至此,SVM分类器已经训练好了,把因子数据塞进predict函数,就能输出每个样本的预测值,咱分别把训练集和测试集的预测标签插回到原来的数据集当中,用来计算预测的准确率。

y_train_pred = classifier.predict(X_train)
y_test_pred = classifier.predict(X_test)
data_train['pred'] = y_train_pred
data_test['pred'] = y_test_pred
accuracy_train = 100 * data_train[data_train.rise==data_train.pred].shape[0] / data_train.shape[0]
accuracy_test = 100 * data_test[data_test.rise==data_test.pred].shape[0] / data_test.shape[0]
print('训练集预测准确率:%.2f%%' %accuracy_train)
print('测试集预测准确率:%.2f%%' %accuracy_test)

输出结果:

训练集预测准确率57.52%
测试集预测准确率52.35%

从结果当中看出,训练集的预测准确率明显比测试集的高,这是因为整个模型都是在训练集数据上训练出来的,对测试集数据则还很“陌生”,这就相当于高考数学考卷都是你们学校的数学老师出的,整体来看,你们的平均分就非常可能比其他同级别的学校高。

光看准确率还不够直观,咱还要看一下如果纯粹按照这个择时模型的预测结果进行投资,能获得多少收益,此处只使用测试集进行模拟。

假设指数可以多空交易,如果模型预测为1(上涨),第二天策略的收益率就是指数的涨幅,如果模型预测为0(下跌),第二天策略的收益率就是指数的涨幅的相反数,有了每天的日收益率之后,通过dataframe自带的累乘函数cumprod,就可以得到择时策略和沪深300指数的净值曲线,为了方(tou)便(lan)起见,不考虑交易费率,以及按照收盘价成交。

import matplotlib.pyplot as plt

#策略日收益率
data_test['strategy_pct'] = data_test.apply(lambda x: x.pct if x.pred>0 else -x.pct, axis=1)
#策略和沪深300的净值
data_test['strategy'] = (1.0 + data_test['strategy_pct']).cumprod()
data_test['hs300'] = (1.0 + data_test['pct']).cumprod()
# 粗略计算年化收益率
annual_return = 100 * (pow(data_test['strategy'].iloc[-1], 250/data_test.shape[0]) - 1.0)
print('SVM 沪深300指数择时策略的年化收益率:%.2f%%' %annual_return)

#将索引从字符串转换为日期格式,方便展示
data_test.index = pd.to_datetime(data_test.index)
ax = data_test[['strategy','hs300']].plot(figsize=(16,9), color=['SteelBlue','Red'],
                                          title='SVM 沪深300指数择时策略净值  by 量化君')
plt.show()

六. 调节参数

从上一步的测试当中看出,训练集和测试集的预测准确率只有57%和52%,不算理想,说明还有很大的提升空间,还可以对模型进行优化改进。

比如说,现在使用的5个因子,还没有反应到价格波动的本质,还可以增改更多的因子。

还比如说,SVM模型当中的惩罚系数C过小,对错误样本的容忍度过高,RBF核函数不适合作为这个数据集的映射转换函数。

再比如说,甚至连SVM模型本身也是一个参数,也可以更改,比如说可以换成其他的机器学习分类模型。

也就是说到这调节参数这一步,如果训练好的模型结果不能让自己满意,就可以重新将前5步走一遍。


咱终于吭哧吭哧走完了,利用机器学习构建量化择时策略的全流程,相信大伙儿对机器学习相关概念和建模流程也有了初步的了解,基于这个建模框架流程,将来的你可以玩得更花,可以采用更高频的数据、更鲁棒的模型、更多的交易标的、更有效的因子、更有趣的预测任务,以及~~~遇见更好的自己。


参考资料

周志华,2016年1月,书籍《机器学习》

华泰金工,2017年8月,研报《人工智能选股之支持向量机模型》

阿泽,2020年6月,知乎《【机器学习】支持向量机 SVM(非常详细)》


我是 @quantkoala,一枚大写的量化/程序化策略源码捕手,喜欢全方位收集分享市面上主流的策略源码(股票+期货+外汇),在『量化藏经阁』和『量化藏经阁Max』社群入口)中,持续分享量化策略源码和量化知识等干货(目前已分享200+套精品策略),欢迎关注点赞&联系沟通,探讨共赢&成果共享,相互交流&共同进步!!!
常在线,多交流,多沟通!!!

更多相关资料请见:

编辑于 2023-03-20 19:23・IP 属地广东