Numeraiã§åŠã¶éèæç³»åã¢ãã«è©äŸ¡ææš
éšã«ãè² ãã
颚ã«ãè² ãã
å¬ã«ã
å€ã®éç±çžå Žã«ãè² ãã¬
ããã¹ããªäºæž¬ãæã¡
匷欲ã¯ãªã
決ããŠæ²èгãã
ãã€ãéãã«å©çãéããŠãã
...
ããèšãã¢ãã«ã
ç§ã¯äœããã
by ??? (20??幎)
åæžã
ããã«ã¡ã¯ãæ¥æ¬çæé²æ¢å§å¡äŒã§ã(ãã£ãèãã)ã
ããŠãçããã¯ä»æ¥ã仿¥ãšãŠçæãåãæµããŠããã£ããããšæããŸãããã®åå ã¯æ§ã ãããããšæããŸããããããããåå ãããããªãããšããæ¹ãã»ãšãã©ã§ã¯ãªãã§ããããã
çæããŠããã®ã«åå ãããããªãããšããã®ã¯ãèšããŸã§ããããŸããã倧å€ãªã¹ãã¬ã¹ã§ããæ¥œããæããããããšå°ããªå³¶ã«éã³ã«è¡ã£ããæ®ºäººäºä»¶ãèµ·ããŠèª°ãç¯äººãããããªããã©å®¹çè ã®äººãã¡ãšäžç·ã«æ®ãããªããšãããªãã€ã¡ãŒãžã§ãïŒ?ïŒã
ã©ããçæãããªãããããŒã¯ã€ã®ã¢ãã«ã¯ããããåžæ³ã«åŒ±ãããšãæ€èšŒã§ãããã£ãŠãŠãä»ã¯ãã®åžæ³ã ããçæãªãããŒããšåå ãåãã£ãã»ããæ°ãæ¥œã§ãããäœããã¢ãã«ãæ¢ããã»æ¹è¯ãããªã©ã®ã¢ã¯ã·ã§ã³ã«ç§»ããŸãã
åå ãããããªããšã仿¥ã¯çæãªãã ããææ¥ã¯ççãªããããïŒããªã©ãšNYã®ã«ããã±ãŒããããçãèãã«åãããã¢ã¯ã·ã§ã³ãèµ·ãããçæãç©ã¿éããããšã«ãªããŸãã
ã§ã¯ã©ããããèªåã®ã¢ãã«ãæ£ããè©äŸ¡ã§ããããã«ãªãã§ããããïŒ
ãã¬ãŒãã£ã³ã°ã¯æç³»åããŒã¿ãåãæ±ããŸããããæç³»åã®è©äŸ¡ææšã䜿ã£ãŠåæããããšã§ãæ§ã ãªè§åºŠããèªåã®ã¢ãã«ã客芳çã«è©äŸ¡ã§ããŸãã
å æ¥ã®ãã±ãã³ã®ã€ãã³ããããŒã±ããAIéçºã«ããããããžã§ã¯ããããžã¡ã³ããšçŸå Žã®ã¢ãã«æŽ»çšã¹ãã·ã£ã«ãã«ãããŠãäž»å¬ã®Alpacaåå±±ããã以äžã®ãããªããšãèšåãããŠããŸããã
-ããã¬ãŒãã£ã³ã°ã«ãããæç³»åã®è©äŸ¡ææšã«é¢ããå®äŸãç ç©¶ã¯ãNumeraiã®ãããªã³ãã¥ããã£ãååšããå®éã®ãã¡ã³ããé瀺ããŠãããŠãããã®ãåŠã¶ãšè¯ãã
-ããã¬âã¢ã ãPâint72ãšãã£ããããžãã¡ã³ãã¯è©äŸ¡æ¹æ³ãé瀺ããŠããªããããæ¢ããŠãç¡é§ã
ããã§ä»åã®äŒç»ã¯ãçŸåšã€ã±ã€ã±ã®ã¯ãªã³ããããžãã¡ã³ãã§ããNumeraiã䜿ã£ãŠããè©äŸ¡ææšãïŒpythonã³ãŒãä»ãã§ïŒç¶²çŸ çã«ç޹ä»ããããšã§ã1人ã§ãçæã«æ©ãæ¹ã ã®æãã«ãªãããšãç®æããããšæããŸãã
Numeraiãšã¯

Numeraiã¯è¿å¹Žæé·èããã¢ã¡ãªã«ã®ã¯ãªã³ããããžãã¡ã³ãã§ããäºæž¬ãªã¿ãŒã³ãèªç€Ÿã®ã¯ãªã³ããèšç®ããã®ã§ã¯ãªããäžçäžã®ããŒã¿ãµã€ãšã³ãã£ã¹ãããã¯ã©ãŠããœãŒã¹ã§åéããã¢ã³ãµã³ãã«ããŠããŒããã©ãªãªãçµãã§ããããšã«æå€§ã®ç¹åŸŽããããŸãã
ã©ããã£ãŠäžçäžã®ããŒã¿ãµã€ãšã³ãã£ã¹ãããäºæž¬å€ãéããã®ïŒ( ã»âã»)
ããŒã¿ãµã€ãšã³ãã£ã¹ãã£ãŠæãªã®ïŒ( ã»âã»)
ãšããçåã¯åœç¶ãããããšæããŸãããNumeraiã¯ïŒçŸåšdailyã§ïŒã³ã³ããéå¬ããå®éã®ã©ã€ããã¹ãã§ã®çµæãè¯ããã°NMRãšããæå·è³ç£ãïŒåå è ã®stakeé¡ã«æ¯äŸãã圢ã§ïŒåå è ã«æ¯æãããšã§ãããŒã¿ãµã€ãšã³ãã£ã¹ãã«äºæž¬å€ãæåºããã€ã³ã»ã³ãã£ããäžããŠããŸãã
ã³ã³ãã®çš®é¡ãšããŠãNumeraiãç¹åŸŽéãã¿ãŒã²ãããçšæããTournamentãšãNumeraiã¯ã¿ãŒã²ããã®ã¿çšæãåå è ãç¬èªã®ããŒã¿ã䜿çšããSignalsã®2ã€ããããŸãã
ã¢ãã«ã®è©äŸ¡ææšã¯ãªã倧åãªã®ãïŒ
ç¹°ãè¿ãã«ãªããŸããNumeraièªèº«ã¯ã¢ãã«ãäœãããåå è ããäºæž¬å€ã ããåéããŠããããã§ããããã¢ã³ãµã³ãã«ããŠåŸããããã¬ãŒãã£ã³ã°ã·ã°ãã«ïŒNumeraiã§ã¯ã¡ã¿ã¢ãã«ãšåŒãã§ããŸãïŒãäžäœå šäœã©ãããæ§è³ªãæã£ãŠãããã®ãªã®ããåéæç¹ã§ã¯å šããã£ãŠäžæãªããã§ãã
ããã§ãNumeraièªèº«ã¯æ©é¢æè³å®¶ãšããŠãå¹Žéæ©æ§ãè¶ å¯è£å±€ã®ãéãéçšããŠããããã§ãããã
ããªãããããããã©ã·ã°ãã«ã§ãããã§ãã¿ãªããã®ãé䜿ã£ãŠãã€ã¬ãååŒããŸã£ã^^ã
ãšããããã«ã¯ãããŸããã
ããããè§åºŠããåæããã©ããããªã¹ã¯ãåã£ãŠããã®ãããã³ãããŒã¯ãšæ¯èŒããŠäœãç¹ã«åªããŠããããªã©æããã«ããæè³å®¶ã«çŽåŸããŠããããªããã°ãéãé ããŠããããŸããã
ïŒäºæž¬å€ã®ïŒã¯ã©ãŠããœãŒã·ã³ã° x æå·è³ç£ïŒã䜿ã£ãã€ã³ã»ã³ãã£ãèšèšïŒ x æ©æ¢°åŠç¿ïŒã«ãããã¬ãŒãã£ã³ã°ã·ã°ãã«ïŒãšããããã®ã³ã³ã»ãããè²ã æ°ããNumeraiã§ããããæè³å®¶ã説åŸããããŒãã«ã¯ä»ã®æ©é¢æè³å®¶ãããã¯ããã«é«ãã§ãããã
ããããŸããNumeraiãã¢ãã«ã®è©äŸ¡ã«ã¬ãã«ãªããªããšãããªãçç±ã®1ã€ç®ã§ãã
ãã1ã€ã¯ãããããäºæž¬å€ãæåºããã³ã³ãåå è ã«ãããŒããªãäºæž¬å€ãæåºãããŠã¯å°ãããã§ãããããŒããªããã®ãäœåãšéããŠãSP500ãªã©ã®ãã³ãããŒã¯ã«ãåãŠãŸãããçæ³çã«ã¯ãåå è èªèº«ãèªèº«ã®ã¢ãã«äºæž¬å€ãæ£ããæ€èšŒããåºæ¥äžãã£ãèªä¿¡ã®ããã匷ããäºæž¬å€ãããããéãããã¬ãŒãã£ã³ã°ã·ã°ãã«ã«ãããã®ã§ãã
ãããå®çŸããããã«ã¯ãNumeraiããåå è ãžã®ãã£ãŒãããã¯ãæ¬ ãããŸããããã¡ãããNMRè²°ããè²°ããªããšããã©ã€ããã¹ãåŸã®ãã£ãŒãããã¯ã¯ãããŸãããããããã©ã€ããã¹ã以åã«æ€èšŒæéã®äºæž¬å€ãã©ããããè¯ããããªã®ããæ§ã ãªè©äŸ¡ææšãèšç®ããŠãããŠåå è ã«ãã©ãŒãããã¯ããŠãããã°ãåå è ã®ã¢ããªã³ã°ã®å©ãã«ãªããŸãããã®ãããéå»ã®æ€èšŒæéã®äºæž¬å€ãæåºãããšãæ§ã ãªè©äŸ¡ææšãäžæ°ã«èšç®ããŠãããä»çµã¿ãNumeraiã®websiteäžã«æŽåãããŠããŸãã
Numeraiã¯åå è
ã«å ±é
¬ãæããŸãããè³å Žãšéã£ãŠéå¶ãšåå è
ã¯ãããã¢ãã«ãäœã£ãŠã»ããããããã¢ãã«ãäœãããããšåãæ¹åãåããŠããŸãã®ã§ãNumeraiã¯åå è
ãããè¯ãã¢ããªã³ã°ãã§ãããããïŒãªãœãŒã¹ã®ç¯å²å
ã§ïŒãããšããããå¿æŽãããŠãããŸããNumeraiãã¢ãã«ã®è©äŸ¡ææšãããããå
¬éããŠãããã®ããã®äžç°ã§ããæ¬¡ç« ã§ã¯ãTC (True Contribution)ãšãã£ãçã匷ãããNumeraiã³ã³ãç¬èªã®ãã®ã¯é€ããæ ªãä»®æ³é貚ã®ãã¬ãŒãã£ã³ã°ãªã©ã§ãæ±çšçã«äœ¿ããããªãã®ã«çµã£ãŠç޹ä»ããŠãããŸãã
è©äŸ¡ææš
è©äŸ¡ææšãããã©ãŒãã³ã¹ç³»ããªã¹ã¯ç³»ããã®ä»ã®3çš®é¡ã§èŠãŠãããŸãããã®è€æ°èŠç¹ã倧åã§ããäžèŠãã€ããã©ãŒãã³ã¹ã«èŠããã¢ãã«ã§ãã倧ããé·ãè² ããææãäžéšãããããããªãã§ããããããªãšã人éã¯ãªããªãã¹ãã¬ã¹ã«èããããªãã§ãã
ææšãèšç®ããããã®ããŒã¿ãšããŠã¯ãå奿 ª x eraïŒååŒæéç²åºŠïŒåäœã®ã¬ã³ãŒããå«ãŸããpandas dataframe (df)ãæ³å®ããŸãã
dfã«ã¯ãéæã³ãŒã (ticker)ãeraïŒdailyãªãæ¥ä»ïŒãtargetïŒäºæž¬å¯Ÿè±¡ã®ãªã¿ãŒã³ïŒããããŠã¢ããªã³ã°ã«äœ¿çšããç¹åŸŽéfeatures (feature1, feature2, feature3, ...)åã³ãã¢ãã«äºæž¬å€ãå«ãŸããpredictã«ã©ã ããããšããŸãã
ChatGPTããã«ã¢ãã¯ãäœã£ãŠããããŸãããããããªã€ã¡ãŒãžã§ãïŒãããŸã§ã€ã¡ãŒãžã§äžèº«ã«æå³ã¯ãããŸããïŒã

