ProbSpaceというデータ分析をしたい人が集まり「金融」や「購買」と言った多彩なテーマのコンペティションで競い合うためのプラットフォームがある。
そのコンペで
「米国株式市場 将来株価予測」
なるものが開催!
されていた。
期間:2021/9/21(火) 0:00 – 12/12(日) 22:00 JST
またかよーーー(泣
「J-Quants データ分析コンペ」終了時に、現在開催中の株価予測のコンペを調査した時には全く見つからなかったぞ(泣!
このコンペには条件があるようだ。
MITライセンス化を優勝条件と定めておりますため、コンペ終了後、モデルを自由にご活用いただいて問題ございません。(優勝された方は、当ルールについてご了承願います)
これが理由なのかデータ分析専門家やシステムトレーダーの方が上位入賞してない印象を受けた。
運ゲーなコンペだったのかもしれないが、あまり掲示板でも議論が活発ではないね。
どちらにせよ私には到底達成し得ない快挙を成し遂げた方々の手法な訳なので、精一杯学ばせてもらおう。
コンペの概要
あらためて「米国株式市場 将来株価予測」の内容を確認してみる。
純粋なテクニカル指標だけを用いたテーブルデータの機械学習コンペだね。
- 米国証券取引所の株価推移データを用いて、1週間後の株価を予測するアルゴリズムを開発する
- 2011/11/13~2019/11/17週の計419週間にわたる米国株データ(終値)をもとに、2019/11/24週の終値を予測する
- 外部データは使用不可
- モデルの予測性能は評価関数RMSLE(Root Mean Squared Logarithmic Error)で評価する
次に上位入賞者の利用した学習モデルと特徴量をまとめておく。
【1位】 stripe氏
趣味でデータサイエンスをやっている方とのこと。
非エンジニアでプログラミングも初心者だとプロフィールには書いてある。
コンペに9件応募してメダルを多数獲得しているのに「初心者」はないでしょ!
過去に初心者テニス大会に何度か参加したけど、テニススクールで中級にも関わらず周りが強すぎで1勝もできたことが無いよ(泣
ソースコードは公開されているがR言語なのでよく分からない。
【使用モデル】
- LighrGBM 単一モデル
【パラメータチューニング】
- ベイズ最適化
主な特徴量
- 年・月・年の始まりからの週数・四半期
- 4・13・26・52週間隔での移動平均・移動標準偏差
- 一定値以上(or以下)の株価へのフラグ付与と、その累積値
- 一定期間における目的変数がプラス(orマイナス)である割合、増減回数
全く読めなかったが、騰落レシオのような計算をしているのかな。
【2位】 shoji9x9氏
AtCoderに主に取り組んでいる方。株式投資もされているようだ。
【使用モデル】
ダウとNASDAQで値動きが違うため、市場毎にモデルを分けて9個のモデルでアンサンブルしている。
- LighrGBM 単一モデル
- 多層パーセプトロン(MLP)
- エクストラツリー(ExtraTree:Extremely Randomized Trees)
【パラメータチューニング】
- Optune
コンペ最終盤は主にハイパーパラメーターチューニングを昼夜問わず行っていました(他にスコア改善する方法も思いつかなかったので)。これによりスコア改善しました
この改善方法は好きじゃないなぁ……。
特徴量
多くのモデルでは特徴量を追加した方がスコアが下がったため、DT-SN氏の特徴量をベースにしているとのこと。
- ラグ特徴量(4週分)
- 上場年
- セクターごとの平均株価
- 業界ごとの平均株価
- 市場ごとの平均株価
- 年、月、日ごとの平均株価
- 年初から何週目か
- 52週単純移動平均、標準偏差
- 指数移動平均乖離率
- ヒストリカルボラティリティ
- リターン
- 歪度(Skewness)
- 尖度(Kurtosis)
- 分散
- 最小値、最大値
- 前週から価格が上昇したか、下落したか
- AverageTrueRange(ATR)
- RSIIndicator(RSI)
- MACD
- BollingerBands(ボリンジャーバンド)
- StochasticOscillator(ストキャスティックス)
- PSARIndicator(パラボリックSAR)
- KSTIndicator(KST オシレーター)
- シャープレシオ
- 標準化
- PCA
- K-Means
その他、qiiaに次のようなこともまとめられている。
- 時系列データは定常性を意識した特徴量設計をすることが重要(株価そのものではなく前週との株価の差を予測する、差分変換や対数差分変換を行い定常性のあるものを予測する)
- 特徴量にセクター平均価格の差などを追加したが、スコアは悪くなった
- LightGBMのモデルが単体では最もスコアがよかった
【3位】 Akahachi氏
プロフィールは不明。
コンペの対象銘柄に「ファンド・投資信託」が含まれており、普通の株ではなく、債券を投資対象にしたファンドがある事を発見されたようで、他の人とは異なるアプローチをとっている。
対象銘柄を株と投資信託・ファンドの2グループに分け、株式市場と債券市場全体の値動きから個別銘柄の値動きを予測
- 推定対象の銘柄には普通の「株」と投資信託・ファンド(以降、ファンド類)が混在している
- ファンド類には債券のみを投資対象にしたものが相当数ある
この観点は日本株でも有効かもしれない。
また、回帰手法の変更や特徴量の追加・変更をするつもりがなかったのでCVもしてないとの事。
【使用モデル】
- LighrGBM 単一モデル
株価があまりに低い銘柄は市場全体の値動きとは無関係に極端な値動きをしがちなため、期間中1度でも株価が1ドルを下回ったことのある銘柄はtrainの対象から外したそうだ。
特徴量
【株】
- 同時点のVTHR(バンガード・ラッセル3000 ETF)の収益率
- 前週のVTHRとの相対収益率(順張り的な投資行動を反映)
- セクター(Sectorにより市場との連動性が違いそう)
- IPO年(特に欠損値に意味がありそう(古参の企業))
【ファンド類】
- 同時点のVTHRの収益率
- 同時点のIEFの収益率
通常追加するようなテクニカル指標が無かった。
【6位】 Baran氏
アメリカ赴任中の研究者であり、活動範囲拡大に向けデータサイエンス挑戦中らしい。
株価の変化率のトレンドが似ている株式をクラスタリングで取得
【使用モデル】
- LighrGBM
【パラメータチューニング】
- Optune
特徴量
統計処理などで目的関数や説明変数を作成したとのこと
- 各株式のラグ特徴量
- 移動平均値
- セクタのラベルエンコーディング
- 産業のラベルエンコーディング
- 歪度(Skewness)
- 尖度(Kurtosis)
- 年、月、日、週数
- 平均化
- 標準化(Standardization)
- 正規化(Normalization)
- 差分最大値
- 差分最小値
- 差分の上位25%値
- 差分の上位75%値
- 差分ミディアム値
これらが株価予測にどのように効果があるのか不明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
df['Year'] = df['Week'].map(train_date).dt.year df['Month'] = df['Week'].map(train_date).dt.month df['Day'] = df['Week'].map(train_date).dt.day df['WeekOfYear'] = df['Week'].map(train_date).dt.isocalendar().week.astype(int) df["y_mean"] = df[['id', "y"]].groupby(['id'])["y"].transform(lambda x: x.mean()) df["y_std"] = df[['id', "y"]].groupby(['id'])["y"].transform(lambda x: x.std(ddof=0)) df["y_norm"] = (df["y"] - df["y_mean"]) / df["y_std"] df["y_norm_all"] = (df["y"] - df["y"].mean()) / df["y"].std(ddof=0) df["y_diff"] = df[['id', "y"]].groupby(['id'])["y"].transform(lambda x: x.diff(1)) df["y_diff_norm_all"] = (df["y_diff"] - df["y_diff"].mean()) / df["y_diff"].std(ddof=0) val_col = "y_diff" df['y_diff_max'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.max()) df['y_diff_min'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.min()) df['y_diff_median'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.median()) df['y_diff_0.75'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.quantile(0.75)) df['y_diff_0.25'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.quantile(0.25)) df['y_diff_kurt'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.kurt()) df['y_diff_skew'] = df[["id", val_col]].groupby(['id'])[val_col].transform(lambda x: x.skew()) |
まとめ
今まで扱ったことが無い特徴量が使われており、試したいものが幾つかあった。
今年はTwitter上で流れているコンペ情報ぐらいは拾えるようにしておこう。
……と書いた矢先に見つけた。
Kaggleで 中国のヘッジファンド 九坤投資(Ubiquant)による「Market 予測」コンペが開催中。
私のノウハウじゃ通用しないだろうが、データ落としてDiscussionぐらいは見ておこう。