Hi,

I'm trying to include a rolling window for hourly BTCUSD (Market: GDAX) data in my algo but keep receiving the following error:

Runtime Error: KeyNotFoundException : 'BTCUSD' wasn't found in the QuoteBars object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey("BTCUSD")
   at QuantConnect.Data.Market.DataDictionary`1.get_Item(Symbol symbol) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Common/Data/Market/DataDictionary.cs:line 226

   at QuantConnect.Data.Market.QuoteBars.get_Item(Symbol symbol) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Common/Data/Market/QuoteBars.cs:line 56

  at OnData
    self.quoteBarWindow.Add(data.QuoteBars[self.btcusd]) #add BTCUSD data to rolling window
===
   at Python.Runtime.PyObject.Invoke(PyTuple args in main.py:line 79

 KeyNotFoundException : 'BTCUSD' wasn't found in the QuoteBars object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey("BTCUSD")
   at QuantConnect.Data.Market.DataDictionary`1.get_Item(Symbol symbol) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Common/Data/Market/DataDictionary.cs:line 226

   at QuantConnect.Data.Market.QuoteBars.get_Item(Symbol symbol) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Common/Data/Market/QuoteBars.cs:line 56
	 (Open Stacktrace)

I've tried requesting the same data (i.e. Hourly BTCUSD 1 Jan 2020 - 1 Jan 2021) in a separate algo and it's worked. I've also tried the algo in question with EURUSD data instead and it's worked fine.

Am I missing something obvious or can anybody see where I'm going wrong?

I've tried to attach the failed backtest but don't know if it worked. The code for the algo in question is copied below anyway just in case. Yes I know my coding is terrible.

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 BTCUSD 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 BTCUSD 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 BTCUSD 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()

 

Any help at all would be much appreciated, thanks

 

 

 

Author