[Ubiquant Market Prediction]investment_idの数によるレジーム推定

こんにちは、tmrtjと申します。

私は現在株式会社音圧爆上げくんという会社に所属しており、プロKagglerとして活動しています。


私は現在プロKagglerとして、業務の一環で、現在Kaggleで行われているコンペの一つである[Ubiquant Market Prediction](https://www.kaggle.com/c/ubiquant-market-prediction)に参加しています。



また、Kaggle上にNoteBookとして同じ内容の記事を英語で投稿しています。
リンクは[こちら](https://www.kaggle.com/tmrtj9999/number-of-investment-id)です。

# はじめに
金融市場のデータ分析においてよく使われる用語として「レジーム」というものがあります。レジームとは相場環境のことを指します。一般に、相場環境は時期によって異なることが知られており、上昇トレンドが継続している相場、あまり変動のない相場などに分けられます。このレジームがわかると、強気相場であれば普段の相場環境よりも上昇しやすいなどの推測ができ、予測に役立ちます。

相場のレジームを推定する方法は種々ありますが、今回は各時間ごとの銘柄数を用いるアイデアを紹介します。


<br>
<br>

これは```investment_id```を```time_id```ごとに合計したものを時系列順にグラフにしたものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2465043/ebde236a-7b6e-2acc-d44c-979b8f55cc3d.png)

グラフを見ると```time_id```ごとにかなり数が違うことがわかります。



推測でしかありませんが、投資対象は実際の運用を想定して一定のルールのもとに決められているのではないかと考えられます。（すなわち、ホスト側が意図的に与えられる銘柄数を絞ったり増やしたりはしていないということです。）

なぜ銘柄数が異なるのかは色々な理由が考えられます。売買の停止や再会があるから、IPOがあるから、流動性が高まって投資ユニバースに加わったから、など。

この推測が正しいのであれば、提供される銘柄の数は相場環境の変化を反映しているということになります。

以上のような仮説に基づき、特徴量として```investment_id```の数を加えることをアイディアとして考えました。


# 実装

まずはデータを読み込んでDataFrameにします。
```num_investment.parquet```は与えられているtrainデータに、['num_investment']として、それぞれの```time_id```ごとの```investment_id```の数を特徴量として加えたものです。
データは[こちら](https://www.kaggle.com/tmrtj9999/number-of-investment-id/data)にあります。

```
import os
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
%matplotlib inline

import warnings
warnings.filterwarnings('ignore')

import lightgbm as lgbm
from lightgbm import *

df = pd.read_parquet('../input/num-investment/num_investment.parquet')
```
<br>
<br>
DataFrameをtrainデータとvalデータに分割します。

```
from sklearn.model_selection import KFold, train_test_split


features = [f'f_{i}' for i in range(300)] + ['num_investment']
target = 'target'
 

df_features = df[features]

X_train, X_val, Y_train, Y_val = train_test_split(df_features, df[target], train_size=0.95, shuffle=False)

df = [[]]
df_features = [[]]
```

<br>
<br>
LightGBMを訓練します。

```
import warnings
import numpy as np
import lightgbm as lgb
from scipy.stats import pearsonr

warnings.simplefilter('ignore')

lgb_train = lgb.Dataset(X_train, Y_train)
lgb_eval = lgb.Dataset(X_val, Y_val, reference=lgb_train)

params = {'seed': 1,
          'verbose' : -1,
           'objective': "regression",
           'learning_rate': 0.02,
           'bagging_fraction': 0.1,
           'bagging_freq': 1,
           'feature_fraction': 0.1,
           'max_depth': 6,
           'min_child_samples': 50,
           'num_leaves': 64}
        
        
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=3000,
                valid_sets=lgb_eval,
                verbose_eval=False,
                early_stopping_rounds=3,
                )
```
<br>
<br>
LightGBMの特徴量重要度を表示します。

```
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report


feature = gbm.feature_importance(importance_type='gain')


f = pd.DataFrame({'number': range(0, len(feature)),
             'feature': feature[:]})
f2 = f.sort_values('feature',ascending=False)

#features' name
label = X_2.columns[0:]

#feature rank
indices = np.argsort(feature)[::-1]

for i in range(len(feature)):
    print(str(i + 1) + "   " + str(label[indices[i]]) + "   " + str(feature[indices[i]]))

```

<br>

**結果**

```
1   f_231   9385.3265914917
2   num_investment   7186.120182514191
3   f_225   4742.920154094696
4   f_153   4128.9165995121
5   f_145   4047.8693885803223

// ... 略
```


特徴量重要度を見てわかるように、```num_investment```が特徴量として効いていることが分かると思います。



<br>
次に、この学習させたモデルを用いて予測を提出してみます。

```
import ubiquant
env = ubiquant.make_env()   # initialize the environment
iter_test = env.iter_test()    # an iterator which loops over the test set and sample submission
for (test_df, sample_prediction_df) in iter_test:
    
    
    
    test_df.drop(['row_id'], axis=1, inplace=True)
    test_df.drop(['investment_id'], axis=1, inplace=True)
    test_df['num_investment'] = len(test_df)
    pred = gbm.predict(test_df)
    sample_prediction_df['target'] = pred  
    env.predict(sample_prediction_df)   # register predictions

```



```'num_investment'``` を加えた以外は一切変えずに提出したところ、LBは0.12→0.124に向上しました。


<br>


# 最後に

株式会社音圧爆上げくんではプロKagglerを募集しています。
ご興味のある方はぜひ以下のリンクをご覧ください。
[Wantedlyリンク](https://www.wantedly.com/projects/748285)