Dear Vladimir,
sorry for the late response. Was occupied by other events and unable to log in in the past couple of days.
Here is the code i used for live paper trading. Basically, i changed the code to ONData, instead of Scheule.On and had it on paper trading since Mid July 2021. Now i have two issues with the code:
- it generated signal to buy equity ETF while the backtest stayed in bond ETF all the time
- the code seems to generate/cancel orders every minute after a trade signal is created (you can refer to the attached Pic)

===============================================
import numpy as np
class MarketTimingRuleStrategy(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 8, 1) # Set Start Date
#self.SetEndDate(20019, 12, 31) #Set End Date
#self.SetEndDate(2021,3,7)#set end date
self.cap = 100000
# Teck Stocks: Largest 100 companies on NASDAQ
self.STK1 = self.AddEquity('SSO', Resolution.Daily).Symbol
# Tech Stock: FDN tracks a market-cap-weighted index of the largest and most liquid U.S. Internet companies.
self.STK2 = self.AddEquity('FDN', Resolution.Daily).Symbol
# US Treasury Long Term ETF: TLT tracks a market-weighted index of debt issued by the US Treasury with remaining maturities of 20 years or more.
self.BND1 = self.AddEquity('TLT', Resolution.Daily).Symbol
# US Treasury Medium Term ETF: TLH tracks a market-weighted index of debt issued by the U.S. Treasury. Remaining maturity must be between 10 and 20 years.
self.BND2 = self.AddEquity('TLH', Resolution.Daily).Symbol
self.ASSETS = [self.STK1, self.STK2, self.BND1, self.BND2]
# Overall Market
self.MKT = self.AddEquity('SPY', Resolution.Daily).Symbol
# Industrial: XLI tracks the broad US industrial sector. A 7% drop over an approx. 3-month trading period is considered a substantial drop
self.XLI = self.AddEquity('XLI', Resolution.Daily).Symbol
# Utlities: XLU tracks a market-cap-weighted index of US utilities stocks drawn exclusively from the S&P 500.
self.XLU = self.AddEquity('XLU', Resolution.Daily).Symbol
# iShares Silver Trust (NYSEArca: SLV)
self.SLV = self.AddEquity('SLV', Resolution.Daily).Symbol
# SPDR Gold Trust (NYSEArca: GLD)
self.GLD = self.AddEquity('GLD', Resolution.Daily).Symbol
# Currencies ETF: FXA tracks the changes in value of the Australian dollar relative to the US dollar.
self.FXA = self.AddEquity('FXA', Resolution.Daily).Symbol
# Currencies ETF: FXF tracks the changes in value of the Swiss Franc relative to the US dollar.
self.FXF = self.AddEquity('FXF', Resolution.Daily).Symbol
# Resources: Investco's 3 industrial metals DBB ETF. A 7% drop over an approx. 3-month trading period is considered a substantial drop
self.DBB = self.AddEquity('DBB', Resolution.Daily).Symbol
# Currencies ETF: UUP provides inverse exposure to an index of USDX futures contracts that rises in value as the dollar appreciates relative to a basket of world currencies.
self.UUP = self.AddEquity('UUP', Resolution.Daily).Symbol
# Natural Resources: IGE tracks a market-cap-weighted index of US-listed natural-resource-related companies.
self.IGE = self.AddEquity('IGE', Resolution.Daily).Symbol
# Cost of debt: The SHY ETF (iShares 1-3 Year Treasury Bond) provides the signal. SHY tracks short-term US Treasury debt (1-3 years)
# and changes in this debt’s ‘risk-free’ interest yield should be indicative of changes in firms’ cost of debt which is based on the
# risk-free rate (risk-free rate + risk premium). A 60 basis points increase (i.e., drop in the bond price) over an approx. 3-month
# trading period is considered substantial.
self.SHY = self.AddEquity('SHY', Resolution.Daily).Symbol
self.FORPAIRS = [self.XLI, self.XLU, self.SLV, self.GLD, self.FXA, self.FXF]
self.SIGNALS = [self.XLI, self.DBB, self.IGE, self.SHY, self.UUP]
self.PAIR_LIST = ['S_G', 'I_U', 'A_F'] #Silver - Gold, Industrial - Utilities, Australia - Swiss Franc
self.INI_WAIT_DAYS = 15
self.SHIFT = 55
self.MEAN = 11
self.RET = 126
self.EXCL = 5
self.leveragePercentage = 101
self.selected_bond = self.BND1
self.selected_stock = self.STK1
self.init = 0
self.bull = 1
self.exit = None
self.count = 0
self.outday = 0
self.in_stock = 0
self.spy = []
self.wait_days = self.INI_WAIT_DAYS
self.wt = {}
self.real_wt = {}
#self.SetWarmUp(timedelta(126))
#self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 100),
# self.calculate_signal)
#self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 120),
# self.trade_out)
#self.Schedule.On(self.DateRules.WeekEnd(), self.TimeRules.AfterMarketOpen('SPY', 120),
# self.trade_in)
#self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose('SPY', 0),
# self.record_vars)
symbols = self.SIGNALS + [self.MKT] + self.FORPAIRS
for symbol in symbols:
self.consolidator = TradeBarConsolidator(timedelta(days = 1))
self.consolidator.DataConsolidated += self.consolidation_handler
self.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
self.lookback = 252
self.history = self.History(symbols, self.lookback, Resolution.Daily)
if self.history.empty or 'close' not in self.history.columns:
return
self.history = self.history['close'].unstack(level=0).dropna()
self.update_history_shift()
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
self.history = self.history.iloc[-self.lookback:]
self.update_history_shift()
def update_history_shift(self):
self.history_shift_mean = self.history.shift(self.SHIFT).rolling(self.MEAN).mean()
def returns(self, symbol, period, excl):
prices = self.History(symbol, TimeSpan.FromDays(period + excl), Resolution.Daily).close
return prices[-excl] / prices[0]
# edit: AK 1
def OnData(self, data):
mom = (self.history / self.history_shift_mean - 1)
mom[self.UUP] = mom[self.UUP] * (-1) # measure of USD (what is the relationship?)
mom['S_G'] = mom[self.SLV] - mom[self.GLD] # momentum of silver - gold. silver has fallen more than gold in bear markets, but risen more than gold in bull markets.
mom['I_U'] = mom[self.XLI] - mom[self.XLU] # momentum of industrial minus utilities (increase of risk taking appetite as cyclical stocks outperform defensive stocks)
mom['A_F'] = mom[self.FXA] - mom[self.FXF] # momentum of australia minus swiss franc (increase of risk taking appetite as australian is commodities currency vs franc which is a saft haven currency)
# numpy.nanpercentile(a, q, axis=None, out=None, overwrite_input=False, interpolation='linear', keepdims=<no value>)[source]
# Compute the qth percentile of the data along the specified axis, while ignoring nan values.
# Returns the qth percentile(s) of the array elements.
pctl = np.nanpercentile(mom, 1, axis=0)
# Get out (i.e. a short time out of 15 days) once there's extreme price movements in any (or some or all) of the signals, as defined by pct1,
# If this short-time out is accompanied by a clear signal from any of the 3 signal pairs, which signifies that the fundamentals have changed,
# then stay out for much longer, as the wait.days become 15*15 days.
extreme = mom.iloc[-1] < pctl
self.wait_days = int(
max(0.50 * self.wait_days,
self.INI_WAIT_DAYS * max(1,
np.where((mom[self.GLD].iloc[-1]>0) & (mom[self.SLV].iloc[-1]<0) & (mom[self.SLV].iloc[-2]>0), self.INI_WAIT_DAYS, 1),
np.where((mom[self.XLU].iloc[-1]>0) & (mom[self.XLI].iloc[-1]<0) & (mom[self.XLI].iloc[-2]>0), self.INI_WAIT_DAYS, 1),
np.where((mom[self.FXF].iloc[-1]>0) & (mom[self.FXA].iloc[-1]<0) & (mom[self.FXA].iloc[-2]>0), self.INI_WAIT_DAYS, 1)
)))
adjwaitdays = min(60, self.wait_days)
# self.Debug('{}'.format(self.wait_days))
if (extreme[self.SIGNALS + self.PAIR_LIST]).any():
#if (extreme[self.PAIR_LIST]).any():
self.exit = True
self.bull = False
self.outday = self.count
if self.count >= self.outday + adjwaitdays:
self.bull = True
self.count += 1
self.Plot("In Out", "in_market", int(self.bull))
self.Plot("In Out", "num_out_signals", extreme[self.SIGNALS + self.PAIR_LIST].sum())
self.Plot("Wait Days", "waitdays", adjwaitdays)
if self.returns(self.BND1, self.RET, self.EXCL) < self.returns(self.BND2, self.RET, self.EXCL):
self.selected_bond = self.BND2
#if self.returns(self.BND1, self.RET, self.EXCL) < self.returns(self.BND2, self.RET, self.EXCL):
# self.selected_bond = self.BND1
elif self.returns(self.BND1, self.RET, self.EXCL) > self.returns(self.BND2, self.RET, self.EXCL):
self.selected_bond = self.BND1
if self.returns(self.STK1, self.RET, self.EXCL) < self.returns(self.STK2, self.RET, self.EXCL):
self.selected_stock = self.STK2
#if self.returns(self.STK1, self.RET, self.EXCL) < self.returns(self.STK2, self.RET, self.EXCL):
# self.selected_stock = self.STK1
elif self.returns(self.STK1, self.RET, self.EXCL) > self.returns(self.STK2, self.RET, self.EXCL):
self.selected_stock = self.STK1
if not self.bull:
for sec in self.ASSETS:
self.wt[sec] = 0.99 if sec is self.selected_bond else 0
self.Debug("Exiting the market; trading BONDS")
self.Notify.Email("alan.chenhd@outlook.com", "Live Trade Executed", "Bond bought")
self.trade()
if self.bull:
for sec in self.ASSETS:
self.wt[sec] = 0.99 if sec is self.selected_stock else 0
self.Debug("Entering the market; trading STOCKS")
self.Notify.Email("alan.chenhd@outlook.com", "Live Trade Executed", "Equity bought")
self.trade()
def trade(self):
for sec, weight in self.wt.items():
if weight == 0 and self.Portfolio[sec].IsLong:
self.Liquidate(sec)
cond1 = weight == 0 and self.Portfolio[sec].IsLong
cond2 = weight > 0 and not self.Portfolio[sec].Invested
if cond1 or cond2:
self.SetHoldings(sec, weight)
if cond1:
self.Debug("Invested; selling")
elif cond2:
self.Debug("Not invested; buying")
===================
Many thanks for your help and Best wishes
Alan