株式自動売買プログラムを実践に投入すると精度が急落する謎現象の原因究明に奮闘していた話。

目次。

 

はじめに。

株式自動売買プログラム開発関連の文章は、以下のカテゴリーにまとめているので、興味のある方はどうぞ。

blog.sun-ek2.com

 

株式売買に必要な処理(売買注文発注とか)を全部自動化するコード、ディープラーニングを使って株価を予測するコードを一通り書き終えたので、開発したプログラムを実践に投下してみた。

blog.sun-ek2.com

 

検証段階では上手くいったから、きっと実践でも上手くいくはず!…なんて淡い期待を抱いていたが、実際に動かしてみると、どんどん損失が膨らんでいった。

 

これは、ヤバいと思い、プログラムを停止させ、保有している建玉を全て返済し、上手くいかなかった原因の究明を行ったというのが今回の話。

 

僕の本職は、生物系の実験屋さんなので、原因追及は実験の合間に行っていた。データ解析などといった泥臭い作業をずっと行っていたが、なかなか原因が分からず、気づけば1か月半もの時間が過ぎてしまった。。。

 

LSTM関連の文章から誘導されて、この文章を読んでいる人もいるので、本題に入る前に、これまでのあらすじを軽くまとめようと思う。

 

 

 

 

これまでのあらすじ。

一から完全自作で株式自動売買プログラム開発がしたいと思うようになる。

詳しく知りたい人は、以下の文章をどうぞ。

blog.sun-ek2.com

 

この文章は、2019年に書いたが、自動売買プログラムの開発を行いたいと思い経った時期は、2019年ではなく、大学受験期の頃。当時は、株式ではなく、FXをターゲットにしていた。

 

高校の頃は、Javaという言語を使ってプログラムを書いていた。

blog.sun-ek2.com

 

株式自動売買プログラムも元々、Javaで開発していたが、今はPythonで書いている。

blog.sun-ek2.com

 

Pythonは「ハッカーになろう(How To Become A Hacker)」という文章がきっかけで、高校の頃に独学した。

(「ハッカーになろう(How To Become A Hacker)」は、僕がものすごく影響を受けた文章の1つ)

cruel.org

 

 

株式売買に必要な処理(ログイン、売買注文の発注など)の全自動化。

株をやっている人なら「システムトレード(シストレ)」という言葉をよく聞くことがあると思う。僕の開発しているプログラムをシストレソフトだという人がたまにいるが、一般に出回っているものと僕が開発しているものはちょっと違う。

 

大きな違いは、株式売買に必要な処理も全自動化されているということ。シストレソフトは、売買シグナルは出してくれるが、代わりに売買注文は出してくれない。僕のプログラムは、自動で売買注文を出してくれるので、一日100件、1000件もの売買注文を楽々出すことができる。

 

詳しいやり方は、以下の文章で説明しているので、よければどうぞ。売買注文発注の裏側で、証券会社と僕のパソコンがどういった通信を行っているか調べて、それを勝手にやるプログラムを組んでいるだけ。

blog.sun-ek2.com

blog.sun-ek2.com

 

 

短期・長期移動平均線を使った売買アルゴリズムの開発を始める。

短期・長期移動平均線を使って、株式の売買タイミングを判断するというのは、株式売買の入門書には必ず載っている方法だと思う。

 

僕が買った株式売買の本にももちろん書いてあったので、とりあえず移動平均線から始めることにした。

 

詳しくは以下の文章をどうぞ。

blog.sun-ek2.com

 

 

ディープラーニングを使った売買アルゴリズムの開発を始める。

移動平均線を使ったアルゴリズムの開発をしていたが、なかなか上手くはいかなかった。それに加えて、株式売買やFX以外の分野で応用することができない移動平均線に膨大な時間を費やすことに疑問を持つようになった。

 

「移動平均線って僕の研究者としてのキャリアに全く応用できないのでは?そんなものに時間をかける意味はないのでは?」

 

その後、量子コンピュータ上で動く量子ディープラーニングの論文をいくつか読んだ。これがきっかけで、ディープラーニングを使ったアルゴリズムを開発したいと思うようになった。

blog.sun-ek2.com

 

移動平均線を他の分野に応用することはできないが、ディープラーニングなら他の分野に応用することができる。

 

ディープラーニングを使ったアルゴリズムを開発する理由は、他にもたくさんあるが、話が長くなりそうなので割愛。

blog.sun-ek2.com

blog.sun-ek2.com

blog.sun-ek2.com

blog.sun-ek2.com

 

 

開発したプログラムを実際の株式売買に導入。

ディープラーニングを使った売買アルゴリズムが書けたので、実際に開発したプログラムを使って売買を始めた。

 

株式売買に必要な処理(ログイン、売買注文の発注など)の全自動化は、現物取引でしか行っておらず、信用取引用のコードは書いていなかった。そのため、手動で株式売買を行う傍ら、徐々に手動部分を自動化していった。

blog.sun-ek2.com

 

 

あれ?どんどんと損失が…。

開発した株式自動売買プログラムを稼働させて、少し経った頃、何かがおかしいと思うようになった。検証段階と比べて、明らかに勝率が低い。

 

たまたまかな…と思い、もうしばらく開発したプログラムを稼働させていると、やっぱり何かがおかしいと思うようになった。

 

しかし、おかしいと思っているにも関わらず、原因究明の傍ら、プログラムを止めることなく動かし続けていた。僕は、金銭的に大きな問題を抱えているので、2・3年以内にきちんとした経済的な基盤を確保しておかないと、人生が詰んでしまうと思っていて、とにかく焦っていた。

 

さらにプログラムを動かし続けていると、損失がどんどん膨らんでいった。

 

これは流石にヤバいなと思い、ここでようやくプログラムを止め、株式の建玉を全て返済し、実践に投入すると予測精度が急落する問題の解明に本格的に取りかかった。

 

株式自動売買プログラム稼働から停止までの期間は、約2か月半。

その間、約2,800万円分の株式を買建・売建し、、、そして、約8万6千円分の損失を出してしまった。

 

 

 

 

原因部分:正規化処理(min-max normalization)を行うコード。

原因は、min-max normalizationで正規化処理を行っている部分。入力データを \displaystyle \mathbf{x}、正解データをyとすると、次のように株価データを正規化していた。

 \displaystyle \mathbf{x^{'}}=\frac{\mathbf{x}-min\left(\left[\mathbf{x};y\right]\right)}{max\left(\left[\mathbf{x};y\right]\right)-min\left(\left[\mathbf{x};y\right]\right)}

 \displaystyle y^{'}=\frac{y-min\left(\left[\mathbf{x};y\right]\right)}{max\left(\left[\mathbf{x};y\right]\right)-min\left(\left[\mathbf{x};y\right]\right)}

 

本来は、 \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right)にしなければいけないところを \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)にしていた。正しくは、以下の通り。

 

 \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right) \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)とすると、どういう不都合が起こるのか?簡単な例を1つ挙げる。

 

window size = 5、 \displaystyle \mathbf{x}=\left(100, 200, 100, 200, 100\right)、y = 500だとする。そうすると、 \displaystyle max\left(\left[\mathbf{x}\right]\right) = 200、 \displaystyle min\left(\left[\mathbf{x}\right]\right) = 100。一方で、 \displaystyle max\left(\left[\mathbf{x};y\right]\right) = 500、 \displaystyle min\left(\left[\mathbf{x};y\right]\right) = 100となる。

 

 \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right)で正規化を行うと、 \displaystyle \mathbf{x^{'}}=\left(0,1,0,1,0\right) \displaystyle y^{'}=4

 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化を行うと、 \displaystyle \mathbf{x^{'}}=\left(0,0.25,0,0.25,0\right) \displaystyle y^{'}=1

 

 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化を行うと、正規化した \displaystyle \mathbf{x}の情報から容易にyの値を推定することができてしまう。min-max normalizationされたデータは0から1の間を取り、かつ0と1はmin、maxとして必ず存在しているので、 \displaystyle \mathbf{x^{'}}=\left(0,0.25,0,0.25,0\right)のように正規化された \displaystyle \mathbf{x^{'}}に1が含まれていなければ、容易に \displaystyle y^{'}が1であることが分かってしまう。

 

つまり、 \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right) \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)にすることによって、学習時、ニューラルネットワークに \displaystyle \mathbf{x}の情報しか渡していなくても、正規化処理によって、無意識のうちにyの情報も渡していたのである。

 

検証段階では、 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化を行い、実際に稼働するとき、つまりyのデータがない場合は、 \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right)で正規化を行っていた。

…これが検証段階と実践段階で予測精度が大きく異なっていた原因。

 

 

『【株式自動売買×ディープラーニング】LSTMで日経平均株価予測を行うプログラムを書いてみた話。』の再考。

blog.sun-ek2.com

 

上記の文章内にソースコード(プログラムコード)を掲載している。しかし、問題の正規化を行うコードはそもそも割愛している。そのため、掲載しているソースコード自体には、何も問題はないと思う。

 

 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化した結果と言う意味で、上記の文章に載せた結果は正しい。しかし \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化した検証段階の結果がいくらよかったとしてもそれは全く役に立たない。実際の株価予測では、yの値は分からないからである。つまり、 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化をした結果がよければ、株価予測の結果もよくなるというのは正しくない。

 

