Hi All,
We are trying to implement a simple RSI strategy using consolidators to create two RSI indicators (15 mins and 2 mins). We have come across a weird problem/potential bug, whereby the second RSI indicator stops reporting values after a certain number of days (see attached plot).
Is there a problem with the way we are implementing the RSI indicators via consolidation, or is this something weirder?
Here is our backtest code:
from datetime import datetime, date, time
from datetime import timedelta
class TradeStrategyTest(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021,3, 1) #Set Start Date
self.SetEndDate(2021,6,14) #Set End Date
self.SetCash(30000) #Set Strategy Cash
self.AddEquity("OCGN", Resolution.Minute)
self.RSI1 = self.RSI("OCGN", 14, MovingAverageType.Wilders)
self.RSI2 = self.RSI("OCGN", 14, MovingAverageType.Wilders)
self.SetWarmUp(210)
self.Securities["OCGN"].FeeModel = ConstantFeeModel(1.00)
self.ticket = None # Flag for position status
self.previous_day_close = self.Securities["OCGN"].Price
self.expiry = self.Time
self.openPrice = -1 # Pointer to store opening price
self.lowestPrice = -1 # pointer to store latest daily high
# Set TimeZone
self.SetTimeZone("America/New_York")
# Create our consolidators
Con1 = TradeBarConsolidator(timedelta(minutes=15))
Con2 = TradeBarConsolidator(timedelta(minutes=2))
# Register our Handlers
Con1.DataConsolidated += self.On_W1
Con2.DataConsolidated += self.On_W2
# Register the indicaors with our stock and consolidator
RSI1_Ind = self.RegisterIndicator("OCGN", self.RSI1, Con1)
RSI2_Ind = self.RegisterIndicator("OCGN", self.RSI2, Con2)
# Finally add our consolidators to the subscription
# manager in order to receive updates from the engine
RSI1_Sub = self.SubscriptionManager.AddConsolidator("OCGN", Con1)
RSI2_Sub = self.SubscriptionManager.AddConsolidator("OCGN", Con2)
def OnData(self, data):
# Set local variables
close = self.Securities["OCGN"].Close
quantity = self.CalculateOrderQuantity("OCGN",1)
AskPrice = self.Securities["OCGN"].AskPrice
BidPrice = self.Securities["OCGN"].BidPrice
Spread = (AskPrice - BidPrice)
# Warm up Codition
if self.IsWarmingUp or not data.Bars.ContainsKey("OCGN") or not self.RSI1.IsReady or not self.RSI2.IsReady:
return
# Setup Open and Close Prices and Bars
if self.Time >= self.expiry:
self.previous_day_close = data["OCGN"].Close
self.expiry = Expiry.EndOfDay(self.Time)
self.previous_bar_close = data["OCGN"].Close
change_from_close = (((self.previous_bar_close - self.previous_day_close) / self.previous_bar_close)*100)
#Obtain Low of Day and Update
bar = data["OCGN"]
if not bar.IsFillForward and self.lowestPrice < 0:
self.openPrice = bar.Open
self.lowestPrice = bar.Low
if self.lowestPrice < 0:
return
price = bar.Low
if price < self.lowestPrice: # If we observe a new low
self.lowestPrice = price
# DEBUG FLAGS - IMPORTANT!!!!
#self.Debug(f"Equity Data: {data['OCGN']}")
#self.Debug(f"RSI2: {self.RSI2.Current.Value}")
#self.Debug(f"Time: {self.Time}")
#self.Debug(f"UTC Time: {self.UtcTime}")
# IMPORTANT!!! Time variables to set open/close times and compare them to current time.
# Convert times to variables (necessary for time comparison)
currentTime = self.Time
openTime = time(9,30)
closeTime = time(12,0)
# Convert string to format that can be compared (comparison does not work if you do not do this)
# These comparisons are to test it works, before implementing them in the # Buy Conditions function below
# It is OK to comment them out here, as they are just for testing. Real function below.
#currentTime.strftime('%H%M') >= openTime.strftime('%H%M')
#currentTime.strftime('%H%M') <= closeTime.strftime('%H%M')
# Buy Conditions
if not self.Securities["OCGN"].Invested and self.ticket is None and (currentTime.strftime('%H%M') >= openTime.strftime('%H%M') and currentTime.strftime('%H%M') <= closeTime.strftime('%H%M')):
# If buy conditions are satisfied then place MarketOrder
if ((self.RSI1.Current.Value <=70 and self.RSI1.Current.Value >=20) and (self.RSI2.Current.Value >=0 and self.RSI2.Current.Value <=25)):
self.ticket = self.MarketOrder("OCGN", quantity, tag ="Market Buy") and self.Debug(f"RSI1 Value: {self.RSI1.Current.Value}, RSI2 Value: {self.RSI2.Current.Value}")
# Place Profit take and Stop Loss orders then reset to None
self.LimitOrder("OCGN", -quantity, close * 1.03, tag = "Profit Take")
self.StopMarketOrder("OCGN", -quantity, close * 0.99, tag = "Stopped Out")
self.ticket = None
# Close position if open for more than 15 minutes and set ticket to None
if self.ticket is not None and (self.Time > self.ticket.Time + timedelta(minutes = 15)):
self.Liquidate()
self.ticket = None
# Cancel remaining order if limit order or stop loss order is executed
def OnOrderEvent(self, orderEvent):
order = self.Transactions.GetOrderById(orderEvent.OrderId)
if order.Status == OrderStatus.Filled:
if order.Type == OrderType.Limit or order.Type == OrderType.StopMarket:
self.Transactions.CancelOpenOrders(order.Symbol)
if order.Status == OrderStatus.Canceled:
self.Log(str(orderEvent))
#Reset Daily :Pointer
def OnEndOfDay(self, symbol):
self.lowestPrice = -1
def On_W1(self,sender,bar):
'''
This method will be called every time a new 30 minute bar is ready.
bar = The incoming Tradebar. This is different to the data object in OnData()
'''
self.Plot('RSI', 'W1', self.RSI1.Current.Value)
#self.Plot('RSI', 'W2', self.RSI2.Current.Value)
def On_W2(self,sender,bar):
'''
This method will be called every time a new 30 minute bar is ready.
bar = The incoming Tradebar. This is different to the data object in OnData()
'''
#self.Plot('RSI', 'W1', self.RSI1.Current.Value)
self.Plot('RSI', 'W2', self.RSI2.Current.Value)
Any help would be greatly appreciated!
Varad Kabade
Hi MAK KHALIL,
In the above algorithm, we are trying to plot very frequently(every 2 minutes), which generates a huge amount of data; hence some points are not plotted. Refer to the following doc for more information regarding the charting limitation. We recommend plotting the indicator value once at the end of the day. We cannot use the shortcut method to create an indicator(self.RSI, self.MOMP) when updating them at custom value. Please use Indicator constructor(RelativeStrengthIndex()) in this case. Refer to the attached backtest to see the above changes.
Best,
Varad Kabade
Mak K
Hi Varad kabade , thanks for the info, this was extremely helpful!
I am curious though, why did you remove these two lines of code in the back test, do they not follow best practice for registering an indicator?
Varad Kabade
Hi MAK,
No, the above code doesn't violate best practices. In the algorithm above, we manually update the RSI in the consolidation handlers
Â
so
Â
isn't necessary. Either approach works.Best,
Varad Kabade
Mak K
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!