為替の予測をやってみた(その3 〜フォーワードテストの準備編〜)

この記事の概要

  • 前回の記事の続き
    • LSTMでの予測に基づいたトレードのバックテストではかなり良好な結果を得た。
  • フォーワードテストのために売買のシステムを組んでみた。
    • その概要について記載する。

前回のおさらい

バックテストを実施

とある2週間の過去データにて

  • trades: 296回
  • winrate: 0.91%
  • payoff: 4.6705 

と驚異的な成績を得た。

フォーワードテストのために売買システムを組んでみた

上記のバックテストの結果の様には、うまくいかないだろうと思いつつも、期待を込めて売買のシステム作製しました。

今回作製した為替自動売買システムの概要図

f:id:MorinoKuma3:20190512214522p:plain
売買システム概要v2

簡単に各機能等を以下に記載します。

Predictor

機能:Oanda APIを使用して予測で使用する直近の為替データを取得し、
   LSTMモデルで将来の価格帯を予測する。
   予測で使用した為替データと予測した結果をそれぞれ保存する。

今回は、5分毎に30分後の価格帯を予測します。 5分毎に実行するために、Scheduleライブラリを使用しました。

参考にした記事: qiita.com

Agent

機能:Predictorが予測した結果を基に、トレードするかしないか判断する。
   トレードすると判断した場合、Oanda APIを通じて注文をする。

今回は、予測結果である「prediceted_data.json」のファイル更新を監視し、 更新をトリガーにして、処理を実行するようにしました。

ファイル更新の監視には、watchdogを使用しました。

参考にした記事: qiita.com

また、注文のAPIの使い方については以下を参考にしました。

http://www.algo-fx-blog.com/fx-api-oanda-v20-python-order/

Viewer

機能:Predictorが保存した予測した結果を、予測に使用した為替データへOverLayしてグラフ描画する。

Viewerは、フォーワードテストには関係ないです。
狙い通りに動作しているか確認するためにグラフ描画するようにしました。

実行すると以下の様に、直近のキャンドルチャートと、予測した結果を(直線で)描画するようにしました。 figure class="figure-image figure-image-fotolife" title="Viewer_exsample">f:id:MorinoKuma3:20190512212521p:plain

Viewer_exsample

上記の例では、30分後には値が上昇すると予測しています。

グラフ描画には、Plotly Dashを使用しました。 plot.ly

次回

次の記事では、この売買システムを使用してフォーワードテストをした結果についてまとめたいと思います。

為替の予測をやってみた(その2 〜機械学習編〜)

この記事の概要

  • 前回の記事の続き
    • 30分後の価格帯の予測にチャレンジ
  • LSTM(Long Term Short Memory)にて予測
    • 予測精度としては今ひとつ。。
  • 売買のシステムに組込んだと仮定してバックテストを実施
    • 売買のシステムにて使用するなら非常によい結果に!

前回のおさらい

前回は予測する対象を決めるために、 3ヶ月分の為替データを分析しました。

分析内容

  1. 為替データを、移動平均線を中心とした-10〜+10の領域に区分けした。
  2. t=0と30分後(t=30)の価格の分布を確認した。

その結果、t=30の価格帯を予測するモデルを作成し、 最終的には、t=0の価格とそのモデルの予測したt=30の価格差に応じて、買い(Long)/トレードしない(NA)/売り(Short)を選択するシステムを作製することとしました。

詳しくは前回の記事を参照してください。

予測モデルの構築

さて、ここからが今回の内容になります。

モデルの選定

今回は、 LSTM(Long Term Short Memory)を選択しました。
LSTMについては、さまざまな記事がありますので、そちらをご覧ください。

私は以下などを参照にしました。

わかるLSTM ~ 最近の動向と共に - Qiita

LSTMネットワークの概要 - Qiita

KerasでLSTMを学習する手順を整理してみた | 自調自考の旅

