Created
November 1, 2024 15:36
-
-
Save jefferythewind/cfd1a7e750dbee3d14756a136ea92306 to your computer and use it in GitHub Desktop.
Quick script to backtest Numerai's Crypto Meta Model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import yfinance as yf | |
from numerapi import CryptoAPI | |
import pandas as pd | |
import numpy as np | |
import warnings | |
import datetime | |
api = CryptoAPI() | |
api.download_dataset( | |
"crypto/v1.0/historical_meta_models.csv", | |
"historical_meta_models.csv" | |
) | |
#load historical MM | |
mm = pd.read_csv('historical_meta_models.csv') | |
mm['date'] = pd.to_datetime( mm['date'] ) | |
# get historical daily data for all tickers available in historical MM | |
all_data = [] | |
for symbol in mm['symbol'].unique(): | |
print( symbol ) | |
data = yf.Ticker(f"{symbol}-USD") | |
# first_date = datetime.datetime.utcfromtimestamp(data.info['firstTradeDateEpochUtc']).strftime('%Y-%m-%d') | |
try: | |
hist = data.history( | |
# start=first_date, | |
start='2024-01-01', | |
end=datetime.date.today().strftime('%Y-%m-%d'), | |
interval="1d" | |
) | |
hist['symbol'] = symbol | |
if hist.iloc[-1].name.strftime('%Y-%m-%d') == ( datetime.date.today() - datetime.timedelta(days=1) ).strftime('%Y-%m-%d'): | |
all_data.append( hist ) | |
except: | |
continue | |
df = pd.concat( all_data ) | |
df['date'] = pd.to_datetime( df.index.strftime('%Y-%m-%d') ) | |
df.set_index('date') | |
# only keep weekday prices | |
df = df.loc[ df.index.weekday <= 4 ] | |
# compute future 20 day log returns | |
lag_days = 20 | |
df['lr'] = df.groupby('symbol')['Close'].transform( | |
lambda y: np.log(y).diff(lag_days).shift(-lag_days) | |
) | |
#join metamodel predictions with log returns and initialize portfolio weight vector | |
x = df.set_index(['symbol','date']).join( mm.set_index(['symbol','date']), how='outer' ) | |
x = x[['lr','meta_model']].dropna() | |
x = x.loc[ x['lr'].abs() <= 1000 ] | |
x['w'] = 0 | |
# Sort by date and meta_model, then assign weights based on the top and bottom ranks within each date group | |
x = x.sort_values(by=['date', 'meta_model'], ascending=False) | |
def assign_top_bottom_w(group): | |
n = len(group) | |
# Initialize all weights to 0 | |
group['w'] = 0 | |
# Assign 1 to the top 10 ranks and -1 to the bottom 10 ranks | |
group.iloc[:10, group.columns.get_loc('w')] = 1 | |
group.iloc[-10:, group.columns.get_loc('w')] = -1 | |
# normalize portfolio weights | |
group['w'] = (1/lag_days) * group['w'] / np.sum( np.abs( group['w'] ) ) | |
# print['w'] | |
return group | |
# Apply the function by date group | |
x = x.groupby('date', group_keys=False).apply(assign_top_bottom_w) | |
# view portoflio performance | |
x.groupby('date').apply( lambda y: y['lr'] @ y['w'] ).cumsum().plot( | |
title='Numerai CRYPTO MM Long 10 Short 10 Equal Weight Dollar Neutral\n Cumulative Log Return @ 1X Leverage\n Only Resolved Rounds' | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment