【機械学習】LSTMを使ってFX予測を行ってみた【未来予測】

こんにちは!aidemy研修生の深水です。最近AIが話題となることが多いですが、皆さんこんなこと思ったことありませんか?

「もし俺が自動でFXや株などのトレードをしてくれるAIを作れば、一生自動的にお金が手に入るのでは?」 私深水はこんな安易な思いから機械学習を勉強し始め、FXの未来のレートを予測するAIの開発にチャレンジを始めました。何度も壁や試練にぶちあたりましたが、得るものは多かったです。

ただ結論から言うと、性能の良いAIを開発するのは難しいですが、その開発のプロセスで学べるものは多いので皆さんに一度チャレンジすることをお勧めします。

このブログはそんなFX予測AIを作りたいという人のために、私がやったことをまとめていきたいと思います。

対象者

  • FXに興味がある人
  • 機械学習や時系列予測に興味がある人

概要

FX のレートのデータをOanda APIを使って取得する。
kerasのLSTMモデルを構築し、レートの回帰予測を行う。

結果からの考察

  • Lenovo ideapad 330 CPU intel core i 7th
  • Google colaboratory
  • Python 3.6.7

Google colaboratory を用いると環境構築もしなくていいうえ、Google のGPUを用いることができ、大幅な時間短縮ができます。

codexa「Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ」

データの取得

まず、FXのレートを取得するのですが、取引所であるOandaがAPIを公開しているためそこからデータを取得することができます。Oanda APIを利用するにはOandaの本口座あるいはデモ口座を開設すれば利用できます。
各自のAPIキー(api_key)と口座番号(acount_id)をメモして起きましょう。

OANDA Japan

メモができたら以下のように、pipを用いてgithubからoandapyをインポートする。

!pip install git+https://github.com/oanda/oandapy.git 
# oandapy をインポート
import oandapy 

# OANDA API アクセストークンと口座ID
ACCOUNT_ID = "XXX-XXX-XXXXXXX-XXX"
ACCESS_TOKEN = "sample_sample_abcabcabcabcabcabcabcabcabcabcabc_sample_sample" 

# デモアカウントでAPI呼び出し 
oanda = oandapy.API(environment="practice", access_token= 
ACCESS_TOKEN )

# また、以下のモジュールを使用します。

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import oandapy
import configparser
import pytz
import datetime
from datetime import datetime, timedelta


from keras.models import Sequential
from keras.layers import Activation, Dense
from keras.layers import LSTM
from keras.layers import Dropout

それでは過去レートを取得します。ここではoandapyに含まれるget_history という関数を用います。この関数はOanda にて説明がありますので以下のものを参考にするとよいです。

要点をいうとinstrumentを指定することで何のレートか指定でき、 granularityでデータの時間間隔が指定できる。下の”D”は一日ごとを意味する。ほかにもendやstartで取得するデータの開始時刻や終了時刻を指定できます。あと使いそうなものとしては、count を指定することができます。count は指定しなければ500個、最大5000個のデータを取得できます。

今回はUSD_JPYのレートのデータを取得しました。

response = oanda.get_history(instrument="USD_JPY", granularity="D")

またデータを5000個以上取得したいときは、データを取得するたびに最後の時間を取得して、その後for分などで時間を指定しなが取得するのがよいでしょう。以下のサイトが参考になります。

取得したデータをpandasに変換します。このresを出力してみるとデータの形がどのようになっているのか理解できるでしょう。

res = pd.DataFrame(response["candles"])

次は機械学習のプロセスで言う「前処理」を行ってあげましょう。

データから時間を軽く確認すると特殊な形をとっています。一般的にAPIで扱う日付は世界基準に統一することが多く「ISO 8601」が採用されます。OANDAも同様です。

ただ、これでは使いづらいので日付をまずは日本時間に変換をしましょう。今回は下記のコードでISOの文字列からdatetimeへ。

#日付の調整する
#文字列 -> datetime
 def iso_jp(iso):
     date = None
     try:
         date = datetime.strptime(iso, '%Y-%m-%dT%H:%M:%S.%fZ')
         date = pytz.utc.localize(date).astimezone(pytz.timezone("Asia/Tokyo"))
     except ValueError:
         try:
             date = datetime.strptime(iso, '%Y-%m-%dT%H:%M:%S.%f%z')
             date = dt.astimezone(pytz.timezone("Asia/Tokyo"))
         except ValueError:
             pass
     return date

変更を実行

#上の関数を実行
res["time"] = res["time"].apply(lambda x: iso_jp(x))
["time"] = res["time"].apply(lambda x: iso_jp(x))

resのカラムを”time”と”open”に変更

df = res[['time', 'openAsk']] df.columns = ['time', 'open']

データの加工

次にデータをLSTM用の形に変換します。
ではLSTMがどんなものか軽く紹介しましょう。LSTMは時系列予測をするためのライブラリです。
LSTMの仕組みをざっと理解するために、どうやってこれができたかを見ていきましょう。

LSTMはまずRNNを基としており、RNNはさらにニューラルネットワークを基とし手出来ています。つまりLSTMの祖父がニューラルネットワーク、父がRNNです。

それぞれの特徴を見ると、ニューラルネットワークは過去の出来事を考慮できないという欠点があり、RNNは長い記憶に対応できないという欠点があります。そこで長い過去の記憶を考慮できるようにしたのがLSTM(
Long short-term memory )が開発されました。
LSTMにはWindowと呼ばれる「窓」という考えがあります。これは「どれくらいの期間を基に予測をするか?」をさしています。

今回は窓を10日間としてコーディングをこれからやっていきます。つまり、言い換えれば、LSTMは10日間の為替レートのデータを1セットとして、それを何セットも学習することで、新しい10日間のデータからその次の未来の為替レートを予測してくれる訳です!図としては下のようになります。

次にデータをトレインデータ とテストデータ、同時に未来予測に用いる10個のデータ(latest)を分割します。今回はデータセットが全部で500個あるので最初の480個と残りの20個に分割します。最終的に行いたいのは最初の480個からモデルを作り、481個目から490個までの10個からモデルを用いて491個目から500個目までを予測し、実際のデータと比べて精度を評価します。

それぞれの長さを今回の数字にした理由は特にありませんが、分割した時の未来予測する回数、すなわち、len(test)- (窓の長さ)は記録しておきましょう。また481から490までのデータをlatestに代入します。

では、まず480個目の時間を確認して

df[478:482]

その時間をsplit_dateに代入して起き、また”time”のカラムを消去します。

#windowを設定
window_len = 10

split_date = '2018/12/12 07:00:00'
train, test = df[df['time'] < split_date], df[df['time']>=split_date]
latest = test[:window_len]
del train['time']
del test['time']
del latest['time']
length = len(test)- window_len

次にデータをLSTM用に変換します。今回LSTMに突っ込むのは単にレートの値ではなく、窓の先頭の値で窓の全部の値を割ってそこから1を引いて変化率に直します。 そこ で未来の値として予測するのは窓の次の値と窓の先頭の値の変化率になります。

このようにした理由は、レートの変動には基本的には法則性はないと言われていますが、その変動を生み出してる世界の数多くのトレーダーは直前のレートの変化を見てます。(私はプロのトレーダーではないですが、、)

つまりは数字として大切にすべきはレートの絶対的な値段を追うことではなく、相対的な変化率なのです。そのため今回は次のように変形しました。

イメージは以下の図です。

以下のようにデータをLSTM型に直す関数を定義します。

# LSTMへの入力用に処理の関数

def data_maker(data):
  data_lstm_in=[]
  if len(data)==window_len:
    temp = data[:window_len].copy()
    temp = temp / temp.iloc[0] - 1
    data_lstm_in.append(temp)
  for i in range(len(data) - window_len):
      temp = data[i:(i + window_len)].copy()
      temp = temp / temp.iloc[0] - 1
      data_lstm_in.append(temp)
  return data_lstm_in

実行します。ここでlstm_outのデータを以下のように用意します。

#関数の実行
train_lstm_in = data_maker(train)
lstm_train_out = (train['open'][window_len:].values / train['open'][:-window_len].values)-1
test_lstm_in = data_maker(test)
lstm_test_out = (test['open'][window_len:].values / test['open'][:-window_len].values)-1
latest_lstm_in = data_maker(latest)

最終的にはnumpyにして学習させるのでここで変換させる関数を定義して実行します。

# PandasのデータフレームからNumpy配列へ変換しましょう
def pd_to_np(data_lstm_in):
  data_lstm_in = [np.array(data_lstm_input) for data_lstm_input in data_lstm_in]  #array のリスト
  data_lstm_in = np.array(data_lstm_in) #np.array
  return data_lstm_in
train_lstm_in = pd_to_np(train_lstm_in)
test_lstm_in = pd_to_np(test_lstm_in)
latest_lstm_in = pd_to_np(latest_lstm_in)

モデルの定義

いよいよLSTMへのデータの変換が完了。次にLSTMを訓練します。今回は機械学習ライブラリの「Keras」を利用してモデル構築を行います。KerasですがバックエンドでTensorFlowを使います。

機械学習関連のライブラリの大半は英語のドキュメントばっかりですが、Kerasは数少ない日本語ドキュメントが用意されているライブラリです!公式ドキュメントで大体のことは説明されてます。ぜひ一度読むことをおお勧めします。

# LSTMのモデルを設定
def build_model(inputs, output_size, neurons, activ_func="linear",
                dropout=0.25, loss="mae", optimizer="adam"):
    model = Sequential()
 
    model.add(LSTM(neurons, input_shape=(inputs.shape[1], inputs.shape[2])))
    model.add(Dropout(dropout))
    model.add(Dense(units=output_size))
    model.add(Activation(activ_func))
 
    model.compile(loss=loss, optimizer=optimizer)
    return model

ではこれで設定はokです。

データの学習と予測

ではいよいよ訓練データを使ってモデルの訓練を行いましょう!epochsを10と設定してるので、10回の反復処理が行われます。google colaboratory 上で1分くらいで終わりました

# ランダムシードの設定
np.random.seed(202)
 
# 初期モデルの構築
yen_model = build_model(train_lstm_in, output_size=1, neurons = 20)
 
# データを流してフィッティングさせましょう
yen_history = yen_model.fit(train_lstm_in, lstm_train_out, 
                            epochs=10, batch_size=1, verbose=2, shuffle=True)

以下のような出力結果なら成功でしょう。

Epoch 1/10  - 3s - loss: 0.0080 
Epoch 2/10  - 2s - loss: 0.0060 
Epoch 3/10  - 2s - loss: 0.0057 
Epoch 4/10  - 2s - loss: 0.0053 
Epoch 5/10  - 2s - loss: 0.0050 
Epoch 6/10  - 2s - loss: 0.0049 
Epoch 7/10  - 2s - loss: 0.0050 
Epoch 8/10  - 2s - loss: 0.0046 
Epoch 9/10  - 2s - loss: 0.0047 
Epoch 10/10  - 2s - loss: 0.0046 

次に作成したmodelを用いて、491個目から500個目までの予測を行います。以下のように先ほどlengthに記録した予測する長さ分for文を回して予測を行います。その際変化率からレートに変換することや、もう一度LSTM型に変換したり、numpyとpandasの変換を行ったりすることを忘れないでおきましょう。

# #未来の値
empty = []
future_array = np.array(empty)
for i in range(length):
  pred = (((np.transpose(yen_model.predict(latest_lstm_in))+1) * latest['open'].values[0])[0])[0]
  future_array= np.append(future_array,pred)
  data ={'open':[pred]}
  df1 = pd.DataFrame(data)
  latest =pd.concat([latest,df1],axis=0)
  latest.index = range(0,window_len+1)
  latest = latest.drop(0,axis=0)
  latest_lstm_in =pd_to_np(latest_lstm_in)

これにてfuture_arrayに未来のレートが記録されましたので、これであとは結果を見やすくグラフの形に出しましょう

