Overall Statistics |
Total Trades 124 Average Win 2.68% Average Loss -0.37% Compounding Annual Return 21.246% Drawdown 12.200% Expectancy 6.377 Net Profit 295.410% Sharpe Ratio 1.667 Probabilistic Sharpe Ratio 95.737% Loss Rate 10% Win Rate 90% Profit-Loss Ratio 7.18 Alpha 0.127 Beta 0.2 Annual Standard Deviation 0.088 Annual Variance 0.008 Information Ratio 0.32 Tracking Error 0.144 Treynor Ratio 0.735 Total Fees $417.36 Estimated Strategy Capacity $4100000.00 Lowest Capacity Asset XLK RGRPZX100F39 |
## SIMON LesFlex June 2021 ## ## Modified by Vladimir and Frank ### Key Short—Term Economic Indicators. The Key Economic Indicators (KEI) database contains monthly and quarterly statistics ### (and associated statistical methodological information) for the 33 OECD member and for a selection of non—member countries ### on a wide variety of economic indicators, namely: quarterly national accounts, industrial production, composite leading indicators, ### business tendency and consumer opinion surveys, retail trade, consumer and producer prices, hourly earnings, employment/unemployment, ### interest rates, monetary aggregates, exchange rates, international trade and balance of payments. Indicators have been prepared by national statistical ### agencies primarily to meet the requirements of users within their own country. In most instances, the indicators are compiled in accordance with ### international statistical guidelines and recommendations. However, national practices may depart from these guidelines, and these departures may ### impact on comparability between countries. There is an on—going process of review and revision of the contents of the database in order to maximise ### the relevance of the database for short—term economic analysis. ### For more information see: http://stats.oecd.org/OECDStat_Metadata/ShowMetadata.ashx?Dataset=KEI&Lang=en ### Reference Data Set: https://www.quandl.com/data/OECD/KEI_LOLITOAA_OECDE_ST_M-Leading-indicator-amplitude-adjusted-OECD-Europe-Level-ratio-or-index-Monthly # Further links: # https://app.hedgeye.com/insights/77156-chart-of-the-day-what-works-in-which-quad?type=macro # https://stockcharts.com/freecharts/rrg/ # https://seekingalpha.com/article/4434713-sector-rotation-strategy-using-the-high-yield-spread # https://www.oecd.org/sdd/compositeleadingindicatorsclifrequentlyaskedquestionsfaqs.htm # https://www.quantconnect.com/forum/discussion/11566/kei-based-strategy/p1 import numpy as np from QuantConnect.Python import PythonQuandl class QuandlImporterAlgorithm(QCAlgorithm): def Initialize(self): # Leading Indicator, Amplitude Adjusted, Oecd — EUROPE, Level, Ratio Or Index #self.quandlCode = "OECD/KEI_LOLITOAA_OECDE_ST_M" # Leading Indicator, Amplitude Adjusted, Oecd — TOTAL, Level, Ratio Or Index self.quandlCode = "OECD/KEI_LOLITOAA_OECD_ST_M" ## Optional argument - your personal token necessary for restricted dataset Quandl.SetAuthCode("RXk7Mxue6oH1TM-U8b7c") self.SetStartDate(2015,1, 1) #Set Start Date self.SetEndDate(datetime.today() - timedelta(1)) #Set End Date self.SetCash(100000) #Set Strategy Cash # LIVE TRADING if self.LiveMode: self.Debug("Trading Live!") self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) # Group Trading # Use a default FA Account Group with an Allocation Method self.DefaultOrderProperties = InteractiveBrokersOrderProperties() # account group created manually in IB/TWS self.DefaultOrderProperties.FaGroup = "KEI1x" # supported allocation methods are: EqualQuantity, NetLiq, AvailableEquity, PctChange self.DefaultOrderProperties.FaMethod = "AvailableEquity" # set a default FA Allocation Profile # Alex: I commented the following line out, since it would "reset" the previous settings #self.DefaultOrderProperties = InteractiveBrokersOrderProperties() # allocation profile created manually in IB/TWS # self.DefaultOrderProperties.FaProfile = "TestProfileP" #Algo Start # Benchmark using qqq & bond only? self.use_qqq_tlt_only = False # Tickers self.SetBenchmark("SPY") self.SPY = self.AddEquity('SPY', Resolution.Hour).Symbol self.stock = self.AddEquity('QQQ', Resolution.Hour).Symbol self.bond = self.AddEquity('TLT', Resolution.Hour).Symbol self.XLF = self.AddEquity('XLF', Resolution.Hour).Symbol self.XLE = self.AddEquity('XLE', Resolution.Hour).Symbol self.XLB = self.AddEquity('XLB', Resolution.Hour).Symbol self.XLI = self.AddEquity('XLI', Resolution.Hour).Symbol self.XLY = self.AddEquity('XLY', Resolution.Hour).Symbol self.XLP = self.AddEquity('XLP', Resolution.Hour).Symbol self.XLU = self.AddEquity('XLU', Resolution.Hour).Symbol self.XLK = self.AddEquity('XLK', Resolution.Hour).Symbol self.XLV = self.AddEquity('XLV', Resolution.Hour).Symbol self.XLC = self.AddEquity('XLC', Resolution.Hour).Symbol self.GLD = self.AddEquity('GLD', Resolution.Hour).Symbol self.AGG = self.AddEquity('AGG', Resolution.Hour).Symbol self.TIPS = self.AddEquity('TIPs', Resolution.Hour).Symbol symbols = ['QQQ', 'TLT', 'XLF', 'XLE', 'XLB', 'XLI', 'XLY', 'XLP', 'XLU', 'XLK', 'XLV', 'XLC','SPY','GLD','AGG','TIPS'] # Rate of Change for plotting self.sharpe_dict = {} for symbol in symbols: self.sharpe_dict[symbol] = SharpeRatio(symbol, 42, 0.) self.RegisterIndicator(symbol, self.sharpe_dict[symbol], Resolution.Daily) self.SetWarmup(42) # Vars self.init = True self.regime = 0 self.kei = self.AddData(QuandlCustomColumns, self.quandlCode, Resolution.Daily, TimeZones.NewYork).Symbol self.sma = self.SMA(self.kei, 1) self.mom = self.MOMP(self.kei, 2) self.Schedule.On(self.DateRules.EveryDay(self.stock), self.TimeRules.AfterMarketOpen(self.stock, 31), self.Rebalance) def Rebalance(self): if self.IsWarmingUp or not self.mom.IsReady or not self.sma.IsReady: return initial_asset = self.stock if self.mom.Current.Value > 0 else self.bond if self.init: self.SetHoldings(initial_asset, 1) self.init = False # Return the historical data for custom 90 day period #keihist = self.History([self.kei],self.StartDate-timedelta(100),self.StartDate-timedelta(10)) # Return the last 1400 bars of history keihist = self.History([self.kei], 6*220) #keihist = keihist['Value'].unstack(level=0).dropna() # Define adaptive tresholds keihistlowt = np.nanpercentile(keihist, 15.) keihistmidt = np.nanpercentile(keihist, 50.) keihisthight = np.nanpercentile(keihist, 90.) kei = self.sma.Current.Value keimom = self.mom.Current.Value if self.use_qqq_tlt_only == True: # KEI momentum if (keimom >= 0) and (not self.regime == 1): self.regime = 1 self.Liquidate() self.SetHoldings(self.stock, .99) elif (keimom < 0) and (not self.regime == 0): self.regime = 0 self.Liquidate() self.SetHoldings(self.bond, .99) else: if (keimom > 0 and kei <= keihistlowt) and (not self.regime == 1): # RECOVERY self.regime = 1 self.Debug(f'{self.Time} 1 RECOVERY: INDUSTRIAL / MATERIALS / CUSTOMER DISCR / TECH') self.Liquidate() self.SetHoldings(self.XLI, .24) self.SetHoldings(self.XLK, .25) self.SetHoldings(self.XLB, .25) self.SetHoldings(self.XLY, .25) elif (keimom > 0 and kei >= keihistlowt and kei < keihistmidt) and (not self.regime == 2): # EARLY EXPANSION - Technology, Transporation self.regime = 2 self.Debug(f'{self.Time} 2 EARLY: INDUSTRIAL / CUSTOMER DISCR / FINANCIAL') self.SetHoldings(self.XLI, .29) self.SetHoldings(self.XLK, .20) self.SetHoldings(self.XLB, .10) self.SetHoldings(self.XLY, .25) self.SetHoldings(self.XLF, .10) elif (keimom > 0 and kei >= keihistmidt and kei < keihisthight) and (not self.regime == 3): # REBOUND - Basic Materials, Metals, Energy, High Interest Finance self.regime = 3 self.Debug(f'{self.Time} 3 REBOUND: INDUSTRIAL / TECH / MATERIALS') self.Liquidate() self.SetHoldings(self.XLI, .39) self.SetHoldings(self.XLK, .40) self.SetHoldings(self.XLB, .10) self.SetHoldings(self.XLF, .10) elif (keimom > 0 and kei >= keihisthight) and (not self.regime == 4): # TOP RISING - High Interest Finance, Real Estate, IT, Commodities, Precious Metals self.regime = 4 self.Debug(f'{self.Time} 4 TOP RISING: INDUSTRIAL / TECH / FINANCIAL') self.Liquidate() self.SetHoldings(self.XLI, .33) self.SetHoldings(self.XLK, .33) self.SetHoldings(self.XLF, .33) elif (keimom < 0 and kei >= keihisthight) and (not self.regime == 3.7): # TOP DECLINING - Utilities self.regime = 3.7 self.Debug(f'{self.Time} 4 TOP DECLINING: BOND / UTILITIES') self.Liquidate() self.SetHoldings(self.bond, .94) self.SetHoldings(self.XLU, .05) elif (keimom < 0 and kei <= keihisthight and kei > keihistmidt) and (not self.regime == 2.7): # LATE - self.regime = 2.7 self.Debug(f'{self.Time} 5 LATE: HEALTH / TECH / CUSTOMER DISCR') self.Liquidate() self.SetHoldings(self.XLV, .35) self.SetHoldings(self.XLK, .20) self.SetHoldings(self.XLY, .20) self.SetHoldings(self.AGG, .20) elif (keimom < 0 and kei <= keihistmidt and kei > keihistlowt) and (not self.regime == 1.7): # DECLINE - Defensive Sectors, Utilities, Consumer Staples self.regime = 1.7 self.Debug(f'{self.Time} 6 DECLINE: BOND / UTILITIES') self.Liquidate() self.SetHoldings(self.bond, .40) self.SetHoldings(self.AGG, .15) self.SetHoldings(self.TIPS, .15) self.SetHoldings(self.XLU, .10) self.SetHoldings(self.GLD, .10) elif (keimom < 0 and kei <= keihistlowt) and (not self.regime == 0.7): # BOTTOM DECLINING self.regime = 0.7 self.Debug(f'{self.Time} 7 BOTTOM DECLINING: BOND / UTILITIES') self.Liquidate() self.SetHoldings(self.bond, .40) self.SetHoldings(self.AGG, .15) self.SetHoldings(self.TIPS, .15) self.SetHoldings(self.XLU, .10) self.SetHoldings(self.GLD, .10) self.Plot("LeadInd", "SMA(LeadInd)", 100. * self.sma.Current.Value) self.Plot("LeadInd", "keihistlowt", 100. * keihistlowt) self.Plot("LeadInd", "keihistmidt", 100. * keihistmidt) self.Plot("LeadInd", "keihisthight", 100. * keihisthight) self.Plot("MOMP", "MOMP(LeadInd)", min(2., max(-2., self.mom.Current.Value))) self.Plot("MOMP", "Regime", self.regime) #self.Plot("MOM", "XLF", self.sharpe_dict['XLF'].Current.Value) #self.Plot("MOM", "XLE", self.sharpe_dict['XLE'].Current.Value) #self.Plot("MOM", "XLB", self.sharpe_dict['XLB'].Current.Value) #self.Plot("MOM", "XLI", self.sharpe_dict['XLI'].Current.Value) #self.Plot("MOM", "XLY", self.sharpe_dict['XLY'].Current.Value) #self.Plot("MOM", "XLP", self.sharpe_dict['XLP'].Current.Value) #self.Plot("MOM", "XLU", self.sharpe_dict['XLU'].Current.Value) #self.Plot("MOM", "XLK", self.sharpe_dict['XLK'].Current.Value) #self.Plot("MOM", "XLV", self.sharpe_dict['XLV'].Current.Value) #self.Plot("MOM", "XLC", self.sharpe_dict['XLC'].Current.Value) # Quandl often doesn't use close columns so need to tell LEAN which is the "value" column. class QuandlCustomColumns(PythonQuandl): def __init__(self): # Define ValueColumnName: cannot be None, Empty or non-existant column name self.ValueColumnName = "Value"