LSTMでFX予測をやってみよう(機械学習初心者向けチュートリアル)

選定した理由:

  • 実装例など情報が豊富
  • 私自身が実際に実装したことがなかったため、勉強がてら試してみたかった

まぁ、いろいろな手法と比べてみるつもりですので、興味を持った手法を試すこととしました。

モデルの構築

モデルの概要:

  • 予測の対象:30分後(t=30)の価格帯
  • 訓練データ:予測時点での価格とそれ以前の300分の価格(t=-300〜t=0)
    • 今回は簡素化のため、インジケータは使用しませんでした
  • 訓練データは、2ヶ月分。テストデータと検証データはそれぞれ半月分
  • LSTMは1層
    • 多層にすると精度が向上するかなどは別途検証したいと思っています

実装は以下のような感じです。

def build_model(inputs, outputs, neurons, activ_func,
                dropout=0.1, loss="mean_squared_error", optimizer):
    model = Sequential()
 
    model.add(LSTM(neurons, input_shape=(inputs.shape[1], inputs.shape[2])))
    model.add(Dropout(dropout))
    #model.add(Dense(units=outputs.shape[1]))
    model.add(Dense(units=outputs.shape[1],
                kernel_initializer='random_uniform',
                bias_initializer='zeros'))
    model.add(Activation(activ_func))
    model.compile(loss="categorical_crossentropy",
              optimizer=optimizer, metrics=['categorical_accuracy'])

    return model

model = build_model(train_x_data, train_y_data, neurons = 20,activ_func="softmax",optimizer="RMSprop")

history = model.fit(train_x_data, train_y_data, 
                            epochs=100, batch_size=32, verbose=1, validation_split=0.2, callbacks=[es_cb, tb_cb, cp_cb])

学習を実行すると、以下の様に学習していきました。

f:id:MorinoKuma3:20190408205304p:plainf:id:MorinoKuma3:20190408205307p:plainf:id:MorinoKuma3:20190408205337p:plainf:id:MorinoKuma3:20190408205341p:plain

学習を実行すると、以下の様に学習していきました。

accuracyは60%を超えてきました!
これは期待できそうです。

次に混合行列を確認します。

f:id:MorinoKuma3:20190408211317p:plain

区分の上の方と下の方では上下限に張り付き気味に予測されていますが、悪くないと思います。

続けて、recall, precision, f-scoreを見ていきたいと思います。

f:id:MorinoKuma3:20190408212108p:plain

0.2...あれ?こんなもんか。。 もっといいかと思いました。

しかし、sklearnのclassification_reportが便利すぎて、地味に感動しました。

単純に予測の時点(t=0)に近い区分を、予測値(t=30)としているだけということはないだろうか...?
っと不安になったので、 t=0と、予測されたt=30とのマトリックスも確認することにしました。

f:id:MorinoKuma3:20190408212657p:plain

おや?意外にも悪くない感じで予測ができていそうです。

では、次にt=0の価格とそのモデルの予測したt=30の価格差に応じて、買い(Long)/トレードしない(NA)/売り(Short)を選択するシステムを仮定して、 買い(Long)/トレードしない(NA)/売り(Short)での混合行列を確認してみましょう。

f:id:MorinoKuma3:20190408214352p:plain

おぉ!恐ろしくいいですね!! ShortやLongのところで、逆の予想がないのがとっても素晴らしいです!!

f:id:MorinoKuma3:20190408214813p:plain

recall, precision, f-scoreも良いですね。

実際のトレードだったら、もうウハウハです(笑)

モデルを使ったバックテスト

どれくらいウハウハか確認するために簡易的ですが、今回バックテストをしてみました。

フレームワークは、pybacktestを使用しました。

その結果がこちらです。

------------------------------------------
|  Backtest(test, 2019-07-04 13:43 JST)  |
------------------------------------------

backtest:
  days: 14
  from: '2018-07-15 20:05:00'
  to: '2018-07-30 09:15:00'
  trades: 296
