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
Eoin
Nvm, solved it - needed to insert the following before line 79 in the above:
Eoin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!