from AlgorithmImports import *


class PivotBasedTradingAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2024, 1, 1)
        # self.SetEndDate(2020, 1, 1)
        self.SetCash(100000)

        self.Debug(f"The algorithm time zone is set to: {self.TimeZone}")
        self.SetTimeZone("America/New_York")

        # Set brokerage model
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        # Add symbol with 5-minute resolution
        # Add symbol
        self.symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
        
        # Create a 5-minute consolidator
        five_minute_consolidator = TradeBarConsolidator(timedelta(minutes=5))
        five_minute_consolidator.DataConsolidated += self.OnDataConsolidated
        self.SubscriptionManager.AddConsolidator(self.symbol, five_minute_consolidator)

        # Create a daily consolidator
        daily_consolidator = TradeBarConsolidator(timedelta(days=1))
        daily_consolidator.DataConsolidated += self.DailyDataConsolidated
        self.SubscriptionManager.AddConsolidator(self.symbol, daily_consolidator)


        # Schedule function to liquidate 15 minutes before market close
        self.Schedule.On(self.DateRules.EveryDay(self.symbol), 
                         self.TimeRules.BeforeMarketClose(self.symbol, 15), 
                         self.LiquidatePosition)

        # Initialize RollingWindow for high, low, and close
        self.highWindow = RollingWindow[float](2) # Holds the last 2 high prices
        self.lowWindow = RollingWindow[float](2) # Holds the last 2 low prices
        self.closeWindow = RollingWindow[float](2) # Holds the last 2 close prices


        # Initialize pivot point attributes
        self.r1 = None
        self.r2 = None
        self.s1 = None
        self.s2 = None

        # Warm up the RollingWindows
        self.SetWarmUp(2, Resolution.Daily)

    def DailyDataConsolidated(self, sender, bar):
        # This handler is called when the daily consolidator publishes a new consolidated bar
        if self.IsWarmingUp:
            return

        # Add the new data to the rolling windows for daily high, low, and close
        self.highWindow.Add(bar.High)
        self.lowWindow.Add(bar.Low)
        self.closeWindow.Add(bar.Close)

        # Calculate pivot points using daily data
        self.CalculatePivotPoints()

    def OnDataConsolidated(self, sender, bar):

        '''
        This is called for each 5-minute bar of data. If the pivot points haven't been calculated yet (they are None), it returns early. 
        If it's the first 5-minute bar of the day (9:30 AM), it calculates the pivot points. 
        The trading logic checks if the current bar's closing price is greater than r1 to enter a long position or if it's less than r1 to exit the position.
        '''
        if self.IsWarmingUp:
            return
            
        self.Debug(f"Processing bar for {bar.EndTime}: Close={bar.Close}")
        # Skip if pivot points haven't been calculated yet
        if self.r1 is None or self.r2 is None or self.s1 is None or self.s2 is None:
            self.Debug(f"Pivot points not yet calculated {self.Time}")
            return
        
            self.Debug(f"Pivot points: R1={self.r1}, R2={self.r2}, S1={self.s1}, S2={self.s2}")

        # Trading logic
        if bar.Close > self.r1 and not self.Portfolio.Invested:
            self.Debug(f"Signal to go Long: 5m Close: {bar.Close} > R1: {self.r1}")
            self.SetHoldings(self.symbol, 1)  # Long entry
        elif self.Portfolio.Invested and bar.Close < self.r1:
            self.Debug(f"Signal to go Close: 5m Close: {bar.Close} < R1: {self.r1}")
            self.Liquidate()  # Exit position

    def CalculatePivotPoints(self):
        '''
        Calculates the pivot points using the previous day's high, low, and close prices.
        This method is called at the beginning of the trading day to set the r1, r2, s1, and s2 values.
        '''
        # Check if rolling windows are ready if not, return
        if not (self.highWindow.IsReady and self.lowWindow.IsReady and self.closeWindow.IsReady):
            self.Debug("Not enough data to calculate pivot points.")
            return

        previousHigh = self.highWindow[1]
        previousLow = self.lowWindow[1]
        previousClose = self.closeWindow[1]
        self.Debug(f"Stored previous day's OHLC: High={previousHigh}, Low={previousLow}, Close={previousClose}")
        

        # If variables are set and ready, calculare the pivot levels
        if previousHigh is not None and previousLow is not None and previousClose is not None:

            p = (previousHigh + previousLow + previousClose) / 3
            self.r1 = 2 * p - previousLow
            self.r2 = p + (previousHigh - previousLow)
            self.s1 = 2 * p - previousHigh
            self.s2 = p - (previousHigh - previousLow)
            self.Debug(f"Calculated Pivot Points - R1: {self.r1}, R2: {self.r2}, S1: {self.s1}, S2: {self.s2}")
        else:
            self.Debug("Cannot calculate pivot points.")
        

    def LiquidatePosition(self):
        '''
        Liquidates any invested position 15 minutes before the market close.
        '''
        # Liquidate 15 minutes before market close
        if self.Portfolio.Invested:
            self.Liquidate(self.symbol)
236376_1706125181.jpgI am trying to get data consolidators to work. I shared a backtest which does not execute any trades but I have since updated my code and get the following error: I will also share my updated blcok of code leading to this error. Any help in understanding and resolving this issue would be greatly appreciated. I believe I am properly setting up the consolidators for my trading logic but it just does not process correctly. I would really love to undertand what I am doing wrong. I am using a 5minute and daily consolidator after subscribing to the asset's minutely data.