Overall Statistics |
Total Trades 293 Average Win 0.12% Average Loss -0.15% Compounding Annual Return 2.039% Drawdown 8.700% Expectancy -0.529 Net Profit 6.924% Sharpe Ratio 0.366 Probabilistic Sharpe Ratio 12.400% Loss Rate 74% Win Rate 26% Profit-Loss Ratio 0.79 Alpha -0.016 Beta 0.229 Annual Standard Deviation 0.062 Annual Variance 0.004 Information Ratio -0.832 Tracking Error 0.175 Treynor Ratio 0.099 Total Fees $293.00 Estimated Strategy Capacity $25000000.00 |
import pandas as pd from pandas.tseries.offsets import BDay from pandas.tseries.offsets import BMonthEnd class InternFundAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2018, 1, 18) self.SetCash(2000) # Risk Management self.hwm = self.Portfolio.TotalPortfolioValue self.max_dd = 500 ### Treasury Strategy { self.TS_AR = .2 # allocation ratio self.tlt = self.AddEquity('TLT', Resolution.Minute).Symbol self.Schedule.On(self.DateRules.MonthEnd(self.tlt), self.TimeRules.BeforeMarketClose(self.tlt, 1), self.MonthlyClose) self.Schedule.On(self.DateRules.EveryDay(self.tlt), self.TimeRules.AfterMarketOpen(self.tlt, 1), self.EveryDayAfterMarketOpen) ### } ### 60:40 Strategy { self.SF_AR = .5 self.weight_by_ticker = {'SPY': 0.6, 'AGG': 0.4, 'VXX': 0.1} self.sixty_forty_tickers = list(self.weight_by_ticker.keys()) for ticker in self.sixty_forty_tickers: self.AddEquity(ticker, Resolution.Minute) self.sixty_forty_rebalance = True ### } ### Turnaround Tuesday Strategy { self.TT_AR = .2 self.spy = self.AddEquity("SPY", Resolution.Minute) self.symbol = self.spy.Symbol self.quantity = 0 self.monday_open_price = 0 self.monday_open = False self.monday_close = False self.tuesday_open = False self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen("SPY", 0), self.SignalMondayOpen) self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.BeforeMarketClose("SPY", 0), self.SignalMondayClose) self.Schedule.On(self.DateRules.Every(DayOfWeek.Tuesday), self.TimeRules.AfterMarketOpen("SPY", 0), self.SignalTuesdayOpen) ### } ### Reverse George Douglas Taylor Strategy { self.GDT_AR = .2 tickers = ['IWM', 'QQQ'] self.symbol_data_by_symbol = {} for ticker in tickers: symbol = self.AddEquity(ticker, Resolution.Minute).Symbol self.symbol_data_by_symbol[symbol] = SymbolData(symbol, self) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 1), self.EveryDayBeforeMarketClose) ### } ### Leveraged ETFS { self.LE_AR = 0.05 self.AddEquity('TQQQ', Resolution.Minute) # 3x QQQ self.AddEquity('UBT', Resolution.Minute) # 3x 20yr Treasury self.AddEquity('UST', Resolution.Minute) # 3x 10yr Treasury self.LE_tickers = ['TQQQ', 'UBT', 'UST'] self.weeks = 0 self.Schedule.On(self.DateRules.WeekStart('UST'), self.TimeRules.AfterMarketOpen('UST', 150), self.LE_Rebalance) ###} ### Bull Market Gap Down Rebound { self.GDR_AR = 0.3 self.sma_short = SimpleMovingAverage(50) self.sma_long = SimpleMovingAverage(200) self.max = Maximum(50) closes = self.History(self.symbol, 200, Resolution.Daily).loc[self.symbol].close for time, close in closes.iteritems(): self.sma_short.Update(time, close) self.sma_long.Update(time, close) self.max.Update(time, close) self.last_close = None self.rebound_quantity = 0 self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.BeforeMarketClose(self.symbol, 15), self.FifteenBeforeClose) ### } def OnData(self, data): # Risk Management value = self.Portfolio.TotalPortfolioValue if value > self.hwm: self.hwm = value if self.hwm - value > self.max_dd: self.Debug("Max DD reached") self.Quit() ### 60:40 Strategy { if self.sixty_forty_rebalance: for ticker in self.sixty_forty_tickers: if data.ContainsKey(ticker): weight = self.weight_by_ticker[ticker] quantity = self.CalculateOrderQuantity(ticker, weight * self.SF_AR / sum(self.weight_by_ticker.values())) if quantity >= 1: self.MarketOrder(ticker, quantity) self.sixty_forty_rebalance = False ### } ### Turnaround Tuesday Strategy { if not data.ContainsKey(self.symbol) or data[self.symbol] is None: return if self.monday_open: self.monday_open = False self.monday_open_price = data[self.symbol].Open elif self.monday_close: self.monday_close = False if data[self.symbol].Close < self.monday_open_price: # Monday is a down day self.quantity = self.CalculateOrderQuantity(self.symbol, self.TT_AR) if self.quantity >= 1: self.MarketOrder(self.symbol, self.quantity) self.monday_open_price = 0 elif self.tuesday_open: self.tuesday_open = False if self.quantity >= 1: self.MarketOnCloseOrder(self.symbol, -self.quantity) self.quantity = 0 ### } def EveryDayAfterMarketOpen(self): ### Treasury Strategy { offset = BMonthEnd() last_day = offset.rollforward(self.Time) trigger_day = last_day - BDay(4) if self.Time == trigger_day: self.SetHoldings(self.tlt, self.TS_AR) ### } ### Reverse George Douglas Taylor Strategy { # Update days_held tracker for each symbol for symbol, symbol_data in self.symbol_data_by_symbol.items(): if self.Securities[symbol].Invested: symbol_data.days_held += 1 self.make_orders('open') ### } ### Bull Market Gap Down Rebound { if self.last_close is None: return # Enter if: # - Gapped down this morning # - Yesterday closed at the highest close in the last 50 bars # - Short MA > Long MA if self.Securities[self.symbol].Open < self.last_close and \ self.max.Current.Value == self.last_close and \ self.sma_short > self.sma_long: self.rebound_quantity = self.CalculateOrderQuantity(self.symbol, self.GDR_AR) self.MarketOrder(self.symbol, self.rebound_quantity) ### } def EveryDayBeforeMarketClose(self): ### Reverse George Douglas Taylor Strategy { self.make_orders('close') # Exit orders with days held == 1 for symbol, symbol_data in self.symbol_data_by_symbol.items(): if symbol_data.days_held == 1: symbol_data.days_held = 0 self.Liquidate(symbol) ### } ### Bull Market Gap Down Rebound { def FifteenBeforeClose(self): if self.rebound_quantity > 0: self.MarketOrder(self.symbol, -self.rebound_quantity) self.rebound_quantity = 0 ### } def MonthlyClose(self): ### 60:40 Strategy { self.sixty_forty_rebalance = True ### } ### Treasury Strategy { self.Liquidate(self.tlt) ### } ### Turnaround Tuesday { def SignalMondayOpen(self): if self.spy.IsTradable: self.monday_open = True def SignalMondayClose(self): if self.monday_open_price: self.monday_close = True def SignalTuesdayOpen(self): if self.quantity: self.tuesday_open = True ### } ### Bull Market Gap Down Rebound { def OnEndOfDay(self): self.last_close = self.Securities[self.symbol].Price self.sma_short.Update(self.Time, self.last_close) self.sma_long.Update(self.Time, self.last_close) self.max.Update(self.Time, self.last_close) ### } ### Reverse George Douglas Taylor Strategy { def make_orders(self, at): for symbol, symbol_data in self.symbol_data_by_symbol.items(): if at == 'open': price = self.Securities[symbol].Open else: price = self.Securities[symbol].Close signal = symbol_data.generate_signal(price, at) if signal: if self.Securities[symbol].Invested: # Extend exit date symbol_data.days_held -= 1 else: # Make order quantity = self.CalculateOrderQuantity(symbol, self.GDT_AR / len(self.symbol_data_by_symbol)) if quantity >= 1: self.MarketOrder(symbol, quantity) ### } ### Leveraged ETFs { def LE_Rebalance(self): if self.weeks % 2 == 0: alloc = self.LE_AR / len(self.LE_tickers) for ticker in self.LE_tickers: quantity = self.CalculateOrderQuantity(ticker, alloc) if quantity >= 1: self.MarketOrder(ticker, quantity) self.weeks += 1 ### } ### Reverse George Douglas Taylor Strategy { class SymbolData: open_price = None days_held = 0 def __init__(self, symbol, algorithm): self.symbol = symbol self.window = RollingWindow[TradeBar](3) # Warm up history history = algorithm.History(symbol, 3, Resolution.Daily) for idx, row in history.iterrows(): tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.window.Add(tradebar) # Setup consolidator self.consolidator = TradeBarConsolidator(timedelta(1)) self.consolidator.DataConsolidated += self.ConsolidationHandler algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator) def ConsolidationHandler(self, sender, consolidated): self.window.Add(consolidated) def generate_signal(self, price, at): if at == 'open': self.open_price = price # If up two days in a row if self.window[2].Close < self.window[1].Close and self.window[1].Close < self.window[0].Close: # Gaps up the third day if at == 'open' and self.window[0].Close < self.open_price: return True # Short open # Doesn't gap up the third day, but the third day is an up day if at == 'close' and self.window[0].Close >= self.open_price and self.window[0].Close < price: return True # Short close ### }