本文将使用最简单的KNN算法,基于真实的股票数据集来制定交易策略,并计算它所带来的收益。
首先我们使用之前学过的 datareader 来获取股票数据,这里需要导入一些必要的库,输入代码如下:
#导入Pandas
import pandas as pd
#导入金融数据获取模块datareader
import pandas datareader.data as web
#导入numpy,-会儿会用到
import numpy as np
运行代码,如果程序没有报错,就说明导入成功。接下来,我们可以定义一个获取股票数据的函数,以便未来还可以复用。输入代码如下:
#首先我们来定义一个函数,用来获取数据
#传入的三个参数分别是开始日期、结束日期和输出的文件名
def load_stock(start_date, end_date, output_file):
#首先让程序尝试读取己下载并保存的文件
try:
df = pd.read_pickle(output_file)
# 中如果文件已存在,则输出"载入股票数据文件完毕"
print('输入股票数据文件完毕')
# 如果没有找到文件,则重新下载文件
except FileNotFoundError:
print("文件未找到,重新下载中")
# 这里指定下载601318的交易数据
# 下载源为yahoo
df = web.DataReader('601318.SS','yahoo', start_date, end_date)
# 下载成功后保存为pickle文件
df.to_pickle(output_file)
# 通知我们下教完成
print("下教完成")
#最后将下载的数据表进行返回
return df
运行代码之后,就完成了西数的定义。下面就可以使用这个西数来获取数据。输入代码如下:
#下面使用我们定义好的西数来获取交易数据
#获取三年的数据,从2017年3月9日至2020年的3月5日
#保存为名为601318的pickle文件
zgpa = load_stock(start_date ="2017-03-09",
end_date = "2020-03-05",
output_file = "601318.pkl")
运行代码,会得到以下结果:
文件未找到,重新下载中
下载完成
因为这里是第一次使用 load_stook
函数来获取数据,所以程序会提示没在找到文什,并重新开始下载文件。稍等片到之后我们便可以我到程序告知数据下载完成。如果读者朋友想要查看己经下载的数据,则可以使用下面这行代码。
#查看数据的前五行
zgpa.head()
运行代码,可以得到如表所示的结果。
Date(日期) | High(最高价) | LOW(最低价) | Open(开盘价) | Close(收盘价) | Volume(成交量) | Adi Close(调整后的收盘价〕 |
---|---|---|---|---|---|---|
2017-03-09 | 35.799999 | 35.500000 | 35.770000 | 35.779999 | 37796652.0 | 33 418541 |
2017-03-10 | 35.770000 | 35.580002 | 35.709999 | 35.599998 | 20744676.0 | 33.250423 |
2017-03-13 | 36.040001 | 35.560001 | 35.599998 | 35.970001 | 35999002.0 | 33.596004 |
2017-03-14 | 36.130001 | 35.810001 | 35.990002 | 35.939999 | 27696420.0 | 33.567982 |
2017-03-15 | 36.000000 | 35.759998 | 35.880001 | 35.959999 | 26872050.0 | 33.586662 |
从表3.1 中可以看到,股票数据已经成功加载,包括的字段有 Date (日期)、High(最高价)、Low(最低价)、Open(开盘价)、Close(收盘价)、Volume(成交量),和 Adi Close(调整后的收盘价)。
接下来我们做一点简单的特征工程,以便进行后面的工作。这里用每日开盘价减去收盘价,并保存为一个新的特征:用最高价减去最低价,保存成另外一个特征。同时,如果股票次日收盘价高于当日收盘价,则标记为 1,代表次日股票价格上涨;反之,如果次日收盛价低于当日收盘价,则标记为-1,代表股票次日价格下跌或者不变。这个过程可以称为创建股票的交易条件 (trading condition)。输入代码如下:
#下面我们来定义一个用于分类的函数,给数据表增加三个字段
#首先是开盘价减收盘价,命名为pen-close
#其次是最高价减最低价,命名为High-Low
def classification_tc(df):
df['Open-Close']= df['Open'] - df['Close']
df['High-Low'] = dE['High'] - df['Low']
# 添加
# 一个target字段,如果次日收盘价高于当日收盘价,则标记为1,反之为-1
df['target'] = np.where(df['close'].shift(-1)>df['close'], 1, -1)
#去掉有空值的行
df = df.dropna()
# 将open-Close和High-Low作为数据集的特征
X = df[['Open-Close','High-Low']]
# 将target赋值给y
y = df['target']
# 将x与y进行返回
return(X, y)
运行代码,就完成了这个函数的定义。由于我们通过股票价格变化的情况对数据进行了分类,即1代表价格上涨,-1代表价格下跌或不变,这个交易条件可以用来训练分类模型。让模型预测某只股票在下一个交易日价格上涨与否。
接下来,我们就使用上一步中定义的函数来处理下载好的股票数据,生成训练集与验证集,并训练一个简单的模型,以执行我们的交易策略。输入代码如下:
# 使用classification tc函数生成数据集的特征与目标
df, X, y = classification_tc(zqpa)
#将数据集拆分为训练集与验证集
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = 0.8)
运行代码后,我们会得到训练集与预测集。现在就使用 KNN 算法来进行模型的训练,并查看模型的性能。输入代码如下:
#创建个KNN买例,n_neighbors取95
knn_clf = kNeighborsclassitier(n_neighbors=95)
#使用KNN拟合训练集
knn_clf.fit(x_train, y_traln)
#输出模型在训练集中的准确率
print(knn.clf.score(X_train, y_train))
#输出模型在验证集中的准确率
print(knn_clf.score(X_test, y_test))
0.5421686746987951
0.541095890410959
从代码运行结果可以看到,使用经处理的数据集训练的KNN模型,在训综集中的淮确率是 54% 左右,在验证集中的谁确率也是 54% 左右。这个准确率远谈不上理想,相当于只有一半时间里模型对股价的涨跌预测正确。原因是我们训练模型的样木特征确实太少了,无法支撑模型做出正确的判断。不过大家也不要担心,我们只是初步做一个演示而己。
既然模型己经可以做出预测(先不论谁确率如何),接下来我们就可以来验证一下,使用模型预测作为交易信号 (trading signal)来进行交易,并且与基准收益进行对比。首先我们要计算出基准收益和基于模型预测的策略所带来的收益。输入代码如下:
#使用KNN模型预测每日股票的涨跌,保存为Predict_ signal
df['predict_Signal']=knn.reg.predict(x)
#在数据集中添和一个字段,用当日收盘价除以前一日收盘价,并取其自然对数
df['Return'] = np.log(df['Close']/df['close'].shift(1))
#查看一下
df.head ()
Date(日期) | High(最高价) | (字段省略) | Predict Signal(交易信号) | Return(对数收益) |
---|---|---|---|---|
2017-03-09 | 35.799999 | ...... | 1 | NaN |
2017-03-10 | 35.770000 | ...... | 1 | -0.005043 |
2017-03-13 | 36.040001 | ...... | 1 | 0.010340 |
2017-03-14 | 36.130001 | ...... | 1 | -0.000834 |
2017-03-15 | 36.000001 | ...... | 1 | 0.000556 |
从上表中可以看到,数据表中的Predict Sienal 存储的是KNN模型票涨跌的预测,而 Retumn 是指当日股票价格变动所带来的收益。
下面我们定义一个函数,计算一下累计的基准收益。输入代码如下:
#定义一个计算累计基准收益的函数
def cum_return(df, split_value):
# 该股票基准收益为Return的总和乘以100,这里只计算预测集的结果
cum_return = df[split_value:]['Return'].cumsum()*100
#将讦算结果进行返回
return cum_returns
运行代码,就完成了这个西数的定义。接下来我们再定义一个函数,计算基于KNN模型预测的交易信号所进行的策略交易带来的收益。输入代码如下:
# 定义一个计算使用策略交易的收益
def strategy_return(df, split_value):
# 使用策略交易的收益为模型Return乘以模型预测的涨跌幅
df['strategy_Return']= df['Return']*df['Predict_Signal'].shift(1)
#将每日策略交易的收益加和并乘以100
cum_strategy_return = df[split_value:]['Strategy_Return'].cumsum()*100
#将计算结果进行返回
return cum_strategy_return
定义完上面的西数之后,我们就可以很快计算出算法模型所带来的累计收益了。