まずはトレインデータでの予測を行います。ただこれに関しては窓の10個を未来予測したデータを追加してそこからpredictを行います。なので予測結果としては実際の値と、予測値はあまり差がありません。modelが問題なくできていることをここでは確認します。

青を実際のレート、赤を予測としてplotすると

plt.figure(figsize=(10,8))
plt.plot(df[df['time']< split_date]['time'][window_len:].astype(datetime),
         train['open'][window_len:], label='Actual', color='blue')
plt.plot(df[df['time']< split_date]['time'][window_len:].astype(datetime),
         ((np.transpose(yen_model.predict(train_lstm_in))+1) * train['open'].values[:-window_len])[0], 
         label='Predicted', color='red')
plt.show()

差も小さく成功と言えそうですね!!
そして最後にお待ちかねの、未来予測です。ドキドキ!!
plotすると、じゃじゃーん!!

plt.figure(figsize=(10,8))
plt.plot(df[df['time']>= split_date]['time'][window_len:].astype(datetime),
         test['open'][window_len:], label='Actual', color='blue')
plt.plot(df[df['time']>= split_date]['time'][window_len:].astype(datetime),
        future_array,label='future',color='green')
plt.show()

一つ目の谷は予測できなかったものの、二つ目の谷は予測できました。果たしてこの結果がどのようなことを意味するのか、次の考察で考えてみます。

考察

さあ上の結果が、FX予測機としてどのようなことが言えるのか考えてみましょう。FX予測機としては波形が実際の形に似ていればよいのではなく、レートが上がる前にわかり、下がる前にわかることが理想です。実際のトレードでは緑の予測線は実際のレートの後追いをすることが多いので、おそらく負けるでしょう。

あと予測してる範囲も今回10日なので、もっと予測する範囲を広げてみると同んあるか見てみます。今回はテストデータを400個と100個に分けて90日分の予測してみました。その結果が下の図です。

見てみるとはじめだけ少しあってますが、しばらくするとかなりずれてますね。あと周期関数になっています。これを見るとずれが少ないのは最初の1週間弱であることが分かります。これより今回のモデルの予測が有効な期間はおよそ2,3日程度であることが言えるでしょう。(まだ実用的ではないですけど)
またデータを5000個用意して10個分の未来予測をしてみた。

精度的にはあまり変わらないか、悪くなってますね。

課題

また今後の課題としては、

  • データの個数と幅を変えた時にどうなるか
  • 複数のレートや、ほかの経済指標をどう導入するか
  • データの加工手法の改善
  • トレードで利益を最大にする強化学習を行う

今回は一日単位でのデータのみを扱ってきましたが、データの幅にはほかにも調節可能です。また単に一つの為替レートだけでなく、USドルやユーロ、ポンドやその他の貨幣なども、そして平均株価の変動などの経済指標も取り入れることができれば改善できるところが多くあると思います。

またデータの加工手法も今回は単純に除法による変化率を適用しましたが、ほかにも対数差分をとるなどの方法もあるので、ほかの形にもチャレンジするのも課題でしょう。

そして最終的に理想とされるのは、レートの予測に加えて利益を最大化できる強化学習ができればなお理想に近づきます。

まとめ

今回FXのレートの未来予測を行いましたが、やはりすぐに実用できるような精度のものを作るのは簡単にはいきませんでした。もし自動的に未来をよくしてトレードまでしてくれるシステムを作るにはまだまだ時間と労力とスキルがいりそうですね。
ただ今回実用的なものを作れなかったものの、私は機械学習やその他のスキルで大きく得るものがありました
この記事を参考に皆さんの時系列予測の学習の第一歩になれたら大変うれしいです。

参考文献

プログラミング未経験からでもAIスキルが身につくAidemy Premium




PythonやAIプログラミングを学ぶなら、オンライン制スクールのAidemy Premiumがおすすめです。
「機械学習・ディープラーニングに興味がある」
「AIをどのように活用するのだろう?」
「文系の私でもプログラミング学習を続けられるだろうか?」
少しでも気になることがございましたら、ぜひお気軽にAidemy Premiumの【オンライン無料相談会】にご参加いただき、お悩みをお聞かせください!