🎅

[Numerai Tips] Numeraiの現状使用されていないTargetの基礎的な解析

2022/12/24に公開約5,000字

サマリー

  • Numeraiのtargetは現状nomi_20が使われているが、その他の変数を使うとCorrやTCが改善する可能性がある。
  • 20 daysの変数は相関が高く、スピアマン相関係数値が0.70〜0.80を超えているものが多いので、これら変数を使うと特にCorrが改善するかも?(以下がSharpe ratioが高かったです)
    • target_arthur_20
    • target_william_20
    • target_jerome_20

はじめに

Numeraiにおいて現状スコアリングに使われているターゲットは普段1つだけですが、他のターゲットにも予測に重要な情報が眠っている可能性があります。以下の記事においてはNumeraiのCEOが「jerome_60はblendingによく効くターゲット」と言っています(!!)

https://zenn.dev/katsu1110/articles/60c777d15e01d5

本記事では、スコアリングに使用しているターゲットとスコアリングに使用していないターゲットとの相関を解析してみました。


ソースコード

Kaggle notebook 上にもソースコードをおいています。

https://www.kaggle.com/nishimoto/numerai-tournament-correlations-among-targets

import os
import pandas as pd
import scipy.stats as st
import matplotlib.pylab as plt

from numerapi import NumerAPI
from collections import defaultdict
from contextlib import redirect_stderr
from tqdm import tqdm_notebook as tqdm

napi = NumerAPI()
with redirect_stderr(open(os.devnull, 'w')):
    napi.download_dataset("v3/numerai_training_data_int8.parquet", "numerai_training_data.parquet")
    napi.download_dataset("v3/numerai_validation_data_int8.parquet", "numerai_validation_data.parquet")
    napi.download_dataset("v3/numerai_live_data_int8.parquet", "numerai_live_data.parquet")
    napi.download_dataset("v3/numerai_datasets.zip", "numerai_datasets.zip")


df_train = pd.read_parquet("numerai_training_data.parquet")
df_valid = pd.read_parquet("numerai_validation_data.parquet")

df_train_targets = df_train.loc[:, df_train.columns.str.startswith('target')]
df_valid_targets = df_valid.loc[:, df_valid.columns.str.startswith('target')]

df_train_targets["era"] = df_train["era"]
df_valid_targets["era"] = df_valid["era"]

era_set_train = sorted(set(df_train["era"]))
era_set_valid = sorted(set(df_valid["era"]))

dd_train = defaultdict(lambda: defaultdict(int))
for era in tqdm(era_set_train):
    df_train_targets_era = df_train_targets.query("era == @era").drop("era", axis=1)
    for col in df_train_targets_era.columns:
        if col == "target":
            continue
        
        # 60 days はNaNの値があるため、ignore(omit)しています。
        dd_train[era][col] = st.spearmanr(df_train_targets_era["target"], df_train_targets_era[col], nan_policy="omit")[0]

dd_valid = defaultdict(lambda: defaultdict(int))
for era in tqdm(era_set_valid):
    df_valid_targets_era = df_valid_targets.query("era == @era").drop("era", axis=1)
    for col in df_valid_targets_era.columns:
        if col == "target":
            continue
        
        # 60 days はNaNの値があるため、ignore(omit)しています。
        dd_valid[era][col] = st.spearmanr(df_valid_targets_era["target"], df_valid_targets_era[col], nan_policy="omit")[0]

df_spearmanr_train = pd.DataFrame(dd_train)
df_spearmanr_valid = pd.DataFrame(dd_valid)

df_spearmanr_train_mean_std = df_spearmanr_train.drop("target_nomi_20").T.agg(["mean", "std"]).T
df_spearmanr_valid_mean_std = df_spearmanr_valid.drop("target_nomi_20").T.agg(["mean", "std"]).T

df_spearmanr_train_mean_std["sharpe_ratio"] = df_spearmanr_train_mean_std["mean"] / df_spearmanr_train_mean_std["std"]
df_spearmanr_valid_mean_std["sharpe_ratio"] = df_spearmanr_valid_mean_std["mean"] / df_spearmanr_valid_mean_std["std"]

display(df_spearmanr_train_mean_std.sort_values("sharpe_ratio", ascending=False))
display(df_spearmanr_valid_mean_std.sort_values("sharpe_ratio", ascending=False))

df_spearmanr_train.loc[df_spearmanr_train.index.str.endswith("_20"), :].T.drop("target_nomi_20", axis=1).plot()
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=12)
plt.title("data: train, target: 20 days")
plt.xlabel("Era")
plt.ylabel("Spearman's Rho")
plt.show()

df_spearmanr_train.loc[df_spearmanr_train.index.str.endswith("_60"), :].T.plot()
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=12)
plt.title("data: train, target: 60 days")
plt.xlabel("Era")
plt.ylabel("Spearman's Rho")
plt.show()

df_spearmanr_valid.loc[df_spearmanr_valid.index.str.endswith("_20"), :].T.drop("target_nomi_20", axis=1).plot()
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=12)
plt.title("data: valid, target: 20 days")
plt.xlabel("Era")
plt.ylabel("Spearman's Rho")
plt.show()

df_spearmanr_valid.loc[df_spearmanr_valid.index.str.endswith("_60"), :].T.plot()
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=12)
plt.title("data: valid, target: 60 days")
plt.xlabel("Era")
plt.ylabel("Spearman's Rho")
plt.show()

結果

trainの相関係数とそのsharpe ratio

validの相関係数とそのsharpe ratio

相関係数の変遷


所感

  • 結果だけ見ると 60 days はあまりBlendingする気にならない...笑
    • TC狙いならこっちでも良さそうですね。
  • パッとできそうなのは20 daysの相関が高い変数をBlendingすること?Corrの改善には効きそうですよね。以下がSharpe ratioが高そうです
    • target_arthur_20
    • target_william_20
    • target_jerome_20

Discussion

ログインするとコメントできます