ニューラルネットワークでFXの予測を試してみた。(2) 変動した価格の移動平均の予測

# 前置き

基本初心者ですので調べ切れていない部分は思い込みでやっています。
ばかちんなので動かしてみないとわかりまてん。
今回は予測精度の高いものの意味がわかったようなわからないような気がします。

#参考
[Using Recurrent Neural Networks To Forecasting of Forex](https://arxiv.org/abs/cond-mat/0304469)

Probability of correct　sign of increments　forecasting:　0.7~0.8
とかなってるすごい。でも変動した価格の移動平均だった。
そしてめっちゃ古い論文でした。

変動した価格とその移動平均から次の変動した価格の移動平均を予測します。（わかりにくい）

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

# 参考との違い
RNNで書かれていますがMLPでやります。
定常過程には対数差分で書かれていますが引き算します。
Activationはrelu

おそらく同じようなRNN(LSTM)と対数差分でもためしたのですが、そんな変わんなかったと思うです。


## データ
通貨 : USD_JPY
足 : 1分足

## CSV読むところから

```py
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, LSTM
from keras.layers.convolutional import Conv1D, MaxPooling1D
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 = 'M1'
MA_WINDOW = 5
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[:1200]
    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:]
    row["ma0"] = row["move0"].rolling(window=MA_WINDOW).mean()
    row = row.iloc[MA_WINDOW-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))
    ma_scaler = MinMaxScaler(feature_range=(-1, 1))
    ma_scaler = ma_scaler.fit(row["ma0"].values.reshape(-1, 1))
    row["ma0"] = ma_scaler.transform(row["ma0"].values.reshape(-1, 1))
    return row,scaler,ma_scaler
```
CSV取得部分
ほとんどかわりません。

```py
row["move0"] = row.close - row.close.shift()
row = row.iloc[1:]
row["ma0"] = row["move0"].rolling(window=MA_WINDOW).mean()
row = row.iloc[MA_WINDOW-1:]
```
ここで単純に引き算しどれだけ値が動いたかにしています。
また単純移動平均にしていますが、値動きの単純移動平均なのでチャートでよく見るやつとは違います。

```py
def crateData():
    row,scaler,ma_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["ma"+str(i)] = row.ma0.shift(i)
    row = row.iloc[TIME_SERIES:]
    x = pd.DataFrame()
    for i in range(TIME_SERIES):
        x["move"+str(i)] = row["move"+str(i)]
        x["ma"+str(i)] = row["ma"+str(i)]
    y = pd.DataFrame()
    y["answer"] = row.ma0.shift(-1)
    row["answer"] = row.ma0.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,ma_scaler
row, x, y, x_test, y_test, train_size,scaler,ma_scaler = crateData()
print(row.tail(10))
row['close'].plot()
plt.show()
```
xが特徴でTIME_SERIES(5)個前までの値段の変動とそのMA_WINDOW(5)個分の移動平均になります。
yが答えで値段の変動の5個分の移動平均をいれています。
言い換えると過去５個分の値段変動とその移動平均から次の移動平均を予測します。

```text
                        close     move0       ma0     move1       ma1  \
time                                                                    
2017-09-12 14:06:00  109.4215 -0.141869  0.034169 -0.128028  0.093394   
2017-09-12 14:07:00  109.4215 -0.266436 -0.029613 -0.141869  0.034169   
2017-09-12 14:08:00  109.4110 -0.411765 -0.079727 -0.266436 -0.029613   
2017-09-12 14:09:00  109.4090 -0.294118 -0.143508 -0.411765 -0.079727   
2017-09-12 14:10:00  109.4075 -0.287197 -0.248292 -0.294118 -0.143508   
2017-09-12 14:11:00  109.4095 -0.238754 -0.312073 -0.287197 -0.248292   
2017-09-12 14:12:00  109.4095 -0.266436 -0.312073 -0.238754 -0.312073   
2017-09-12 14:13:00  109.4280 -0.010381 -0.047836 -0.266436 -0.312073   
2017-09-12 14:14:00  109.4390 -0.114187  0.070615 -0.010381 -0.047836   
2017-09-12 14:15:00  109.4575 -0.010381  0.252847 -0.114187  0.070615   

                        move2       ma2     move3       ma3     move4  \
time                                                                    
2017-09-12 14:06:00 -0.197232  0.047836 -0.335640  0.020501 -0.169550   
2017-09-12 14:07:00 -0.128028  0.093394 -0.197232  0.047836 -0.335640   
2017-09-12 14:08:00 -0.141869  0.034169 -0.128028  0.093394 -0.197232   
2017-09-12 14:09:00 -0.266436 -0.029613 -0.141869  0.034169 -0.128028   
2017-09-12 14:10:00 -0.411765 -0.079727 -0.266436 -0.029613 -0.141869   
2017-09-12 14:11:00 -0.294118 -0.143508 -0.411765 -0.079727 -0.266436   
2017-09-12 14:12:00 -0.287197 -0.248292 -0.294118 -0.143508 -0.411765   
2017-09-12 14:13:00 -0.238754 -0.312073 -0.287197 -0.248292 -0.294118   
2017-09-12 14:14:00 -0.266436 -0.312073 -0.238754 -0.312073 -0.287197   
2017-09-12 14:15:00 -0.010381 -0.047836 -0.266436 -0.312073 -0.238754   

                          ma4    answer  next_close  
time                                                 
2017-09-12 14:06:00  0.052392 -0.029613    109.4215  
2017-09-12 14:07:00  0.020501 -0.079727    109.4110  
2017-09-12 14:08:00  0.047836 -0.143508    109.4090  
2017-09-12 14:09:00  0.093394 -0.248292    109.4075  
2017-09-12 14:10:00  0.034169 -0.312073    109.4095  
2017-09-12 14:11:00 -0.029613 -0.312073    109.4095  
2017-09-12 14:12:00 -0.079727 -0.047836    109.4280  
2017-09-12 14:13:00 -0.143508  0.070615    109.4390  
2017-09-12 14:14:00 -0.248292  0.252847    109.4575  
2017-09-12 14:15:00 -0.312073  0.161731    109.4495  
```

move0~4が過去の変動,ma0~4が移動平均,answerが予測するべき値。

元のチャートはこんな感じ
![download.png](https://qiita-image-store.s3.amazonaws.com/0/198768/56fd6798-ff94-c2a0-bf35-c123689f2a80.png)

## 相変わらずMLPを作る

```py
def mlp():
    model = Sequential()
    model.add(Dense(50, input_dim=x.shape[1]))
    model.add(Activation('relu'))
    model.add(Dropout(0.2))
    model.add(Dense(20))
    model.add(Activation('relu'))
    model.add(Dense(1))
    model.compile(
        loss='mse',
        optimizer='adam')
    return model
model = mlp()
```

##機械学習

```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=6000,
          verbose=2,
          shuffle=False)
```

```text
Epoch 1/6000
0s - loss: 0.0692
Epoch 2/6000
0s - loss: 0.0310
Epoch 3/6000
0s - loss: 0.0239
Epoch 4/6000
0s - loss: 0.0203
・
・
・
Epoch 5998/6000
0s - loss: 0.0060
Epoch 5999/6000
0s - loss: 0.0056
Epoch 6000/6000
0s - loss: 0.0056
```
##結果

```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/2d1bb65f-a5ac-8050-1f3a-ca78dab90f6b.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/109 [=======>......................] - ETA: 0sTest
score: 0.00656724036799
0.006になった。学習データとテストデータでのlossがほとんど変わらないのを見ると特徴はちゃんととらえたらしい。

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

```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[:20], color='blue')
plt.plot(output[:,0][:20], color='red')
plt.show()
```
見にくいので最後の20個だけのグラフにしてみる。
![download.png](https://qiita-image-store.s3.amazonaws.com/0/198768/f75db48d-6d79-ca17-6a85-81df74fa3c39.png)

赤が予測。
上下の予測は参考通りよくできているみたい。
計算してみる。

```py
ma_predictions = list()
ma_up_down = 0
for i in range(len(output)):
    ma_predict = output[i,0]
    row_index = train_size+i
    real = y_test.values[i] - row.ma0.iloc[row_index]

    predict = ma_predict - row.ma0.iloc[row_index]
    if predict >= 0 and real >= 0 :
        ma_up_down += 1
    elif predict < 0 and real < 0 :
        ma_up_down += 1    
    ma_predictions.append(ma_predict)
print('ma up down predict %d/%d=%f   ' % (ma_up_down,len(output),(ma_up_down/len(output))))
```

ma up down predict 77/109=0.706422  
移動平均の上がるか下がるかの正答率0.70 
（前の記事に0.74って書いたんですけど出力消してしまいまんた！）

パラメータそんないじってないしEMAも入れてないのもあり少し値が低いですが、参考に近い値です。

## Closeに戻してみる。
```py
import math
def invertMA(row,yhat,row_index,scaler,ma_scaler):
    # scaleをもとにもどす
    yhat = ma_scaler.inverse_transform([[yhat]])[0,0]
    yhat *= MA_WINDOW
    # 移動平均の分を引いて
    for i in range(MA_WINDOW-1):
        moved = scaler.inverse_transform([[ row["move0"].iloc[row_index-i]]])
        yhat -= moved[0,0]
    # 1個前のcloseをたす
    yhat += row["close"].iloc[row_index]
    return yhat

close_predictions = list()
close_up_down = 0
for i in range(len(output)):
    ma_predict = output[i,0]
    row_index = train_size+i

    predicted_close = invertMA(row,ma_predict,row_index,scaler,ma_scaler)
    real = row.next_close.values[i] - row.close.iloc[row_index]
    predict = predicted_close  - row.close.iloc[row_index]
    if predict >= 0 and real >= 0 :
        close_up_down += 1
    elif predict < 0 and real < 0 :
        close_up_down += 1  
    close_predictions.append(redicted_close)
print('close up down predict %d/%d=%f   ' % (close_up_down,len(output),(close_up_down/len(output))))
```
close up down predict 54/109=0.495413   
終値の上がるか下がるかの正答率0.49
なるほど。あかーん！

#まとめ

季節や時間帯によって特徴のある売上の予測などではその特徴を捉えある程度の精度を出すと思いますが、
同様のやり方でFXや株価の予測を行ってもほとんどランダムに近くがんばってテストデータで60%程度のcloseの上下の予測しかできないみたいです。

Googleの[財務時系列データを使用した機械学習](https://cloud.google.com/solutions/machine-learning-with-financial-time-series-data)もS&Pの上下の予測で72%となっていました。
今回の移動平均の上下の予測も70%の精度でした。
この２つの70%を超える精度で上下の予測をしてる例には違いがあります。

今回の移動平均の場合closeの値動き5個分の移動平均なので"次の移動平均が上下どちらかの予測"は"5個前の値動きと次の値動きが上下どちらかの予測"になります。（わかりにくい）
1~4個前の値動きは予測の判断材料につかっているということですね。

GoogleのS&Pは夕方市場が閉まりS&Pのcloseが確定し、その後他の市場が動き次の日になって市場がまた開きS&Pのcloseが確定されます。
他の市場の動きを学習にいれることでS&Pの市場が閉まっている間のデータを使い次の日のcloseの上下を予測しています。

どちらも時系列で前の値の時間->学習データの時間->予測する値の時間となっています。
時系列的にも前の値と次の値の間の予測に必要なデータが入っているから精度が高いのですね。

次は何を試そうかしら。