05 May 2017, 10:00

LSTMで為替の予測をしてみた

RNN(LSTM)入門2日目。依然として、全然わかりません。

このあたりが自分のスキルの限界なのかもしれない。Udacity DLNDで株の値動きの予測をする動画が紹介された。

FXのシステムトレードを以前やっていたので、ディープラーニングで学んだことが生かせればいいなと思い、株を為替に置き換えて、LSTMで為替の値動きの予測をしてみた。

この記事は以下の記事をベースに書いている。

また、以下のkaggleコンペのkernelsも大いに参考にした。

準備

  <p>
    まずは、為替データを準備する。以下のサイトから、2017/4/24 の USD/JPY の 10分刻みのデータをダウンロードする。
  </p>

  <ul>
    <li>
      <a href="https://www.dukascopy.com/swiss/english/marketwatch/historical/">Historical Data Feed :: Dukascopy Bank SA | Swiss Forex Bank | ECN Broker | Managed accounts | Swiss FX trading platform</a>
    </li>
  </ul>

  <p>
    githubにも使用したデータをアップロードした。
  </p>

  <ul>
    <li>
      <a href="https://github.com/tsu-nera/futurismo/blob/main/blog/ipynb/USDJPY_Candlestick_10_m_BID_24.04.2017-25.04.2017.csv">futurismo/USDJPY_Candlestick_10_m_BID_24.04.2017-25.04.2017.csv</a>
    </li>
  </ul>

  <h3 id="環境">
    環境<a class="anchor-link" href="#環境">¶</a>
  </h3>

  <ul>
    <li>
      TensorFlow 1.1.0
    </li>
    <li>
      Keras 2.0.2
    </li>
  </ul>
</div>

LSTM Network for Regression

  <p>
    チュートリアルにしたがって順にコードを書いていく。
  </p>
</div>

In [1]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="kn">from</span> <span class="nn">__future__</span> <span class="k">import</span> <span class="n">print_function</span>

import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) from keras.layers.core import Dense, Activation, Dropout from keras.layers.recurrent import LSTM from keras.models import Sequential from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error import matplotlib.pyplot as plt import math

    <div class="output_subarea output_stream output_stderr output_text">
      <pre>Using TensorFlow backend.

In [2]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># parameters to be set </span>

look_back = 1 epochs = 30 batch_size = 1

In [3]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># fix random seed for reproducibility</span>

np.random.seed(7)

PrePare Data

In [4]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># read all prices using panda</span>

dataframe = pd.read_csv(‘USDJPY_Candlestick_10_m_BID_24.04.2017-25.04.2017.csv’, header=) dataframe.head()

Out[4]:

    <div class="output_html rendered_html output_subarea output_execute_result">
      <div>
        <table class="dataframe" border="1">
          <tr style="text-align: right;">
            <th>
            </th>

            <th>
              Local time
            </th>

            <th>
              Open
            </th>

            <th>
              High
            </th>

            <th>
              Low
            </th>

            <th>
              Close
            </th>

            <th>
              Volume
            </th>
          </tr>

          <tr>
            <th>
            </th>

            <td>
              24.04.2017 00:00:00.000
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              0.0
            </td>
          </tr>

          <tr>
            <th>
              1
            </th>

            <td>
              24.04.2017 00:10:00.000
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              0.0
            </td>
          </tr>

          <tr>
            <th>
              2
            </th>

            <td>
              24.04.2017 00:20:00.000
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              0.0
            </td>
          </tr>

          <tr>
            <th>
              3
            </th>

            <td>
              24.04.2017 00:30:00.000
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              0.0
            </td>
          </tr>

          <tr>
            <th>
              4
            </th>

            <td>
              24.04.2017 00:40:00.000
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              109.072
            </td>

            <td>
              0.0
            </td>
          </tr>
        </table>
      </div>
    </div>
  </div>
</div>

In [5]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="n">dataset</span> <span class="o">=</span> <span class="n">dataframe</span><span class="p">[</span><span class="s1">'Close'</span><span class="p">]</span>

In [6]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># reshape to column vector</span>

#close_prices = close.values.reshape(len(close), 1) dataset = dataset.values dataset = dataset.astype(‘float32’) close_prices = dataset.reshape((-1,1))

ここで、データを0~1の間に正規化する。scikit-learnにMinMaxScalerという便利な関数があって、fit_transformとすると、正規化できる。戻すときは、inverse_transformをコールする。

In [7]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># normalize the dataset</span>

scaler = MinMaxScaler(feature_range=(, 1)) close_prices = scaler.fit_transform(close_prices)

訓練用データを 23, テスト用データを 1/3に分ける。

In [8]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># split data into training set and test set</span>

train_size = int(len(close_prices) * 0.67) test_size = len(close_prices) - train_size train, test = close_prices[:train_size,:], close_prices[train_size:len(close_prices),:]

print(‘Split data into training set and test set… Number of training samples/ test samples:’, len(train), len(test))

    <div class="output_subarea output_stream output_stdout output_text">
      <pre>Split data into training set and test set... Number of training samples/ test samples: 192 96

create_dataset()で訓練用のラベルを生成する。現在のデータから一つ先(look_back=1)のデータ、つまり10分後の値動きを予想する。

  <p>
    ちなみにこの関数は便利で、look_backを2にすると、2つ前のデータ、つまり現在と10分前のデータから10分後のデータを予測することができる。look_backパラメータを調整することで、過去のデータから10分後のデータを予測できる。これを元の記事では、Window Methodと書いてある。
  </p>
</div>

In [9]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># convert an array of values into a time series dataset </span>

# in form # X Y # t-look_back+1, t-look_back+2, …, t t+1

def create_dataset(dataset, look_back): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), ] dataX.append(a) dataY.append(dataset[i + look_back, ]) return np.array(dataX), np.array(dataY)

# convert Apple’s stock price data into time series dataset trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back) (trainX.shape, trainY.shape, testX.shape, testY.shape)

Out[9]:

    <div class="output_text output_subarea output_execute_result">
      <pre>((190, 1), (190,), (94, 1), (94,))</pre>
    </div>
  </div>
</div>

In [10]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># reshape input of the LSTM to be format [samples, time steps, features]</span>

trainX = np.reshape(trainX, (trainX.shape[], 1, trainX.shape[1])) testX = np.reshape(testX, (testX.shape[], 1, testX.shape[1])) (trainX.shape, testX.shape)

Out[10]:

    <div class="output_text output_subarea output_execute_result">
      <pre>((190, 1, 1), (94, 1, 1))</pre>
    </div>
  </div>
</div>

Build Model & Training

  <p>
    Kerasを利用してLSTMネットワークのモデルを作り、トレーニングを実施する。
  </p>
</div>

In [11]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># create and fit the LSTM network</span>

model = Sequential() model.add(LSTM(4, input_shape=(1, look_back))) model.add(Dense(1)) model.compile(loss=‘mean_squared_error’, optimizer=‘adam’) model.fit(trainX, trainY, epochs=epochs, batch_size=batch_size,verbose=2)

    <div class="output_subarea output_stream output_stdout output_text">
      <pre>Epoch 1/30

1s - loss: 0.1224 Epoch 230 0s - loss: 0.0332 Epoch 330 0s - loss: 0.0204 Epoch 430 0s - loss: 0.0160 Epoch 530 0s - loss: 0.0117 Epoch 630 0s - loss: 0.0082 Epoch 730 0s - loss: 0.0056 Epoch 830 0s - loss: 0.0041 Epoch 930 0s - loss: 0.0034 Epoch 1030 0s - loss: 0.0031 Epoch 1130 0s - loss: 0.0030 Epoch 1230 0s - loss: 0.0030 Epoch 1330 0s - loss: 0.0029 Epoch 1430 0s - loss: 0.0029 Epoch 1530 0s - loss: 0.0030 Epoch 1630 0s - loss: 0.0029 Epoch 1730 0s - loss: 0.0029 Epoch 1830 0s - loss: 0.0029 Epoch 1930 0s - loss: 0.0029 Epoch 2030 0s - loss: 0.0029 Epoch 2130 0s - loss: 0.0030 Epoch 2230 0s - loss: 0.0030 Epoch 2330 0s - loss: 0.0029 Epoch 2430 0s - loss: 0.0029 Epoch 2530 0s - loss: 0.0030 Epoch 2630 0s - loss: 0.0030 Epoch 2730 0s - loss: 0.0030 Epoch 2830 0s - loss: 0.0029 Epoch 2930 0s - loss: 0.0029 Epoch 3030 0s - loss: 0.0030

  <div class="output_area">
    <div class="prompt output_prompt">
      Out[11]:
    </div>

    <div class="output_text output_subarea output_execute_result">
      <pre>&lt;keras.callbacks.History at 0x7f29913c3b00&gt;</pre>
    </div>
  </div>
</div>

Predict

In [12]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># make predictions</span>

trainPredict = model.predict(trainX) testPredict = model.predict(testX)

In [13]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># invert predictions and targets to unscaled</span>

trainPredict = scaler.inverse_transform(trainPredict) trainY = scaler.inverse_transform([trainY]) testPredict = scaler.inverse_transform(testPredict) testY = scaler.inverse_transform([testY])

In [14]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># calculate root mean squared error</span>

trainScore = math.sqrt(mean_squared_error(trainY[], trainPredict[:,])) print(‘Train Score: %.5f RMSE’ % (trainScore)) testScore = math.sqrt(mean_squared_error(testY[], testPredict[:,])) print(‘Test Score: %.5f RMSE’ % (testScore))

    <div class="output_subarea output_stream output_stdout output_text">
      <pre>Train Score: 0.09638 RMSE

Test Score: 0.06509 RMSE

In [15]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># shift predictions of training data for plotting</span>

trainPredictPlot = np.empty_like(close_prices) trainPredictPlot[:, :] = np.nan trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict

# shift predictions of test data for plotting testPredictPlot = np.empty_like(close_prices) testPredictPlot[:, :] = np.nan testPredictPlot[len(trainPredict)+(look_back*2)+1:len(close_prices)-1, :] = testPredict

In [16]:
<div class="inner_cell">
  <div class="input_area">
    <div class=" highlight hl-ipython3">
      <pre><span class="c1"># plot baseline and predictions</span>

plt.plot(scaler.inverse_transform(close_prices)) plt.plot(trainPredictPlot) plt.plot(testPredictPlot) plt.show()

    <div class="output_png output_subarea ">
      <img src="http://futurismo.biz/wp-content/uploads/a3468b2a3ce342ea6e059347483820d6.png" />
    </div>
  </div>
</div>

予測結果は上の図。あまりにフィットしすぎて、なんかウソっぽいけれども、一応これが結果。

  <p>
    ちなみに、元の記事だとこの先もあるが、stepや state, stacked LSTMs が読んでもよくわからなかった。もう少し時間が必要。
  </p>

  <ul>
    <li>
      How to create an LSTM for a regression and a window formulation of the time series problem.
    </li>
    <li>
      How to create an LSTM with a time step formulation of the time series problem.
    </li>
    <li>
      How to create an LSTM with state and stacked LSTMs with state to learn long sequences.
    </li>
  </ul>

  <p>
    元記事を元に試してみたけれども、修正を加えるたびに精度が悪くなっていくのだった。
  </p>
</div>

おわりに

  <p>
    LSTMを FXのストラテジに応用できるか考えてみたのだけれども、よいストラテジが思いつかない。
  </p>

  <p>
    単純に回帰ならば、LSTMを使わなくてももっと簡単な方法がある。
  </p>

  <p>
    トレーニングに時間がかかるので、リアルタイムに過去のデータを処理して未来の値を予測できるのか?
  </p>

  <p>
    とはいえ、面白い話題なので、もう少し調べてみる。
  </p>
</div>