Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return -95.059% Drawdown 3.600% Expectancy 0 Net Profit -1.904% Sharpe Ratio -6.603 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.076 Beta 2.613 Annual Standard Deviation 0.121 Annual Variance 0.015 Information Ratio -5.853 Tracking Error 0.079 Treynor Ratio -0.306 Total Fees $3.66 Estimated Strategy Capacity $5000000.00 Lowest Capacity Asset TQQQ UK280CGTCB51 |
""" SEL(stock selection part) Based on the 'Momentum Strategy with Market Cap and EV/EBITDA' strategy introduced by Jing Wu, 6 Feb 2018 adapted and recoded by Jack Simonson, Goldie Yalamanchi, Vladimir, Peter Guenther, Leandro Maia and Simone Pantaleoni https://www.quantconnect.com/forum/discussion/3377/momentum-strategy-with-market-cap-and-ev-ebitda/p1 https://www.quantconnect.com/forum/discussion/9678/quality-companies-in-an-uptrend/p1 https://www.quantconnect.com/forum/discussion/9632/amazing-returns-superior-stock-selection-strategy-superior-in-amp-out-strategy/p1 https://www.quantconnect.com/terminal/#live-view-tab I/O(in & out part) The Distilled Bear in & out algo Dan's original notes: "This is based on Peter Guenther great “In & Out” algo. Included Tentor Testivis recommendation to use volatility adaptive calculation of WAIT_DAYS and RET. Included Vladimir's ideas to eliminate fixed constants Help from Thomas Chang" https://www.quantopian.com/posts/new-strategy-in-and-out https://www.quantconnect.com/forum/discussion/9597/the-in-amp-out-strategy-continued-from-quantopian/ """ from QuantConnect.Data.UniverseSelection import * import math import numpy as np import pandas as pdv import scipy as sp class EarningsFactorWithMomentum_InOut(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 7, 29) self.SetEndDate(2021, 7, 30) #Set End Date self.cap = 100000 self.SetCash(self.cap) self.DISTILLED_BEAR_Init = 0 res = Resolution.Minute # Holdings ### 'Out' holdings and weights self.BND1 = self.AddEquity('TMF', res).Symbol #TLT; TMF for 3xlev self.TMF = self.AddEquity('TMF', res).Symbol self.BOND = self.AddEquity('TLT', res).Symbol self.quantity = {self.BND1: 0, self.BOND:0} self.TQQQ = self.AddEquity('TQQQ', res).Symbol ##### In & Out parameters ##### # Feed-in constants self.INI_WAIT_DAYS = 15 # out for 3 trading weeks self.wait_days = self.INI_WAIT_DAYS # Market and list of signals based on ETFs self.MRKT = self.AddEquity('SPY', res).Symbol # market self.QQQ = self.AddEquity('QQQ', res).Symbol # tech market self.TQQQ = self.AddEquity('TQQQ', res).Symbol self.GOLD = self.AddEquity('GLD', res).Symbol # gold self.SLVA = self.AddEquity('SLV', res).Symbol # vs silver self.UTIL = self.AddEquity('XLU', res).Symbol # utilities self.INDU = self.AddEquity('XLI', res).Symbol # vs industrials self.METL = self.AddEquity('DBB', res).Symbol # input prices (metals) self.USDX = self.AddEquity('UUP', res).Symbol # safe haven (USD) self.FORPAIRS = [self.GOLD, self.SLVA, self.UTIL, self.INDU, self.METL, self.USDX] # Specific variables self.DISTILLED_BEAR = 1#999 self.BE_IN = 1#999 self.BE_IN_PRIOR = 0 self.VOLA_LOOKBACK = 126 self.WAITD_CONSTANT = 85 self.DCOUNT = 0 # count of total days since start self.OUTDAY = (-self.INI_WAIT_DAYS+1) # dcount when self.be_in=0, initial setting ensures trading right away self.fill_act = 0 # Variables for charts self.spy = [] self.qqq = [] self.benchmark = [] self.portfolio_value = [self.cap] * 60 self.year = self.Time.year self.saw_qqq_base = [] self.saw_portfolio_base = [] # set a warm-up period to initialize the indicator self.SetWarmUp(timedelta(350)) ##### Momentum & fundamentals strategy parameters ##### self.UniverseSettings.Resolution = res self.AddUniverse(self.UniverseCoarseFilter, self.UniverseFundamentalsFilter) self.num_coarse = 100 #200 self.num_screener = 30 #100 # changed from 15 self.num_stocks = 4 # lowered from 10 self.formation_days = 126 #126 was original value self.lowmom = False self.data = {} self.setrebalancefreq = 60 # X days, update universe and momentum calculation self.updatefinefilter = 0 self.symbols = None self.reb_count = 0 self.fill_act_symbol = None self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 30), # reduced time self.rebalance_when_out_of_the_market) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose('SPY', 0), self.record_vars) self.Schedule.On( self.DateRules.WeekEnd(), self.TimeRules.AfterMarketOpen('SPY', 29), self.Weeklydeposit) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 25), # reduced time self.first_day_liquidation) #Calculate the share per min based on the trading volume per min fill_time = [] p = 1 #interval(minutues) y = int((390-30)/p) # total market open hour (minutes) (390(Total minute)-Dealying 30mins after open ) for i in range(1, y): fill_time.append(i*p + 30) for x in fill_time: self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', x), # reduced time self.fill) self.vollookback = 30 #volume calculation lookback (15mins) self.volcalsymbols = [self.BND1] + [self.TQQQ] # Setup daily consolidation symbols = [self.MRKT] + [self.QQQ] + self.FORPAIRS for symbol in symbols: self.consolidator = TradeBarConsolidator(timedelta(days=1)) self.consolidator.DataConsolidated += self.consolidation_handler self.SubscriptionManager.AddConsolidator(symbol, self.consolidator) # Warm up history self.lookback = 126 self.history = self.History(symbols, self.lookback, Resolution.Daily) #calculate close price if self.history.empty or 'close' not in self.history.columns: return self.history = self.history['close'].unstack(level=0).dropna() #EMA data self.averages = {} self.buy = 3 def UniverseCoarseFilter(self, coarse): if not (((self.DCOUNT-self.reb_count)==self.setrebalancefreq) or (self.DCOUNT == self.OUTDAY + self.wait_days - 1)): self.updatefinefilter = 0 return Universe.Unchanged self.updatefinefilter = 1 # drop stocks which have no fundamental data or have too low prices selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 5)] # rank the stocks by dollar volume filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in filtered[:self.num_coarse]] def UniverseFundamentalsFilter(self, fundamental): if self.updatefinefilter == 0: return Universe.Unchanged filtered_fundamental = [x for x in fundamental if (x.ValuationRatios.EVToEBITDA > 0) and x.OperationRatios.OperationRevenueGrowth3MonthAvg.Value > 0 and (x.EarningReports.BasicAverageShares.ThreeMonths > 0) and float(x.EarningReports.BasicAverageShares.ThreeMonths) * x.Price > 6e9 and x.SecurityReference.IsPrimaryShare and x.SecurityReference.SecurityType == "ST00000001" and x.SecurityReference.IsDepositaryReceipt == 0 and x.CompanyReference.IsLimitedPartnership == 0] top = sorted(filtered_fundamental, key = lambda x: x.ValuationRatios.EVToEBITDA, reverse=True)[:self.num_screener] self.symbols = [x.Symbol for x in top] self.updatefinefilter = 0 self.reb_count = self.DCOUNT # fine_series = pd.Series(self.symbols) # self.Log('EV/EBITDA Rank: ' + str(fine_series)) # self.Notify.Sms("+18605138853", 'EV/EBITDA Rank: ' + str(fine_series)) return self.symbols def OnSecuritiesChanged(self, changes): #for security in changes.AddedSecurities: # if security.Symbol not in self.data: # self.data[security.Symbol] = SymbolData(security.Symbol, self.formation_days, self) addedSymbols = [] for security in changes.AddedSecurities: addedSymbols.append(security.Symbol) if security.Symbol not in self.data: self.data[security.Symbol] = SymbolData(security.Symbol, self.formation_days, self) if len(addedSymbols) > 0: history = self.History(addedSymbols, 1 + self.formation_days, Resolution.Daily).loc[addedSymbols] for symbol in addedSymbols: try: self.data[symbol].Warmup(history.loc[symbol]) except: self.Debug("not added in Universe :" + str(symbol)) continue def consolidation_handler(self, sender, consolidated): self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close self.history = self.history.iloc[-self.lookback:] def derive_vola_waitdays(self): volatility = 0.6 * np.log1p(self.history[[self.MRKT]].pct_change()).std() * np.sqrt(252) wait_days = int(volatility * self.WAITD_CONSTANT) returns_lookback = int((1.0 - volatility) * self.WAITD_CONSTANT) return wait_days, returns_lookback def first_day_liquidation(self): if self.DCOUNT == 0: self.Liquidate() self.Log("first day liquidation") def rebalance_when_out_of_the_market(self): if self.Portfolio.TotalPortfolioValue > 10000000: if self.fill_act == 0: del self.quantity[self.BND1] self.BND1 = self.BOND self.quantity = {self.BND1 : 0} self.volcalsymbols = [self.BND1] + [self.TQQQ] elif self.Portfolio.TotalPortfolioValue <= 10000000 : if self.fill_act == 0: del self.quantity[self.BND1] self.BND1 = self.TMF self.quantity = {self.BND1 : 0} self.volcalsymbols = [self.BND1] + [self.TQQQ] self.vol_history = self.History(self.volcalsymbols, 150, Resolution.Minute) self.volumes = self.vol_history['volume'].unstack(level = 0).dropna() self.TQQQsharepertick = self.volumes[self.TQQQ].rolling(window=self.vollookback).mean().iloc[-1]*0.3 self.BND1sharepertick = self.volumes[self.BND1].rolling(window=self.vollookback).mean().iloc[-1] self.TQQQsharepertick = math.floor(self.TQQQsharepertick) self.BND1sharepertick = math.floor(self.BND1sharepertick) self.wait_days, returns_lookback = self.derive_vola_waitdays() ## Check for Bears returns = self.history.pct_change(returns_lookback).iloc[-1] silver_returns = returns[self.SLVA] gold_returns = returns[self.GOLD] industrials_returns = returns[self.INDU] utilities_returns = returns[self.UTIL] metals_returns = returns[self.METL] dollar_returns = returns[self.USDX] self.DISTILLED_BEAR = (((gold_returns > silver_returns) and (utilities_returns > industrials_returns)) and (metals_returns < dollar_returns) ) if self.DISTILLED_BEAR_Init == 0: if self.DISTILLED_BEAR: self.DISTILLED_BEAR_Init = 1 self.Debug("Market Sentiment : Distilled Bear - Initialized") self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - Initialized") if self.DISTILLED_BEAR: SPY_hist = self.History(self.Symbol("SPY"), 200, Resolution.Daily) SPY_hist_price = SPY_hist.close if (SPY_hist_price.rolling(window = 3).mean()[-1] - SPY_hist_price.rolling(window = 30).mean()[-1])/SPY_hist_price.rolling(window = 30).mean()[-1] > 0.025: self.DISTILLED_BEAR = False self.BE_IN = True self.Debug("Market Sentiment : Distilled Bear - In the market") self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - In the market") self.DISTILLED_BEAR_Init = 0 # elif SPY_hist_price.rolling(window = 3).mean()[-1] > SPY_hist_price.rolling(window = 9).mean()[-1] > SPY_hist_price.rolling(window = 32).mean()[-1] > SPY_hist_price.rolling(window = 72).mean()[-1] > SPY_hist_price.rolling(window = 180).mean()[-1] : # self.DISTILLED_BEAR = False # self.BE_IN = True # self.Debug("Market Sentiment : Distilled Bear - In the market") # self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - In the market") # self.DISTILLED_BEAR_Init = 0 else: self.Debug("Market Sentiment : Distilled Bear - Out of the market") self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - Out of the market") elif self.DISTILLED_BEAR == False and self.BE_IN == True: self.Debug("Market Sentiment : Bull") self.Notify.Sms("+18605138853", "Market Sentiment : Bull") elif self.DISTILLED_BEAR == False and self.BE_IN == False: self.Debug("Market Sentiment : Distilled Bear - Wait for the confirmation (15 trading days)") self.Notify.Sms("+18605138853", "Market Sentiment : Wait for the confirmation (15 trading days)") # Determine whether 'in' or 'out' of the market if self.DISTILLED_BEAR: self.BE_IN = False self.OUTDAY = self.DCOUNT # if self.quantity[self.BND1] == 0: for symbol in self.quantity.copy().keys(): if symbol == self.BND1: continue elif self.quantity[symbol] <= self.TQQQsharepertick: self.Order(symbol, - self.quantity[symbol]) self.Debug([str(self.Time), str(symbol), str(-self.quantity[symbol])]) self.quantity[symbol] = 0 del self.quantity[symbol] self.fill_act = 0 elif self.quantity[symbol] > self.TQQQsharepertick: if self.fill_act == 3: self.Order(symbol, - self.TQQQsharepertick) self.Debug([str(self.Time), str(symbol), str(-self.TQQQsharepertick)]) self.quantity[symbol] -= self.TQQQsharepertick self.fill_act = 2 self.fill_act_symbol = symbol quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[self.BND1].Close history_BND1 = self.History(self.BOND, 300, Resolution.Daily) Open = history_BND1.open.unstack(0)[self.BOND][-1] Close = history_BND1.close.unstack(0)[self.BOND][-1] Stable_PriceAction = Open < Close * 1.02 hlc3 = (history_BND1.close.unstack(0)[self.BOND][-1] + history_BND1.open.unstack(0)[self.BOND][-1] + history_BND1.high.unstack(0)[self.BOND][-1]) /3 Close_history = history_BND1.close.unstack(0)[self.BOND] self.averages[self.BND1] = SelectionDataShort(Close_history) if ((hlc3 > self.averages[self.BND1].MA13.Current.Value and self.averages[self.BND1].MA5.Current.Value > self.averages[self.BND1].MA13.Current.Value and Close_history.pct_change(1)[-1] > -0.015) or Stable_PriceAction): self.buy = 1 else: self.buy = 0 if self.buy == 1: if self.fill_act == 0 and self.quantity[self.BND1] == 0: if math.floor(quantity) <= self.BND1sharepertick: self.quantity[self.BND1] = math.floor(quantity) self.Order(self.BND1, self.quantity[self.BND1]) self.Debug([str(self.Time), str(self.BND1), str(self.quantity[self.BND1])]) self.fill_act = 3 elif math.floor(quantity) > self.BND1sharepertick: sym = self.BND1 self.Order(sym, self.BND1sharepertick) self.Debug([str(self.Time), str(sym), self.BND1sharepertick]) self.quantity[sym] = self.BND1sharepertick self.fill_act = 1 self.fill_act_symbol = self.BND1 elif self.buy == 0: if self.fill_act == 3 and self.quantity[self.BND1] > 0: if self.quantity[self.BND1] <= self.BND1sharepertick: self.Order(self.BND1, - self.quantity[self.BND1]) self.Debug([str(self.Time), str(self.BND1), str(-self.quantity[self.BND1])]) self.quantity[self.BND1] = 0 self.fill_act = 0 elif self.quantity[self.BND1] > self.BND1sharepertick: self.Order(self.BND1, - self.BND1sharepertick) self.Debug([str(self.Time), str(self.BND1), str(-self.BND1sharepertick)]) self.quantity[self.BND1] -= self.BND1sharepertick self.fill_act = 2 self.fill_act_symbol = self.BND1 if (self.DCOUNT >= self.OUTDAY + self.wait_days): self.BE_IN = True # Update stock ranking/holdings, when swithing from 'out' to 'in' plus every X days when 'in' (set rebalance frequency) if (self.BE_IN and not self.BE_IN_PRIOR) or (self.BE_IN and (self.DCOUNT==self.reb_count)): chosen_df = self.calc_return(self.symbols) self.rebalance() self.Debug('Best momentum (EV/EBITDA): ' + str(chosen_df)) self.Notify.Sms("+18605138853", 'Best momentum (EV/EBITDA): ' + str(chosen_df)) self.BE_IN_PRIOR = self.BE_IN self.DCOUNT += 1 self.Notify.Sms("+18605138853", "Net LIQ :" + str(self.Portfolio.TotalPortfolioValue)) self.create_charts() def rebalance(self): # chosen_df = self.calc_return(self.symbols) # self.Log('Best momentum (EV/EBITDA): ' + str(chosen_df)) # self.Notify.Sms("+18605138853", 'Best momentum (EV/EBITDA): ' + str(chosen_df)) # self.chosen_df = chosen_df.iloc[:self.num_stocks] # self.Log("Chosen 4 tickers :" + str(self.chosen_df)) if self.quantity[self.BND1] > 0 : if self.quantity[self.BND1] <= self.BND1sharepertick: self.Order(self.BND1, - self.quantity[self.BND1]) self.Debug([str(self.Time), str(self.BND1), str(-self.quantity[self.BND1])]) self.quantity[self.BND1] = 0 self.fill_act = 0 elif self.quantity[self.BND1] > self.BND1sharepertick: if self.fill_act == 3 : self.Order(self.BND1, - self.BND1sharepertick) self.Debug([str(self.Time), str(self.BND1), str(-self.BND1sharepertick)]) self.quantity[self.BND1] -= self.BND1sharepertick self.fill_act = 2 self.fill_act_symbol = self.BND1 # weight = 1 / self.num_stocks for symbol in self.quantity.copy().keys(): if symbol == self.BND1 and self.quantity[symbol] > 0: pass # # elif symbol != self.TQQQ: # # self.Order(symbol, - self.quantity[symbol]) # # self.Debug([str(self.Time), str(symbol), str(-self.quantity[symbol])]) # # del self.quantity[symbol] # # self.fill_act = 0 # elif symbol == self.TQQQ: # # self.Debug("Availible funds :" + str(self.Portfolio.TotalPortfolioValue * 0.99)) # quantity = self.Portfolio.TotalPortfolioValue * 0.1 / self.Securities[symbol].Close # # self.Log(f'Quantity for {symbol} is {quantity} (line 338)...') # if self.fill_act == 3: # if math.floor(quantity) != self.quantity[symbol]: # if (math.floor(quantity) - self.quantity[symbol]) > 0: # if (math.floor(quantity) - self.quantity[symbol]) > self.TQQQsharepertick: # self.Order(symbol, (math.floor(quantity) - self.quantity[symbol])) # self.Debug([str(self.Time), str(symbol), (math.floor(quantity) - self.quantity[symbol])]) # self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol]) # self.fill_act = 1 # self.fill_act_symbol = self.TQQQ # elif (math.floor(quantity) - self.quantity[symbol]) <= self.TQQQsharepertick: # self.Order(symbol, math.floor(quantity) - self.quantity[symbol]) # self.Debug([str(self.Time), str(symbol), str(math.floor(quantity) -self.quantity[symbol])]) # self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol]) # self.fill_act = 3 if self.TQQQ not in self.quantity.keys(): quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[self.TQQQ].Close if self.fill_act == 0 : if math.floor(quantity) <= self.TQQQsharepertick: self.quantity[self.TQQQ] = math.floor(quantity) self.Order(self.TQQQ, self.quantity[self.TQQQ]) self.Debug([str(self.Time), str(self.TQQQ), str(self.quantity[self.TQQQ])]) self.fill_act = 3 elif math.floor(quantity) > self.TQQQsharepertick: sym = self.TQQQ self.Order(sym, self.TQQQsharepertick) self.Debug([str(self.Time), str(sym), self.TQQQsharepertick]) self.quantity[sym] = self.TQQQsharepertick self.fill_act = 1 self.fill_act_symbol = self.TQQQ def Weeklydeposit(self): if self.fill_act == 3: #Only when all buying fill activity is finished, also you can avoid first day error since some self. are not identified. for symbol in self.quantity.copy().keys(): if symbol == self.BND1 and self.quantity[symbol] > 0: quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[symbol].Close if math.floor(quantity) != self.quantity[symbol]: if (math.floor(quantity) - self.quantity[symbol]) > 0: if (math.floor(quantity) - self.quantity[symbol]) > self.BND1sharepertick: self.Order(symbol, (math.floor(quantity) - self.quantity[symbol])) self.Debug([str(self.Time), str(symbol), (math.floor(quantity) - self.quantity[symbol]), "Weekly Rebalance"]) self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol]) self.fill_act = 1 self.fill_act_symbol = self.BND1 elif (math.floor(quantity) - self.quantity[symbol]) <= self.BND1sharepertick: self.Order(symbol, math.floor(quantity) - self.quantity[symbol]) self.Debug([str(self.Time), str(symbol), str(math.floor(quantity) -self.quantity[symbol]), "Weekly Rebalance"]) self.quantity[symbol] += math.floor(quantity) - self.quantity[symbol] self.fill_act = 3 elif symbol == self.TQQQ and self.quantity[symbol] > 0: quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[symbol].Close if math.floor(quantity) != self.quantity[symbol]: if (math.floor(quantity) - self.quantity[symbol]) > 0: if (math.floor(quantity) - self.quantity[symbol]) > self.TQQQsharepertick: self.Order(symbol, (math.floor(quantity) - self.quantity[symbol])) self.Debug([str(self.Time), str(symbol), (math.floor(quantity) - self.quantity[symbol]), "Weekly Rebalance"]) self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol]) self.fill_act = 1 self.fill_act_symbol = self.TQQQ elif (math.floor(quantity) - self.quantity[symbol]) <= self.TQQQsharepertick: self.Order(symbol, math.floor(quantity) - self.quantity[symbol]) self.Debug([str(self.Time), str(symbol), str(math.floor(quantity) -self.quantity[symbol]), "Weekly Rebalance"]) self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol]) self.fill_act = 3 def fill(self): if self.fill_act_symbol : if self.fill_act ==1 or self.fill_act ==2: sym = self.fill_act_symbol # self.Debug(["ongoing fill activity symbol", str(sym)]) # algorithm.Portfolio[security.Symbol].Invested: if self.fill_act == 2: #Sell fill if sym == self.TQQQ: sharepertick = self.TQQQsharepertick elif sym == self.BND1: sharepertick = self.BND1sharepertick if self.quantity[sym] > sharepertick: self.Order(sym, - sharepertick) self.Debug([str(self.Time), str(sym), str(-sharepertick)]) self.quantity[sym] -= sharepertick self.fill_act = 2 elif self.quantity[sym] <= sharepertick: self.Order(sym, - self.quantity[sym]) self.Debug([str(self.Time), str(sym), str(-self.quantity[sym])]) self.quantity[sym] = 0 self.fill_act = 0 if sym != self.BND1: if sym != self.BOND: del self.quantity[sym] if self.BE_IN: self.rebalance() elif self.BE_IN == False and self.quantity[self.BND1] == 0: self.rebalance_when_out_of_the_market() elif self.fill_act == 1: if sym == self.BND1: sharepertick = self.BND1sharepertick elif sym == self.TQQQ: sharepertick = self.TQQQsharepertick quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[sym].Close if (math.floor(quantity) - self.quantity[sym]) > 0: if math.floor(quantity) != self.quantity[sym] and self.quantity[sym] != 0: if (math.floor(quantity) - self.quantity[sym]) > sharepertick: self.Order(sym, sharepertick) self.Debug([str(self.Time), str(sym), sharepertick]) self.quantity[sym] += sharepertick self.fill_act = 1 elif (math.floor(quantity) - self.quantity[sym]) <= sharepertick: self.Order(sym, math.floor(quantity) - self.quantity[sym]) self.Debug([str(self.Time), str(sym), str(math.floor(quantity) - self.quantity[sym])]) self.quantity[sym] += math.floor(quantity) - self.quantity[sym] self.fill_act = 3 else: self.fill_act = 3 def calc_return(self, stocks): ret = {} for symbol in stocks: try: ret[symbol] = self.data[symbol].Roc.Current.Value except: self.Debug(str(symbol)) continue df_ret = pd.DataFrame.from_dict(ret, orient='index') df_ret.columns = ['return'] sort_return = df_ret.sort_values(by = ['return'], ascending = self.lowmom) return sort_return def record_vars(self): pass def create_charts(self): # Record variables ### SPY benchmark self.spy.append(self.history[self.MRKT].iloc[-1]) spy_perf = self.spy[-1] / self.spy[0] * self.cap self.Plot('Strategy Equity', 'SPY', spy_perf) ### Annual saw tooth return comparison: Portfolio VS QQQ self.portfolio_value.append(self.Portfolio.TotalPortfolioValue) if (self.DCOUNT==1) or (self.Time.year!=self.year): self.saw_qqq_base = self.history[self.QQQ].iloc[-1] self.saw_portfolio_base = self.Portfolio.TotalPortfolioValue self.benchmark = self.history[self.QQQ].iloc[-1] saw_qqq_return = (self.benchmark / self.saw_qqq_base -1) saw_portfolio_return = self.portfolio_value[-1] / self.saw_portfolio_base - 1 self.Plot('Annual Saw Tooth Returns: Portfolio VS QQQ', 'Annual portfolio return', round(saw_portfolio_return, 4)) self.Plot('Annual Saw Tooth Returns: Portfolio VS QQQ', 'Annual QQQ return', round(float(saw_qqq_return), 4)) ### IN/Out indicator and wait days self.Plot("In Out", "in_market", int(self.BE_IN)) self.Plot("Wait Days", "waitdays", self.wait_days) ### Leverage account_leverage = self.Portfolio.TotalHoldingsValue / self.Portfolio.TotalPortfolioValue self.Plot('Leverage', 'leverage', round(account_leverage, 4)) self.year = self.Time.year class SymbolData(object): def __init__(self, symbol, roc, algorithm): self.Symbol = symbol self.Roc = RateOfChange(roc) self.algorithm = algorithm self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily) algorithm.RegisterIndicator(symbol, self.Roc, self.consolidator) def Warmup(self, history): for index, row in history.iterrows(): self.Roc.Update(index, row['close']) class SelectionDataShort(): #3. Update the constructor to accept a history array def __init__(self, Close_history): self.MA1 = ExponentialMovingAverage(1) self.MA3 = ExponentialMovingAverage(3) self.MA5 = ExponentialMovingAverage(7) self.MA9 = ExponentialMovingAverage(9) self.MA13 = ExponentialMovingAverage(13) self.MA20 = ExponentialMovingAverage(20) self.MA25 = ExponentialMovingAverage(25) self.MA34 = ExponentialMovingAverage(34) self.MA50 = ExponentialMovingAverage(50) self.MA55 = ExponentialMovingAverage(55) self.MA72 = ExponentialMovingAverage(72) self.MA89 = ExponentialMovingAverage(89) self.MA100 = ExponentialMovingAverage(100) self.MA200 = ExponentialMovingAverage(200) self.MA75 = ExponentialMovingAverage(75) self.RSI = RelativeStrengthIndex(14, MovingAverageType.Simple) #4. Loop over the history data and update the indicators for time, price in Close_history.items(): self.update(time, price) def is_ready(self): return self.MA9.IsReady and self.MA1.IsReady and self.MA3.IsReady and self.MA5.IsReady and self.MA13.IsReady and self.MA20.IsReady and self.MA25.IsReady and self.MA34.IsReady and self.MA50.IsReady and self.MA55.IsReady and self.MA72.IsReady and self.MA89.IsReady and self.MA75.IsReady and self.MA100.IsReady and self.MA200.IsReady and self.RSI.IsReady def update(self, time, price): self.MA1.Update(time, price) self.MA3.Update(time, price) self.MA5.Update(time, price) self.MA9.Update(time, price) self.MA13.Update(time, price) self.MA20.Update(time, price) self.MA25.Update(time, price) self.MA34.Update(time, price) self.MA55.Update(time, price) self.MA72.Update(time, price) self.MA89.Update(time, price) self.MA75.Update(time, price) self.MA20.Update(time, price) self.MA50.Update(time, price) self.MA100.Update(time, price) self.MA200.Update(time, price) self.RSI.Update(time, price)