摘要及声明
1:本文主要介绍通过分类树算法预测股价;
2:本文主要为理念的讲解,模型也是笔者自建,文中假设与观点是基于笔者对模型及数据的一孔之见,若有不同见解欢迎随时留言交流;
3:笔者希望搭建出一套交易体系,原则是只做干货的分享。后续将更新更多内容,但工作学习之余的闲暇时间有限,更新速度慢还请谅解;
4:模型实现基于R 4.1.2;
之前笔者写过几篇股价的预测文章,不过大多是讲解理念和研究方法,没有落实到具体的可行模型上。本文基于决策树算法简单介绍一个股价预测模型的框架并用实际数据简单实现一个分类树模型,回测结果准确率达80%以上。本文主要内容如下:
目录
1. 决策树背后的金融理念
2. 基于行业/同类型公司的学习
3. 用分类树预测股价涨跌
3.1 数据准备
3.2 构建分类树
3.3 回测结果
4.总结
笔者在上期曾介绍过决策树(决策树的实现及可视化方法总结),当时提到决策树的几个重要性质。其中一个性质是路径依赖,也就说决策树在决策的过程严格按照从根节点到叶节点的路径。可以说,大部分的股票买卖决策都和决策树的决策方式相吻合。不知道大家有没有看到过一些投顾的某某选股法,例如先在市场中选择ROE大于20%的,然后在其中选择PE小于30倍的,接着再筛选出PB小于2倍的,最后再加点技术指标,例如出现了MACD背离的。然后投顾会告诉你这样一通操作下来市场上四千支票子就不剩多少了,就买它们然后一直拿(套)着,等等。
被这么忽悠过的举手。玩笑归玩笑,这个过程实际上就是以决策树的方式进行选股。但问题还是在于我们如何能够判定20%的ROE一定是好的决策依据?是依靠经验还是信仰?抑或是瞎猜呢?因此这里笔者引入决策树算法,依靠强大的数据支撑算出那个更为可靠的决策依据。
由于各行各业的差异较大,同一个指标在不同的行业就是天差地别。例如银行的PB指标低不是一年两年了,年年都有人叫银行估值水平低,但看到说PB低作为买入依据的笔者都笑了。银行估值水平低是不假,但买进去也不见得一定会赚。之前笔者写过一篇相对估值的文章,相对估值的一个重要理念是一定要有一个标准比较才能说某个公司相对低/高估。银行业的PB指标都很低,这是行业特点,单说银行业PB水平低而不说比较基准是什么就什么也看不出来。
不同行业的数据差异会让机器学习算法十分困惑,就好比只告诉算法PB高低却不给它比较的基准。上一秒看的银行业都是破净的PB,下一秒看的全是软件开发这种高PB。数据集自带的这种天然差别对算法误差降低,提高拟合度非常不利,换句话说就是机器都懵了。因此在做类似这种模型的时候一定要将数据差异考虑进去。
由于上期已经介绍过决策树的实现,本期就不在技术问题上做过多讨论了。在语言方面笔者选用R语言,比起Python,R语言在决策树可视化上友好不少,笔者也在上期对两种语言做了比较,对用Python实现感兴趣的可以看上期内容。
数据方面笔者选取的是Tushare金融大数据平台的API,由于之前是用Python写的数据获取代码,本期又需要用R语言建模,文章代码如果一会变一种语言的可能阅读体验不是很好,因此这里就不展示数据获取的代码了。
数据集划分上,笔者采用半导体行业近3年的季度财务和市场指标。笔者挑选出了6个自变量:PE,PB,ROE,ROE季度增速,Z-Score,季度股东人数增长;笔者选取财报公布后下一期的涨跌幅作为因变量。这里唯一让笔者不太满意的是财报公布期没有把一年进行均匀切分,因此笔者只能将一年的预测期非均等的拆成四份:
图一:财务数据及预测数据获取时点
财务数据是迟滞的,笔者对次年一月预测期的划分不太满意,毕竟上图一季报和年报预测期重合了很大一部分,但如果只预测次年7-8月又使得预测期太短,因此这里稍微妥协了一下,笔者认为季报数据这样划分已经是比较合理的方式,如果有更好的建议欢迎告知。
将获取到的三年数据分别存入三个csv文件,然后用R语言读取,下面展示一下19年的:
df <- read.csv("data19.csv")
View(head(df))
图二:半导体2019年行业数据集前五列
由于笔者想构建分类树,这里将标签转换成哑变量:
df$Pct_chg[df$Pct_chg > 0] <- 1
df$Pct_chg[df$Pct_chg <= 0] <- 0
统计一下各个变量分布:
X time Holders Z_Score
Min. : 0.0 Min. :20201 Min. :-0.59596 Min. :-3.7616
1st Qu.: 69.5 1st Qu.:20201 1st Qu.:-0.06627 1st Qu.: 0.6101
Median :139.0 Median :20202 Median : 0.00000 Median : 1.0930
Mean :139.0 Mean :20202 Mean : 0.08540 Mean : 1.0806
3rd Qu.:208.5 3rd Qu.:20203 3rd Qu.: 0.19510 3rd Qu.: 1.7242
Max. :278.0 Max. :20204 Max. : 1.87646 Max. : 4.2757
Roe Delta_Roe PE PB
Min. :-0.63396 Min. :-715.6174 Min. : 0.00 Min. : 0.000
1st Qu.: 0.02017 1st Qu.: -0.4393 1st Qu.: 41.33 1st Qu.: 3.530
Median : 0.05944 Median : -0.0642 Median : 86.31 Median : 5.439
Mean : 0.25232 Mean : -2.5666 Mean : 199.51 Mean : 8.328
3rd Qu.: 0.10858 3rd Qu.: 0.3006 3rd Qu.: 174.04 3rd Qu.: 9.271
Max. :41.11373 Max. : 26.7727 Max. :3308.41 Max. :60.968
Pct_chg
Min. :0.000
1st Qu.:0.000
Median :1.000
Mean :0.638
3rd Qu.:1.000
Max. :1.000
PE,PB最小值都为0是因为Tushare的接口将亏损企业的PE,PB都设置为0。
拟合分类树,查看最佳拟合优度:
fit.tree <- rpart(Pct_chg~Holders+Z_Score+Roe+Delta_Roe+PE+PB, data=df, method="class")
printcp(fit.tree)
plotcp(fit.tree)
图三:分类树的最佳拟合参数
图三可以看到,分类树在cp值在0.037时误差最小,接下来以0.037的复杂度参数构建分类树:
fit.prune <- prune.rpart(fit.tree, cp=0.037)
rpart.plot(fit.prune)
可视化有图四:
图四:构建完成的分类树(参数CP=0.037)
可以看到,算法将PB,Z-score这些判断节点都drop掉了。如果不设置参数,树的规模会变大,分类也更细,但根据拟合优度图,大树的误差没有小树低:
图五:不设置复杂度参数的分类树
通过图四可以发现,模型并没有将ROE作为任何判断依据,反而将股东人数变化状况,ROE的变化情况作为比较重要的判断依据。
笔者通过2019年的数据训练分类树,接着利用2020与2021年的数据进行回测,最后使用混淆矩阵测试分类效果。下面以20年回测为例计算混淆矩阵:
df_predict <- read.csv("C:/Users/Administrator/Desktop/data20.csv")
tree.predict <- predict(fit.tree, df_predict[3:8], type="class")
df_predict$Pct_chg[df_predict$Pct_chg > 0] <- 1
df_predict$Pct_chg[df_predict$Pct_chg <= 0] <- 0
table(predict=tree.predict, actual=df_predict$Pct_chg)
可得:
actual
predict 0 1
0 21 4
1 10 53
稍微计算一下几个重要比率吧:
可以看到,准确率为84.9%,其它指标也都颇为不错,只是模型在预测下跌时表现没有预测上涨表现好。
再看21年的:
df_predict <- read.csv("C:/Users/Administrator/Desktop/data21.csv")
tree.predict <- predict(fit.tree, df_predict[3:8], type="class")
df_predict$Pct_chg[df_predict$Pct_chg > 0] <- 1
df_predict$Pct_chg[df_predict$Pct_chg <= 0] <- 0
table(predict=tree.predict, actual=df_predict$Pct_chg)
可得:
actual
predict 0 1
0 67 20
1 34 158
几个重要比率:
与2020年的表现差不了太多,笔者还是较为满意的。
以上模型仅供参考,笔者也只是用了三年的简单数据指标,回测评价指标也比较简单。但笔者认为决策树还是很强大的,本身笔者也在实务中使用。只是笔者并非将之作为交易的唯一依据,更多是作为一个参考。后面笔者还会出个两期在投资组合管理和风控方面应用的决策树算法,敬请期待。