ããã©ãŒãã³ã¹ç³»
ã¢ãã«ã®ããã©ãŒãã³ã¹ãå®éããè©äŸ¡ææšã§ãã
Corr
äºæž¬å€ãšã¿ãŒã²ããã®é äœçžé¢ä¿æ°ã§ããäºæž¬å€ãšã¿ãŒã²ãããçžé¢ããŠããã°ãäºæž¬ãããŸããã£ãŠãããã§ãããã
å®è£ ã¯ããšãã°ä»¥äžã®ããã«ããŸãã
def get_corr(df, target_name=target, pred_name='predict'):
'''Takes df and calculates spearm correlation from pre-defined cols'''
# method="first" breaks ties based on order in array
return np.corrcoef(
df[target_name],
df[pred_name].rank(pct=True, method="first")
)[0,1]
ãã¬ãŒãã£ã³ã°ã®æç³»åã®æèã§ã¯ãå šã¬ã³ãŒãã«å¯Ÿã1ã€ã®corrãèšç®ããã®ã§ã¯ãªããååŒæéç²åºŠ (era)åäœã§èšç®ããã»ããè¯ãã§ããCorrããã€ãã¹ã«ãªããããªææã«äœããã£ãã®ã調ã¹ããã£ããã«ãªããŸãããCorrãeraåäœã§èšç®ããããšã§corrã®ååžãèŠãããšãã§ããã®ã§ãææã«ããã°ãã€ãçæ§ã ãªãªã¹ã¯ææšãããããèšç®ã§ããŸãã
Corrã«éã£ã話ã§ã¯ãããŸããããeraåäœã§ææšãèšç®ãããšãã¯pandasã®groupbyã䜿ã£ãŠãapply颿°ã§eraããšã«èšç®ãè¡ããŸãã
corrs = df.groupby('era').apply(lambda d : get_corr(d, target_name=target, pred_name='predict'))
Numerai Corr
UKIããã«è§ŠçºãããŠæ¥æ¬æ ªã§LS (Long Short)æŠç¥ã«åãçµãã§ããæ¹ãå€ããšæããŸãããããããLSã§ååŒããã®ã¯è³éçã«ãæ¯åæ°éæãéçããšããæ¹ãå€ãã®ã§ã¯ãªãã§ããããïŒã¯ãç§ã§ãïŒã
ãã®å ŽåãïŒåºæ¬çã«ïŒã¢ãã«äºæž¬å€ã®TOP Néæãè²·ããBOTTOM Néæã売ããšããååŒãããããã§ãã®ã§ãå®ã¯backtestã§eraããšã«å šã¬ã³ãŒãã§corrãèšç®ããã®ã¯ãããŸãçŸå®ã«å³ããŠãããšã¯èšããŸããã
ãšããã®ã¯ãeraããšã«ããšãã°TOPIX500ãªã500éæçšåºŠãNumerai Signalsãªã5,000éæçšåºŠã¬ã³ãŒãããããŸããããã®ãã¡å®éã«ååŒã§ããã®ãäžäœãšäžäœæ°ïŒ ã§ãããšããããå¥ã«ãã®äžäœäžäœä»¥å€ã®éšåã§çžé¢ããŠãããããŠãŸããã©ãã§ãããã®ã§ããã
ãã ãååŒå¯Ÿè±¡ã®äžäœäžäœæ°ïŒ ã®éšåã¯ããããªçžé¢ããŠããŠã»ããããã§ãã
ãããã©ãè©äŸ¡ææšã§è¡šçŸãããã§ããã1ã€ã®æ¹æ³ãšããŠã¯ãååŒå¯Ÿè±¡ã®Top Bottom (TB) Néæã ãeraããšã«äœ¿ã£ãŠcorrãèšç®ããããšããŸãèããããŸããNumeraiã ãšTB = 200ã§ãããå人ã ãš200éæäžåºŠã«å£²è²·ããã®ã¯å³ããããšæããŸãããããšãã£ãŠæ°éæã ãšeraããšã®ãµã³ãã«æ°ãæ¿æžããŠããŸããä¿¡é Œã§ããcorrãåŸãããªãæãããããŸãã
ããã§Numeraiã¯ç¬èªã«Numerai_CorrïŒååãŸããŸã...ïŒãæäŸããŠããŸãã
def numerai_corr(preds, target):
"""
https://github.com/numerai/example-scripts/blob/8f1abbc56c7a1a19b8c6e615a8bd0d9becb7b244/utils.py#L175C1-L185C52
"""
# rank (keeping ties) then gaussianize predictions to standardize prediction distributions
ranked_preds = (preds.rank(method="average").values - 0.5) / preds.count()
gauss_ranked_preds = scipy.stats.norm.ppf(ranked_preds)
# center targets around 0
centered_target = target - target.mean()
# raise both preds and target to the power of 1.5 to accentuate the tails
preds_p15 = np.sign(gauss_ranked_preds) * np.abs(gauss_ranked_preds) ** 1.5
target_p15 = np.sign(centered_target) * np.abs(centered_target) ** 1.5
# finally return the Pearson correlation
return np.corrcoef(preds_p15, target_p15)[0, 1]
ïŒãã¢ãœã³ïŒçžé¢ä¿æ°ãèšç®ããåã«1.5ä¹ããããšã§ãEraããšã«ã¬ã³ãŒãæ°ãæžããããšãªããäºæž¬å€ãã¿ãŒã²ããã®ååžã®ç«¯ïŒããŒã«ïŒã®éšåã匷調ããŠå¯Ÿå¿ããããã«ããŠããŸããã
çŸåšã¯ãã®numerai_corrãNumeraiã³ã³ãã®ã¹ã¿ã³ããŒããªè©äŸ¡ææšã«ãªã£ãŠããŸã (CORR20V2)ã
ç§ã¯ããã«ãªã£ãŠããNumeraiã§åãŠãªããªããŸãããå©ããŠãã ããã
FNC (Feature Neutral Correlation)
FNCãCorrãšåæ§ã«çžé¢ä¿æ°ã§ãããäºæž¬å€ãããç¹åŸŽéã»ããã«ãã£ãŠçŽäº€å (neutralize)ããåŸãã¿ãŒã²ãããšã®çžé¢ãèšç®ãããã®ã§ãã
çŽäº€åã£ãŠäœã ã( ã»âã»)ïŒ
ãšæãããæ¹ãå€ããããããŸããããããã§ã¯ã¢ãã«äºæž¬å€ããç¹åŸŽéã®ç·åœ¢æåãæ§é€ããæ°åŠçæç¶ãã®ããšãæããŸãã
ã©ãããŠãã£ããåŠç¿ããç¹åŸŽéã®ïŒç·åœ¢ïŒæåãé€ãã¡ãããã ã?( ïŒâïŒ)
ãšæãããæ¹ãå€ããããããŸããããåºæ¬çã«éèæç³»åã®ç¹åŸŽéãšã¿ãŒã²ãããªã¿ãŒã³ã®çžé¢èªäœãäžå®å®ã§ãããããããã«äŸåããã¢ãã«ã®äºæž¬å€ããã¹ãããäžå®å®ãªãã®ã«ãªããŸãã
RSIãšããªãã¡ããã¯ãã¹ã ãã§åã¡ç¶ããããã誰ãèŠåŽããªãã£ãŠè©±ã§ããã
ããã§ã䜿çšããç¹åŸŽéïŒNumeraiã®å Žåã420åã®ç¹åŸŽéãããªãV3 medium feature setã䜿ã£ãŠããïŒãšã¿ãŒã²ããã®ç·åœ¢äºæž¬æåãé€ããéç·åœ¢ã®æåã ããæ®ãããšã§ãæ©æ¢°åŠç¿ã«ããç¹åŸŽé矀ãšã¿ãŒã²ããã®å®å®ããé¢ä¿ãæ§ç¯ããããšèšã詊ã¿ãfeature neutralizationã§ãããFNCã¯ãã®çŽäº€ååŸã®ã¢ãã«äºæž¬å€ãšã¿ãŒã²ããã®çžé¢ä¿æ°ã§ãã
def calculate_fnc(sub, targets, features):
"""
https://docs.numer.ai/numerai-tournament/scoring/feature-neutral-correlation
Args:
sub (pd.Series)
targets (pd.Series)
features (pd.DataFrame)
"""
# Normalize submission
sub = (sub.rank(method="first").values - 0.5) / len(sub)
# Neutralize submission to features
f = features.values
sub -= f.dot(np.linalg.pinv(f).dot(sub))
sub /= sub.std()
sub = pd.Series(np.squeeze(sub)) # Convert np.ndarray to pd.Series
# FNC: Spearman rank-order correlation of neutralized submission to target
fnc = np.corrcoef(sub.rank(pct=True, method="first"), targets)[0, 1]
return fnc
ã¢ãã«ã衚é¢äžã®ç¹åŸŽéãšã¿ãŒã²ããã®çžé¢ã ãåŠç¿ããŠããããããšãçŽäº€åããäºæž¬å€ãšã¿ãŒã²ãããšã®çžé¢ (FNC)ãæ¥µããŠ0ã«è¿ããªã£ãŠããã¯ã¯äœãæ å ±æ®ã£ãŠãªãããããã£ãŠãªã£ãŠé¢çœãã§ãïŒçœç®ïŒ
éã«ããŸãã¢ããªã³ã°ã§ããã°ãçŽäº€åã¯åŒ·åãªæŠåšã«ãªããŸããèªãŸããŠãªãæ¹ã¯ãã²âã®UKIããã®èšäºãã芧ãã ããã
æ©æ¢°åŠç¿ã«ããæ ªäŸ¡äºæž¬ Kaggleã®JPXã³ã³ããçµããŠ
Sharpe
ã¢ãã«ã®è©äŸ¡ææšãšããŠæããã䜿ãããã®ããã®Sharpe ratioã§ããããNumeraiã§ã¯å ã»ã©Corrã®ã»ã¯ã·ã§ã³ã§èšç®ããeraããšã®çžé¢ä¿æ° (corrs) ã䜿ã£ãŠã以äžã®ããã«èšç®ããŠããŸãã
sharpe = corrs.mean() / corrs.std(ddof=0)
çžé¢ä¿æ°ã®eraããšã®å¹³åå€ãæšæºåå·®ã§å²ã£ããã®ã§ããªã¹ã¯ïŒæšæºåå·®ïŒèª¿æŽæžã¿ããã©ãŒãã³ã¹ã衚çŸããŸãã
ããããªæé軞ã®ããžãã¯ãæã£ãŠããæ¹ã¯ãannualizeãšãã£ãŠå¹Žåäœã«sharpeãçŽããšæ¯èŒããããã§ãã
ãã®å®è£ ã¯ã以äžã®quantstatsã®sharpeã®èšç®ãèŠãŠããã ãããšè¯ãããšæããŸãããã®ã©ã€ãã©ãªã¯Numeraié¢ä¿ãªããreturnã®pandas seriesãããã°æ±çšçã«äœ¿ãããã¡ããã¡ãè²ã ãªè©äŸ¡ææšãåæã«èšç®ã綺éºãªã¬ããŒããäœã£ãŠãããã®ã§ãã·ã¹ãã ãã¬ãŒããŒã«ã¯å¿ é ã ãšæããŸãã
def sharpe(returns, rf=0.0, periods=252, annualize=True, smart=False):
"""
Calculates the sharpe ratio of access returns
If rf is non-zero, you must specify periods.
In this case, rf is assumed to be expressed in yearly (annualized) terms
Args:
* returns (Series, DataFrame): Input return series
* rf (float): Risk-free rate expressed as a yearly (annualized) return
* periods (int): Freq. of returns (252/365 for daily, 12 for monthly)
* annualize: return annualize sharpe?
* smart: return smart sharpe ratio
"""
if rf != 0 and periods is None:
raise Exception("Must provide periods if rf != 0")
returns = _utils._prepare_returns(returns, rf, periods)
divisor = returns.std(ddof=1)
if smart:
# penalize sharpe with auto correlation
divisor = divisor * autocorr_penalty(returns)
res = returns.mean() / divisor
if annualize:
return res * _np.sqrt(1 if periods is None else periods)
return res
ãsharpeãïŒãè¶ ããã°ãããŒã±ãã£ã³ã°éšéã¯å¿ èŠãªãã
ãšãããããžãã¡ã³ãæ¥çã®æ ŒèšïŒïŒïŒããããããã§ããSharpeã2ãè¶ ãããããããããããããªãŒã¯ãããããªãã®ã§ãŸãç§ã«ããžãã¯ãçžè«ããããšè¯ãããšæããŸãã¯ãã
ãªã¹ã¯ç³»
次ã¯ãªã¹ã¯ç³»ã§ããã©ãã ãããã©ãŒãã³ã¹ãææã«ãã£ãŠã°ãã€ãã®ããæå€§ã©ãã ãã®æå€±ãèŠæããªããšãããªãã®ãããªã©ãæããŠãããŸãã
Standard Deviation
Sharpeã®ãšããã§ãåºãŠããŸããããã©ãã ãåçã«å€åãããããèšç®ããŠããããšã§ãäœèšã«ãã©ãã©ããªãã§ããïŒãããããªãïŒã§ãã倧ããåã£ãŠå€§ããè² ãããç¹°ãè¿ããããªã¢ãã«ã ãšãå³è©äžããã®æçæ²ç·ã§ã人éã«ã¯ã¹ãã¬ã¹ã§ãããã
ãã¡ããAnnualizeãããšç°ãªãæé軞ã®ããžãã¯ãæ¯èŒããããã®ã§ããŸããŸãquantstatsããã³ãŒããåŒçšããŸãã
def volatility(returns, periods=252, annualize=True, prepare_returns=True):
"""Calculates the volatility of returns for a period"""
if prepare_returns:
returns = _utils._prepare_returns(returns)
std = returns.std()
if annualize:
return std * _np.sqrt(periods)
return std
Feature Exposure
FNCã®ã»ã¯ã·ã§ã³ãšå€§ããé¢é£ããŸãããfeature exposureã¯ã¢ãã«äºæž¬å€ãšç¹åŸŽéã®é äœçžé¢ã§ããNumeraiã§ã¯ç¹åŸŽéå šãŠãšã¢ãã«äºæž¬å€ã®é äœçžé¢ä¿æ°ãèšç®ãããã®å¹³åå€ãæå€§å€ãææšãšããŠäœ¿ããŸãã
ãããŸãäºæž¬å€ãšç¹å®ã®ç¹åŸŽéã®çžé¢ãé«ããšããã£ããæ©æ¢°åŠç¿ããŠãæ å ±ãšããŠã¯ãšããç¹åŸŽéãšã»ãŒäžç·ããšããããšã§ãã®ã§ããã®ç¹åŸŽéã®æã€ãªã¹ã¯ãã¢ãã«äºæž¬å€ãåãç¶ãã§ããããšã«ãªããŸããåã人ã«ãããã£ãŠæ©æ¢°åŠç¿ãããïŒãã£ãŠèšãããŠæ¥ããããæããããŸãã
Feature neutralizationåŸãã¢ãã«äºæž¬å€ã®feature exposureãèšç®ããããšã§ãæ¬åœã«äºæž¬å€ãç¹å®ã®ç¹åŸŽéã«äŸåããŠããªããã©ããã確èªããããšãã§ããŸãã
å®è£ äŸã¯â
PREDICTION_NAME = 'predict'
def feature_exposures(df, feature_names=features):
exposures = []
for f in feature_names:
fe = spearmanr(df[PREDICTION_NAME], df[f])[0]
exposures.append(fe)
return np.array(exposures)
def max_feature_exposure(df):
return np.max(np.abs(feature_exposures(df)))
def feature_exposure(df):
return np.sqrt(np.mean(np.square(feature_exposures(df))))
Max Drawdown
MDDãšç¥ãããããããŸãããæ€èšŒæéã§èŠ³æž¬ãããæå€§ã®æå€±ã衚çŸããŸããNumeraiã§ã¯å ã»ã©Corrã®ã»ã¯ã·ã§ã³ã§èšç®ããeraããšã®çžé¢ä¿æ° (corrs) ã䜿ã£ãŠã以äžã®ããã«èšç®ããŠããŸãã
ã·ã¹ãã ãã¬ãŒãã§returnsã®pandas seriesãããã°ãquantstatsã§ãèšç®ããŠãããŸãã
def get_max_drawdown(corrs):
"""
https://github.com/numerai/example-scripts/blob/8f1abbc56c7a1a19b8c6e615a8bd0d9becb7b244/utils.py#L278C18-L278C18
"""
rolling_max = (
(corrs + 1)
.cumprod()
.rolling(window=9000, min_periods=1) # arbitrarily large
.max()
)
daily_value = (corrs + 1).cumprod()
max_drawdown = -((rolling_max - daily_value) / rolling_max).max()
return max_drawdown
ã©ã€ãã§Max Drawdownã芳枬ããåã«ã¢ãã«ãæ¢ãããæ¹åãè¡ããããšããã§ãããéå»ã«æ§ã ãªåãçµã¿ãããããã§ãã
Alpacaåå±±ããã«ãããã±ãã³ã§ã®ãåç
åºæ¬çã«ã¯ã䜿çšããŠããç¹åŸŽéãäºæž¬å€ïŒSHAPå€ãªã©ïŒã®ååžãéå»ãšå€§ããéã£ãŠããªããã確èªããããšã§ç°åžžæ€ç¥ã詊ã¿ããã®ã®ããã§ãã
ã¢ãã«ã§ãããæµãã£ã±ãªãã§ã¯ãªããç£èŠãå¿ èŠã£ãŠããšã§ãããæµããŠããã¢ãã«ã®ç£èŠã«ã€ããŠã®ããããããããMLOpsã®è©±ãªãã ãããšæããŸããããã¬ãŒãã£ã³ã°ã®æèã ãšæ¡å€ç¥èŠãåºãŠããªãæ°ãããã®ã§ã誰ãå ±æããŠãããŠããããã§ããïŒ
ãã®ä»
ããã©ãŒãã³ã¹ç³»ãšããªã¹ã¯ç³»ãšãèšãããããªèšããªããããªãã€ãã§ãã
ReversalsãšARLã«ã€ããŠã¯ãNumeraiã§ã¯ã¢ãã«äºæž¬å€ãšããããç¹åŸŽéããšã«èšç®ããŠãç¹åŸŽééžæã®åèã«ããããã®æ°å€ãšããŠäžããããŠããŸãã
Reversals
æ€èšŒæéã«ãããŠããã®ç¹åŸŽéãšã¿ãŒã²ãããšã®çžé¢ã®ãµã€ã³ (+ or -) ãäœåºŠå ¥ãæ¿ãã£ããã«ãŠã³ããããã®ã§ããäœãå€ã§ããã°ãå®å®ããç¹åŸŽéã§ãããšèšããŸãã
ARL (Average Run Length)
æ€èšŒæéã«ãããŠããã®ç¹åŸŽéãã¿ãŒã²ãããšé«ãçžé¢ãç¶æããŠããé£ç¶eraæ°ã§ããé«ãå€ã§ããã°ãå®å®ããç¹åŸŽéã§ãããšèšããŸãã
Autocorr
eraããšã®çžé¢ä¿æ° (corrs) ãçšããŠèšç®ãããeraããããããšãã®èªå·±çžé¢ä¿æ°ã§ããé«ãå€ã§ããã°ãå®å®ããç¹åŸŽéãäºæž¬å€ã§ãããšèšããŸãã
Churn
Autocorrãšè¿ãèãã§ãeraåäœã§ã®corrã®èªå·±çžé¢ã§ã¯ããã®ã§ããããã¡ãã¯åå¥éæç²åºŠãå ¥ã£ãŠãããã©ããããé »ç¹ã«ããŒããã©ãªãªã®å ¥ãæ¿ããçºçããŠãããå®éããŠãããã®ã§ããNumeraiã§ã¯ãSignalsã«æè¿ (2023/06/03)å°å ¥ãããè©äŸ¡ææšã§ãChurnãé«ããšå ¥ãæ¿ãïŒTurnoverïŒãé »ç¹ã«èµ·ããŠãããäžå®å®ãªããŒããã©ãªãªã§ããããšã瀺åããŠããŸããNumeraiããã¯ã0.15以äžãæãŸãããšããæéãåºãŠããŸãã
å®è£ ã¯â
def calculate_churn_stats(
df,
pred_col='predict',
ticker_col='ticker',
era_col='era'
):
"""
https://forum.numer.ai/t/churn-new-signals-diagnostics-metric/6417
"""
# rank and normalize per era
df[f'{pred_col}_ranked'] = df.groupby(era_col)[pred_col].apply(lambda group: (group.rank() - 0.5) / len(group.dropna()))
# fill na with 0.5
df[f'{pred_col}_ranked_filled'] = df[f'{pred_col}_ranked'].fillna(0.5)
# Sort the dataframe and set a multi-index with id_col and era_col
df = df.sort_values([ticker_col, era_col], ascending=[True, False])
df.set_index([ticker_col, era_col], inplace=True)
# drop duplicates
df = df.loc[~df.index.duplicated(keep='first')]
# Unstack the dataframe to ensure every combination of id_col and era_col has a row
df_unstacked = df.unstack(level=ticker_col)
# Shift the pred_col within each id_col group
shifted_df_unstacked = df_unstacked[f'{pred_col}_ranked_filled'].shift(-1)
# Stack the dataframe back to a regular dataframe
df_shifted = df_unstacked.stack(dropna=False)
df_shifted[f'{pred_col}_ranked_filled_prev'] = shifted_df_unstacked.stack(dropna=False)
# Calculate Spearman correlation
churns = df_shifted.groupby(level=era_col).apply(lambda group: 1 - group[f'{pred_col}_ranked_filled'].corr(group[f'{pred_col}_ranked_filled_prev'], method='spearman'))
# Calculate churn stats
churn_stats_df = churns.agg(['mean', 'std', 'max']).rename(
index={'mean': 'churn_mean', 'std': 'churn_std', 'max': 'churn_max'})
return churn_stats_df
çµããã«
Numeraiãå ¬éããŠããã¢ãã«è©äŸ¡ææšã¯äžéãã¯ããã£ãããšæããŸããæ°åæãªã©ã¯ç®å®ãããã€ã€ããã¢ãã«ãæ¹è¯ããäžã§äœ¿ã£ãŠãããç¿ããããæ £ããã®ãè¯ããããããŸããã
Binâ«ïžnceãâ«ïžKXãæ¥æ¬ããã®ã¢ã¯ã»ã¹å¶éãåºãŠããŠããŸã£ãŠããŸãããNumeraiããŒã¯ã³ (NMR)ãstakeããªããŠãNumeraièªäœã¯ã§ããŸãã®ã§ããã£ãããšãªã人ã¯ãšããããNumerai TournamentãSignalsã§ã¢ãã«ãäœã£ãŠæåºããŠã¿ãŠãdiagnosticsãèŠãŠã¿ãã®ãããããããªãããšæããŸãã
Tournament...çŽè¿ã¯ããé£ãã

Signals...ChurnãããŸããããªã...

ããã§ã¯Happy Modeling!
Discussion