こんにちは!Insight Edge データサイエンティストの伊達です。
昨年9月より、Kaggleにて株価予測コンペ「Optiver - Trading at the Close」が開催され、4000以上のチームが参加する盛り上がりを見せました。 私は参加できませんでしたが、Insight Edgeの分析プロジェクトでも時系列データを扱う場面があるため、今回の記事では、コンペ終了後に共有された上位解法から得られた学びを紹介したいと思います。
今回の記事の執筆にあたって、コンペで共有されたディスカッションやノートブックだけでなく、国内の金融データサイエンスコミュニティであるマケデコ(Market API Developer Community)様が今年5月に開催された「OptiverコンペKaggle上位解法勉強会」での発表内容も参考にさせて頂きました。
目次
Optiverコンペについて
今回のコンペの課題は、アメリカの代表的な株式市場の1つであるNASDAQにおいて、15時50分〜16時(取引終了)という最後の10分間で、各銘柄の株価がどう動くかを予測するというものです。
なぜ最後の10分間の株価予測なのか?その背景に、近年、米国株市場において取引終了直前の短時間での売買量が増加しており、市場に与える影響が大きくなっていることがあるようです。
米国株の取引時間は通常390分だが、ここにきて最後の10分間のみが相場にとって重要になりつつあるかもしれない。 アルゴリズム取引を開発するベストExリサーチがまとめたデータによると、S&P500種株価指数採用銘柄の株式売買では、全体の約3分の1が最後の10分間に執行されている。この割合は2021年の27%から上昇している。
また、NASDAQでは取引方式としてマーケットメイク方式が採用されており、オークション方式のように投資家同士が直接売買するのではなく、NASDAQが認可したマーケットメーカーと呼ばれる業者が投資家の売買注文を成立させます。
今回のコンペの主催者であるOptiverはNASDAQにおけるマーケットメーカーであり、取引が集中する「最後の10分間」で各銘柄の株価がどう動くかを予測することはOptiverにとって非常に重要なタスクです。
問題設定
今回のコンペのターゲットは「各銘柄の株価が60秒後にどう動くか」です。 より正確に書くと、「各銘柄の加重平均価格(WAP)の60秒後のリターン」から「インデックスの加重平均価格の60秒後のリターン」を引いた値がターゲットとなっています:
$$(\frac{\text{StockWAP}_{t+60}}{\text{StockWAP}_{t}} - \frac{\text{IndexWAP}_{t+60}}{\text{IndexWAP}_{t}}) * 10000$$
- 加重平均価格(WAP):板における買い・売り値の加重平均
$$\frac{\text{買い値} * \text{売り数量} + \text{売り値} * \text{買い数量}}{\text{買い数量} + \text{売り数量}}$$
- インデックス:日経平均のように、NASDAQを構成する各銘柄の株価を加重平均した値
評価指標としては、平均絶対誤差(MAE)が採用されていました。
データ期間と評価
今回のコンペの課題は時系列予測であるため、各チームは締切までにノートブックを提出し、Kaggle側が各チームの提出したノートブックと評価期間データ(サブミッション締切より後のデータ)を用いて精度を評価する「Code Competition」となっていました。
データ期間の長さは下記の通りになっていたようです:
- 学習用:96週分
- Publicリーダーボード評価用:9週分
- 評価に使われないデータ:7週分
- Privateリーダーボード評価用:6週分
データ項目
今回のコンペで提供されたデータは下記の通りです:
- 銘柄ID
- 日付ID
- クロージング・オークション開始(15:50)から経過した秒数(
seconds_in_bucket
) - ターゲット
- 価格系データ
bid/ask_price
:最良気配(最も高い買値、最も安い売値)wap
:買値・売値の加重平均価格far_price
:オークション注文板(auction order book)で取引量が最大となる約定価格。板情報(order book)は考慮されない。near_price
:オークション注文板と板情報を合わせた時に取引量が最大となる約定価格reference_price
:最良気配の範囲(最も安い売値〜最も高い買値)に制限されたnear_price
- サイズ・インバランス系データ
bid/ask_size
:板情報において最良気配の買い/売り注文量imbalance_size
:reference_price
で約定した時、約定しない注文量imbalance_buy_sell
:オークションのインバランスの方向(1: 買い側、-1: 売り側、0:インバランスなし)matched_size
:reference_price
で約定した時、約定する注文量
Optiverコンペの上位解法まとめ
上位解法一覧
本記事執筆時点で公開されている上位解法をまとめます。 今回は、特徴量、モデル構成、再学習の有無、後処理、バリデーション戦略という5つの観点で各解法を整理しました。
1位: hydさん(スコア: 5.4030)
- 特徴量
- よく使われる特徴量に加え、「秒群」を使ったグループ集計(後述)による特徴量を使用
- 変数選択:CatBoostの重要度で上位300個
- モデル構成:CatBoost + GRU + Transformer
- 再学習(オンライン学習):あり
- 後処理:各銘柄の予測値の加重平均を引く
- バリデーション戦略:時系列分割(学習期間最後の81日分でバリデーション)
- その他工夫
- polars使用によるデータ処理効率化?
- 学習データを日付ごとに保存しておくことで、推論時のメモリ制約の中で使える特徴量の数をできるだけ増やした
6位: Daniel FGさん(スコア: 5.4285)
- 特徴量:よく使われる特徴量40個弱
- モデル:Transformer(少し設定を変えた3個)+ GRU
- 再学習(オンライン学習):あり
- 後処理:一つのモデルを除き、予測値の合計が0になる制約を加えて学習させた
- バリデーション戦略:時系列分割(学習期間最後の121日分でバリデーション)
7位: NimaShahbaziさん(スコア: 5.4300)
- 特徴量:よく使われる特徴量に加え、価格やサイズデータが日付/秒軸で計算した中央値からどれだけ乖離しているか("deviation features")を使用
- モデル:LightGBM + LSTM + CNN
- 再学習(オンライン学習):あり
- 後処理:なし?
- バリデーション戦略:時系列分割
9位: ADAM.さん(スコア: 5.4352)
- 特徴量
- よく使われる特徴量に加え、テクニカル指標(MACD)などを使用
- 変数選択:特徴量を10〜30個ずつグループ分けし、各グループを追加したときにバリデーション結果が良くなるか確認。グループ単位の結果が良かったら、その中で一つずつ追加して同様に結果を確認し、グループ内で5〜10個程度のみ採用。
- モデル:XGBoost 3個
- 学習期間最後の45日間のデータの重みを増やした
- 再学習(オンライン学習):あり
- 後処理:各銘柄の予測値の加重平均を引く
- バリデーション戦略:時系列分割
- その他工夫
- polars使用
- pandas dataframeのメモリ使用量を減らす
ポイントになったと思われる技術
特徴量
ほとんどの上位解法の特徴量エンジニアリングは、こちらのノートブック で紹介されているような基本的なものと大きく変わらない印象です。
1位解法 では"magic features"として、「秒群」を使ったグループ集計による特徴量が紹介されていました。
これは、クロージング・オークション開始(15:50)から経過した秒数が、
- 0〜290秒であればグループ0
- 300〜470秒であればグループ1
- 480〜540秒であればグループ2
として、各銘柄について、同じグループ内の最初の秒の値・移動平均値との比率を計算するものだそうです。
グループの区切り方については、公式のチュートリアル にもあるように、
- 15:55(300秒):NASDAQからclosing informationの公表が開始
- 15:58(480秒):Limit-On-Close (LOC) 注文の受付停止
というドメイン知識が使われたのだと思われます。
また、1位解法では、同じ日・秒について、銘柄軸で計算された平均値との比率やランクも使われていたそうです。 1位解法の"magic features"についての説明は、マケデコ勉強会でのnishimotoさんの発表 が分かりやすかったので、おすすめです。
モデル
モデルに関して、上位解法では
- GBDTとニューラルネット(1位、7位)
- GBDTのみ(9位)
- ニューラルネットのみ(6位)
の3パターンになっていました。
興味深かったのは、1位解法が、CatBoostに加えて、GRUを時系列軸のモデリング(入力シェイプ:バッチサイズ × 55時点 × 隠れ層サイズ)に、Transformerを銘柄軸(クロスセクション)のモデリング(入力シェイプ:バッチサイズ × 200銘柄 × 隠れ層サイズ)に使うことで、アンサンブル全体でのモデル多様性を持たせていたことです。 コードコンペの制約があり、使用できるモデル数や特徴量が限られる中で、モデルの多様性を最大限広げている辺り、さすが1位だなという印象です。
再学習(オンライン学習)
今回のコンペでの評価はCode Competition形式で、評価期間の各時点において、それより前の時点のターゲットの値が随時提供される形になっていました。 つまり各参加者は、提出するノートブックの中で、随時提供される評価期間中のデータを使ったモデルの再学習(オンライン学習)を導入するチャンスがありました。 上記の上位解法の全てがこの再学習を使用しており、上位入賞の鍵になった要素と考えられます。
後処理
今回のコンペのターゲットは、
「各銘柄の加重平均価格(WAP)の60秒後のリターン」から「インデックスの加重平均価格の60秒後のリターン」を引いた値
でした。
ターゲットの計算で使われるインデックスは今回のコンペ用にOptiverによって作成されたもので、公式からはインデックスがどう構成されているか説明されていませんでした。 しかし、有志によりインデックスの計算方法が推測 されており、今回のインデックスは各銘柄の株価ではなく60秒後リターンの加重平均になっていたようで、インデックス構成用の各銘柄の重みが公開されていました。
今回の1・9・15位の解法ではこの知見と重みを用いて、モデルが出力した各銘柄の予測値から、推測された重みを使った予測値の加重平均を引くという後処理が使われていました。 これは、各銘柄でインデックスのリターンを引いているため、ある時点での全銘柄の予測値を合計すると0になるという知識を使ったものです。
上記以外のアプローチとして、6位と14位の解法では、単純に各時点で全銘柄の予測値の合計を引くという処理を入れていたようです。
バリデーション戦略
今回確認した上位解法の全てが、バリデーション戦略として、学習期間の最後の部分を検証用データとする時系列分割(time-series split)を採用していました。 Public LB・Private LBともに、評価用データは学習期間よりも後の時期のデータとなっており、CVのようなランダム分割ではなく、より新しいデータでバリデーションすることで評価期間での精度を正確に推測することが重要だったようです。
他の解法ではCVを採用したものもあり、今回のようなタスクではCVを使うか、時系列分割を使うか、あるいは CPCV などのように、より複雑なものを使うか、判断が難しかったのではないかと思われます。
まとめ
上位解法の内容から、今回のコンペのキーになったと考えられるのは下記のポイントです:
- 特徴量:ドメイン知識に基づく特徴量エンジニアリング、実行制約がある中で有効な特徴量をできるだけ多く使うための実装上の工夫
- モデルの多様性:GBDT + GRU/CNN(時系列方向のパターンを捉える)+ Transformer(クロスセクション:銘柄間の関係性を捉える)
- 再学習(オンライン学習):非定常なタスクにおいて、新たに手に入るターゲットデータを使ってモデルを更新
- 後処理:ドメイン知識に基づく予測値の後処理
- バリデーション戦略:非定常なタスクにおいて、評価期間に近い期間のデータを使ったバリデーション
今回のように4000チーム以上が参加するような大きなコンペでは、特徴量、モデル、推論時に手に入るターゲットを使った再学習、予測値の後処理、バリデーション戦略と、全方位で高いクオリティの解法を作り上げる必要があるのだなと感じました。
今回確認した上位解法の全てがソロ参加のチームによるもので、3名がGrandmaster、1名が Masterの称号を獲得されているようです。 さらに、1位を獲得した hydさん は、本記事執筆時点でコンペ部門のランキングが世界1位となっています。 強者があらゆる創意工夫を尽くし、上位を争ったという意味で、非常に良いコンペだったと思いますし、自分にとっても上位解法の知見は学びが多かったです。
次回金融系のコンペが開催されたら、ぜひ次こそ自分も参加してみたいと思います。