Hi

I tested the effect of the window lenght used for percentage change of the switching pairs. (pairs used to decide on In-OUT)

First test shows the difference between a window length of 2-800 days for the whole period from 2008 until today (one return for the whole period and variation of the window lenght)

Second test, I calculated the return with a rolling window of one year and than looked for the best performing window and its return.

Both test were done for different single pairs. IN-OUT was done with just the SPY. In the first test I switched between SPY and IEF and in the second test between SPY and Cash.

From the first test it looked that around 60 days give the best result/return for the whole period.

The second test, with a rolling window return and picking the optimal window, showed that the optimal window lenght ist ALL OVER THE PLACE….

And now? How should we decide which window length to use and to avoid fitting the window to the best historical result?

Please don't understand me wrong, I'm personally using this method for my investment and I really want to improve the method, but don't know how.

Here the result for the pair DBB/UUP. Upper graphic shows the optimal window lenght.

Following the notebook with the code:

`# Make sure the plot shows up`

%matplotlib inline

# Import libraries that we need

import pandas as pd

import numpy as np

import yfinance as yf

import pyfolio as pf

import matplotlib.pyplot as plt

symbols_to_use = '^VIX ^VXV SPY QQQ MDY IEF TLT DBB UUP XLY XLP SLV GLD XLI XLU FXA FXF FXC FXY SSO QLD MVV SPXL TQQQ MIDU'

# indicator from in-out Quantopian

# self.slv_sid = symbol('SLV').sid # silver

# self.gld_sid = symbol('GLD').sid # gold

# self.xli_sid = symbol('XLI').sid # Consumer Industrial Select Sector SPDR Fund

# self.xlu_sid = symbol('XLU').sid # Consumer Utility Select Sector SPDR Fund

# self.fxa_sid = symbol('FXA').sid # Swiss franc

# self.fxf_sid = symbol('FXF').sid # Ausi dollar

# indicator from Toma Hentea

# self.dbb_sid = symbol('DBB').sid # Invesco DB Base Metals Fund

# self.uup_sid = symbol('UUP').sid # Invesco DB US Dollar Index Fund

# self.xly_sid = symbol('XLY').sid # Consumer Discretionary Select Sector SPDR Fund

# self.xlp_sid = symbol('XLP').sid # Consumer Staples Select Sector SPDR Fund

def timing_test(data,symbl_1, symbl_2,start,end):

data['eqity_ret'] = data['QQQ'].pct_change(1)

data['bonds_ret'] = data['IEF'].pct_change(1)

cagr = np.zeros(end)

for i in range(start,end):

window_risk_on_off = i

# definition of momentum of tickers used to define risk on risk off

data['a_mom'] = data[symbl_1].pct_change(window_risk_on_off)

data['b_mom'] = data[symbl_2].pct_change(window_risk_on_off)

#shift 1 day to avoid hindsight bias

data['a_mom'] = data['a_mom'].shift()

data['b_mom'] = data['b_mom'].shift()

# now we make the decision if risk is on or off

data["risk_off"] = np.where ((data['a_mom']<data['b_mom']),1,0)

data["dummy"] = np.where(data['risk_off'] == 1, data['bonds_ret'], data['eqity_ret'])

strategy_ret_daily = data["dummy"][i:]

days=len(strategy_ret_daily)

cagr[i] = (((((strategy_ret_daily+1).prod())**(1/days))**252-1)*100).round(2)

fig, ax = plt.subplots(figsize=(20,5))

ax.plot(cagr)

ax.set(xlabel='window', ylabel='cagr',

title= symbl_1 + ' vs '+ symbl_2 )

ax.grid()

fig.savefig("test.png")

plt.show()

def timing_test_2(data,roll_wind,symbl_1, symbl_2,start,end):

strategy_ret_daily_rolling = pd.DataFrame()

result = pd.DataFrame(columns=['best_window','best_returns','sec_best_window','sec_best_returns'])

# ticker of benchmark investment

data['eqity_ret'] = data['SPY'].pct_change(1)

data['bonds_ret'] = data['IEF'].pct_change(1)

data['cash_ret'] = data['bonds_ret']

data['cash_ret'] = 0.0

cagr = np.zeros(end)

#strategy_ret_daily_rolling = np.zeros((end,len_data))

# running the strategie for all momentum windows

for i in range(start,end,10):

window_risk_on_off = i

#print(i)

# definition of momentum of tickers used to define risk on risk off

data['a_mom'] = data[symbl_1].pct_change(window_risk_on_off)

data['b_mom'] = data[symbl_2].pct_change(window_risk_on_off)

#shift 1 day to avoid hindsight bias

data['a_mom'] = data['a_mom'].shift()

data['b_mom'] = data['b_mom'].shift()

# now we make the decision if risk is on or off

data["risk_off"] = np.where ((data['a_mom']<data['b_mom']),1,0)

#data["dummy"] = np.where(data['risk_off'] == 1, data['bonds_ret'], data['eqity_ret'])

