前置き
基本初心者ですので調べ切れていない部分は思い込みでやっています。
ばかちんなので動かしてみないとわかりまてん。
今回は学習できてない場合どうなるかわかりました。
#参考
Artificial neural networks approach to the forecast of stock marketprice movements
環境
Windows10
Python 3.5.2
keras2.0 + TensorFlow1.3
jupyter
次のcloseを予測してみる
データ
通貨 : USD_JPY
足 : 日足
CSV読むところから
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.optimizers import Adam
from sklearn.preprocessing import MinMaxScaler
from keras.layers.core import Flatten
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
PAIR = 'USD_JPY'
TIME_SPAN = 'D'
TIME_SERIES = 5
def createRow():
row = pd.read_csv("./data/" + TIME_SPAN + "/" + PAIR + ".csv")
row.time = pd.to_datetime(row.time)
row = row.set_index('time')
row = row.iloc[:1000]
row = row.iloc[::-1]
row = row.drop(labels=['open','high','low','volume'], axis=1)
row["move0"] = row.close - row.close.shift()
row = row.iloc[1:]
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(row["move0"].values.reshape(-1, 1))
row["move0"] = scaler.transform(row["move0"].values.reshape(-1, 1))
return row,scaler
CSV取得部分
row["move0"] = row.close - row.close.shift()
ここで単純に引き算しどれだけ値が動いたかにしています。
非定常過程を定常過程にするというらしいです。
定常過程にしないとおもいっきり予測がずれたりするはずです。
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(row["move0"].values.reshape(-1, 1))
row["move0"] = scaler.transform(row["move0"].values.reshape(-1, 1))
activationをtanh(hyperbolic tangent)で行うので-1~1の間に収まるよう標準化します。
本当はここで未知のデータのはずであるテストデータをfitに入れるのはいけないのですがご愛敬。
def crateData():
row,scaler = createRow()
train_size = int(len(row) * 0.90)+TIME_SERIES
for i in range(1,TIME_SERIES):
row["move"+str(i)] = row.move0.shift(i)
row = row.iloc[TIME_SERIES:]
x = pd.DataFrame()
for i in range(TIME_SERIES):
x["move"+str(i)] = row["move"+str(i)]
y = pd.DataFrame()
y["answer"] = row.move0.shift(-1)
row["answer"] = row.move0.shift(-1)
row["next_close"] = row.close.shift(-1)
y = y.iloc[:-1]
x = x.iloc[:-1]
row = row.iloc[:-1]
return row, x[:train_size], y[:train_size], x[train_size:], y[train_size:], train_size,scaler
row, x, y, x_test, y_test, train_size,scaler = crateData()
print(row.tail(10))
row['close'].plot()
plt.show()
10%をテストデータにして分けています。
xが特徴でTIME_SERIES(5)個前までの値段の変動になります。
yが答えで次の値段の変動をいれています。
言い換えると過去5個分の値段変動から次の値段変動を予測します。
time | close | move0 | move1 | move2 | move3 | move4 | answer | next_close |
---|---|---|---|---|---|---|---|---|
2017-08-30 06:00:00 | 110.2455 | 0.259903 | 0.263574 | 0.095107 | 0.063475 | 0.266963 | 0.045259 | 109.9745 |
2017-08-31 06:00:00 | 109.9745 | 0.045259 | 0.259903 | 0.263574 | 0.095107 | 0.063475 | 0.201299 | 110.2560 |
2017-09-01 06:00:00 | 110.2560 | 0.201299 | 0.045259 | 0.259903 | 0.263574 | 0.095107 | -0.029584 | 109.7200 |
2017-09-04 06:00:00 | 109.7200 | -0.029584 | 0.201299 | 0.045259 | 0.259903 | 0.263574 | -0.134364 | 108.8130 |
2017-09-05 06:00:00 | 108.8130 | -0.134364 | -0.029584 | 0.201299 | 0.045259 | 0.259903 | 0.236885 | 109.2205 |
2017-09-06 06:00:00 | 109.2205 | 0.236885 | -0.134364 | -0.029584 | 0.201299 | 0.045259 | -0.094683 | 108.4540 |
2017-09-07 06:00:00 | 108.4540 | -0.094683 | 0.236885 | -0.134364 | -0.029584 | 0.201299 | -0.050201 | 107.8450 |
2017-09-08 06:00:00 | 107.8450 | -0.050201 | -0.094683 | 0.236885 | -0.134364 | 0.029584 | 0.559980 | 109.3965 |
2017-09-11 06:00:00 | 109.3965 | 0.559980 | -0.050201 | -0.094683 | 0.236885 | 0.134364 | 0.339688 | 110.1680 |
2017-09-12 06:00:00 | 110.1680 | 0.339688 | 0.559980 | -0.050201 | -0.094683 | 0.236885 | 0.212031 | 110.4875 |
move0~4が過去の変動,answerが予測するべき値。
row['move0'].plot()
plt.show()
row['move0'].describe()
count 993.000000
mean 0.124792
std 0.189740
min -1.000000
25% 0.030008
50% 0.128010
75% 0.223752
max 1.000000
move0はこんな感じ。値下がったときのスパイクが大きくて平均が0になってないけどよいんかな。
modelをつくろう
とりあえず簡単な多層パーセプトロン(MLP)を使う。
CNN,RNN(LSTM)は画像と同じぐらいデータが複雑になったら試すことにする。
def mlp():
model = Sequential()
model.add(Dense(50, input_dim=x.shape[1]))
model.add(Activation('tanh'))
model.add(Dropout(0.2))
model.add(Dense(20))
model.add(Activation('tanh'))
model.add(Dense(1))
model.compile(
loss='mse',
optimizer='adam')
return model
model = mlp()
##機械学習
上がるか下がるかのバイナリ回帰だとよくわかんなくなっちゃうのでmseで値を予測します。
さあ30000回(多すぎ)学習してください。
history = model.fit(
x.values.reshape(x.shape[0], x.shape[1]),
y.values.reshape(y.shape[0], y.shape[1]),
batch_size=32,
epochs=30000,
verbose=2,
shuffle=False)
verbose=2でlossだけ出るようになります。
Epoch 1/30000
1s - loss: 0.0462
Epoch 2/30000
0s - loss: 0.0422
Epoch 3/30000
0s - loss: 0.0428
・
・
・
Epoch 29998/30000
0s - loss: 0.0212
Epoch 29999/30000
0s - loss: 0.0205
Epoch 30000/30000
0s - loss: 0.0212
##結果
どんなあんばい?
plt.plot(history.history['loss'][25:])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
学習データに対する収束はしている模様。
テストデータでもmseのlossの計算をやってみる。
score = model.evaluate(
x_test.values.reshape(x_test.shape[0], x_test.shape[1]),
y_test.values.reshape(y_test.shape[0], y_test.shape[1]))
print('Test score:', score)
32/89 [=========>....................] - ETA: 0s
Test score: 0.0332293912266
0.033になった。
今度はテストデータから予測した値と答えを比較してみる。
from sklearn.metrics import mean_squared_error
from math import sqrt
output = model.predict(x_test.values.reshape(x_test.shape[0], x_test.shape[1]))
plt.figure(figsize=(12, 6), dpi=72)
plt.plot(y_test.values, color='blue')
plt.plot(output[:,0], color='red')
plt.show()
赤が予測。
見にくいので最後の20個だけのグラフにしてみる。
plt.figure(figsize=(12, 6), dpi=72)
plt.plot(y_test.values[:20], color='blue')
plt.plot(output[:,0][:20], color='red')
plt.show()
スパイクのおかげで上にずれちゃってわかりにくいや。
予測と正解の差分をグラフにしてみる。
ma_diff= pd.Series(output[:,0] - y_test.values[:,0])
plt.figure(figsize=(12, 6), dpi=72)
plt.plot(ma_diff, color='red')
plt.plot(np.full((len(ma_diff)),ma_diff.mean()), color='blue')
plt.show()
一応上がったり下がったりの予測をちゃんとしているみたい。
なんとなくトレンドも見えるがわかりにくい。
予測した値をちゃんとcloseの値にもどそう。
import math
def invertMA(row,yhat,row_index,scaler):
yhat = scaler.inverse_transform([[yhat]])
yhat = yhat[0,0]
yhat += row["close"].iloc[row_index]
return yhat
predictions = list()
up_down = 0
for i in range(len(output)):
yhat = output[i,0]
row_index = train_size+i
yhat = invertMA(row,yhat,row_index,scaler)
real = row.next_close.iloc[row_index] - row.close.iloc[row_index]
predict = yhat - row.close.iloc[row_index]
if predict >= 0 and real >= 0 :
up_down += 1
elif predict < 0 and real < 0 :
up_down += 1
predictions.append(yhat)
print('up down predict %d/%d=%f ' % (up_down,len(output),(up_down/len(output))))
up down predict 55/89=0.617978
上がるか下がるかの正答率0.61
グラフでも比較
plt.figure(figsize=(18, 9), dpi=72)
plt.plot(row["next_close"].iloc[train_size:].values, color='blue')
plt.plot(predictions, color='red')
plt.show()
赤が予測です。なにかしらをちゃんと学習した模様。
学習してくれないときほとんど上にずれていたりしました。
#まとめ
かなり施行して61%の確立で上がるか下がるかを予測するものになりました。
上昇トレンドの時のデータを使用したりするとほとんど上がると予測して58%の確立がでたりしたこともあったので実際どうなのか怪しい数値です。
参考にしたArtificial neural networks approach to the forecast of stock marketprice movementsでは上がるか下がるかの予測でS&Pは同じぐらいのAccuracyですが、fxのAccuracyが高すぎます。
計算間違いか相当特殊なデータじゃないかなという結論になりました。
次はUsing Recurrent Neural Networks To Forecasting of Forexを元に移動平均の予測を記載するかもです。
MLPでさくっとかましても74%ぐらいで移動平均の上下の予測ができました。