随着获取数据的渠道越来越多以及开源的机器学习模型的解决方案架构日渐成熟,机器学习(Machine Learning)在量化投资领域的应用也越来越广泛。用机器学习模型预测股票价格需要掌握比监督机器学习(supervised learning)更多的概念和技术。股票预测是一个充满挑战的领域,需要专门的技术来解决一些问题,如处理非稳定性(non-stationarity)、共线性( collinearity)和信噪比(low signal-to-noise)等。
本系列文章将完整地讲述如何用机器学习模型预测股票价格的方法与过程,但仅限于介绍专门的方法解决某一方面的问题,不提供任何交易秘籍或策略,只是提供更有价值、更具有普遍意义的一组技术方法,可以用机器人方式创建自己的交易策略。
无论是在机器学习模型开发,还是在金融分析和量化交易中,数据准备是最基础的一项工作,相关从业人在数据采集、整理和初加工处理方面要耗费大量的时间和精力,但是没有数据,要开展其他工作都是“巧妇难为无米之炊”。本系列文章也先从数据准备开始,编程语言用python,使用pandas和scikit-learn工具包以及pandas和scikit-learn结合使用,准备数据也采用了适合python使用的结构或格式。
根据股票行情的原始数据,以dataframe类型存放数据,在此基础上进行特征提取并产生相关的输出结果,尤其是对记录时间 t = 0 的处理非常重要,会影响某个时间级别上下一个周期的结果。数据规划方法如下:
- 在训练模式(training model)下抽取x和y的值很琐碎,所以把 x 根据特征切分成多个列,把y中的输出分为一列;
- 在不同的时间级别之间频繁地切换时,只改变输出的列 y;
- 防止使用没有发生的未来数据
- 在X中只使用特征列,可以使用高效的panda的join、merge和concat方法快速对齐数据,提供给训练模式使用
- 实践证明,这样做可以节约大量的调试时间。
实例
获取股票行情原始数据
本例中股票行情数据格式如下:
代码,日期,开盘价,最高价,最低价,收盘价,成交量,成交额
300012,2022-02-24,20.03,20.06,19.06,19.40,15124707,295822016.00
300012,2022-02-25,19.60,19.92,19.50,19.68,12862232,254371216.00
300012,2022-02-28,19.78,21.10,19.52,20.81,21290282,435365664.00
300012,2022-03-01,20.76,20.89,20.28,20.55,10720702,220226576.00
300012,2022-03-02,20.39,20.58,20.20,20.31,7000770,142371968.00
300012,2022-03-03,20.49,20.57,19.80,19.89,8410349,169247312.00
导入watched_list.csv文件中的股票日线行情,该文件内容如下:
0002142
1600036
1601166
1601318
0002241
1600031
0000338
0300012
注:文件中编码格式为: 市场代码 + 股票代码,0代码深市,1代表沪市。
导入股票行情的程序代码如下:
import pandas as pd
import csv
target_files = []
with open('D:\SecurityData\watched_stocks.csv','r') as csv_file: #watched_stocks.csv, watch_list.csv
csv_reader = csv.reader(csv_file)
for line in csv_reader:
str_line = str(line)
lt,rt = str_line[2:3], str_line[3:9]
if lt == '0':
target_files.append('D:\SecurityData\MD_daily\SZ#'+ rt +'.csv') # 23123-
else:
target_files.append('D:\SecurityData\MD_daily\SH#'+ rt +'.csv') # 18515-
#print(target_files)
print('选定股票的csv文件个数:',len(target_files))
print('target_files 的前 5 个元素:')
print(target_files[0:5])
以上代码运行结果如下:
选定股票的csv文件个数: 8
target_files 的前 5 个元素:
[‘D:\\SecurityData\\MD_daily\\SZ#002142.csv’, ‘D:\\SecurityData\\MD_daily\\SH#600036.csv’, ‘D:\\SecurityData\\MD_daily\\SH#601166.csv’, ‘D:\\SecurityData\\MD_daily\\SH#601318.csv’, ‘D:\\SecurityData\\MD_daily\\SZ#002241.csv’]
截取480天的日线行情数据
代码如下:
df_list = []
# D:\SecurityData\MD_daily\SZ#002142.csv
finalmd_filelist = []
for filename in target_files:
try:
headers = ['symbol', 'date', 'open', 'high','low','close','volume','amnt']
dtypes = {'symbol':'str', 'date':'str', 'open':'float', 'high':'float','low':'float','close':'float','volume':'int','amnt':'float'}
parse_dates = ['date'] # 字符型解析为日期型
df = pd.read_csv(filename, sep=',', header=None, names=headers, dtype=dtypes, parse_dates=parse_dates)
if (len(df) > 480):
del df['amnt']
df_cut = df[(len(df)-481):]
df_cut.reset_index(drop=True,inplace=True)
df_cut = df_cut.set_index(['date','symbol'])
df_list.append(df_cut)
finalmd_filelist.append(filename[25:])
else:
print(f'{filename} 的长度:{len(df)}')
except:
print('error:',filename)
print(df_list[0])
print('df_list_len: ',len(df_list))
#print(finalmd_filelist)
print('finalmd_filelist: ',len(finalmd_filelist))
运行结果如下:
open high low close volume
date symbol
2020-03-03 002142 26.83 27.15 26.46 26.87 27523448
2020-03-04 002142 26.62 27.16 26.42 26.75 27569739
2020-03-05 002142 26.88 28.06 26.70 27.86 44950054
2020-03-06 002142 27.69 28.14 27.33 27.48 24759915
2020-03-09 002142 26.90 27.41 26.52 27.22 32220580
... ... ... ... ... ...
2022-02-25 002142 38.30 38.55 37.55 37.78 29986248
2022-02-28 002142 37.70 38.21 37.29 38.10 23032349
2022-03-01 002142 38.11 38.73 37.49 38.44 30165840
2022-03-02 002142 38.11 38.50 38.01 38.11 17172189
2022-03-03 002142 38.43 39.60 38.27 39.40 30704080
[481 rows x 5 columns]
df_list_len: 8
finalmd_filelist: 8
把所选股票的收盘价拼接在名称为prices的dataframe中,
prices = pd.concat(df_list,axis=0)
num_obs = prices.close.count()
print(num_obs)
特征提取(Feature Extract)
现在创建了2只股票的5个特征,数据截止的交易日 t,为了简化起见,删除了所有含有空值的行,因为scikit-learn 不能处理这类空值数据。代码如下:
features = pd.DataFrame(index=prices.index)
features['volume_change_ratio'] = prices.groupby(level='symbol').volume.diff(1) / prices.groupby(level='symbol').shift(1).volume
features['momentum_5_day'] = prices.groupby(level='symbol').close.pct_change(5)
features['intraday_chg'] = (prices.groupby(level='symbol').close\
.shift(0) - prices.groupby(level='symbol').open\
.shift(0))/prices.groupby(level='symbol').open.shift(0)
features['day_of_week'] = features.index.get_level_values('date').weekday
features['day_of_month'] = features.index.get_level_values('date').day
features.dropna(inplace=True)
print(features.tail(10))
以上代码运行结果如下:
volume_change_ratio momentum_5_day intraday_chg \
date symbol
2022-02-18 300012 0.264820 -0.024602 -0.018447
2022-02-21 300012 -0.029458 0.005473 0.000000
2022-02-22 300012 -0.188978 -0.058994 -0.027486
2022-02-23 300012 0.175002 -0.013203 0.036466
2022-02-24 300012 0.036998 -0.066410 -0.031453
2022-02-25 300012 -0.149588 -0.026706 0.004082
2022-02-28 300012 0.655256 0.029688 0.052073
2022-03-01 300012 -0.496451 0.056012 -0.010116
2022-03-02 300012 -0.346986 0.006442 -0.003923
2022-03-03 300012 0.201346 0.025258 -0.029283
day_of_week day_of_month
date symbol
2022-02-18 300012 4 18
2022-02-21 300012 0 21
2022-02-22 300012 1 22
2022-02-23 300012 2 23
2022-02-24 300012 3 24
2022-02-25 300012 4 25
2022-02-28 300012 0 28
2022-03-01 300012 1 1
2022-03-02 300012 2 2
2022-03-03 300012 3 3
设计输出(outcome)结果
outcomes = pd.DataFrame(index=prices.index)
# 下一个交易日开盘价的变化
outcomes['open_1'] = prices.groupby(level='symbol').open.shift(-1)/prices.groupby(level='symbol').close.shift(0)-1
# 下一个交易日收盘价的变化
func_one_day_ahead = lambda x: x.pct_change(-1)
outcomes['close_1'] = prices.groupby(level='symbol').close\
.apply(func_one_day_ahead)
func_five_day_ahead = lambda x: x.pct_change(-5)
outcomes['close_5'] = prices.groupby(level='symbol').close\
.apply(func_five_day_ahead)
print((outcomes.tail(15)))
以上代码运行结果如下:
open_1 close_1 close_5
date symbol
2022-02-11 300012 -0.006271 0.031343 0.025223
2022-02-14 300012 0.007960 -0.028046 -0.005443
2022-02-15 300012 0.000484 0.011247 0.062693
2022-02-16 300012 0.002445 -0.015881 0.013380
2022-02-17 300012 -0.008662 0.027695 0.071134
2022-02-18 300012 -0.000495 0.000495 0.027439
2022-02-21 300012 -0.009896 0.038541 -0.028832
2022-02-22 300012 0.000514 -0.035679 -0.053041
2022-02-23 300012 -0.007433 0.040206 -0.006401
2022-02-24 300012 0.010309 -0.014228 -0.024635
2022-02-25 300012 0.005081 -0.054301 NaN
2022-02-28 300012 -0.002403 0.012652 NaN
2022-03-01 300012 -0.007786 0.011817 NaN
2022-03-02 300012 0.008863 0.021116 NaN
2022-03-03 300012 NaN NaN NaN
注意:因为要在不同的时间期限切换,所以把切换时间点设定为负值,这是pandas对未来时间做的约定,在时间期限的截止点用null值表示,输出中有更多的null值,表示更远的未来。这里没有用dropna()处理数据,因为要用open_1。把这些数据拼接在一起,就可以用scikit-learn训练简单的线性模型,用所有的特征预测close_1。
# 选取有效的 y 和 X 的所有行创建一个序列 y 和一个数据表 X(dataframe格式)
y = outcomes.close_1
X = features
Xy = X.join(y).dropna()
y = Xy[y.name]
X = Xy[X.columns]
print(y.shape)
print(X.shape)
以上代码的运行结果:
(3800,)
(3800, 5)
用线性回归方法预测股票价格
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X,y)
print("模型度量: "+ str(model.score(X,y)))
print("相关系数: ")
print(pd.Series(model.coef_,index=X.columns).sort_values(ascending=False))
以上代码运行结果:
模型度量: 0.0015311462494447259
相关系数:
intraday_chg 0.028121
momentum_5_day 0.007383
day_of_week 0.000104
day_of_month -0.000009
volume_change_ratio -0.000910
dtype: float64
从结果看,这个方法没有实际应用价值,只是为了方便演示。
用随机森林方法预测股票价格
from sklearn.ensemble import RandomForestRegressor
y = outcomes.open_1
X = features
Xy = X.join(y).dropna()
y = Xy[y.name]
X = Xy[X.columns]
print(y.shape)
print(X.shape)
model = RandomForestRegressor(max_features=3)
model.fit(X,y)
print("模型度量: "+ str(model.score(X,y)))
print("各特征的重要性度量: ")
print(pd.Series(model.feature_importances_,index=X.columns)\
.sort_values(ascending=False))
以上代码运行结果如下:
(3800,)
(3800, 5)
模型度量: 0.8617588021955569
各特征的重要性度量:
momentum_5_day 0.286442
intraday_chg 0.256806
volume_change_ratio 0.237144
day_of_month 0.140524
day_of_week 0.079085
dtype: float64
从上述两种方法的运行结果看,随机森林方法比线性回归方法有了很大的改进,但明显有过度拟合的现象。
以下为其中一只股票的价格预测结果的部分内容:
print(pd.Series(model.predict(X),index=X.index).tail(10))
date symbol
2022-02-17 300012 -0.006470
2022-02-18 300012 -0.000266
2022-02-21 300012 -0.008691
2022-02-22 300012 -0.000065
2022-02-23 300012 -0.003559
2022-02-24 300012 0.008192
2022-02-25 300012 0.003273
2022-02-28 300012 -0.001825
2022-03-01 300012 -0.004394
2022-03-02 300012 0.005940
dtype: float64

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/76380
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!