performance:
  PF: 48.5013
  RF: 298.4667
  averages:
    gain: 0.0007
    loss: -0.0001
    trade: 0.0006
  payoff: 4.6705
  profit: 0.1791
  winrate: 0.9122

f:id:MorinoKuma3:20190408223026p:plain

右肩上がりです!

ASKとBIDの差や滑りとかを考慮していないので、実際はもっとよくないですが、 それでも十分予測の有用性を確認できたと思います。

次回は、フォワードテストを試してみたいと思います。

為替の予測をやってみた(その1〜データ取得編〜)

この記事の概要

目的: 為替予測のためのデータ取得についての備忘録

内容:

  • 為替データの入手
    • OANDA API v20を利用した
  • グラフ描画
    • plotlyを利用した
  • 予測の方針
    • 30分後の価格帯を予測することとした

為替データの入手

OANDA APIの呼び出し

まず、OANDA APIを使用に必要なライブラリーとインポートし、APIキーを設定します。

from oandapyV20.contrib.factories import InstrumentsCandlesFactory
from oandapyV20 import API

client = API(access_token=access_token)

※OANDA APIを利用した為替データの入手には、OANDA Japan(https://www.oanda.jp/)での口座開設が必要です。 私はデモ口座を開設しています。

次に、APIに接続して過去レートを取得し、保存します。

def cnv(r, h):
    for candle in r.get('candles'):
        ctime = candle.get('time')[0:19]
        try:
            rec = "{time},{complete},{o},{h},{l},{c},{v}".format(
                time=ctime,
                complete=candle['complete'],
                o=candle['mid']['o'],
                h=candle['mid']['h'],
                l=candle['mid']['l'],
                c=candle['mid']['c'],
                v=candle['volume'],
            )
        except Exception as e:
            print(e, r)
        else:
            h.write(rec+"\n")
_from = '2018-05-01T00:00:00Z' 
_to = '2018-07-31T00:00:00Z'
gran = 'M5'
instr = 'EUR_USD'  

params = {
    "granularity": gran,
    "from": _from,
    "to": _to
}

with open("/tmp/{}.{}.csv".format(instr, gran), "w") as O:
    for r in InstrumentsCandlesFactory(instrument=instr, params=params):
        print("REQUEST: {} {} {}".format(r, r.__class__.__name__, r.params))
        rv = client.request(r)
        cnv(r.response, O)

今回は、上記の様に以下の様なデータを取得しました。

  • 銘柄:EUR/USD
  • 単位:5分足
  • 期間:2018-05-01〜2019-07-31

また、上記のコードは、以下を参照しました。

参考: github.com

しばらくして、データの取得・保存が終わったら、データを読み出します。

df = pd.read_csv("/tmp/EUR_USD.M5.csv" ,header=None,usecols=[0,2,3,4,5,6])
df.columns = ['time','open', 'high', 'low', 'close','volume']
#df.index = 'time'
df = df.set_index('time')
df.head()

f:id:MorinoKuma3:20190407223238p:plain

上記の様に、'open', 'high', 'low', 'close','volume'を読み込むことができました。

また、グラフで見てるとこんな感じです。

f:id:MorinoKuma3:20190407224040p:plain

グラフ描画には以下を参照にして、plotlyにて描画しました。
個人的な趣味で、移動平均線(EMA)とchannelが好きなので併せて表示しています。

参考: Pythonでローソク足チャートの表示(Plotly編) - Qiita
Python&Colab:TA-Lib でテクニカル分析、Plotly でローソク足の描画 - Investment Tech Hack

以下の記事の様に、値を予測するモデルを作成するのは面白そうです。

LSTMでFX予測をやってみよう(機械学習初心者向けチュートリアル)

しかし、ドンピシャで値を予測するのは難しそうですし、かつ、この手の予測は、結局予測する時点と同じ価格を予測値とするのが最も誤差が小さくなるという結果になるのではないか、と思っています。

そのため、今回は値をドンピシャで予測をするのではなく、ある程度の領域で区切り、その区分内に入るかどうかを予測するモデルを構築しようと考えました。

区分の分け方:

  • 1区分の幅:ChannelのUpperとLowerを10分割した値
  • EMAを中心にした-10〜+11区分の計21分類f:id:MorinoKuma3:20190407235846p:plain
    • 最大・最小の区分はそれ以上・以下の領域を含む
  • 対象:Closeの値

上記の分け方で、とある時間(t=0)で、Closeの値がどの区分に含まれるかをヒストグラムで表すと以下の様になりました。

f:id:MorinoKuma3:20190407234757p:plain

当然ですが、移動平均値付近(e0,e-1)をピークにきれいに分布しているのがわかります。
※このグラフは-10〜+11区分(e-10〜e10)としています。

では、次にt=0から30分後(t=30)の値は、この区分に入るかをグラフ化してみます。

f:id:MorinoKuma3:20190407234802p:plain

※このグラフは-10〜+11区分(e-10〜e10)としています。
値がドリフトしているためか、両端の区分が多くなっています。 両端が増加してるので、ほどほど値動きがあり、かつ、単調なトレンドがないとも読み取れると思います。

また、上記のt=0とt=30をマトリックスにすると以下の様な感じです。

f:id:MorinoKuma3:20190407235846p:plain

やはり、あまり値が変化しないケースが多々あることが見て取れます。 ただ、思ったより値動きがありそうで期待できます。

今回は、t=0からt=30にかけて、3区分以上変化する場合に買い(Long)もしくは、売り(Short)をトレードのルールとして仮決めします。 このルールに沿って分類を総計した結果が以下です。

f:id:MorinoKuma3:20190408000655p:plain

図らずとも、程よくよい比率になりました!

次回は、上記の動作買い(Long)/売買しない(NA)/売り(Short)を正解データ(期待する動作)とし、 為替データから予測できるか試していきたいと思います。

YOLO3でポリープ検出AI作ってみた

今回作ってみたもの

このような画像から

f:id:MorinoKuma3:20190321155454p:plain

以下のように、ポリープを検出AIを作ってみました!

f:id:MorinoKuma3:20190321155538p:plain

 

 経緯

・Udemyで物体検出を学び、

・Qiitaで記事を見て、背中をそっと押してもらえたので、作ってみました。

 学習

 当初はルンバを改造して、何かしらを追尾させたいと思い学習を始めました。

独学より効率がよいと思い立ち以下の講座を受けました。アンカーボックスなど基本的な概念など学ぶことができました:)

www.udemy.com

実践

受講後に何作ろかと考えている時に、以下の記事を読み、自分も真似てポリープ検出AIを作ってみることに!

qiita.com

作成の流れ

大体記事通りですが、以下です。

データ準備

以下から内視鏡の画像データをDL。

The Kvasir Dataset

以下のツールを使い、アノテーションファイルを作成。

BBox-Label-Tool

記事と同様に500枚の学習データ準備。

Training

記事と同様にYOLOv3を採用。
https://github.com/qqwweee/keras-yolo3

順調に学習していたと思いきや、ResourceExhaustedErrorが発生。。。(悲)

エラー回避

 GPUのメモリが足りないようなので、以下の変更で必要なメモリサイズをコンパクトにして無事動いた。

・画像サイズの削減

  416x416 -> 128x128

・batchサイズの削減

  64 -> 32

検出

こんな感じでわかりやすいものは検出できるようになりました!

f:id:MorinoKuma3:20190321164413p:plain

ただ、以下の画像の様に検出できないものもありました。

f:id:MorinoKuma3:20190321164709p:plain

まだまだ改善の余地はありますね。

人の目で見ても見づらいので、画像サイズを削減したせいでしょうか。

今後

画像サイズ削減しないで学習させてみたいと思います。