また繰り返しになるが、学習時に明示的に正解データyをニューラルネットワークに与えなかったとしても、 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)を用いる正規化処理によって、間接的に正解データyをネットワークに与えてしまっている。上記の文章で紹介したプログラムの予測結果が良かった原因は、暗示的な正解データの提示によるものではないかと思う。

 

上記の文章には、1営業日後の株価の予測に加えて、5、20、60、120営業日後の株価予測の結果も載せていた。

 

window sizeをwとすると、120営業日後予測をする場合、

 \displaystyle max\left(\left[\mathbf{x_{1}}, ...,\mathbf{x_{w}}, \mathbf{x_{w+1}}, ...,\mathbf{x_{w+119}};y\left(=\mathbf{x_{w+120}}\right)\right]\right)

 \displaystyle min\left(\left[\mathbf{x_{1}}, ...,\mathbf{x_{w}}, \mathbf{x_{w+1}}, ...,\mathbf{x_{w+119}};y\left(=\mathbf{x_{w+120}}\right)\right]\right)

を使って、正規化している。

 

新型コロナウイルスによる大暴落を120営業日前から予測できたと書いたが、それは正規化をしている段階で120営業日後yの情報が暗示的にネットワークの入力に加わった状態で学習が進んだことが原因だと思う。

 

コロナで株価が24,000円から17,000円くらいになった。

入力データ \displaystyle \mathbf{x}=\left(24000, 24000, 24000, 24000, 24000\right)が天井で、120営業日後に最安値y = 17000を記録していたとする。

 

これを正規化すると、 \displaystyle \mathbf{x^{'}}=\left(1,1,1,1,1\right) \displaystyle y^{'}=0。min-max normalizationしたデータは、0から1の間の値を取り、データには必ず0と1が含まれる。入力データが \displaystyle \mathbf{x^{'}}=\left(1,1,1,1,1\right)であれば、 \displaystyle y^{'}が0であることは容易に推定できてしまう。

 

 

『【株式自動売買×ディープラーニング】LSTMで上場企業、約4000社の株価予測をしてみた話。』の考察。

blog.sun-ek2.com

 

こちらも同様に文章内にソースコード(プログラムコード)を掲載している。しかし、問題の正規化を行うコードはそもそも割愛している。そのため、掲載しているソースコード自体には、何も問題はないと思う。

 

アルゴリズム自体は、『【株式自動売買×ディープラーニング】LSTMで日経平均株価予測を行うプログラムを書いてみた話。』で紹介したものと全く同じ。

 

上記の文章内で掲載している結果は、データを \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化して得られる結果としては正しい。しかし実際の株価予測ではyが分からないため、この結果は株価の予測精度を表しているものではない。

 

実際の株価予測のように \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right)で正規化を行い、検証を行うと、成功確率は平均で約52%だった…。うーん。

 

 

 

 

原因究明への道のり。

最初に言った通り、株式自動売買プログラムを実践に投入すると精度が急落する原因の究明に約1か月半かかった。

 

最初は、株式自動売買プログラムを動かして、株式売買をする傍ら、原因究明を行っていた。しかし損失が膨らんできたので、原因究明に集中することにした。おかしいと思った瞬間から、プログラムを止めて、原因究明に集中しておけば、もっと早く原因が特定できたし、損失も膨らむことがなかったのに…。

 

原因究明に時間がかかったのは、正規化処理(min-max normalization)が原因であると全く思っていなかったから。ずっと、別のところを調べていた。

 

 

検証用のソースコードと実践用のソースコードがなんか違うのでは?

株価予測の心臓部であるニューラルネットワーク部分は、検証用と実践用で同じであるが、それ以外は微妙に違う。例えば、検証用は入力として使う株価データを僕のパソコンのディレクトリから取ってくる。一方で、実践用は入力として使うデータを証券会社のサーバーから取ってくる。

 

まずは、「ニューラルネットワークの入力データを準備するコードにバグがあって、実践用の入力が変化してしまったのではないか?」、「ニューラルネットワークから出力されたデータを使いやすいように加工するコードにバグがあって、実践用の最終出力が変化してしまったのではないか?」といった予想を検証していった。

 

しかし、入力データを用意するコード、出力データから最終出力を生成するコードには、バグは見つからなかった。

 

 

予測データを解析し、モデルの性能を評価するコードが間違えているのでは?

作ったモデルがどれだけの性能を有しているのか?例えば、株価の上昇・下落予想の正解率はどのくらいか?予測値と真値の誤差はどのくらいか?このモデルを走らせると、どのくらい利益がでるのか?

…といったことを知るためには、モデルが出力した予測値を解析するためのプログラムが必要。

 

今度は、このプログラムにバグがあるのではないかと疑い始めた。

 

「モデルを評価するプログラムにバグがあって、正解率などが水増しされているのではないか?」と思い、実践用のプログラムで数日間、株価予測を行い、得られたデータを評価用のプログラムに加えてみた。その結果、Excelを使って、手動で算出した正解率と大きく乖離していなかったので、評価プログラムのバグという可能性が消えた。

 

 

リアルタイムで取ってきている株価データがおかしいのでは?

可能性は、かなり低いが、一応、疑ってみた。株価データは、リアルタイムで証券会社のサーバーから取ってきている。「証券会社から取ってきた株価データと東証の株価データが完全にシンクロしておらず、証券会社からデータを取ってくるタイミング(証券会社のサーバーのメンテナンス前後とか)によって、入力データが変化するのでは?」という予想を立ててみた。

 

もちろん、東証が開いているときには、刻々と株価データは変化している。そのため、例えば10月2日の株価データを10月3日のメンテナンス前後に取ってきた場合と10月4日のメンテナンスからしばらく経ったときに取ってきた場合で差があるかどうか調べてみた。結果は、差はなかった。当たり前と言えば、当たり前であるが。

 

 

ニューラルネットワークの重みの違いによって結果が大きく変わるのでは?

ニューラルネットワークの学習は、確率的再急降下法によって行われる。全体からランダムにデータが選ばれて、バッチが形成される。その後、バッチをニューラルネットワークに入力し、出力から損失関数を計算する。その後、その損失関数が小さくなるように重みを更新する。

 

バッチは、ランダムに作られるため、ニューラルネットワークの重みは、エポック数、隠れ層サイズといったハイパーパラメータが完全に一緒だったとしても学習ごとに異なる。

 

「検証時に使った重みと実践時に使った重みは違っていた。検証時には上手く学習できたときの重みを、実践時には上手く学習できなかったときの重みを使っていたのかも?」という予想を立ててみた。

 

これを検証するためにn=3でデータを取ってみた。ニューラルネットワークのハイパーパラメータはすべて一緒であるが、重みはそれぞれ異なる。

 

重みが異なると、予測結果はかなり違ってはいたが、正答率などは大して変わらなかった。そのため、その仮説も正しくなさそう。

 

 

 

 

僕のパソコンのディレクトリ、証券会社のサーバーからそれぞれ株価データを取ってきて検証用のプログラムで予測したらどうなる?

例えば、僕のパソコンのディレクトリから取ってきた10月1日から15日までの株価データで10月6日から10月16日までの株価を検証用のプログラムで予測したものと、証券会社のサーバーから取ってきた10月1日から15日までの株価データで10月6日から10月16日までの株価を検証用のプログラムで予測したものに違いがあるのか調べてみた。

 

ある仮説があって、その検証のためにやったのではない。ただ何となく、両者を比べてみることにした。

 

両者を比べてみると、驚きの結果が得られた。ほとんど予測値は一緒なのだが、何故か10月16日の予測値が両者で違う…。

 

ここを掘り下げれば、何か分かるのでは?

 

 

最新日だけ予測値に差異があるので、もしや…正規化処理(min-max normalization)が原因?

最新日だけ予測値が異なることが判明して、ようやく正規化処理(min-max normalization)が原因ではないかと疑いを持ち始めた。データの一部、それも境界(先ほどの例えでいうと、10月16日のこと。16日以降の正解データはない)にあたるデータが違うとなれば、もうここしかない。

 

正規化処理(min-max normalization)を見ると、10月16日以外を予測するときの入力データは、 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right)で正規化されていて、10月16日を予測するときの入力データは、 \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right)で正規化されていることが分かった。

 

