量化之路

用支持向量鸡预测股票价格走势

2019-03-18  本文已影响0人  伏星宇
向量鸡怒啄Navi

在2018年CSGO伦敦Major比赛中,主办方别出心裁请来一位专业人员预测比赛结果——图中的这只小鸡。它通过选择带有队标的米盒进行预测。
这只小鸡不负众望,淘汰赛阶段的所有比赛全部预测正确,包括决赛。
只不过,它选择的队伍全都输掉了比赛,贯彻了“啄谁谁死”的宗旨,反向预测拉满。
有网友表示,人家就是在选输的那边,人类理解反了而已。

这种用鸡做预测的技术是目前非常先进的人工智能技术的一种,即支持向量鸡。
支持向量鸡非常善于解决N个类别的分类问题,具体方法为,在鸡的面前摆上N个完全一样的米盒,放上等量的大米,观察鸡先啄哪一个,即可得到待预测问题的分类。
同样,支持向量鸡也可以用于预测股票价格走势。具体方法为,用2个米盒分别代表“涨”、“跌”,观察向量鸡先啄哪一盒即可。
(本段内容纯属胡说八道)

下面正经介绍通过python中支持向量机(svm)预测指数涨跌的简单应用,数据来源依然是tushare。
首先是支持向量机的概念:

在机器学习中,支持向量机(英语:support vector machine,常简称为SVM,又名支持向量网络)是在分类与回归分析中分析数据的监督式学习模型与相关的学习算法。给定一组训练实例,每个训练实例被标记为属于两个类别中的一个或另一个,SVM训练算法创建一个将新的实例分配给两个类别之一的模型,使其成为非概率二元线性分类器。SVM模型是将实例表示为空间中的点,这样映射就使得单独类别的实例被尽可能宽的明显的间隔分开。然后,将新的实例映射到同一空间,并基于它们落在间隔的哪一侧来预测所属类别。
除了进行线性分类之外,SVM还可以使用所谓的核技巧有效地进行非线性分类,将其输入隐式映射到高维特征空间中。(来源:wiki)


支持向量机分类思想

略过复杂的数学公式,一言以蔽之:使分类超平面与最近的样本点之间的距离最大化。
最早用于刻画超平面的函数是线性的,1992年Bernhard E. Boser、Isabelle M. Guyon和弗拉基米尔·万普尼克提出了一种通过将核技巧(最初由Aizerman et al.提出)应用于最大边界超平面来创建非线性分类器的方法。所得到的算法形式上类似,除了把点积换成了非线性核函数。

常见核函数
解法上,简单说来,计算SVM分类器可以归结为一个目标函数可微的约束优化问题。
传统的解法是通过拉格朗日对偶,得到进一步简化。由于拉格朗日对偶大大减少了计算量,现有的算法改进基本都在围绕对偶问题做文章。
目前先进的算法包括次梯度下降和坐标下降。当处理大的稀疏数据集时,这两种技术已经被证明有着显著的优点——当存在许多训练实例时次梯度法是特别有效的,并且当特征空间的维度高时,坐标下降特别有效。而坐标下降本质上也是基于对偶问题。
具体公式可以通过网络搜索,不再赘述。

本文思路分3步:
(1)获取数据和处理:以股票涨跌幅(pct_ch)和前一日收盘价(pre_close)的N日MA作为输入,以股票当日涨跌的0-1分类作为输出,其中1代表涨,0代表跌。

def get_data(ma):
    fig, ax = plt.subplots()
    #tushare获取数据
    df = ts.pro_bar(pro_api=api, ts_code='600887.SH', adj='qfq', start_date='20180101', end_date='20190318')
    df.index = pd.to_datetime(df.trade_date)
    #按日期排序
    df = df.sort_values(by = 'trade_date')
    #输入参数移动平均化
    dfMA = df.rolling(window=ma, center=False).mean()
    #只保留需要的参数
    df.drop(['ts_code', 'close', 'trade_date', 'open', 'high', 'low', 'pre_close', 'change', 'vol', 'amount'], axis=1, inplace=True)
    dfMA.drop(['ts_code', 'trade_date', 'open', 'high', 'low', 'close', 'change'], axis=1, inplace=True)
    #print(df.head())
    #print(dfMA.tail())
    #画图
    plt.plot(df)
    plt.plot(dfMA.pct_chg)
    #去除移动平均产生的前若干行的空值
    y = df[ma:].values
    x = dfMA[ma:].values
    #分类
    for i in range(len(y)):
        if y[i]>0:
            y[i]=1
        else:
            y[i]=0
    return x, y

