LoginSignup
0
0

kaggle初学者がJPX東証予想をやってみた

Posted at

以下の過去コンペへの取り組みの備忘録を残そうと思います。
https://www.kaggle.com/competitions/jpx-tokyo-stock-exchange-prediction/overview

コンペ概要

  • 予測対象銘柄(約2000銘柄)から予測された株式の利益を順位付けし、上位200銘柄と下位200銘柄を比較して、その差異を評価する
  • 評価はシャープレシオに基づいて評価され、上位200銘柄が購入、下位200銘柄が空売りとして扱われ翌日売買したと仮定し合計リターンが計算される

コンペの把握

ひとまずEasy to understand the competitionを読んでいき、どういったデータがあるのか理解していきます

前準備

まずstock_pricesテーブルのTargetカラムが翌日と翌々日の終値の変化率という理解で正しいか確認します

翌日と翌々日の終値の変化率
tmpdf["Close_shift1"] = tmpdf["Close"].shift(-1)
tmpdf["Close_shift2"] = tmpdf["Close"].shift(-2)

tmpdf["rate"] = (tmpdf["Close_shift2"] - tmpdf["Close_shift1"]) / tmpdf["Close_shift1"]
tmpdf

上記のコードを実行して得られた値とstock_pricesテーブルのTargetカラムの値が一致したため理解は正しいようです

次に各Targetカラムを参照してrank付を行います

ランキング
tmpdf2["rank"] = tmpdf2["Target"].rank(ascending=False,method="first") -1 
tmpdf2 = tmpdf2.sort_values("rank").reset_index(drop=True)
tmpdf2

ランクの数が小さいほど変化率が正の値として大きいためその銘柄は購入すると利益が出る可能性があり、ランクの数が大きいほど変化率が負の値として大きいため、空売りすると利益が出る可能性があリます

ランク付けした結果から上位200銘柄を抜き出し、1~2の間の数値で重み付をします

linspace()
重み付け用の等差数列を生成
重み付
tmpdf2_top200 = tmpdf2.iloc[:200,:]
weights = np.linspace(start=2, stop=1, num=200)
tmpdf2_top200["weights"] = weights
tmpdf2_top200["calc_weights"] = tmpdf2_top200["Target"] * tmpdf2_top200["weights"]
tmpdf2_top200.head(3)

得られた値を用いてSupを求めます

Sup = \frac{\sum_{i=1}^{200} (r(up_i,t)*linearfunction(2,1)_i)) }{Average(linearfunction(2,1))}
Sup
Sup = tmpdf2_top200["calc_weights"].sum()/np.mean(weights)
Sup

同様の処理を下位銘柄でも行いSdownを求めます

Sup,Sdownから日次スプレッド収益を算出します

daily spread return
daily_spread_return = Sup - Sdown
daily_spread_return

これは毎日計算され、Scoreは日次スプレッド収益の平均を標準偏差で割ったものになります

Score = \frac{Average(R_{day_1-day_x})}{STD(R_{day_1-day_x})}

上記の流れをまとめると以下になります

前準備

import numpy as np
import pandas as pd


def calc_spread_return_sharpe(df: pd.DataFrame, portfolio_size: int = 200, toprank_weight_ratio: float = 2) -> float:
    """
    Args:
        df (pd.DataFrame): predicted results
        portfolio_size (int): # of equities to buy/sell
        toprank_weight_ratio (float): the relative weight of the most highly ranked stock compared to the least.
    Returns:
        (float): sharpe ratio
    """
    def _calc_spread_return_per_day(df, portfolio_size, toprank_weight_ratio):
        """
        Args:
            df (pd.DataFrame): predicted results
            portfolio_size (int): # of equities to buy/sell
            toprank_weight_ratio (float): the relative weight of the most highly ranked stock compared to the least.
        Returns:
            (float): spread return
        """
        assert df['Rank'].min() == 0
        assert df['Rank'].max() == len(df['Rank']) - 1
        weights = np.linspace(start=toprank_weight_ratio, stop=1, num=portfolio_size)
        purchase = (df.sort_values(by='Rank')['Target'][:portfolio_size] * weights).sum() / weights.mean()
        short = (df.sort_values(by='Rank', ascending=False)['Target'][:portfolio_size] * weights).sum() / weights.mean()
        return purchase - short

    buf = df.groupby('Date').apply(_calc_spread_return_per_day, portfolio_size, toprank_weight_ratio)
    sharpe_ratio = buf.mean() / buf.std()
    return sharpe_ratio

2021年だけスコアを計算

日によっては2000銘柄以下の日があるため、2000銘柄ある日だけをピックアップします
結果2000銘柄あるのは2020年の12月23日以降だとわかったので、絞り込みランク表示し、スコアを算出
スコアが高いほど利益が出る

選別
idcount = stock_prices.groupby("Date")["SecuritiesCode"].count().reset_index()
plt.plot(idcount["Date"],idcount["SecuritiesCode"])
idcount.loc[idcount["SecuritiesCode"]==2000,:]
stock_prices2 = stock_prices.loc[stock_prices["Date"]>= "2021-01-01"].reset_index(drop=True)
stock_prices2["Rank"] = stock_prices2.groupby("Date")["Target"].rank(ascending=False,method="first") -1 
stock_prices2["Rank"] =stock_prices2["Rank"].astype("int") # floatだとエラー
score = calc_spread_return_sharpe(stock_prices2, portfolio_size= 200, toprank_weight_ratio= 2)

各フォルダの意味

  • data_specifications
    各ファイルのカラムの意味を定義するファイルがあるフォルダ

  • train_files
    トレーニングデータ

  • Supplemental_files
    最新の株価データ

  • example_test_files
    提出時に提供されるファイル形式の例。Targetは含まれていないので、Closeから自分で計算する

各ファイルについて

  • stcok_prices
    日々の終値やTargetカラムなどのデータがある
  • trades
    前営業週の取引量を集計したもの
  • secondary_stock_prices
    流動性の低い証券データも含まれており、市場全体の評価の参考になる
  • financials
    四半期決算報告書
  • stock_list
    証券コードと会社の名前が載っています

一通りどういうコンペかザックリ分かったので次回はどのような解析をすればいいか学んでいこうと思います

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0