Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-5.588
Tracking Error
0.14
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
# region imports
from AlgorithmImports import *
from datetime import timedelta
#from QuantConnect.Data.Custom.CBOE import *
# endregion

class CombinedAlgorithm(QCAlgorithm):

    def Initialize(self):
        # Initialize
        self.SetStartDate(2023, 1, 10)
        self.SetEndDate(2023, 2, 3)
        self.SetCash(10000) 
        self.SPY = self.AddEquity('SPY', Resolution.Minute).Symbol
        self.SetWarmUp(timedelta(days=20))

        # Universe Selection
        self.AddUniverse(self.MyCoarseFilterFunction)
        self.UniverseSettings.Resolution = Resolution.Minute

        # Scheduled Events
        self.Schedule.On(self.DateRules.EveryDay(),self.TimeRules.AfterMarketOpen(self.SPY),self.MarketOpen)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.SPY, 3), self.LiquidateToggle)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.SPY, 1), self.ResetWatchlist)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.SPY), self.MarketClose)

        # Variables 
        self.Watchlist = []
        self.macdBySymbol = {}
        self.highs = {}
        self.lows = {}
        self.entry_price = {}
        self.trade_count = {}

        # Toggles 
        self.liquidate = False
        
    
    def MyCoarseFilterFunction(self, coarse):
        sorted_by_dollar_volume = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 10 and x.Price < 500], 
                                key=lambda x: x.DollarVolume, reverse=True)
        selected = [x.Symbol for x in sorted_by_dollar_volume[:20]]

        return selected
    
    # SCHEDULED FUNCTIONS
    def MarketOpen(self):
        for symbol in self.Watchlist:
            self.highs[symbol] = []
            self.lows[symbol] = []
            self.entry_price[symbol] = []
            self.trade_count[symbol] = 0
            self.Log(symbol)
    
    def LiquidateToggle(self):
        self.liquidate = True

    def ResetWatchlist(self):
        self.Watchlist = []
        self.macdBySymbol = {}
        self.liquidate = False
    
    def MarketClose(self): 
        return

    def OnData(self, data): 
        if self.IsWarmingUp:
            return   

        # Accessing Data
        release = data.Get(EstimizeRelease)

        for key, value in release.items(): 
            self.Watchlist.append(value.Symbol)

        for symbol in self.Watchlist:
            if not symbol.Value in [x.Value for x in self.macdBySymbol]: continue
            symbol_m =  [x for x in self.macdBySymbol if symbol.Value== x.Value][0]
            symbol_data = self.macdBySymbol[symbol_m]

            if symbol_data is None or not self.macdBySymbol[symbol_m].warmed_up:
                continue

            # VARIABLES
            symbol_price = self.Securities[symbol].Price
            held_stocks = self.Portfolio[symbol].Quantity
            min_high = self.Securities[symbol].High
            min_low = self.Securities[symbol].Low

            prev_macd = symbol_data.prev_macd
            current_macd = symbol_data.current_macd
            prev_macd_slope = symbol_data.prev_macd_slope
            current_macd_slope = symbol_data.current_macd_slope
            current_signal = symbol_data.current_signal

            # Twenty - Five Minute Range
            if symbol in self.highs and symbol in self.lows and len(self.highs[symbol]) < 25 and len(self.lows[symbol]) < 25:
                self.highs[symbol].append(min_high)
                self.lows[symbol].append(min_low)
            if symbol in self.highs and symbol in self.lows and len(self.highs[symbol]) == 25 and len(self.lows[symbol]) == 25:
                five_min_high = max(self.highs[symbol])
                five_min_low = min(self.lows[symbol])
                five_min_range = five_min_high - five_min_low

                
    def OnSecuritiesChanged(self, changes):
        for security in changes.AddedSecurities:
            if security.Symbol not in self.macdBySymbol:
                estimize_release_symbol = self.AddData(EstimizeRelease, security.Symbol).Symbol
                history = self.History([estimize_release_symbol], 10, Resolution.Daily)

                self.AddEquity(security.Symbol, Resolution.Minute)
                symbol_data = SymbolData(self, security.Symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Minute)
                self.macdBySymbol[security.Symbol] = symbol_data

                symbol_data.warmed_up = True

class SymbolData:
    def __init__(self, algorithm, symbol, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution):
        self.symbol = symbol
        self.macd = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)
        self.warmed_up = False
        
        self.prev_macd = 0
        self.current_macd = 0

        self.prev_macd_slope = 0
        self.current_macd_slope = 0

        self.current_signal = 0

        # Create a 5-minute consolidator
        self.consolidator = TradeBarConsolidator(timedelta(minutes=5))
 
        # Register the consolidator with the algorithm
        algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)

        # Update the MACD indicator with the 5-minute bars
        self.consolidator.DataConsolidated += self.OnFiveMinuteBar

        history = algorithm.History(symbol, 1000, Resolution.Minute).loc[symbol]
        for idx, bar in history.iterrows():
            tradeBar = TradeBar(idx, symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
            self.consolidator.Update(tradeBar)
        self.warmed_up = True

    def OnFiveMinuteBar(self, sender, bar):
        self.macd.Update(IndicatorDataPoint(bar.Time, bar.Close))
        
        if self.macd.IsReady:
            self.prev_macd = self.current_macd
            self.current_macd = self.macd.Current.Value

            self.prev_macd_slope = self.current_macd_slope
            self.current_macd_slope = self.current_macd - self.prev_macd

            self.current_signal = self.macd.Signal.Current.Value