(2)用train_test_split分组,手动选择参数训练支持向量机,进行预测;或者像这里用GridSearchCV学习下如何便捷的调参。

#建议采用GridSearchCV调参,否则需手动调参
#x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, train_size=0.8)
#clf = svm.SVC(C=0.5, kernel='linear', decision_function_shape='ovr')  # 线性
#clf = svm.SVC(C=0.4, kernel='rbf', gamma=20, decision_function_shape='ovr') #非线性核函数

#测试集
x_train = x[:-1]
x_test = x[-1],
y_train = y[:-1]
y_test = y[-1]

#GridSearchCV自动调参,这里选择了线性函数和径向基函数(rbf)
parameters = {'kernel':('linear', 'rbf'), 'C':[1, 2, 4], 'gamma':[0.125, 0.25, 0.5 ,1, 2, 4]}
svr = svm.SVC()
print(x, y.ravel())
clf = GridSearchCV(svr, parameters)
clf.fit(x, y.ravel())
print('GridSearchCVing...')
cv_result = pd.DataFrame.from_dict(clf.cv_results_)
print(cv_result)
print('best', clf.best_params_)

#手动调参拟合
#clf.fit(x_train, y_train.ravel())

#显示精度
print(clf.score(x_train, y_train))
y_hat = clf.predict(x_train)
show_accuracy(y_hat, y_train, '训练集')
print(clf.score(x_test, y_test))
y_hat = clf.predict(x_test)
show_accuracy(y_hat, y_test, '测试集')

# 输出决策方程和预测值
print('decision_function:\n', clf.decision_function(x_train))
print('\npredict:\n', clf.predict(x_train))

x1_min, x1_max = x[:, 0].min(), x[:, 0].max()  # 第0列的范围
x2_min, x2_max = x[:, 1].min(), x[:, 1].max()  # 第1列的范围
x1, x2  = np.mgrid[x1_min:x1_max:200j, x2_min:x2_max:200j]  # 生成网格采样点
grid_test = np.stack((x1.flat, x2.flat), axis=1)  # 测试点

(3)画图:这里以伊利股份(600887)为例。


收盘价及前日收盘价MA40
分类器结果

数据范围(start_date='20080101', end_date='20190318'),可见GridSearchCV为我们采用了线性分类器,认为该股票前一日涨幅MA40大于0.13%的情况下,当日看涨,与股票价格关系不大。


预测准确率
分类器训练集预测准确率53.08%,测试集预测准确率反而高一些达54.06%。总体上并不算高,还有优化挖掘空间。
运行时间22s左右。

结语:svm还可以用于预测股价走势的反转,当svm连续失效的时候可能就是反转到来的信号。另外二维条件下有斜率的分类器方程,从图中也可看出有趣的含义。高维度情形值得进一步挖掘,但算力是个问题。
完整代码如下:

# -*- coding: utf-8 -*-
from sklearn import svm
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.model_selection import train_test_split
import tushare as ts
import pandas as pd
from sklearn.model_selection import GridSearchCV
import time

api = ts.pro_api('your token')
time_start=time.time()

mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
pro = ts.pro_api()

def get_data(ma):
    fig, ax = plt.subplots()
    #tushare获取数据
    df = ts.pro_bar(pro_api=api, ts_code='600887.SH', adj='qfq', start_date='20080101', end_date='20190318')
    df.index = pd.to_datetime(df.trade_date)
    #按日期排序
    df = df.sort_values(by = 'trade_date')
    #输入参数移动平均化
    dfMA = df.rolling(window=ma, center=False).mean()
    plt.plot(df.close)
    plt.plot(dfMA.pre_close)
    #只保留需要的参数
    df.drop(['ts_code', 'close', 'trade_date', 'open', 'high', 'low', 'pre_close', 'change', 'vol', 'amount'], axis=1, inplace=True)
    dfMA.drop(['ts_code', 'trade_date', 'open', 'high', 'low', 'close', 'change'], axis=1, inplace=True)
    #print(df.head())
    #print(dfMA.tail())
    #去除移动平均产生的前若干行的空值
    y = df[ma:].values
    x = dfMA[ma:].values
    #分类
    for i in range(len(y)):
        if y[i]>0:
            y[i]=1
        else:
            y[i]=0
    return x, y

    #显示预测准确度,可自己设置
def show_accuracy(y_hat, y_test, param):
    pass

ma=40
x, y = get_data(ma)
    #选择预测输入参数
x = x[:,:2]

#建议采用GridSearchCV调参,否则需手动调参
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, train_size=0.8)
#clf = svm.SVC(C=0.5, kernel='linear', decision_function_shape='ovr')  # 线性
#clf = svm.SVC(C=0.4, kernel='rbf', gamma=20, decision_function_shape='ovr') #非线性核函数

#测试集(用于预测1日)
'''
x_train = x[:-1]
x_test = x[-1],
y_train = y[:-1]
y_test = y[-1]
'''

#GridSearchCV自动调参
parameters = {'kernel':('linear', 'rbf'), 'C':[1, 2, 4], 'gamma':[0.125, 0.25, 0.5 ,1, 2, 4]}
svr = svm.SVC()
print(x, y.ravel())
clf = GridSearchCV(svr, parameters)
clf.fit(x, y.ravel())
print('GridSearchCVing...')
cv_result = pd.DataFrame.from_dict(clf.cv_results_)
print(cv_result)
print('best', clf.best_params_)

#手动调参拟合
#clf.fit(x_train, y_train.ravel())

#显示精度
print(clf.score(x_train, y_train))
y_hat = clf.predict(x_train)
show_accuracy(y_hat, y_train, '训练集')
print(clf.score(x_test, y_test))
y_hat = clf.predict(x_test)
show_accuracy(y_hat, y_test, '测试集')

# 输出决策方程和预测值
print('decision_function:\n', clf.decision_function(x_train))
print('\npredict:\n', clf.predict(x_train))

x1_min, x1_max = x[:, 0].min(), x[:, 0].max()  # 第0列的范围
x2_min, x2_max = x[:, 1].min(), x[:, 1].max()  # 第1列的范围
x1, x2  = np.mgrid[x1_min:x1_max:200j, x2_min:x2_max:200j]  # 生成网格采样点
grid_test = np.stack((x1.flat, x2.flat), axis=1)  # 测试点

#绘图
cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0'])
cm_dark = mpl.colors.ListedColormap(['g', 'r'])

print('grid_test = \n', grid_test)
grid_hat = clf.predict(grid_test)  # 预测分类值
grid_hat = grid_hat.reshape(x1.shape)  # 使之与输入的形状相同

alpha = 0.5
fig, ax = plt.subplots()
cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0'])
plt.pcolormesh(x2, x1, grid_hat, cmap=cm_light)     # 预测值的显示
plt.title(u'指数涨跌情况', fontsize=15)
plt.xlabel(u'前一日涨跌MA%s'%ma, fontsize=13)
plt.ylabel(u'前一日收盘价MA%s'%ma, fontsize=13)
plt.scatter(x[:, 1], x[:, 0], c=np.squeeze(y), edgecolors='k', s=15, cmap=cm_dark) # 样本
#plt.plot(x[:, 0], x[:, 1], 'o', color='blue',alpha=alpha, markeredgecolor='k')
plt.ylim(x1_min, x1_max)
plt.xlim(x2_min, x2_max)
#显示网格
plt.grid()

#计时器
time_end=time.time()
print('totally cost %s secs.'%round(time_end-time_start, 2))

plt.show()

参考文章:
https://www.cnblogs.com/luyaoblog/p/6775342.html
http://wenda.chinahadoop.cn/question/4787

上一篇下一篇

猜你喜欢

热点阅读