Skip to content

Instantly share code, notes, and snippets.

@robcarver17
Created October 21, 2025 16:16
Show Gist options
  • Save robcarver17/05c4423589c707767a5b30709bec89ce to your computer and use it in GitHub Desktop.
Save robcarver17/05c4423589c707767a5b30709bec89ce to your computer and use it in GitHub Desktop.
import datetime
import random
import pandas as pd
import numpy as np
from syscore.interactive.progress_bar import progressBar
from systems.accounts.account_forecast import pandl_for_instrument_forecast
import statsmodels.formula.api as sm
import matplotlib
matplotlib.use("TkAgg")
matplotlib.rcParams.update({'font.size': 22})
#### FROM FORECAST ACCURACY TO SR
# generate a price series / series of returns at horizon
def avg_over_many_runs(forecast_horizon: int, noise_coef: float, length_days: int,
p: progressBar,
monte: int=100):
many_runs=[]
for __ in range(monte):
p.iterate()
many_runs.append(one_instant_of_regr_results_and_sr(
forecast_horizon=forecast_horizon,
noise_coef=noise_coef,
length_days=length_days
))
pd_all = pd.DataFrame(many_runs)
return pd_all.mean()
def one_instant_of_regr_results_and_sr(forecast_horizon: int, noise_coef: float, length_days: int):
price = generate_random_price_series(length_days)
risk_adj_return = future_risk_adjusted_returns(price, forecast_horizon)
forecast = calculate_forecast(risk_adj_return, noise_coef)
try:
r_squared, tstat = regression_results(risk_adj_return, forecast)
SR = sharpe_ratio(price, forecast)
except:
return dict(SR=np.nan, r_squared=np.nan, tstat=np.nan)
return dict(SR=SR, r_squared=r_squared, tstat=tstat)
def regression_results(risk_adj_change: pd.Series, forecast: pd.Series):
both = pd.concat([risk_adj_change, forecast], axis=1)
both.columns = ['price', 'forecast']
both = both.dropna()
result = sm.ols(formula="price ~ forecast", data=both).fit()
return result.rsquared, result.tvalues.forecast
def sharpe_ratio( price: pd.Series, forecast: pd.Series):
abs_median_forecast = forecast.abs().median()
pandl = pandl_for_instrument_forecast(price=price, forecast=forecast, target_abs_forecast=abs_median_forecast)
return pandl.sharpe()
def calculate_forecast( risk_adj_change: pd.Series, noise_coeff: float=10):
noise = [random.gauss(0, noise_coeff) for __ in range(len(risk_adj_change))]
return risk_adj_change+noise
def future_risk_adjusted_returns( price: pd.Series, forecast_horizon: int):
stdev_estimate = price.diff().rolling(30, min_periods=3).std()
future_price = price.shift(-forecast_horizon)
px_change = future_price - price
risk_adj_change = px_change / stdev_estimate
return risk_adj_change
def generate_random_price_series(length_days: int):
daily_returns = pd.Series(generate_random_returns(length_days), index=arbitrary_timeindex(length_days))
price = daily_returns.cumsum()
return price
def generate_random_returns(length_days: int):
## mean / stdev don't matter
return [random.gauss(0,1) for __ in range(length_days)]
def arbitrary_timeindex(Nperiods, index_start=datetime.date(2000, 1, 1)):
"""
For nice plotting, convert a list of prices or returns into an arbitrary pandas time series
"""
ans = pd.bdate_range(start=index_start, periods=Nperiods)
return ans
"""
all_horizons = [5,10,21, 43, 62, 125,250] ## 1 week, 2 weeks, 1 month, 2m, 3m, 6m, 1 year
all_noises = [1, 3, 5, 7, 10,20,30, 40,60,70, 80, 100, 150, 200]
length_days=2500 ## 10 years
monte=500
all_results = {}
p = progressBar(monte*len(all_horizons)*len(all_noises))
for forecast_horizon in all_horizons:
all_results[forecast_horizon] = {}
for noise_coef in all_noises:
run_results = avg_over_many_runs(forecast_horizon=forecast_horizon,
noise_coef=noise_coef,
length_days=length_days,
monte=monte,
p=p)
all_results[forecast_horizon][noise_coef] = run_results
def extract_data_to_plot(forecast_horizon: int, all_results: dict):
all_this_item=[]
for noise_coef in all_noises:
SR = getattr(all_results[forecast_horizon][noise_coef], 'SR')
r2 = getattr(all_results[forecast_horizon][noise_coef], 'r_squared')
all_this_item.append(dict(SR=SR, r2=r2))
return pd.DataFrame(all_this_item)
forecast_horizon=250
extract_data_to_plot(forecast_horizon, all_results).plot( 'r2','SR', logx=True)
matplotlib.pyplot.title("%d days" % forecast_horizon)
extract_data_to_plot(5, all_results).plot('SR', 'r2', logy=True)
import pickle
with open("/home/rob/temp/chapter16.pck", 'wb') as f:
pickle.dump(file=f, obj=all_results)
"""
from systems.provided.basic.system import basic_db_futures_system
from systems.provided.rules.carry import carry
from systems.provided.rules.ewmac import ewmac
from systems.trading_rules import TradingRule, create_variations
ewmacrule = TradingRule(ewmac, ['rawdata.get_daily_prices', 'rawdata.daily_returns_volatility'], dict(Lfast=4,Lslow=16))
carryrule = TradingRule(carry, ['rawdata.raw_carry'])
mom_rules = create_variations(ewmacrule, [dict(Lfast=2, Lslow=8), dict(Lfast=4,Lslow=16),
dict(Lfast=8, Lslow=32), dict(Lfast=16, Lslow=64),
dict(Lfast=32, Lslow=128),
dict(Lfast=64, Lslow=256)], key_argname="Lfast", nameformat="Mom_%s_%s")
mom_rules['carry'] = carryrule
system=basic_db_futures_system(trading_rules=mom_rules)
from syscore.pandas.strategy_functions import turnover
from private.projects.futures_book.generate_instrument_list import MASTER_INSTRUMENT_LIST
from systems.accounts.account_forecast import pandl_for_instrument_forecast
def get_results_for_instrument(instrument_code: str, rule_name:str, system):
forecast = system.rules.get_raw_forecast(instrument_code, rule_name)
forecast = 10*forecast/forecast.abs().median()
price = system.rawdata.get_daily_prices(instrument_code)
annual_turnover = turnover(forecast, 10)
forecast_horizon = int((365/annual_turnover)/2.0)
risk_adj_return = future_risk_adjusted_returns(price, forecast_horizon)
r_squared, tstat = regression_results(risk_adj_return, forecast)
SR = sharpe_ratio(price, forecast)
return dict(horizon=forecast_horizon, r2=r_squared, SR=SR)
all_results = []
p=progressBar(len(MASTER_INSTRUMENT_LIST)*len(system.rules.trading_rules()))
for instrument in MASTER_INSTRUMENT_LIST:
for rule in list(system.rules.trading_rules().keys()):
p.iterate()
all_results.append(get_results_for_instrument(instrument, rule, system))
all_results = pd.DataFrame(all_results)
all_results = all_results[all_results.SR>0]
#all_horizons = [5,10,21, 43, 62, 125,250] ## 1 week, 2 weeks, 1 month, 2m, 3m, 6m, 1 year
hedges = [1,8,15,30,50,100,175,300]
hindex=7 ## can't be zero
hmin = hedges[hindex-1]
hmax = hedges[hindex]-1 ##ensure no overlap
subset_of_results = all_results[all_results['horizon'].between(hmin,hmax, inclusive='both')]
subset_of_results.plot.scatter( 'r2','SR', logx=True)
matplotlib.pyplot.title("Horizons between %d and %d days" % (hmin, hmax))
matplotlib.pyplot.show(block=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment