ニューラルネットワークでFXの予測を試してみた。

  • 7
    いいね
  • 3
    コメント

前置き

基本初心者ですので調べ切れていない部分は思い込みでやっています。
ばかちんなので動かしてみないとわかりまてん。
今回は学習できてない場合どうなるかわかりました。

参考

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が予測するべき値。

download.png
元のチャートはこんな感じ

row['move0'].plot()
plt.show()
row['move0'].describe()

download.png

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()

download.png

学習データに対する収束はしている模様。
テストデータでも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()

download.png
赤が予測。
見にくいので最後の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()

download.png

スパイクのおかげで上にずれちゃってわかりにくいや。

予測と正解の差分をグラフにしてみる。

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()

download.png

一応上がったり下がったりの予測をちゃんとしているみたい。
なんとなくトレンドも見えるがわかりにくい。

予測した値をちゃんと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()

download.png
赤が予測です。なにかしらをちゃんと学習した模様。
学習してくれないときほとんど上にずれていたりしました。

まとめ

かなり施行して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%ぐらいで移動平均の上下の予測ができました。