Overall Statistics
Total Trades
5020
Average Win
0.65%
Average Loss
-0.33%
Compounding Annual Return
123.069%
Drawdown
16.500%
Expectancy
0.137
Net Profit
186.567%
Sharpe Ratio
3.121
Probabilistic Sharpe Ratio
96.575%
Loss Rate
62%
Win Rate
38%
Profit-Loss Ratio
1.97
Alpha
0
Beta
0
Annual Standard Deviation
0.256
Annual Variance
0.066
Information Ratio
3.121
Tracking Error
0.256
Treynor Ratio
0
Total Fees
$10793.00
Estimated Strategy Capacity
$13000000.00
Lowest Capacity Asset
NQ Y9CDFY0C6TXD
Portfolio Turnover
1285.80%
#region imports
import pytz
from AlgorithmImports import *
from datetime import time
#endregion
class SimpleNQExample(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2022, 1, 1)  # Set Start Date
        self.SetEndDate(2023, 4, 25)  
        self.SetCash(100000)  # Set Strategy Cash
        
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.SetTimeZone("America/New_York")

        # Set up the future we're looking at
        future = Futures.Indices.NASDAQ100EMini
        self._continuousContract = self.AddFuture(future,
                                                  dataNormalizationMode = DataNormalizationMode.BackwardsRatio,
                                                  dataMappingMode = DataMappingMode.OpenInterest,
                                                  contractDepthOffset = 0,
                                                  extendedMarketHours = True)
        self.commod = self._continuousContract.Symbol
        
        self._continuousContract.SetFilter(timedelta(days=1), timedelta(days=60))

        
        # Set up a 10-period EMA on 5-minute bars
        self._10ema = ExponentialMovingAverage(10)
        self.previous_10ema = None  # Add a variable to store the previous 10EMA value

         # Set up a 500-period EMA on 5-minute bars
        self._500ema = ExponentialMovingAverage(500)
        self.previous_500ema = None  # Add a variable to store the previous 10EMA value

        # Set up a 14-period RSI on 5-minute bars
        self._rsi = RelativeStrengthIndex(12)
        self._5sma_rsi = SimpleMovingAverage(5)
        self.previous_5sma_rsi = None

        # Set up a MACD indicator on 5-minute bars
        self._macd = MovingAverageConvergenceDivergence(26, 100, 9)
        self.previous_macd_histogram = None

        # Set up a Momentum indicator on 5-minute bars
        self._momentum = Momentum(9)
        self._5ema_mom = ExponentialMovingAverage(3)
        self.previous_5ema_mom = None

        # Set up an Ultimate Oscillator indicator on 5-minute bars
        self._ultimate_oscillator = UltimateOscillator(7, 14, 28)
        self._5ema_UA = ExponentialMovingAverage(3)
        self.previous_5ema_UA = None

        # Set up a 9-period EMA on 1-minute bars
        self._9ema = ExponentialMovingAverage(3)
        self.previous_9ema = None  # Add a variable to store the previous 9EMA value

        # Now the handler for that consolidator
        self.MyConsHandler = []
        self.MyConsHandler.append(self.Consolidate(self.commod, timedelta(minutes=5), self._5BarHandler))

        # Add a 1-minute consolidator
        self.MyConsHandler.append(self.Consolidate(self.commod, timedelta(minutes=1), self._1BarHandler))  
            
        self.slope9 = dict()
        self.slope9[0] = 0
        self.slope = dict()
        self.slope[0] = 0
        self.slope2 = dict()
        self.slope2[0] = 0
        self.slope500 = dict()
        self.slope500[0] = 0
        self.slope_rsi = dict()
        self.slope_rsi[0] = 0
        self.slope_rsi2 = dict()
        self.slope_rsi2[0] = 0
        self.slope_macd_histogram = dict()
        self.slope_macd_histogram[0] = 0
        self.slope_macd_histogram2 = dict()
        self.slope_macd_histogram2[0] = 0

        self.slope_mom = dict()
        self.slope_mom[0] = 0
        self.slope_mom2 = dict()
        self.slope_mom2[0] = 0
        self.slope_UA = dict()
        self.slope_UA[0] = 0
        self.slope_UA2 = dict()
        self.slope_UA2[0] = 0
        
        self.firsttradelong = dict()
        self.firsttradelong[0] = 0
        self.firsttradeshort = dict()
        self.firsttradeshort[0] = 0
        

        self.Schedule.On(self.DateRules.EveryDay(self._continuousContract.Symbol), \
                     self.TimeRules.At(23, 0), \
                     Action(self.ResetFirstTrade))
        
        history = self.History(self.commod, 2500, Resolution.Minute)
        for index, row in history.iterrows():
            bar = TradeBar(index[2]-Time.OneMinute, self.commod, row.open, row.high, row.low, row.close, row.volume)
            
            # Allow the consolidator to update
            for consolidator in self.MyConsHandler:
                consolidator.Update(bar)
        
        # Create a chart object for 10EMA and slope
        ema_slope_chart = Chart("EMA and Slope Chart")
        ema_slope_chart.AddSeries(Series("10EMA", SeriesType.Line))
        ema_slope_chart.AddSeries(Series("Slope", SeriesType.Line))
        self.AddChart(ema_slope_chart)
    

    def _5BarHandler(self, consolidatedBar):
               
        # EMA takes a data point, which is a time and a price
        self._10ema.Update(consolidatedBar.EndTime, consolidatedBar.Close)

        # Update 500 EMA
        self._500ema.Update(consolidatedBar.EndTime, consolidatedBar.Close)

        # Update RSI
        self._rsi.Update(consolidatedBar.EndTime, consolidatedBar.Close)

        # Update MACD
        self._macd.Update(consolidatedBar.EndTime, consolidatedBar.Close)

        # Update Momentum
        self._momentum.Update(consolidatedBar.EndTime, consolidatedBar.Close)

        # Update 3EMA of Momentum
        self._5ema_mom.Update(consolidatedBar.EndTime, self._momentum.Current.Value)

        # Update Ultimate Oscillator
        self._ultimate_oscillator.Update(consolidatedBar)      

        # Update 3EMA of Ultimate Oscillator
        self._5ema_UA.Update(consolidatedBar.EndTime, self._ultimate_oscillator.Current.Value)


        

        if self._10ema.IsReady and self.previous_10ema is not None:
            # Calculate the slope
            self.slope2[0] = self.slope[0]
            slope = self._10ema.Current.Value - self.previous_10ema
            self.slope[0] = slope

            # Plot the 10EMA and slope
            #self.Plot("EMA and Slope Chart", "10EMA", self._10ema.Current.Value)
            #self.Plot("EMA and Slope Chart", "Slope", slope)

        if self._500ema.IsReady and self.previous_500ema is not None:
            # Calculate the slope
            slope500 = self._500ema.Current.Value - self.previous_500ema
            self.slope500[0] = slope500

        if self._rsi.IsReady:
            # Update the 5EMA of RSI
            self._5sma_rsi.Update(consolidatedBar.EndTime, self._rsi.Current.Value)

        if self._5sma_rsi.IsReady and self.previous_5sma_rsi is not None:
            # Calculate the slope of the 5EMA of RSI
            self.slope_rsi2[0] = self.slope_rsi[0]
            slope_rsi = self._5sma_rsi.Current.Value - self.previous_5sma_rsi
            self.slope_rsi[0] = slope_rsi

        if self._macd.IsReady and self.previous_macd_histogram is not None:
            # Calculate the slope of the MACD histogram
            self.slope_macd_histogram2[0] = self.slope_macd_histogram[0]
            slope_macd_histogram = self._macd.Histogram.Current.Value - self.previous_macd_histogram
            self.slope_macd_histogram[0] = slope_macd_histogram

        if self._5ema_mom.IsReady and self.previous_5ema_mom is not None:
            # Calculate the slope of the 3EMA of momentum indicator
            self.slope_mom2[0] = self.slope_mom[0]
            slope_mom = self._5ema_mom.Current.Value - self.previous_5ema_mom
            self.slope_mom[0] = slope_mom

        if self._ultimate_oscillator.IsReady:
            # Update the 3EMA of Ultimate Oscillator
            self._5ema_UA.Update(consolidatedBar.EndTime, self._ultimate_oscillator.Current.Value)

        if self._5ema_UA.IsReady and self.previous_5ema_UA is not None:
            # Calculate the slope of the 3EMA of Ultimate Oscillator
            self.slope_UA2[0] = self.slope_UA[0]
            slope_UA = self._5ema_UA.Current.Value - self.previous_5ema_UA
            self.slope_UA[0] = slope_UA


        # Update the previous_500ema value after plotting
        if self._500ema.IsReady:
            self.previous_500ema = self._500ema.Current.Value

        # Update the previous_macd_histogram value after plotting
        if self._macd.IsReady:
            self.previous_macd_histogram = self._macd.Histogram.Current.Value
            
        # Update the previous_10ema value after plotting
        if self._10ema.IsReady:
            self.previous_10ema = self._10ema.Current.Value

        # Update the previous_5ema_rsi value after plotting
        if self._5sma_rsi.IsReady:
            self.previous_5sma_rsi = self._5sma_rsi.Current.Value

        # Update the previous_5ema_mom value after plotting
        if self._5ema_mom.IsReady:
            self.previous_5ema_mom = self._5ema_mom.Current.Value

        # Update the previous_5ema_UA value after plotting
        if self._5ema_UA.IsReady:
            self.previous_5ema_UA = self._5ema_UA.Current.Value
        
        #self.Log("10EMA: {0}, 10 EMA slope: {1}, 500 EMA: {2}, RSI5sma: {3}".format(round(self._10ema.Current.Value, 2), round(self.slope[0], 2), round(self._500ema.Current.Value, 2), round(self._5sma_rsi.Current.Value, 2)))  

    def _1BarHandler(self, consolidatedBar):
        
        
        # EMA takes a data point, which is a time and a price
        self._9ema.Update(consolidatedBar.EndTime, consolidatedBar.Close)

        if self._9ema.IsReady and self.previous_9ema is not None:
            # Calculate the slope
            slope9 = self._9ema.Current.Value - self.previous_9ema
            self.slope9[0] = slope9

        # Update the previous_9ema value after plotting
        if self._9ema.IsReady:
            self.previous_9ema = self._9ema.Current.Value    

    def ResetFirstTrade(self):
        self.firsttradelong[0] = 0
        self.firsttradeshort[0] = 0


    def OnData(self, data: Slice): 
        if not data.ContainsKey(self.commod):
            return

        if self.previous_10ema is None or not self._10ema.IsReady:
            return

        slope = self.slope[0]
        slope_rsi = self.slope_rsi[0] 
        slope_macd_histogram = self.slope_macd_histogram[0]
        slope500 = self.slope500[0]
        slope2 = self.slope2[0]
        slope_rsi2 = self.slope_rsi2[0] 
        slope_macd_histogram2 = self.slope_macd_histogram2[0]
        slope9 = self.slope9[0]
        momslope = self.slope_mom[0]
        momslope2 = self.slope_mom2[0]
        UAslope = self.slope_UA[0]
        UAslope2 = self.slope_UA2[0]
        
        futures_invested = [holding.Symbol for holding in self.Portfolio.Values if holding.Type == SecurityType.Future and holding.Invested]
        futures_invested_short = [(symbol, holding.Quantity) for symbol, holding in self.Portfolio.items() if holding.Type == SecurityType.Future and holding.Symbol.SecurityType == SecurityType.Future and holding.IsShort]
        futures_invested_long = [(symbol, holding.Quantity) for symbol, holding in self.Portfolio.items() if holding.Type == SecurityType.Future and holding.Symbol.SecurityType == SecurityType.Future and holding.IsLong]
        futures_invested_long1 = [symbol for symbol, holding in self.Portfolio.items() if holding.Type == SecurityType.Future and holding.Symbol.SecurityType == SecurityType.Future and holding.IsLong]
        futures_invested_short1 = [symbol for symbol, holding in self.Portfolio.items() if holding.Type == SecurityType.Future and holding.Symbol.SecurityType == SecurityType.Future and holding.IsShort]



        current_time = self.Time.time()

        trading_start = time(8, 0)
        trading_end = time(19, 30)
        #trading_allowed = trading_start <= current_time <= trading_end
        trading_not_allowed = current_time > trading_end
        trading_allowed = trading_start <= current_time <= trading_end and self.Time.weekday() != 6
        


        if slope500 > 0 and trading_allowed:
            if slope > 0 and slope_rsi > 0 and slope9 > 0 and momslope > 0 and UAslope > 0 and not futures_invested_long1:
                
                if self.firsttradelong[0] == 0:
                    self.firsttradelong[0] = 1
                    self.firsttradeshort[0] = 2

                if self.firsttradelong[0] == 2:
                    # Liquidate short positions if any
                    for symbol, quantity in futures_invested_short:
                        self.MarketOrder(symbol, -quantity)  # Liquidate the short position
                
                    self.Log("futures invested: {0}, Long {1}, Short {2}".format(futures_invested, futures_invested_long, futures_invested_short))
                    self.MarketOrder(self._continuousContract.Mapped, 1)
                    self.Log("Going long")
                    self.firsttradeshort[0] = 2

            if slope < 0 and slope_rsi < 0 and slope9 < 0 and momslope < 0 and UAslope < 0 and not futures_invested_short1:
                if slope2 < 0 and slope_rsi2 < 0 and momslope2 < 0 and UAslope2 < 0:
                    if self.firsttradeshort[0] == 0:
                        self.firsttradeshort[0] = 1
                        self.firsttradelong[0] = 2
                        

                    if self.firsttradeshort[0] == 2:
                        for symbol, quantity in futures_invested_long:
                            self.MarketOrder(symbol, -quantity)
                    
                        self.Log("futures invested: {0}, Long {1}, Short {2}".format(futures_invested, futures_invested_long, futures_invested_short))
                        self.MarketOrder(self._continuousContract.Mapped, -1)
                        self.Log("Going short")
                        self.firsttradelong[0] = 2

        if slope500 < 0 and trading_allowed:
            if slope < 0 and slope_rsi < 0 and slope9 < 0 and momslope < 0 and UAslope < 0 and not futures_invested_short1:
                if self.firsttradeshort[0] == 0:
                    self.firsttradeshort[0] = 1
                    self.firsttradelong[0] = 2

                if self.firsttradeshort[0] == 2:               
                    for symbol, quantity in futures_invested_long:
                        self.MarketOrder(symbol, -quantity)
                
                    self.Log("futures invested: {0}, Long {1}, Short {2}".format(futures_invested, futures_invested_long, futures_invested_short))
                    self.MarketOrder(self._continuousContract.Mapped, -1)
                    self.Log("Going short")
                    self.firsttradelong[0] = 2

            if slope > 0 and slope_rsi > 0 and slope9 > 0 and momslope > 0 and UAslope > 0 and not futures_invested_long1:
                if slope2 > 0 and slope_rsi2 > 0 and momslope2 > 0 and UAslope2 > 0:
                    if self.firsttradelong[0] == 0:
                        self.firsttradelong[0] = 1
                        self.firsttradeshort[0] = 2

                    if self.firsttradelong[0] == 2:
                        # Liquidate short positions if any
                        for symbol, quantity in futures_invested_short:
                            self.MarketOrder(symbol, -quantity)  # Liquidate the short position
                    
                        self.Log("futures invested: {0}, Long {1}, Short {2}".format(futures_invested, futures_invested_long, futures_invested_short))
                        self.MarketOrder(self._continuousContract.Mapped, 1)
                        self.Log("Going long")
                        self.firsttradeshort[0] = 2

        if slope500 > 0 and trading_not_allowed:
            if slope > 0 and slope_rsi > 0 and slope9 > 0 and momslope > 0 and UAslope > 0 and not futures_invested_long1:
                # Liquidate short positions if any
                for symbol, quantity in futures_invested_short:
                    self.MarketOrder(symbol, -quantity)  # Liquidate the short position
            if slope < 0 and slope_rsi < 0 and slope9 < 0 and momslope < 0 and UAslope < 0 and not futures_invested_short1:
                if slope2 < 0 and slope_rsi2 < 0 and momslope2 < 0 and UAslope2 < 0:
                    for symbol, quantity in futures_invested_long:
                        self.MarketOrder(symbol, -quantity)

        if slope500 < 0 and trading_not_allowed:
            if slope < 0 and slope_rsi < 0 and slope9 < 0 and momslope < 0 and UAslope < 0 and not futures_invested_short1:
                for symbol, quantity in futures_invested_long:
                        self.MarketOrder(symbol, -quantity)   
            if slope > 0 and slope_rsi > 0 and slope9 > 0 and momslope > 0 and UAslope > 0 and not futures_invested_long1:
                if slope2 > 0 and slope_rsi2 > 0 and momslope2 > 0 and UAslope2 > 0:
                    # Liquidate short positions if any
                    for symbol, quantity in futures_invested_short:
                        self.MarketOrder(symbol, -quantity)  # Liquidate the short position
        
        friday_close_time = time(16, 58)
        if self.Time.weekday() == 4 and current_time == friday_close_time:
            for symbol in futures_invested:
                self.MarketOrder(symbol, -self.Portfolio[symbol].Quantity)  # Liquidate the position