これが原因。

 

 

 

 

今後。

 \displaystyle max\left(\left[\mathbf{x};y\right]\right) \displaystyle min\left(\left[\mathbf{x};y\right]\right) \displaystyle max\left(\left[\mathbf{x}\right]\right) \displaystyle min\left(\left[\mathbf{x}\right]\right)に変えて、min-max normalizationを行い、株価予測をすると、正答率が急激に落ちた。ハイパーパーパラメータをいじったり、Seq2Seqを採用したりしてみたが、全銘柄の平均正答率は最大52%ぐらい。どうしたものか…。

 

けれども、「検証時には高い正答率を出していた株式自動売買プログラムを実践に投入するとなぜか精度が急落する問題」は解決できたので、開発は大きく一歩前に進んだのかなって思う。今まで、精度が急落する理由がずっと謎であった。喩えるなら、ずっと霧の中を進んでいるような感じであった。

 

ようやく、霧が晴れた。現状のモデルの精度は低いが、霧が晴れ、周りを見渡すことができるようになったので、ガンガン開発して、精度が高いモデルを作りたいと思う。

 

次の手は、、、

attention-based recurrent neural network!!

 

 

この文章を読んで、面白い!役に立った!...と思った分だけ、投げ銭していただけると嬉しいです。もちろん任意です。

ofuse.me

 

ブログランキング・にほんブログ村へ
にほんブログ村