ランダムフォレスト株価を予測できなかった話

はじめに

株価予測に関する論文を少し調べていたら、”Predicting the direction of stock market prices using random forest“という論文を見つけました。

N日後(N=30, 60, 90)の終値が上がっているか下がっているかを予測するタスクに取り組んでいて、テクニカル指標をいくつか入力として使いランダムフォレストという機械学習の手法を適用したところ、80%以上の精度で予測が的中したという論文です。

論文中ではApple、ゼネラル・エレクトリック、サムスンを対象としていたのと、学習や評価に使用したデータの詳細が論文から読み取りきれなかったので、日本株の銘柄を対象に追実験してみたいと思います。

今回は、個人的に興味のある銘柄ということでソフトバンクグループ(9984)を対象とします。

データ分析の様子を長々と書くので、結論だけを知りたい方は最後まで飛んでください。

使用するデータ

9884 ソフトバンクグループの日足チャートを使用します。対象期間は 1998/01/16~2019/08/30です。

データは株式投資メモさんからCSVをダウンロードさせていただきました。

調整後終値の折れ線グラフをプロットしてみると次のようになり、各種サイトで見られるソフトバンクグループの株価の動きと一致していそうです。

前処理

データの前処理を行います。

ラベル付け

まずは予測するラベルを用意します。論文中では30日~90日後の株価を予測していましたが、今回は20日後(=土日等も考えると実質1か月後)の株価を予想します。

今回の対象データの中に、20日後に調整後終値が上がっている日は2779回、下がっているもしくは変わらない日は2529回出現しました。大体同程度の回数が出現しています。

先ほどのグラフにラベルを可視化してみると次のような形になります。オレンジ色の線が上下していると思いますが、上側についているときは20日後に株価が上昇している日、下側についているときは20日後に株価が下落している日です。

実際に20日後の終値が上昇しているか下落しているかは、20日後になってみないとわかりませんが、それをランダムフォレストを使って予測してみようというのがこの後やっていくことになります。

指数平滑法の適用

始値、終値等の指標に指数平滑化法を適用します。論文にあるように、元の観察値 \(Y_t\) を下記の数式で平滑化します。

$$S_t=\alpha Y_t + (1 – \alpha) S_{t-1}$$

今回は\(\alpha=0.9\)にして実験してみます。

テクニカル指標の計算

平滑化された指標をもとに、下記のテクニカル指標を計算します。

  • RSI
  • ストキャスティクス
  • Williams%R
  • MACD
  • 20日前からの変化率
  • オンボリュームバランス (OVB)

論文ではOVBも書いてありましたが、実装が面倒になりこの時点で一度ランダムフォレストを学習させてみようと思いました。

ランダムフォレストの学習

訓練データ・テストデータの分割

下記のようにデータを分割しました。訓練データで学習に、検証データはパラメータのチューニングに使います。最終的にテストデータで精度を評価します。

訓練データ1998/01 – 2016/06
検証データ2016/07 – 2017/12
テストデータ2018/01 – 2019/08

ここで問題が…

Scikit-learnのRandomForestClassifierを使ったのですが、デフォルトのパラメータで訓練データの精度(Accuracy)がほぼ100%で分類できてしまう状態になりました。当然過学習を起こしている状態で、検証データ・テストデータに対しては精度が50%前後となってしまいました。

過学習しないようにパラメータを調節してみたり、XGBoostを使ったりしてみましたが、変わらず最終的な精度は50%台をうろうろする程度で、80%台には届きそうもありません。

再現コードを調査

この論文の実験をRで再現しているノートを発見しました。この人の説明やグラフだと、確かにAAPLやMSFTなどの銘柄の上下を8割以上の精度で予測できてそうです。というわけで、コードを追ってみました。

訓練・テストデータの分け方に致命的なミス

すると、訓練データ、テストデータを下記のように分けていることがわかりました。indexという変数に、テストデータで使うためのインデックスを保存するためのコードです。

#Split the data in a 80-20 (train-test) ratio.
index <- sample(1:nrow(stock_indicators), size=0.2*nrow(stock_indicators))

このRのsampleというメソッドを使うと、対象期間をランダムに訓練データとテストデータに分割します。つまり、未来のデータを使って訓練し過去のデータを予測するという状況が起きてしまっており、それは精度が高いよな、納得しました。元論文に訓練・テストデータの分け方の詳細が記述されてなかったですが、おそらく同様の方法をとっているのだと思います。

コードを修正してみる

時系列がシャッフルされないようにし、過去の部分を訓練データ、未来の部分をテストデータとする必要があります。つまり、当該箇所は正しくはこうする必要があります。

index=1:(0.2*nrow(stock_indicators))

さて、この1行だけ修正してコードを再実行してみました。すると、下記のグラフの通りの精度になりました。20日後の株価で45%~60%程度の精度です。

結論

冒頭で紹介した “Predicting the direction of stock market prices using random forest” という論文で紹介されている数値は、どうやら訓練データとテストデータの分け方を時系列でわけるのではなくランダムサンプルで分けてしまっているようで、実験としてはいいのかもしれないが実際の取引に使えるものではないようです。

時系列でデータを分けたところ、日米どちらの銘柄でも精度は 45~60%に収まる程度(多くは50%前後)と、ほぼ予測できないという結果に終わりました。

80%の精度という甘い言葉につられて実験をしてみましたが、現実は甘くないようですね。

関連記事

過去にゴールデンクロスについても分析を行っているので、興味のある方はそちらもご覧ください。

タイトルとURLをコピーしました