data["dummy"] = np.where(data['risk_off'] == 1, data['cash_ret'], data['eqity_ret'])

strategy_ret_daily = data["dummy"][i:]

# 22 days rolling window to see the gains

days=roll_wind

# calculating the rolling returns, on yearly basis

strategy_ret_daily_rolling[i] = strategy_ret_daily.rolling(days).apply(lambda x: (((np.prod(1+x)**(1/days))**252-1)*100).round(2))

len_x = strategy_ret_daily_rolling.shape[0]

#print(strategy_ret_daily_rolling.head())

#strategy_ret_daily_rolling.to_csv('out.csv', index=True)

result = pd.DataFrame(np.zeros([len_x, 12]),columns=[

'window_1','returns_1','window_2','returns_2','window_3','returns_3','window_4','returns_4','window_5','returns_5','window_6','returns_6'

])

result = result.reindex(strategy_ret_daily_rolling.index)

for j in range(0,len_x):

if len(strategy_ret_daily_rolling.iloc[j,:].loc[strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()]) > 0:

# first max

a=strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]

# set max to zero

strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]=0

#second max

b=strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]

# set max to zero

strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]=0

# third max

c=strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]

# set max to zero

strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]=0

# 4 max

d=strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]

# set max to zero

strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]=0

# 5 max

e=strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]

# set max to zero

strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]=0

# 6 max

f=strategy_ret_daily_rolling.iloc[j,(strategy_ret_daily_rolling.iloc[j,:]==strategy_ret_daily_rolling.iloc[j,:].max()).values]

if len(a)>1 or len(b)>1 or len(c)>1 or len(d)>1 or len(e)>1 or len(f)>1 :

result.window_1.iloc[j]=np.nan

result.returns_1.iloc[j]=np.nan

result.window_2.iloc[j]=np.nan

result.returns_2.iloc[j]=np.nan

result.window_3.iloc[j]=np.nan

result.returns_3.iloc[j]=np.nan

result.window_4.iloc[j]=np.nan

result.returns_4.iloc[j]=np.nan

result.window_5.iloc[j]=np.nan

result.returns_5.iloc[j]=np.nan

result.window_6.iloc[j]=np.nan

result.returns_6.iloc[j]=np.nan

else:

result.window_1.iloc[j]=a.index[0]

result.returns_1.iloc[j]=a

result.window_2.iloc[j]=b.index[0]

result.returns_2.iloc[j]=b

result.window_3.iloc[j]=c.index[0]

result.returns_3.iloc[j]=c

result.window_4.iloc[j]=d.index[0]

result.returns_4.iloc[j]=d

result.window_5.iloc[j]=e.index[0]

result.returns_5.iloc[j]=e

result.window_6.iloc[j]=f.index[0]

result.returns_6.iloc[j]=f

fig, ax = plt.subplots(figsize=(20,5))

ax.plot(result.window_6,'b+', result.window_5,'b+',result.window_4,'b+', result.window_3,'b+', result.window_2,'g+',result.window_1,'r+')

ax.set(xlabel='time', ylabel='best_windows= 1. red, 2. green, 3-6 blue',

title= symbl_1 + ' vs '+ symbl_2 )

ax.grid()

fig.savefig("test.png")

plt.show()

fig, ax = plt.subplots(figsize=(20,5))

ax.plot(result.returns_6,'b+', result.returns_5,'b+', result.returns_4,'b+',result.returns_3,'b+', result.returns_2,'g+', result.returns_1,'r+')

ax.set(xlabel='time', ylabel='return for best_window = 1. red, 2. green, 3-6 blue',

title= symbl_1 + ' vs '+ symbl_2 )

ax.grid()

#fig.savefig("test.png")

plt.show()

# import data from yahoo

data = yf.download(symbols_to_use, "2008-01-01", "2021-11-01")['Adj Close']

### testing the best window to caluculate percentage change of the two pairs

timing_test(data,'DBB','UUP',2,800)

timing_test(data,'XLY','XLP',2,800)

timing_test(data,'SLV','GLD',2,800)

timing_test(data,'XLI','XLU',2,800)

timing_test(data,'FXA','FXF',2,800)

timing_test(data,'FXA','FXY',2,800)

timing_test(data,'FXC','FXF',2,800)

timing_test(data,'FXC','FXY',2,800)

### running a rolling window for retuns and selecting the best performing windows during that time

timing_test_2(data,250,'DBB','UUP',20,400)

timing_test_2(data,250,'XLY','XLP',20,400)

timing_test_2(data,250,'SLV','GLD',20,400)

timing_test_2(data,250,'XLI','XLU',20,400)

timing_test_2(data,250,'FXA','FXF',20,400)

timing_test_2(data,250,'FXA','FXY',20,400)

timing_test_2(data,250,'FXC','FXF',20,400)

timing_test_2(data,250,'FXC','FXY',20,400)