小蛇学python(4)利用SVM预测股票涨跌

最近接了一个私活,指导学妹完成毕业设计。核心思想就是利用SVM模型来预测股票涨跌,并完成策略构建,自动化选择最优秀的股票进行资产配置。

在做这个项目的过程中,我体会到想成为一个合格的数据分析或者数据挖掘工程师不仅技术要过关,还需要了解所要挖掘数据涉及到的领域的相关知识。举个例子,在做数据预处理的时候,不知道超额收益率是怎么个意思,查阅资料才了解,超额收益率是股票行业里的一个专有名词,指大于无风险投资的收益率,在我国无风险投资收益率即是银行定期存款。

也稍微打个小广告,简书上如果有需要做毕业设计的同学可以找我私聊哦。而且如果只是简单咨询一些技术问题,学长会很热心得解答。

废话少说,言归正传。这里有关SVM、PCA等等这些与项目相关的数学知识不会提及,我以后会在算法专题里详细描述。

本项目用pycharm + anaconda3.6开发,涉及到的第三方库有pandas,numpy,matplotlib,skllearn。

流程图

Quotation Flowchart.jpg

参数设置

class Para:
    method = 'SVM'                                              #模型选择为SVM
    month_in_sample = range(1,  7 + 1)              #训练集数据对应月份
    month_test = range(8, 12 + 1)                        #测试集数据对应月份
    percent_select = [0.3, 0.3]                              #正反例股票选取比例
    percent_cv = 0.1                                     #交互验证机占样本内数据比例
    path_data = 'C:/my python/python code/stock predict/Datas/Tests/Tests/'                         #输入数据文件路径
    path_result = 'C:/my python/python code/stock predict/Datas/Results/'                #输出数据文件路径
    seed = 42                         #random seed设置随机种子,制造伪随机数
    svm_kernel = 'linear'                            #支持向量机的核函数类型
    svm_c = 0.01                                       #线性支持向量机的惩罚系数
para = Para()

这个就是参数的初始化,没有什么要说的。

数据读取以及标记

#function: label data
#3数据标记
def label_data(data):
    data['return_bin'] = np.nan                                         #在data表格最后添加一列,并命名为return_bin,这一列将记录每个样本的标签
    data = data.sort_values(by = '超额收益', ascending = False)            #将整个表格按照return列的值降序排列
    n_stock_select = np.multiply(para.percent_select, data.shape[0])     #选取的股票个数
    n_stock_select = np.around(n_stock_select).astype(int)               #将上行所选取的股票个数取整,注意n_stock_select是个含有两个整数的列表
    data.iloc[0:n_stock_select[0],-1] = 1
    data.iloc[-n_stock_select[1]:, -1] = 0                               #这两行将表现好的股票标签置1,差的置0
    data = data.dropna(axis = 0)                                         #将没有1,0标签的,即不是最好的前百分之三十也不是最差的前百分之三十股票从表格里剔除
    return data

#4数据读取
for i_month in para.month_in_sample:
    file_name = para.path_data + str(i_month) + '.csv'
    data_curr_month = pd.read_csv(file_name, header = 0)                 #header=0意思是将表格第一行作为列名
    para.n_stcok = data_curr_month.shape[0]
    data_curr_month = data_curr_month.dropna(axis = 0)                   #去除缺省值
    data_curr_month = label_data(data_curr_month)                        #将读入的数据进行标记
    if i_month == para.month_in_sample[0]:
        data_in_sample = data_curr_month
    else:
        data_in_sample = data_in_sample.append(data_curr_month)       
print('数据读取完成')

#5数据预处理
X_in_sample = data_in_sample.loc[:,'货币资金(万元)':'应收分保账款(万元)']
y_in_sample = data_in_sample.loc[:,'return_bin']                       #从样本内数据中分别提取特征与标签因子
X_train,X_cv,y_train,y_cv = train_test_split(X_in_sample,y_in_sample,test_size=para.percent_cv, random_state=para.seed)#按照固定比例随机分配训练集与交叉验证集
pca = decomposition.PCA(n_components=0.95)
pca.fit(X_train)
X_train = pca.transform(X_train)
X_cv = pca.transform(X_cv)                                              #以上是关于主成分分析模型的代码
print('数据预处理完成')

代码的基本功能注释里也写了一些,不过不够全面,我再详细说一下。这三部分代码所实现的功能是读取数据,并对数据进行预处理。我已经把最原始的数据整理好放在了excel表格里,并且将第一个月的全部股票的参数放在一个excel里,并将其命名为1.csv,以此类推,我爬取了157个月的数据,总共有157个excel。因此代码里循环的便是excel的文件名,也就是依次读取excel文件。因为数据量太大,所以我一般调试的时候只跑12个月。所以我在参数初始化阶段,训练集(1,8),测试集(8,12)。


数据截面.png

将数据读取到DataFrame表格里后,并不是全部使用,而是取超额收益值最好的前百分之三十,以及最差的后百分之三十,并在表格后追加一列,列名叫return_bin,将最好最差的百分之三十的股票的return_bin列各赋值1,0。然后将每个读取并加工的excel表格拼接在一起形成一个大表格,从总抽取70个因子作为X_in_sample,抽取return_bin作为y_in_sample作为训练集。

训练模型

#6
print('选择模型')
if para.method == 'SVM':
    model = svm.SVC(kernel=para.svm_kernel, C=para.svm_c)
print('模型选择为SVM')

#7用训练好的模型分别放到训练集和验证集上去预测,用来调参
print('模型开始训练')
if para.method == 'SVM':
    model.fit(X_train, y_train)
    y_pred_train = model.predict(X_train)
    y_score_train = model.decision_function(X_train)
    y_pred_cv = model.predict(X_cv)
    y_score_cv = model.decision_function(X_cv)
print('模型训练结束')

这个也比较好理解,就是选择sklearn库里的svm模块对数据进行训练。svm模型是集成封装好的,拿来用就可以。

模型预测与评价

#8使用训练完成的模型再测试集上做预测
print('模型预测开始')
y_true_test =  pd.DataFrame([np.nan] * np.ones((para.n_stcok, para.month_test[-1])))
y_pred_test =  pd.DataFrame([np.nan] * np.ones((para.n_stcok, para.month_test[-1])))
y_score_test = pd.DataFrame([np.nan] * np.ones((para.n_stcok, para.month_test[-1])))    #先对各种参数做一个初始化
print(y_true_test)
for i_month in para.month_test:                                                         #遍历测试集的每一个月份,每个月份都有上市的所有股票
    file_name = para.path_data + str(i_month) + '.csv'                                  #读取预测集上的数据
    data_curr_month = pd.read_csv(file_name, header=0)
    data_curr_month = data_curr_month.dropna(axis=0)
    X_curr_month = data_curr_month.loc[:,'货币资金(万元)':'应收分保账款(万元)']
    X_curr_month = pca.transform(X_curr_month)
    if para.method == 'SVM':
        y_pred_curr_month = model.predict(X_curr_month)
        y_score_curr_month = model.decision_function(X_curr_month)
    y_true_test.iloc[data_curr_month.index, i_month - 1] = data_curr_month['超额收益'][data_curr_month.index]
    y_pred_test.iloc[data_curr_month.index, i_month - 1] = y_pred_curr_month
    y_score_test.iloc[data_curr_month.index, i_month - 1] = y_score_curr_month
print(y_true_test)
print('模型预测结束')

#9模型评价
print('模型开始评价')
print('training set, accuracy = %.2f'%metrics.accuracy_score(y_train, y_pred_train))
print('training set, ACU = %.2f'%metrics.roc_auc_score(y_train, y_score_train))
print('cv set, accuracy = %.2f'%metrics.accuracy_score(y_cv, y_pred_cv))
print('cv set, ACU = %.2f'%metrics.roc_auc_score(y_cv, y_score_cv))
for i_month in para.month_test:
    y_true_curr_month = pd.DataFrame({'超额收益':y_true_test.iloc[:, i_month - 1]})
    y_pred_curr_month = y_pred_test.iloc[:,i_month - 1]
    y_score_curr_month = y_score_test.iloc[:,i_month - 1]
    y_true_curr_month = y_true_curr_month.dropna(axis=0)
    y_curr_month = label_data(y_true_curr_month)['return_bin']
    y_pred_curr_month = y_pred_curr_month[y_curr_month.index]
    y_score_curr_month = y_score_curr_month[y_curr_month.index]
    print('test set, month %d, accuracy = %.2f'%(i_month, metrics.accuracy_score(y_curr_month, y_pred_curr_month)))
    print('test set, month %d, AUC = %.2f'%(i_month, metrics.roc_auc_score(y_curr_month, y_score_curr_month)))
print('模型评价结束')

现在模型就训练好了,然后就那训练好的模型在测试集上来跑,看看情况到底如何。


模型评价.png

可以看到参数并不是多好,这是训练集上数据太少的原因。那我们来改动一下,把训练集改成(1,10),测试集改成(10,12)看看有没有改变。


模型评价2.png

训练集从只有6个月变成9个月(1-10在代码上体现为1-9),参数情况大有改观。可见数据对机器学习模型训练的重要性。

策略构建以及策略评价

#10策略构建
print('策略构建开始')
para.n_stcok_select = 3
strategy = pd.DataFrame({'return' : [0]*para.month_test[-1], 'value' : [1]*para.month_test[-1]}) #这并不是字典格式,而是表格格式,分别表示每月收益和每月净值,初始值为0和1
print(strategy)
for i_month in para.month_test:
    y_true_curr_month = y_true_test.iloc[:,i_month - 1]
    print(y_true_curr_month)
    y_score_curr_month = y_score_test.iloc[:,i_month - 1]
    y_score_curr_month = y_score_curr_month.sort_values(ascending=False)
    print(y_score_curr_month)
    index_select = y_score_curr_month[0:para.n_stcok_select].index
    print(index_select)
    strategy.loc[i_month-1, 'return'] = np.mean(y_true_curr_month[index_select])
    print(strategy)
strategy['value'] = (strategy['return'] + 1).cumprod()

print('策略构建结束')

#11策略评价
print('策略评价开始')
month_test = np.array(para.month_test)
month_test = month_test - 1
print(strategy.loc[month_test, 'value'])
plt.plot(month_test, strategy.loc[month_test, 'value'],'r-')
plt.show()

ann_excess_return = np.mean(strategy.loc[month_test,'return']) * 12             #策略年化超额收益
print(ann_excess_return)
ann_excess_vol = np.std(strategy.loc[month_test,'return']) * np.sqrt(12)        #策略年化超额收益波动
info_ratio = ann_excess_return/ann_excess_vol                                    #数值越大策略越好
print('annual excess return = %.2f'%ann_excess_return)
print('annual excess volatility = %.2f'%ann_excess_vol)
print('information ratio = %.2f'%info_ratio)
print('以上为策略评价参数')

print('策略评价结束')

所谓策略构建就是选择什么样的股票,代码里将股票按照超额收益率进行排序,然后我设置para.n_stcok_select = 3意思就是选择超额收益率前三名进行购买。

所谓策略评价这里采用的评价体系就是将选择的三支股票的每月超额收益率取平均值乘12,来作为这三只股票在该月的年化收益率。

Figure_1.png

Figure_2.png

以上两张图是选择不同月份做训练集后,模型策略的表现。

在这里还要提及的是这行代码,month_test = np.array(para.month_test)
month_test = month_test - 1。这个涉及到了np数组的高阶用法。一般数组是无法和数字做运算的,可是将普通数组用np.array()加工过后,变成了np数组,他拥有一个广播属性,可以直接与数字运算。该行代码就是将数组里每个元素都减1。

想要数据集跑程序又不会爬虫的可以私聊我,可以分享

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容