class GeekyFluorescentOrangeAntelope(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetEndDate(2021, 1, 1)
self.SetCash(100000)
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)
self.btcusd = self.AddCrypto("BTCUSD", Resolution.Hour).Symbol
self.quoteBarWindow = RollingWindow[QuoteBar](3) #add rolling window for price with 3 datapoints
##add helper variables
#For lower lows#
current_RsiLow = 0
self.current_RsiLow = current_RsiLow
prev_RsiLow = 0
self.prev_RsiLow = prev_RsiLow
price_Lowerlow = 0
self.price_Lowerlow = price_Lowerlow
current_PriceLow = 0
self.current_PriceLow = current_PriceLow
prev_PriceLow = 0
self.prev_PriceLow = prev_PriceLow #this block is for price and RSI lows
#to trigger entry orders#
entry_Primer = 0
self.entry_Primer = entry_Primer # this is to let the algo know to get ready to long
#For placing orders#
self.entryTicket = None
self.stopLimitTicket = None #Limit stop as exit
self.takeProfitTicket = None
self.stopReference = None
#setting up EMA and linking to rolling window
self.ema = self.EMA(self.btcusd, 200, Resolution.Hour)
self.ema.Updated += self.EMAUpdated
self.emaWindow = RollingWindow[IndicatorDataPoint](200)
#History Request for EMA data, returns table with high, low, open, close columns for each bar requested, indexed by close column here. I think this only runs once.
closing_prices = self.History(self.btcusd, 200, Resolution.Hour)["close"]
for time, price in closing_prices.loc[self.btcusd].items():#referencing EURUSD as security for EMA, and updating same for time, price variables
self.ema.Update(time, price)
#setting up RSI and linking to rolling window
self.rsi = self.RSI(self.btcusd, 14, MovingAverageType.Exponential, Resolution.Hour)
self.rsi.Updated += self.RSIUpdated
self.rsiWindow = RollingWindow[IndicatorDataPoint](14)
#History Request for RSI data, returns table with high, low, open, close columns for each bar requested, indexed by close column here. I think this only runs once.
closing_prices = self.History(self.btcusd, 14, Resolution.Hour)["close"]
for time, price in closing_prices.loc[self.btcusd].items():#referencing EURUSD as security for RSI, and updating same for time, price variables
self.rsi.Update(time, price)
#setting up Stochastic and linking to rolling window
self.sto = self.STO(self.btcusd, 14, 3, 3, Resolution.Hour)
self.sto.Updated += self.STOUpdated
self.stoWindow = RollingWindow[IndicatorDataPoint](14)
self.stoKWindow = RollingWindow[float](3)
self.stoDWindow = RollingWindow[float](3)
self.SetWarmUp(14, Resolution.Hour) #have to use warmup function as don't know how to use normal history request with stochastic
def RSIUpdated(self, sender, updated): #function for rolling window, don't fully understand why it's here
self.rsiWindow.Add(updated)
def EMAUpdated(self, sender, updated): #function for rolling window, don't fully understand why it's here
self.emaWindow.Add(updated)
def STOUpdated(self, sender, updated): #function for rolling window, don't fully understand why it's here
self.stoWindow.Add(updated)
def OnData(self, data):
self.quoteBarWindow.Add(data.QuoteBars[self.btcusd]) #add EURUSD data to rolling window
if self.IsWarmingUp: #don't need to reference stochastic indicator here as warm up is a separate function
return
if not self.ema.IsReady:
return
if not self.emaWindow.IsReady:
return
if not self.rsi.IsReady:
return
if not self.rsiWindow.IsReady:
return
if not self.quoteBarWindow.IsReady: #check if rolling window ready
return
if not self.sto.IsReady:
return
if not self.stoWindow.IsReady:
return
self.stoKWindow.Add(self.sto.StochK.Current.Value)
self.stoDWindow.Add(self.sto.StochD.Current.Value)
if not self.stoKWindow.IsReady:
return
if not self.stoDWindow.IsReady:
return
price = self.Securities[self.btcusd].Price
if self.ema.Current.Value < price:
if self.quoteBarWindow[1].Low < self.quoteBarWindow[0].Low and self.quoteBarWindow[1].Low < self.quoteBarWindow[2].Low: #check if low of middle candle lower than flanking candles (i.e. is it a low)
self.prev_PriceLow = self.current_PriceLow #moves current low variable back in list to previous low variable
self.current_PriceLow = self.quoteBarWindow[1].Low #saves current low data as current low variable
self.Log("Current Low: " + str(self.current_PriceLow))
self.Log("Previous Low: " + str(self.prev_PriceLow))
if self.current_PriceLow < self.prev_PriceLow: #check if most recent low is a lower low
self.price_Lowerlow = self.current_PriceLow
if self.rsiWindow[1] < self.rsiWindow[0] and self.rsiWindow[1] < self.rsiWindow[2]: #check if low of middle candle lower than flanking candles (i.e. is it a low). For some reason, if you swap rsiWindow[2] for rsiWindow[self....Count-1], it fucks up.
self.prev_RsiLow = self.current_RsiLow #moves current RSI low variable back in list to previous RSI low variable
self.current_RsiLow = self.rsiWindow[1].Value #saves current RSI low data as current RSI Low variable
if self.current_RsiLow < self.prev_RsiLow: #check if most recent low is a lower low
if not self.quoteBarWindow[1].Low <= self.price_Lowerlow: #check if most recent low is not a lower low
self.entry_Primer = 1
self.Log("Ready...")
if self.entry_Primer == 1 and self.stoKWindow[1] < self.stoDWindow[1] and self.stoKWindow[0] >= self.stoDWindow[0]:
if not self.Portfolio[self.btcusd].IsLong and not self.Transactions.GetOpenOrders(self.btcusd): #checks if already long or if there are active open orders
quantity = self.CalculateOrderQuantity(self.btcusd, 0.1) #calculates number of shares needed such that 10% of buying power is used
self.entryTicket = self.LimitOrder(self.btcusd, quantity, price, "Entry Order") #send limit order w/ tag "Entry Order" and save to variable
self.stopReference = self.current_PriceLow
self.Log("Stop reference/current low: " + str(self.stopReference))
self.Log(price)
self.entry_Primer = 0
self.Log("Primer reset")
self.Plot('Price Chart', 'Low', self.Securities[self.btcusd].Low)
self.Plot('Price Chart', 'High', self.Securities[self.btcusd].High)
self.Plot('Price Chart', 'Open', self.Securities[self.btcusd].Open)
self.Plot('Price Chart', 'Close', self.Securities[self.btcusd].Close)
self.Plot('RSI Chart', 'RSI', self.rsi.Current.Value)
self.Plot("Price Chart", "EMA", self.ema.Current.Value)
self.Plot("Stochastic", "StochK", self.sto.StochK.Current.Value)
self.Plot("Stochastic", "StochD", self.sto.StochD.Current.Value)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status != OrderStatus.Filled: #check if order filled
return
price = self.Securities[self.btcusd].Low
#send stop loss order if entry order filled
if self.entryTicket is not None and self.entryTicket.OrderId == orderEvent.OrderId: #check if entry order placed and OrderID matches (redundant?)
self.stopLimitTicket = self.StopLimitOrder(self.btcusd, -self.entryTicket.Quantity, self.stopReference, self.stopReference) #send stop loss with stop price of previous low
self.Log("Last low: " + str(self.stopReference))
if self.entryTicket is not None and self.entryTicket.OrderId == orderEvent.OrderId: #check if entry order placed and OrderID matches (redundant?)
self.takeProfitTicket = self.StopLimitOrder(self.btcusd, -self.entryTicket.Quantity, self.entryTicket.AverageFillPrice + (1.5 * (self.entryTicket.AverageFillPrice - self.stopReference)), self.entryTicket.AverageFillPrice + (1.5 * (self.entryTicket.AverageFillPrice - self.stopReference))) #send stop loss with 2x risk
self.Log("Entry Fill: " + str(self.entryTicket.AverageFillPrice))
if self.stopLimitTicket is not None and self.stopLimitTicket.OrderId == orderEvent.OrderId:
self.takeProfitTicket.Cancel()
if self.takeProfitTicket is not None and self.takeProfitTicket.OrderId == orderEvent.OrderId:
self.stopLimitTicket.Cancel()