今日も窓辺でプログラム

外資系企業勤めのエンジニアが勉強した内容をまとめておくブログ

モデルの評価とバックテスト ~株価予測(6)~

この記事について

前回、は日経平均の終値が始値より「上がる」か「下がる」か「ほぼ変わらない」かを予測するモデルをTensorFlowで構築しました。
今回の記事では、そのモデルの性能について詳しく評価していきます。

評価対象のモデル

今回評価するモデルは、前回記事で作成したモデルです。

前回記事: 日経平均の終値が始値より上がるか下がるかを予測する ~株価予測(5)~ - 今日も窓辺でプログラム

モデルの詳細は上記の記事を見ていただきたいのですが、簡単に言うと、その日の日経平均の始値に比べて終値がどうなるかを、下記の3クラスで予測するモデルを作成しました。

  • 「上がる」:0.3%以上高くなる
  • 「下がる」:0.3%以上安くなる
  • 「ほぼ変わらない」:それ未満の変動率

入力は各指標(ダウとかDAXとか)に関して、

  • 始値の前々日と前日での変動率
  • 終値の前々日と前日での変動率
  • 前日終値の前日始値からの変動率

の3つを入力し、加えて日経平均に関しては

  • 当日始値の前日終値からの変動率

を入力してあげました。前回は9個の指標を使っていたので、1日当たり合計28個の入力になります。

モデルを評価するにあたって、今回は以下の2つの方法で今回のモデルを評価しました。

  • AccuracyとPrecision
  • バックテスト

また、訓練データとテストデータには、それぞれ以下の期間のデータを用いました。

  • [訓練データ] 2013年~2015年の丸3年分
  • [テストデータ] 2016年1月1日~2016年8月22日

AccuracyとPrecision

定義

Accuracyは次のように定義します。よく精度と呼ばれる数値です。
Accuracy = (予測が当たった個数) / (テストデータの総数)

Precisionは日本語だと適合率と言います。通常2値分類の問題に使われる尺度ですので、今回はUP/DOWN/SAMEのそれぞれのクラスの予測についてPrecisionを計算します。
例えば、UPに対するPrecisionは次のように定義します。
Precision_UP = (UPと予想して当たった個数) / (UPと予想した個数)

他にRecall(再現率)と呼ばれる尺度もよく使われますが、今回のケースに関しては、儲けられるチャンスと取りこぼさないようにするよりは、負けずに着実に増やしていくようなモデルになることが望ましい(と私は思っている)ので、Precisionのみを測定することにしました。

結果

