FXの売買シグナルの「だまし」を機械学習で見破る方法(XGBoost編)
こんばんは、新米データサイエンティスト(@algon_fx)です。今月は本業が極めて忙しいのに加えて、巷で話題になっているデータ分析競技に参加して大忙しです。
とはいえ、時間を見つけてはトレードもやっていますし、もはや趣味でるFX機械学習の研究も進めています。やっぱり楽しいですね、FX機械学習トレード。今は自動売買ではなく、機械学習モデルを売買シグナルやテクニカル指標と捉えてトレードをしています。
さて、前回はPythonでゴールデンクロス(デットクロス)の分析方法を記事にしました。今回はもう一段階進めて、Pythonを使ってゴールデンクロスの「だまし」を学習する流れをまとめます!
冒頭にも書きましたが、ノウハウ公開みたいな類の記事ではありません。あくまで超基本的なやり方をまとめただけです。これを実装したところで利益は100%出ません。これからFX機械学習トレードを始める方の最初の一歩になれば幸いです。
本記事ですが前回の記事の続編です。ゴールデンクロス/デッドクロスの詳しい説明は前回の記事をご参考ください。
売買シグナルの騙しとは?
初心者から上級者までこよなく愛される「ゴールデンクロス」「デッドクロス」ですが、当然ですが100%の確率で売買シグナルが成り立つわけではありません。
ゴールデンクロスは「買いシグナル」ですが、そのシグナル通りに買い注文を入れたのに・・相場は下がってしまい損をした・・なんてことはよくあることです。
このように売買シグナルの意図と反して相場が動くことを「だまし」と呼びます。下のチャートをみてください。これはドル円の10分足のデータを簡単にチャートに落としたものです。(参考:Pythonでローソク足を作る方法)
赤い矢印に注目しましょう。短期移動平均(緑線)が長期移動平均(赤線)を下から上へ突き抜けています。いわゆるゴールデンクロス=買いシグナルです。
ところが、その後に大幅に為替相場は下がり続けていますよね。売買シグナル通り、このタイミングで注文を入れたとすると大損になります。これが「だまし」です!
ゴールデンクロス/デッドクロスの「だまし」を見破る手法は多くあります。基本的な見破る方法としては他のテクニカル指標と組み合わせて売買シグナルを吟味することで、「だまし」を回避できます。
「だまし」を見破ることが可能?!なら機械学習でやってみましょう!ってのが今日のお題です。(笑)
XGBoostとは?
冒頭にも書きましたが、今回は機械学習手法のXGBoostを使って売買シグナルの「だまし」を訓練します。機械学習をやっている方であれば、一度は耳にしたことがあるかと思います。
数学的な厳密な解説は行いませんが、簡単にどのようなアルゴリズムなのか概要を説明します。XGBoostはPythonのオープンソースライブラリで比較的新しい機械学習の手法です。
2014年のデータ分析コンペで使われました。他者を寄せ付けない圧倒的な精度を叩き出し、それ以来Kaggleなどのデータ分析コンペの上位ランカーは必ずと言って良いほど使う手法に定着しました。
XGBoostの仕組みと知っておくべき特徴は以下の通りです。
【XGBoostの仕組み】
・XGBoostはランダムフォレストの勾配ブースティングのフレームワーク
・勾配ブースティングとはアンサンブル学習の手法の一つです
・アンサンブル学習とは複数のモデルを組み合わせる手法です
・つまりランダムフォレストのモデルを多数構築して組み合わせます
・勾配ブースティングは複数モデルの組み合わせ方に工夫があります
・各モデルの弱点(間違った推測)を学習して最終的に間違いを減らします
【XGBoostの特徴】
・大規模データでも非常に高速に処理できるよう設計されています
・設計がとてもフレキシブルで高度なカスタマイズが可能
・ハイパーパラメータが多数あるのでチューニングは必須
・多くのデータに対して非常に高い予測精度がでる
・回帰と分類で使えます
機械学習の現場でもXGBoostは頻繁に使われる手法です。とても身軽かつ、一定の推測精度がでうのでひとまずベースラインを作るときなどにも活用されます。
上記にもある通りXGBoostはランダムフォレストの勾配ブースティングです。ランダムフォレストというのは、決定木のアンサンブル学習です。(参考:決定木を使ってFX予想をやってみる)
ひとまず「身軽で推測精度が優秀なやつ!」ってくらいに理解しておけば問題ないと思います!ただ、特徴にも記載しましたがハイパーパラメータが多いので、必ずと言って良いほどチューニングを行う必要があることも覚えておきましょう。
では、実際にPytyhonを使ってやってみましょう!
STEP1 実行環境とデータ
今回も実行環境はGoogle Colab(グーグルコラボ)を使って行いましょう。もし自身のPCで行う場合は、XGBoostのインストールが必要です。pip経由で簡単にインストールが可能です。
Google Colabを初めて使う方は「知らないと損をするGoogle Colabの隠し技と基本操作」の記事をご参照ください。ブラウザのみで5分もあれば機械学習の実行環境が整います。またXGBoostも事前にインストールされていますので、環境構築も不要です。
データですが、ドル円の10分足のデータを使います。下記のURLからダウンロードが可能です。(こちらは私がOANDA FX APIから作成したデータをCSVファイルに書き出したものです)
【使うCSVファイル】
usd_10min_api.csv
ダウンロードしたら準備完了です。次のステップに行きましょう。
STEP2 データの確認
では、Google Colabを立ち上げて早速始めましょう。まずは今回使うライブラリをインポートします。XGBoostですが、途中でWarning(警告)が出る箇所があるので、あらかじめ非常にする設定をしています。(特にモデル構築に問題はありません)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# ライブラリのインポート import pandas as pd import numpy as np # XGBoost import xgboost as xgb from xgboost import XGBClassifier # Scikit-learn from sklearn.model_selection import train_test_split from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score, precision_score, recall_score from sklearn.grid_search import GridSearchCV # 警告がうざいので非表示にする設定 import warnings warnings.filterwarnings('ignore') # Pandasのデータフレームの最大表示数を設定 pd.options.display.max_rows = 999 pd.options.display.max_columns = 999 |
上記のコードをGoogle Colabの「コードセル」に入力できたら、Shift + Enterでコードの実行をしましょう。
次に為替レートのCSVファイルをGoogle Colabへアップロードします。下記のコードを実行して、「ファイルを選択」からCSVファイルを選んでアップロードしましょう。
1 2 3 4 5 |
# Google Colabへファイルをアップロード from google.colab import files uploaded = files.upload() |
では、CSVファイルをPandasのデータフレーム形式で読み込んであげましょう。同時に単純移動平均も計算をしちゃいます。(参考:Pythonで単純移動平均を算出する方法)
1 2 3 4 5 6 7 8 9 10 11 12 |
# データの読み込み masta = pd.read_csv("./usd_10min_api.csv") df = masta.copy() del df['Unnamed: 0'] # 単純移動平均を計算 df = df[['time', 'close']] df['sma_5'] = np.round(df['close'].rolling(window=5).mean(), 2) df['sma_15'] = np.round(df['close'].rolling(window=15).mean(), 2) df.tail() |
1 2 3 4 5 6 7 8 9 |
--出力 time close sma_5 sma_15 16417 2018/08/10 15:30:00 111.038 110.99 110.90 16418 2018/08/10 15:40:00 111.052 111.00 110.91 16419 2018/08/10 15:50:00 111.013 111.00 110.93 16420 2018/08/10 16:00:00 110.915 110.99 110.94 16421 2018/08/10 16:10:00 110.773 110.96 110.95 |
こちらのデータは2018年3月1日15:40から2018年8月10日 16:10までの期間のデータです。冒頭でも触れましたがOANDA FX APIから取得した実際のドル円の10分足の過去為替レートです。
上記にもある通り今回は期間5と期間15の単純移動平均を使います。これらの期間を変更すれば結果も大きく異なります。時間がある方は検証してみてください。
STEP3 ゴールデンクロスとデッドクロスを検出
では売買シグナルとして「ゴールデンクロス」「デッドクロス」をまずは検出してあげましょう。詳細は「Pythonでゴールデンクロスとデッドクロスを分析する方法」をご参考ください。
今回はささっと、一つのコードにまとめて処理をしちゃいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# 単純移動平均(SMA)の差異 df['diff'] = df['sma_5'] - df['sma_15'] # ゴールデンクロスを確認 asign = np.sign(df['diff']) sz = asign == 0 while sz.any(): asign[sz] = np.roll(asign, 1)[sz] sz = asign == 0 signchange = ((np.roll(asign, 1) - asign) == -2).astype(int) df['cross'] = signchange df['golden'] = df['cross'] # デッドクロスを検出 asign = np.sign(df['diff']) sz = asign == 0 while sz.any(): asign[sz] = np.roll(asign, 1)[sz] sz = asign == 0 signchange = ((np.roll(asign, 1) - asign) == 2).astype(int) df['cross'] = signchange df['dead'] = df['cross'] # デッドクロスの値を変更 df['dead'][df['dead'] == 1] = -1 # データを確認 df[103:109] |
1 2 3 4 5 6 7 8 9 10 |
--出力 time close sma_5 sma_15 diff cross golden dead 103 2018/03/02 08:50:00 106.179 106.16 106.20 -0.04 0 0 0 104 2018/03/02 09:00:00 106.213 106.18 106.20 -0.02 0 0 0 105 2018/03/02 09:10:00 106.263 106.20 106.20 0.00 0 0 0 106 2018/03/02 09:20:00 106.244 106.21 106.19 0.02 0 1 0 107 2018/03/02 09:30:00 106.193 106.22 106.19 0.03 0 0 0 108 2018/03/02 09:40:00 106.210 106.22 106.19 0.03 0 0 0 |
ご覧の通りゴールデンクロスがしっかり算出できています。上記の例ですと9:20の時点でsma_5(短期平均線)がsma_15(長期平均線)を下から上へ抜けている(つまり差分がマイナスからプラスへ転換した)のが確認できますね。
STEP4 前処理と特徴量エンジニアリング
ゴールデンクロスが為替レートから算出できました。次はXGBoostに学習させる特徴量を計算しましょう。特徴量ですがやり方は無数にあります。
どのような特徴量を使ってモデルを構築するのかで推測の制度が大きく変わります。冒頭でも書きましたが、今回の目的は儲けるのが目的ではありません。ですので、超シンプルかつ初歩的な特徴量を使かいます。
今回は下記のアイデアを実装します。
【特徴量 その1】
ゴールデンクロスが出現した時点の終値、短期移動平均の値、長期移動平均の値を使ってみる。
【特徴量 その2】
ゴールデンクロスが出現した時点の終値を基準として、10分前〜50分前の終値の差分を使ってみる。
【特徴量 その3】
ゴールデンクロスが出現した時点を基準として、10分前〜30分前の短期移動平均(sma_5)と長期移動平均(sma_15)の差分を使ってみる。
では、実際にデータの処理を行いましょう。まずは終値の差分の特徴量を計算します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 現在の終値を基準として10〜50分前の終値の差分 df['close_10'] = df['close'].shift(1) df['close_20'] = df['close'].shift(2) df['close_30'] = df['close'].shift(3) df['close_40'] = df['close'].shift(4) df['close_50'] = df['close'].shift(5) df['diff_10'] = df['diff'].shift(1) df['diff_20'] = df['diff'].shift(2) df['diff_30'] = df['diff'].shift(3) df['diff_40'] = df['diff'].shift(4) df['diff_50'] = df['diff'].shift(5) df[103:109] |
1 2 3 4 5 6 7 8 9 10 |
-- 出力 time close sma_5 sma_15 diff cross golden dead close_10 close_20 close_30 close_40 close_50 diff_10 diff_20 diff_30 diff_40 diff_50 103 2018/03/02 08:50:00 106.179 106.16 106.20 -0.04 0 0 0 106.157 106.185 106.145 106.155 106.149 -0.04 -0.06 -0.07 -0.06 -0.06 104 2018/03/02 09:00:00 106.213 106.18 106.20 -0.02 0 0 0 106.179 106.157 106.185 106.145 106.155 -0.04 -0.04 -0.06 -0.07 -0.06 105 2018/03/02 09:10:00 106.263 106.20 106.20 0.00 0 0 0 106.213 106.179 106.157 106.185 106.145 -0.02 -0.04 -0.04 -0.06 -0.07 106 2018/03/02 09:20:00 106.244 106.21 106.19 0.02 0 1 0 106.263 106.213 106.179 106.157 106.185 0.00 -0.02 -0.04 -0.04 -0.06 107 2018/03/02 09:30:00 106.193 106.22 106.19 0.03 0 0 0 106.244 106.263 106.213 106.179 106.157 0.02 0.00 -0.02 -0.04 -0.04 108 2018/03/02 09:40:00 106.210 106.22 106.19 0.03 0 0 0 106.193 106.244 106.263 106.213 106.179 0.03 0.02 0.00 -0.02 -0.04 |
diff_10を確認するとわかりますが、その時点の終値から10分前の終値を差し引いた値なのが確認できます。
続いて短期 – 長期の差異も計算をしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 現在の短期 - 長期の差分を基準として10〜50分前の差分を引く df['close_10'] = df['close'] - df['close_10'] df['close_20'] = df['close'] - df['close_20'] df['close_30'] = df['close'] - df['close_30'] df['close_40'] = df['close'] - df['close_40'] df['close_50'] = df['close'] - df['close_50'] df['diff_10'] = df['diff'] - df['diff_10'] df['diff_20'] = df['diff'] - df['diff_20'] df['diff_30'] = df['diff'] - df['diff_30'] df['diff_40'] = df['diff'] - df['diff_40'] df['diff_50'] = df['diff'] - df['diff_50'] df[103:109] |
1 2 3 4 5 6 7 8 9 10 |
--出力 time close sma_5 sma_15 diff cross golden dead close_10 close_20 close_30 close_40 close_50 diff_10 diff_20 diff_30 diff_40 diff_50 103 2018/03/02 08:50:00 106.179 106.16 106.20 -0.04 0 0 0 0.022 -0.006 0.034 0.024 0.030 0.00 0.02 0.03 0.02 0.02 104 2018/03/02 09:00:00 106.213 106.18 106.20 -0.02 0 0 0 0.034 0.056 0.028 0.068 0.058 0.02 0.02 0.04 0.05 0.04 105 2018/03/02 09:10:00 106.263 106.20 106.20 0.00 0 0 0 0.050 0.084 0.106 0.078 0.118 0.02 0.04 0.04 0.06 0.07 106 2018/03/02 09:20:00 106.244 106.21 106.19 0.02 0 1 0 -0.019 0.031 0.065 0.087 0.059 0.02 0.04 0.06 0.06 0.08 107 2018/03/02 09:30:00 106.193 106.22 106.19 0.03 0 0 0 -0.051 -0.070 -0.020 0.014 0.036 0.01 0.03 0.05 0.07 0.07 108 2018/03/02 09:40:00 106.210 106.22 106.19 0.03 0 0 0 0.017 -0.034 -0.053 -0.003 0.031 0.00 0.01 0.03 0.05 0.07 |
大丈夫そうですね。やっていることは言葉で表すと非常に複雑に聞こえますが、コードを見ればやっていることは単純なのが判ると思います。
続いて正解ラベルを作りましょう。これにも色々と考え方がありますが、今回は「売買シグナルが発生した20分後に決済をする」をルールとして設けましょう。
ですので、正解ラベルとしてはゴールデンクロスが出現した終値から20分後の終値の差分を計算します。この終値の差分の値がマイナスの場合は「0」、プラスの場合は「1」とします。
ゴールデンクロスは「買い」のシグナルですので、20分後の終値が上がっていれば「成功」、逆に下がっている場合は「だまし」というわけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 正解ラベルを作成(20分ホールドした結果) df['profit_2'] = df['close'].shift(-2) df['profit'] = df['profit_2'] - df['close'] # ラベルを1と0にする mask1 = df['profit'] > 0.00 mask2 = df['profit'] <= 0.00 column_name = 'target' df.loc[mask1, column_name] = 1 df.loc[mask2, column_name] = 0 df[103:109] |
1 2 3 4 5 6 7 8 9 |
time close sma_5 sma_15 diff cross golden dead close_10 close_20 close_30 close_40 close_50 diff_10 diff_20 diff_30 diff_40 diff_50 profit_2 profit target 103 2018/03/02 08:50:00 106.179 106.16 106.20 -0.04 0 0 0 0.022 -0.006 0.034 0.024 0.030 0.00 0.02 0.03 0.02 0.02 106.263 0.084 1.0 104 2018/03/02 09:00:00 106.213 106.18 106.20 -0.02 0 0 0 0.034 0.056 0.028 0.068 0.058 0.02 0.02 0.04 0.05 0.04 106.244 0.031 1.0 105 2018/03/02 09:10:00 106.263 106.20 106.20 0.00 0 0 0 0.050 0.084 0.106 0.078 0.118 0.02 0.04 0.04 0.06 0.07 106.193 -0.070 0.0 106 2018/03/02 09:20:00 106.244 106.21 106.19 0.02 0 1 0 -0.019 0.031 0.065 0.087 0.059 0.02 0.04 0.06 0.06 0.08 106.210 -0.034 0.0 107 2018/03/02 09:30:00 106.193 106.22 106.19 0.03 0 0 0 -0.051 -0.070 -0.020 0.014 0.036 0.01 0.03 0.05 0.07 0.07 106.279 0.086 1.0 108 2018/03/02 09:40:00 106.210 106.22 106.19 0.03 0 0 0 0.017 -0.034 -0.053 -0.003 0.031 0.00 0.01 0.03 0.05 0.07 106.262 0.052 1.0 |
ちょっと横長で見辛いですが・・一番右のカラムに「target」が追加されました。最後にデータをゴールデンクロスが出現したものだけを切り分けましょう。
また不要なデータ(時間など)はデータフレームから落としましょう。これでXGBoostへ訓練させる特徴量の作成は完了です。
1 2 3 4 5 6 7 8 9 10 11 12 |
# ゴールデンクロスのみに分ける df_golden = df[df['golden'] == 1] del df_golden['time'] del df_golden['diff'] del df_golden['cross'] del df_golden['golden'] del df_golden['dead'] del df_golden['profit_2'] del df_golden['profit'] df_golden.head() |
1 2 3 4 5 6 7 8 9 |
--出力 close sma_5 sma_15 close_10 close_20 close_30 close_40 close_50 diff_10 diff_20 diff_30 diff_40 diff_50 26 106.786 106.78 106.77 0.011 0.006 -0.004 0.006 0.011 0.01 0.01 0.02 0.03 0.03 38 106.782 106.77 106.75 0.010 -0.011 0.029 0.024 0.090 0.02 0.03 0.05 0.07 0.07 106 106.244 106.21 106.19 -0.019 0.031 0.065 0.087 0.059 0.02 0.04 0.06 0.06 0.08 126 106.049 106.05 106.04 -0.025 -0.005 0.006 0.005 0.001 0.01 0.04 0.07 0.08 0.11 184 105.451 105.39 105.37 0.044 0.050 0.068 0.153 0.147 0.04 0.07 0.10 0.11 0.10 |
STEP5 訓練データとテストデータへ分割
次に全てのデータを訓練データとテストデータへ分割しましょう。訓練データを使ってモデルの訓練を行い、テストデータを使ってモデルの精度を確認するためです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# テストと訓練へスプリット data_array = df_golden.values # 訓練データとテストデータにスプリット n = df_golden.shape[0] p = df_golden.shape[1] train_start = 0 train_end = int(np.floor(0.9*n)) test_start = train_end + 1 test_end = n train_set = data_array[np.arange(train_start, train_end), :] test_set = data_array[np.arange(test_start, test_end), :] # 特徴量とターゲット X_train = train_set[:, :13] y_train = train_set[:, 13:] X_test = test_set[:, :13] y_test = test_set[:, 13:] # 最初のレコードを確認 print(X_train[0]) print(y_train[0]) print(y_train.sum()) print(y_test.sum()) |
1 2 3 4 5 6 7 8 |
--出力 [ 1.07e+02 1.07e+02 1.07e+02 1.10e-02 6.00e-03 -4.00e-03 6.00e-03 1.10e-02 1.00e-02 1.00e-02 2.00e-02 3.00e-02 3.00e-02] [0.] 240.0 30.0 |
大丈夫そうですね。訓練データには「1」が240個含まれています。ターゲットが「1」というのは、ゴールデンクロスが出現した時点で買い注文を行い、20分後に決済をした時に利益がでたことを意味しています。
STEP6 ハイパーパラメータチューニング
XGBoostの概要説明でも触れましたが、XGBoostは多数のハイパーパラメータがあります。それらを細かく調整することで非常に高い推測精度が出ます。
今回は細かいチューニングは行いませんが、やり方だけ記載しておきます。Scikit-learnのグリッドサーチCVを利用すると、非常に簡単にハイパーパラメータの交差検証が行えます。
まずはターゲット(y_train)サイズを変更してあげましょう。本来のサイズは(504, 1)なのですが、Scikit-learnのグリッドサーチでエラーが出ます。(謎の仕様)
1 2 3 4 5 |
# ターゲットのサイズを変更 y_train = y_train.reshape(len(y_train), ) y_train.shape |
1 2 3 4 |
--出力 (504,) |
次にグリッドサーチCVで交差検証をするXGBoostのハイパーパラメータを設定します。今回は簡単なものしかやりませんが、より精度をあげたい方は、ここの値を色々と調整してしっかり交差検証をしてみください。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# XGBoostのハイパーパラメータ の設定 param_cv_1 = { 'n_estimators': [10, 50, 100], 'max_depth': [5, 10], 'subsample': [0.1], 'learning_rate':[0.1], 'objective': ['binary:logistic'], 'seed':[42], 'gamma':[0.1], 'min_child_weight':[1] } |
では交差検証を実行してみます。
1 2 3 4 5 6 |
# グリッドサーチ clf = xgb.XGBClassifier() grid_1 = GridSearchCV(clf, param_grid = param_cv_1, cv=5, scoring='accuracy', verbose=1) grid_1.fit(X_train, y_train) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
--出力 Fitting 5 folds for each of 6 candidates, totalling 30 fits [Parallel(n_jobs=1)]: Done 30 out of 30 | elapsed: 0.6s finished GridSearchCV(cv=5, error_score='raise', estimator=XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1, colsample_bytree=1, gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3, min_child_weight=1, missing=None, n_estimators=100, n_jobs=1, nthread=None, objective='binary:logistic', random_state=0, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None, silent=True, subsample=1), fit_params={}, iid=True, n_jobs=1, param_grid={'n_estimators': [10, 50, 100], 'max_depth': [5, 10], 'subsample': [0.1], 'learning_rate': [0.1], 'objective': ['binary:logistic'], 'seed': [42], 'gamma': [0.1], 'min_child_weight': [1]}, pre_dispatch='2*n_jobs', refit=True, scoring='accuracy', verbose=1) |
データがとても小さいのであっという間に終わりますね(笑)。普段、本職でグリッドサーチCVやると余裕で数時間とかかかりますが。
では交差検証した結果、最も正解率が高かったハイパーパラメータ の組み合わせを確認します。
1 2 3 4 |
# ベストパラメーター表示 grid_1.best_params_ |
1 2 3 4 5 6 7 8 9 10 11 |
--出力 {'gamma': 0.1, 'learning_rate': 0.1, 'max_depth': 5, 'min_child_weight': 1, 'n_estimators': 10, 'objective': 'binary:logistic', 'seed': 42, 'subsample': 0.1} |
ふむふむ、なるほどですね。参考までにですが、このハイパーパラメータ が正解という訳ではありません。各自、色々とハイパーパラメータ の値を調整してみましょう。
STEP7 XGBoostのモデル訓練
では、いろいよ訓練データを使ってXGBoostのモデルを作りましょう!ランダムフォレストの勾配ブースティングをスクラッチでコーディングするのは非常にしんどいですが・・
XGBoostを使うとなんとたったの一行で実装できます(笑)
1 2 3 4 |
# モデル訓練 clf.fit(X_train, y_train) |
1 2 3 4 5 6 7 8 9 |
--出力 XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1, colsample_bytree=1, gamma=0.1, learning_rate=0.1, max_delta_step=0, max_depth=5, min_child_weight=1, missing=None, n_estimators=10, n_jobs=1, nthread=None, objective='binary:logistic', random_state=0, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=42, silent=False, subsample=0.1) |
これでモデル訓練完了です!では、テストデータを使って、本当に学習できているのか確認して今ます。ドキドキな瞬間ですね。
1 2 3 4 5 6 7 |
# テストデータで推測 test_pred = clf.predict(X_test) # 混同行列で確認 confusion_matrix(y_test, test_pred, labels=[1, 0]) |
1 2 3 4 5 |
-- 出力 array([[ 8, 22], [12, 13]]) |
う〜ん・・まぁなんとなくの精度ですね笑。混同行列は分類の推測精度を評価する指標の一つです。上の結果の意味としては、テストデータに含まれるゴールデンクロスの「だまし」は25回あり、うち12回は「だまし」として推測を正しく出来たことを意味します。
正解率(Accuracy)も確認してみましょう。
1 2 3 4 |
# 正解率の確認 print(accuracy_score(y_test, test_pred)) |
1 2 3 4 |
--出力 0.38181818181818183 |
まぁこんなもんですよね。今回の特徴量で作ったゴールデンクロスの売買シグナルのモデルの正解率は38.18%でした。間違いなく、このモデルに従ってトレードしたら「損」しますね(笑)
STEP8 「だまし」の確率を出力してみよう
私がXGBoostを好きな理由の一つに「確率」を出力できる点があります。ステップ6ではモデルはターゲットの値(1か0)を直接出力しました。
確率が出力できるというのは、例えばこのゴールデンクロスは成功(値1)する確率が72%、だまし(値0)の確率は28%といった具合に確率を出すことが可能です。
やってみましょう。
1 2 3 4 5 |
# テストデータで確率を推測 test_proba = clf.predict_proba(X_test) test_proba[1] |
1 2 3 4 |
--出力 array([0.56, 0.44], dtype=float32) |
上記の意味ですがテストデータの2番目の特徴量(ゴールデンクロス)は「だまし」の確率が56%を意味しています。
最後に異なる閾値でどれくらいの確率を信じれば良いのかを確認してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 異なる閾値で分析 thresholds = [.1,.2,.3,.4,.5,.6,.7,.8,.9] for i in zip(thresholds): y_test_predictions = test_proba[:,1] > i cnf_matrix = confusion_matrix(y_test, y_test_predictions, labels=[1, 0]) np.set_printoptions(precision=2) print('Threshold:', i) print('precision Score:', precision_score(y_test, y_test_predictions)) #print('Precision Score:', precision_score(y_train, y_train_predictions)) #print('AVG Precision Score:', average_precision_score(y_train, y_train_predictions)) print(cnf_matrix) print('-------------------------') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
--出力 Threshold: (0.1,) precision Score: 0.5454545454545454 [[30 0] [25 0]] ------------------------- Threshold: (0.2,) precision Score: 0.5454545454545454 [[30 0] [25 0]] ------------------------- Threshold: (0.3,) precision Score: 0.5454545454545454 [[30 0] [25 0]] ------------------------- Threshold: (0.4,) precision Score: 0.5319148936170213 [[25 5] [22 3]] ------------------------- Threshold: (0.5,) precision Score: 0.4 [[ 8 22] [12 13]] ------------------------- Threshold: (0.6,) precision Score: 0.4 [[ 2 28] [ 3 22]] ------------------------- Threshold: (0.7,) precision Score: 0.0 [[ 0 30] [ 1 24]] ------------------------- Threshold: (0.8,) precision Score: 0.0 [[ 0 30] [ 0 25]] ------------------------- Threshold: (0.9,) precision Score: 0.0 [[ 0 30] [ 0 25]] ------------------------- |
どれも微妙ですが・・閾値を0.6とするとテストデータに含まれる25回の「だまし」のうち、22回は正しく検知できているようです。ただ、上の結果を見るとわかりますが、非常に微妙な精度です。
まとめ
いかがでしたでしょうか?今回はXGBoostを使ってゴールデンクロスの売買シグナルの「だまし」を予測する方法をまとめました!
冒頭でも説明した通り、本記事はあくまで「やり方」を解説するのが目的です。全然、精度が出てないじゃん!って思わず・・それぞれ各自、これをスタートラインとして精度改善に挑戦してみてください!
FXで機械学習を使うための初心者向けチュートリアルもまとめています!Pythonの初歩的な知識があれば実装が可能ですので、興味がある方は是非ご覧ください!
決定木を使ってFX予想をやってみる
FXトレードでロジスティック回帰を独自テクニカル指標として活用する方法
ブログ読んでいただきありがとうございます!Twitterでも色々と発信しているので、是非フォローお願いします!
ディスカッション
コメント一覧
まだ、コメントがありません