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

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

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

## コンペの把握
ひとまず[Easy to understand the competition](https://www.kaggle.com/code/hidekazukonishi/easy-to-understand-the-competition/edit
)を読んでいき、どういったデータがあるのか理解していきます

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

```ruby:翌日と翌々日の終値の変化率
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付を行います
~~~ruby:ランキング
tmpdf2["rank"] = tmpdf2["Target"].rank(ascending=False,method="first") -1 
tmpdf2 = tmpdf2.sort_values("rank").reset_index(drop=True)
tmpdf2
~~~
ランクの数が小さいほど変化率が正の値として大きいためその銘柄は購入すると利益が出る可能性があり、ランクの数が大きいほど変化率が負の値として大きいため、空売りすると利益が出る可能性があリます

ランク付けした結果から上位200銘柄を抜き出し、1~2の間の数値で重み付をします
<dl>
<dt>linspace()</dt>
<dd>重み付け用の等差数列を生成</dd>
</dl>

~~~ruby:重み付
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を求めます
~~~math
Sup = \frac{\sum_{i=1}^{200} (r(up_i,t)*linearfunction(2,1)_i)) }{Average(linearfunction(2,1))}
~~~
~~~ruby:Sup
Sup = tmpdf2_top200["calc_weights"].sum()/np.mean(weights)
Sup
~~~

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

Sup,Sdownから日次スプレッド収益を算出します
~~~~ruby:daily spread return
daily_spread_return = Sup - Sdown
daily_spread_return
~~~~

これは毎日計算され、Scoreは日次スプレッド収益の平均を標準偏差で割ったものになります
~~~~math
Score = \frac{Average(R_{day_1-day_x})}{STD(R_{day_1-day_x})}
~~~~

上記の流れをまとめると以下になります
~~~ruby:前準備

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日以降だとわかったので、絞り込みランク表示し、スコアを算出
スコアが高いほど利益が出る

~~~~ruby:選別
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
証券コードと会社の名前が載っています

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