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

# 前置き

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

#参考
[Artiﬁcial neural networks approach to the forecast of stock marketprice movements](https://www.researchgate.net/publication/307639575_Artificial_neural_networks_approach_to_the_forecast_of_stock_market_price_movements)

# 環境
Windows10
Python 3.5.2
keras2.0 + TensorFlow1.3
jupyter

# 次のcloseを予測してみる

## データ
通貨 : USD_JPY
足 : 日足

## CSV読むところから

```py
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取得部分

```py
row["move0"] = row.close - row.close.shift()
```
ここで単純に引き算しどれだけ値が動いたかにしています。
**非定常過程を定常過程にする**というらしいです。
定常過程にしないとおもいっきり予測がずれたりするはずです。


```py
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に入れるのはいけないのですがご愛敬。

```py
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が答えで次の値段の変動をいれています。
言い換えると過去５個分の値段変動から次の値段変動を予測します。

|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](https://qiita-image-store.s3.amazonaws.com/0/198768/333b8944-32e1-fa1b-b889-6d691ceb7f7d.png)
元のチャートはこんな感じ

```py
row['move0'].plot()
plt.show()
row['move0'].describe()
```
![download.png](https://qiita-image-store.s3.amazonaws.com/0/198768/54ee5111-aaf6-435a-de86-d1f808f8bd07.png)

```text
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)は画像と同じぐらいデータが複雑になったら試すことにする。

```py
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回(多すぎ)学習してください。

```py
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だけ出るようになります。

```text
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
```
##結果
どんなあんばい？

```py
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](https://qiita-image-store.s3.amazonaws.com/0/198768/81a33f1e-2976-07fb-5971-9cd267358cd8.png)

学習データに対する収束はしている模様。
テストデータでもmseのlossの計算をやってみる。

```py
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になった。

今度はテストデータから予測した値と答えを比較してみる。

```py
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](https://qiita-image-store.s3.amazonaws.com/0/198768/3ee80b81-c779-a72b-1220-97d647712cfe.png)
赤が予測。
見にくいので最後の20個だけのグラフにしてみる。

```py
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](https://qiita-image-store.s3.amazonaws.com/0/198768/41a71c30-5ffe-e266-ca20-6db72f39121d.png)

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

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

```py
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](https://qiita-image-store.s3.amazonaws.com/0/198768/ab4e5624-47c0-7a3f-4869-03caa95c426c.png)

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

予測した値をちゃんとcloseの値にもどそう。

```py
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

グラフでも比較

```py
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](https://qiita-image-store.s3.amazonaws.com/0/198768/bf7481b6-41e4-9957-e63f-2dcaa44fa658.png)
赤が予測です。なにかしらをちゃんと学習した模様。
学習してくれないときほとんど上にずれていたりしました。

#まとめ
かなり施行して61%の確立で上がるか下がるかを予測するものになりました。
上昇トレンドの時のデータを使用したりするとほとんど上がると予測して58%の確立がでたりしたこともあったので実際どうなのか怪しい数値です。
参考にした[Artiﬁcial neural networks approach to the forecast of stock marketprice movements](https://www.researchgate.net/publication/307639575_Artificial_neural_networks_approach_to_the_forecast_of_stock_market_price_movements)では上がるか下がるかの予測でS&Pは同じぐらいのAccuracyですが、fxのAccuracyが高すぎます。
計算間違いか相当特殊なデータじゃないかなという結論になりました。

次は[Using Recurrent Neural Networks To Forecasting of Forex](https://arxiv.org/abs/cond-mat/0304469)を元に移動平均の予測を記載するかもです。
MLPでさくっとかましても74%ぐらいで**移動平均の**上下の予測ができました。