(2016/08/31追記:プログラムにバグが見つかり、この数値は正しくないことが発覚しています。バグを修正後、記事も正しい数値で上げなおします。)
(2016/9/20追記:正しい数値での記事を投稿しました(クリックで記事へ移動)

上述の訓練データ・テストデータを使用して評価したところ、このような結果になりました。

クラス 実際の日数 モデルが予想した日数 予想のPrecision
UP 95 97 0.660
DOWN 82 97 0.515
SAME 63 47 0.277

Accuracy = 0.5269709543568465

学習データやテストデータの期間を変動させると数字は若干変わりますが、大体Accuracyが50~55%に収まることが多かったです。
上がっていく兆候は割と正しく予測できている(Precision=0.660)用ですが、動きが変わらない日の予測はあまりできない(Precision=0.277)モデルみたいです。
正直、この数字だけ見るとあまりいい数字とは言えません。前日の終値と比べて上がるか下がるかを予測していたころは65~70%ほどのAccuracyだったので、それと比べると惨憺たる結果です。(クラス数が増えているとはいえ。。)

バックテスト

バックテストとは?

機械学習などでよく見るPrecisionなどの評価に加えて、今回はバックテストも行ってみます。

バックテスト(Back Test)とは、システムトレードなどを利用する場合にプログラムなどを利用して当該ストラテジーを過去の為替レートや株価などに当てはめた場合にどのような動きをするのかをチェックするテストのこと。

http://www.finance-dictionay.com/2012/02/post_886.html

要するに、今回のモデルを過去のデータに当てはめたときにどんな動きをするのかを確かめてみる、という評価です。
今回は2013年~2015年までのデータをもとにモデルを訓練し、そのデータが2016年の現在までの動きをどのように予測し、実際にはどうだったのか、という検証をします。

どの程度の損益が得られるか概算をするため、以下のような仮定のもと損益の概算値も計算しました。

  • UPと予想したら1570を100万円分始値で購入、終値で売却
  • DOWNと予想したら1357を100万円分始値で購入、終値で売却
  • SAMEと予想した日は何もしない
  • 1570と1357は、それぞれ日経平均の+2%, -2%の変動率となる
  • 手数料は楽天証券のものを利用して計算

実際には、ETFの値動きが日経平均と完璧に連動するわけではなかったり、始値や終値で売買することは難しかったり(特に、今回のモデルの場合は当日の始値を見た直後に予測を出すため)、100万円ぴったりの購入はできないなど、現実の取引にそぐわない仮定ではあるのですが、そんなに大きくかけ離れた値にはならないのではないか、と個人的には予想しています。またこの仮定だと、取引をする日は毎回100万円分の売買をする仮定ですが、実際に取引をするとなると毎回100万円ではなく前日までの損益で売買代金が変動するはずなので、複利計算になるはずで、実際は損益の幅が若干大きくなるのではないでしょうか。。

結果

(2016/08/31追記:プログラムにバグが見つかり、この数値は正しくないことが発覚しています。バグを修正後、記事も正しい数値で上げなおします。)
(2016/9/20追記:正しい数値での記事を投稿しました(クリックで記事へ移動)

今回の訓練・テストデータの期間の場合だと、テストデータの期間終了後に291.8万円になっている計算でした。9か月で、+192%ほどという、驚異的な数字です。正直、プログラムがあっているのかどうか、何か間違ったことをしているんじゃないのか、というレベルで不安になる結果です。
期間を変えるとこの数字は結構変わるので、これが偶然の結果なのか、意味のある結果なのか、統計的な検定をしてみる必要はあるかと思います。検定についても勉強して、次回以降適用できればと思っています。
(といっても、数回試しても毎回+100%以上のパフォーマンスでした)

例えば、8月に入ってからのバックテストの結果をお見せすると、こんな感じです。
思いっきり逆方向に外している日もありますが(この中だと8/17)、Up/Downと予想したけれどSameだった、というような外し方は、損が少なく済んでいます。

日付 予想 実際 始値 終値 上述仮定の下での利益
2016/8/1 Up Up 16415.31055 16635.76953 26335.16611
2016/8/2 Down Down 16469.67969 16391.44922 8974.938127
2016/8/3 Same Down 16227.28027 16083.11035 0
2016/8/4 Up Up 16168.33984 16254.88965 10181.08422
2016/8/5 Up Same 16278.99023 16254.4502 -3539.933807
2016/8/8 Up Up 16462.28906 16650.57031 22349.24905
2016/8/9 Up Up 16632.41016 16764.9707 15415.02863
2016/8/10 Up Same 16699.08008 16735.11914 3791.293213
2016/8/12 Same Same 16877.17969 16919.91992 0
2016/8/15 Down Same 16866.89063 16869.56055 -841.5873378
2016/8/16 Down Down 16878.66016 16596.50977 32907.79471
2016/8/17 Down Up 16596.25977 16745.64063 -18526.74993
2016/8/18 Down Down 16649.91016 16486.00977 19162.84077
2016/8/19 Same Same 16558.38086 16545.82031 0


ランダムに予測した場合

では、この結果をもって明日から私のお金を実際に運用しようという気になるかというと、不安で不安でとてもそんなことはできません。
この期間でたまたまいい結果が出ただけで、これから半年の予想はボロボロになるんじゃないの?という思いがどうしても芽生えます。
本当は統計的な検定を行ったほうが良いのでしょうが、現時点では私の知識不足なため検定は後ほど行うことにして、今回はUP/DOWN/SAMEをランダムに予測してバックテストを行った結果と比較してみます。

バックテストの予想部分をランダムな0-2の配列に置き換え損益を計算、という検証を10万回繰り返したところ、平均で-5.7%の損失でした。
10万回の施行のうち、50%の利益を上げられるのは3500回程度(3.5%程度)なので、+190%というようはなんとなく偶然ではないような気がします。

ソースコード

いつも通りGitHubに上げておきます。結構コードに手を入れていて、model.py の NikkeiModel にメインのロジックがほとんど入っています。
pred225/model.py at e1727803d03636699a4825d87dfab6adca7da7d3 · kanoh-k/pred225 · GitHub

eval.py に使い方のサンプルも書いてありますが、大体こんな感じでデータの評価等ができます。

# 現時点までの株価データをダウンロード
stockdata = StockData()
stockdata.download()


# 訓練データ、テストデータの期間を設定
train_start_date = np.datetime64("2012-01-01")
train_end_date = np.datetime64("2015-12-31")
test_start_date = np.datetime64("2016-01-01")
test_end_date = np.datetime64("2016-08-31")


# トレーニングする。YourModelName.ckptというファイルでモデルが保存されます。
model = NikkeiModel([], "YourModelName")
model.prepare_training_data(train_start_date, train_end_date)
model.train()

# モデルの評価。Accuracy/Precisionの評価とバックテストを実行。
model.prepare_test_data(test_start_date, test_end_date)
model.evaluate()
model.backtest()

細かい実装等は、ソースコードを参照していただければと思います。

次回予告

次回は統計的に今回の結果が有意なものなのか検証できたらと考えています。
また、とりあえず今週1週間、少額ながら実際の資金を投入して運用してみようと思っているので、記事中で置いた仮定と現実とはどの程度乖離しているのかなど実際に取引の中で感じたことを紹介できればと思っています。



次回記事:
日経平均が日中どのくらい変動するかをTensorFlowで予測する (今までのまとめ) - 今日も窓辺でプログラム