Skip to content

Instantly share code, notes, and snippets.

@jefferythewind
Created November 1, 2024 15:36
Show Gist options
  • Save jefferythewind/cfd1a7e750dbee3d14756a136ea92306 to your computer and use it in GitHub Desktop.
Save jefferythewind/cfd1a7e750dbee3d14756a136ea92306 to your computer and use it in GitHub Desktop.
Quick script to backtest Numerai's Crypto Meta Model
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