I am new to programming and I have been working on my own classes to manage and backtest trades. I wanted to be able to manage trades and signals per symbol so I created a TradableProduct class, this contains a Trade class for managing individual trades based on the TradableProduct.OrderEventsDict Dictionary<int, Trade> where the int is simply the Entry order ID. 

There is also a CustomOrderEvent class that instantiates into Dictionary<string, CustomOrderEvent> to Cue up OrderEvents and Process them by individual TradableProduct object, this Process is locked per symbol/TradableProduct until it is finished,

there are a few other things like TradableProduct.TrendAndStrength() that allows me to sort my products by some variables like trend when I need to.

My problem is I don't seem to be able to trigger limit or stop orders.

I was trying to Que up order events to submit and manage stops but even if I try to submit limit orders when I submit Entry order(which work), my limits and stops never seem to submit. at first I thought it was a problem with my OrderEventCompletion() method but it doesn't seem to matter where I put the order submission method. (I did have a method for just using market orders but I want to be able to chose to have brackets in market).

Any help would be appreciated, sorry its a bit of a mess atm, not sure what I will need and what I can delete later, I haven't decided where to hide those classes and I had trouble trying to create a seperate file. I will link the main.cs file later but I have an upload issue at the moment. cheers.

 

trade.StopLossOrder = StopMarketOrder(trade.Symbol, quantity, trade.StopLossPrice, tag:"Stop Loss");

trade.TargetOrder = LimitOrder(trade.Symbol, quantity, trade.TargetPrice, tag:"Target");

#region imports
    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using MathNet.Numerics;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;   
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Storage;
    using QuantConnect.Data.Custom.AlphaStreams;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{ 
    public enum TrendDirection {Bullish, Bearish, Range, Error}
    public enum Result {Win, Loss, BreakEven, InProgress, Exit, Error};
    public enum TradeSide {Long, Short};
    public class TrendTrading : QCAlgorithm
    {
        private ConcurrentDictionary<int, ManualResetEventSlim> waitHandles = new ConcurrentDictionary<int, ManualResetEventSlim>();
        public readonly Dictionary<Symbol, object> symbolLocks = new Dictionary<Symbol, object>();
        public int EventCounter = int.MinValue;
        public bool AdoptPositionsOn = true;
        public int Period = 10000;
        public decimal StopPercent = 0.5m;
        public decimal TargetPercent = 1m;
        public bool TrailingStopEnabled  = true;
        private List<TradableProduct> TradableProductsList;
        private Dictionary<Symbol, TradableProduct> TradableProductsDict;
        public decimal TradeSize = 100000;
        public decimal openingBalanceWeekly;
        public decimal openingBalanceDaily;
        public bool WeeklyStopHit = false;
        public bool DailyStopHit = false;
        public int MinimumTrendStrengthPercent = 60;
        public int AnalysisLookBackPeriod = 5; // For Trend Strength
        public bool TradingHours;
        public bool AnalysisTime;
        public bool analysisPerformedMorning = false;
        public bool analysisPerformedAfternoon = false;
        public decimal MaxCashValue = 1000000;
        public bool weekendExitFlag;
        public override void Initialize()
        {
            SetStartDate(2008, 1, 2);  //Set Start Date
            SetEndDate(2023, 6, 1);
            SetCash(100000);             //Set Strategy Cash;
            SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin);
            SetTimeZone("UTC");
            SetBenchmark("AUDUSD");
            

            List<string> ProductKeysList = new List<string>
            {
                "AUDUSD",// "EURAUD", "EURCAD", "EURCHF","AUDJPY","EURAUD", "EURCAD", "EURCHF","USDJPY","USDCAD", "USDCHF"
                // "AUDCAD", "AUDCHF", "AUDHKD", "AUDJPY", "AUDNZD", "AUDSGD",
                // "AUDUSD", "CADCHF", "CADHKD", "CADJPY", "CADSGD", "CHFHKD",
                // "CHFJPY", "CHFZAR", "EURAUD", "EURCAD", "EURCHF", "EURCZK",
                // "EURDKK", "EURGBP", "EURHKD", "EURHUF", "EURJPY", "EURNOK",
                // "EURNZD", "EURPLN", "EURSEK", "EURSGD", "EURTRY", "EURUSD",
                // "EURZAR", "GBPAUD", "GBPCAD", "GBPCHF", "GBPHKD", "GBPJPY",
                // "GBPNZD", "GBPPLN", "GBPSGD", "GBPUSD", "GBPZAR", "HKDJPY",
                // "NZDCAD", "NZDCHF", "NZDHKD", "NZDJPY", "NZDSGD", "NZDUSD",
                // "SGDCHF", "SGDHKD", "SGDJPY", "TRYJPY", "USDCAD", "USDCHF",
                // "USDCNH", "USDCZK", "USDDKK", "USDHKD", "USDHUF", "USDINR",
                // "USDJPY", "USDMXN", "USDNOK", "USDPLN", "USDSAR", "USDSEK",
                // "USDSGD", "USDTHB", "USDTRY", "USDZAR", "ZARJPY"
            };
            Log("Products List Created");

            TradableProductsList = new(ProductKeysList.Count);
            TradableProductsDict = new Dictionary<Symbol, TradableProduct>(TradableProductsList.Count());
            
            foreach (var ticker in ProductKeysList)
            {
                var fx = AddForex(ticker, Resolution.Second, Market.Oanda, true, 500);
                Console.WriteLine($"Creating {ticker} Tradable Product");
                TradableProduct tp = new TradableProduct(this, fx ,fx.Symbol, Period, TrailingStopEnabled, TradeSize, MinimumTrendStrengthPercent);
                tp.ResolutionHistory = History<QuoteBar>(tp.Symbol, Period, Resolution.Second);
                tp.ActivateWindows(Period);
                tp.Subscribe();
                //Log($"Subscribed to {tp.Symbol}");
                AddTradableProduct(tp);
                //Log($"Added {ticker} to Tradable Products List");
                Console.WriteLine($"Added {ticker} to Tradable Products List");
            }
            Log("Finished Adding Tradable Products");
            SetWarmUp(TimeSpan.FromDays(4));
            Log("Finished Initializing");
        }

        // we use the add and remove methods so that dictionary and list are always in sync, dictonary is used for fast lookup
        public void AddTradableProduct(TradableProduct tp)
        {
            TradableProductsList.Add(tp);
            TradableProductsDict[tp.Symbol] = tp;
        }

        public void RemoveTradableProduct(TradableProduct tp)
        {
            TradableProductsList.Remove(tp);
            TradableProductsDict.Remove(tp.Symbol);
        }

        // this will be used for selection criteria, any method or indicator can be used
        public void OrderTradableProductsByStrength(int LookBackPeriod, int MinimumTrendStrengthPercent)
        {
            foreach(TradableProduct tp in TradableProductsList)
            {
                if (tp.Subscribed == false){
                    tp.Subscribe();
                }
                tp.Analyse(LookBackPeriod, MinimumTrendStrengthPercent);
            }
            TradableProductsList = TradableProductsList.OrderByDescending(pt => pt.Strength).Take(TradableProductsList.Count()).ToList();  //try order by desscending;
            Log("Finished Ordering Tradable Products by Strength");
        }

        public override void OnData(Slice data)
        { 
            foreach(var bar in data.QuoteBars)
            {
                if (TradableProductsDict.ContainsKey(bar.Key))
                {
                    TradableProductsDict[bar.Key].ResolutionWindow.Add(bar.Value);
                }
            }

            if (Time.DayOfWeek == DayOfWeek.Monday || Time.DayOfWeek == DayOfWeek.Tuesday || Time.DayOfWeek == DayOfWeek.Wednesday || Time.DayOfWeek == DayOfWeek.Thursday)
            {
                weekendExitFlag = false;
            }
            
            if (IsWarmingUp) return;

            //Check if Weekly Stop Hit
            if (Portfolio.TotalPortfolioValue < openingBalanceWeekly * 0.95m)
            {
                Log($"Weekly Stop Hit, Liquidated Portfolio");
                Console.WriteLine($"Weekly Stop Hit, Liquidated Portfolio");
                WeeklyStopHit = true;
            }
            //Check if Weekly Stop Hit
            if (Portfolio.TotalPortfolioValue < openingBalanceDaily * 0.98m)
            {
                Log($"Daily Stop Hit, Liquidated Portfolio");
                Console.WriteLine($"Daily Stop Hit, Liquidated Portfolio");
                DailyStopHit = true;
            }
            
            if(WeeklyStopHit || weekendExitFlag || DailyStopHit)
            {
                foreach (var tp in TradableProductsDict.Values)
                {
                    tp.Liquidate();
                }
            }

            //Reset Weekly stop for new trading week
            if (data.Time.DayOfWeek == DayOfWeek.Sunday && Time.Hour == 17 && (Time.Minute >1 && Time.Minute < 5))
            {
                WeeklyStopHit = false;
                openingBalanceWeekly = Portfolio.Cash;
                Log($"Weekly Stop Reset, Opening Balance = {openingBalanceWeekly}");
                Console.WriteLine($"Weekly Stop Reset, Opening Balance = {openingBalanceWeekly}");
            }
            //Reset Daily stop for new trading day
            if (data.Time.Hour == 24)
            {
                DailyStopHit = false;
                openingBalanceWeekly = Portfolio.Cash;
                Log($"Weekly Stop Reset, Opening Balance = {openingBalanceWeekly}");
                Console.WriteLine($"Weekly Stop Reset, Opening Balance = {openingBalanceWeekly}");
            }

            PreMarketAnalysis();

            TradingHours = (Time.Hour >= 07 && Time.Hour < 09) || (Time.Hour >= 13 && Time.Hour < 15);

            //Control for our resolution triggers
            if (TradableProductsDict.Count() == 0 || TradableProductsDict == null) 
                Console.WriteLine("No Tradable Products");
            
            //Update Our Tradable Product Objects 
            foreach(var bar in data.QuoteBars)
            {
                var tp = TradableProductsDict[bar.Key]; 
                if  (bar.Key == tp.Symbol)
                {
                    ProcessQuedOrderEvents(tp.Symbol);
                    tp.NewTradeAllowed = Portfolio.TotalHoldingsValue < MaxCashValue && !WeeklyStopHit && !DailyStopHit; // -- Not needed as eeach Tradable product has its own max trades allwance
                }       
            }
            
            // Weekend Exit
            if (!weekendExitFlag && Time.DayOfWeek == DayOfWeek.Friday && Time.Hour == 19 && Time.Minute >= 55)
            {
                Console.WriteLine("------Weekend Exit------");
                Log("------Weekend Exit------");
                weekendExitFlag = true;
            }
        }

        public void PreMarketAnalysis()
        {
            // Check if it's time for morning analysis
            if (Time.Hour == 7 && Time.Minute == 01 && !analysisPerformedMorning)
            {
                OrderTradableProductsByStrength(AnalysisLookBackPeriod, MinimumTrendStrengthPercent);   
                Console.WriteLine($"{Time.DayOfWeek} Morning Analysis Time, Tradable Products Ordered by Strength");
                analysisPerformedMorning = true;  // Set flag to avoid duplicate analysis
            }

            // Check if it's time for afternoon analysis
            if (Time.Hour == 13 && Time.Minute == 01 && !analysisPerformedAfternoon)
            {
                OrderTradableProductsByStrength(AnalysisLookBackPeriod, MinimumTrendStrengthPercent);   
                Console.WriteLine($"{Time.DayOfWeek} Afternoon Analysis Time, Tradable Products Ordered by Strength");
                analysisPerformedAfternoon = true;  // Set flag to avoid duplicate analysis
            }

            // Reset flags at the end of each period
            if (Time.Hour != 7 && analysisPerformedMorning)
                analysisPerformedMorning = false;
            if (Time.Hour != 13 && analysisPerformedAfternoon)
                analysisPerformedAfternoon = false;
        }

        public void LiquidateUnwatchedProducts(TradableProduct tradableProduct)
        {
            if (!tradableProduct.Watching && Portfolio[tradableProduct.Symbol].Invested)
            {          
                tradableProduct.Liquidate();
            }
            Log("Liquidate Unwatched Products Complete");
            Console.WriteLine("Liquidate Unwatched Products Complete");  
        }
        public class CustomOrderEvent
        {
            public string EventIdentifier { get; set; }
            public int OrderId { get; set; }
            public Symbol Symbol { get; set; }
            public string Tag { get; set; }
            public OrderStatus Status { get; set; }
            public bool Processed { get; set; }
            public DateTime TimeSubmitted { get; set; }
            public decimal FillPrice { get; set; }
            public string FillPriceCurrency { get; set; }
            public decimal FillQuantity { get; set; }
            public decimal AbsoluteFillQuantity => Math.Abs(FillQuantity);
            public decimal? StopPrice { get; set; }
            public decimal? TriggerPrice { get; set; }
            public decimal? LimitPrice { get; set; }
            public decimal Quantity { get; set; }
            public OrderTicket Ticket { get; set; }

            public CustomOrderEvent(QCAlgorithm algo, OrderEvent orderEvent, int EventCounter)
            {
                EventIdentifier = Convert.ToString(orderEvent.Symbol + orderEvent.OrderId);
                OrderId = EventCounter;
                Symbol = orderEvent.Symbol;
                var order = algo.Transactions.GetOrderById(orderEvent.OrderId);
                Tag = order.Tag;
                Status = orderEvent.Status;
                Processed = false;
                TimeSubmitted = orderEvent.UtcTime;

                // Additional attributes
                FillPrice = orderEvent.FillPrice;
                FillPriceCurrency = orderEvent.FillPriceCurrency;
                FillQuantity = orderEvent.FillQuantity;
                StopPrice = orderEvent.StopPrice;
                TriggerPrice = orderEvent.TriggerPrice;
                LimitPrice = orderEvent.LimitPrice;
                Quantity = orderEvent.Quantity;             
            }
        }

        private object GetSymbolLock(Symbol symbol)
        {
            lock (symbolLocks)
            {
                if (!symbolLocks.ContainsKey(symbol))
                {
                    symbolLocks[symbol] = new object();
                }
                return symbolLocks[symbol];
            }
        }
        
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            lock (GetSymbolLock(orderEvent.Symbol))
            {
                EventCounter++;
                var customOrderEvent = new CustomOrderEvent(this, orderEvent, EventCounter);

                if (!TradableProductsDict[orderEvent.Symbol].OrderEventsDict.ContainsKey(customOrderEvent.EventIdentifier))
                {
                    TradableProductsDict[orderEvent.Symbol].OrderEventsDict.Add(customOrderEvent.EventIdentifier, customOrderEvent);
                }  
                if (TradableProductsDict[orderEvent.Symbol].OrderEventsDict.ContainsKey(customOrderEvent.EventIdentifier))
                {
                    ProcessQuedOrderEvents(orderEvent.Symbol);
                }
            }
        }
        public void ProcessQuedOrderEvents(Symbol symbol)
        {
            lock (symbolLocks)
            {
                var tp = TradableProductsDict[symbol];
                tp.OrderEventsDict = tp.OrderEventsDict.OrderBy(x => x.Value.TimeSubmitted).ToDictionary(x => x.Key, x => x.Value);
                foreach (var customOrderEvent in tp.OrderEventsDict.Values)
                {
                    OrderEventQue(customOrderEvent);
                    if (customOrderEvent.Processed)
                    {
                        tp.OrderEventsDict.Remove(customOrderEvent.EventIdentifier);
                    }
                }
            }
        }
        //Here we wait for the Trade to be added to the Trades Dictionary before we continue on to analyse the OrderEvent
        public void OrderEventQue(CustomOrderEvent customOrderEvent)
        {
            lock (symbolLocks)
            {
                TradableProduct tp = TradableProductsDict[customOrderEvent.Symbol];
                //var order = Transactions.GetOrderById(customOrderEvent.OrderId);
                int eventOrderID = customOrderEvent.OrderId;

                if (customOrderEvent.Tag == null || customOrderEvent.Status == OrderStatus.Invalid || customOrderEvent.Status != OrderStatus.None)
                    return;

                if (customOrderEvent.Tag == "Entry")
                {
                    // Check if the item is already present in the dictionary or if the dictionary is null
                    while (!tp.Trades.ContainsKey(customOrderEvent.OrderId) || tp.Trades[customOrderEvent.OrderId] == null)
                    {
                        // Create a ManualResetEventSlim if it doesn't exist
                        ManualResetEventSlim resetEvent1 = waitHandles.GetOrAdd(eventOrderID, new ManualResetEventSlim(false));
                        // Wait until the item is added
                        resetEvent1.Wait();
                    }
                    if (tp.Trades.ContainsKey(customOrderEvent.OrderId) && waitHandles.TryRemove(customOrderEvent.OrderId, out var resetEvent))
                    {
                        resetEvent.Set();
                        resetEvent.Dispose();
                    }
                }

                if (customOrderEvent.Status != OrderStatus.Filled && customOrderEvent.Status != OrderStatus.Canceled && customOrderEvent.Status != OrderStatus.PartiallyFilled) // removed partial fills
                {
                    return;
                }
                
                switch (customOrderEvent.Tag)
                {
                    case "Entry":
                        Trade trade = tp.Trades[eventOrderID];
                        OrderEventCompletion(trade, customOrderEvent);
                        break;
                    case "Stop Loss":
                        foreach(var t in tp.Trades.Values)
                        {
                            if (t.StopLossOrder.OrderId == customOrderEvent.OrderId)
                            {
                                OrderEventCompletion(t, customOrderEvent);
                            }
                        }
                        break;
                    case "Target":
                        foreach(var t in tp.Trades.Values)
                        {
                            if (t.TargetOrder.OrderId == customOrderEvent.OrderId)
                            {
                                OrderEventCompletion(t, customOrderEvent);
                            }
                        }
                        break;
                    default:
                        throw new Exception("Order Tag Not Recognised");
                }
            }
        }
        public void OrderEventCompletion(Trade trade, CustomOrderEvent customOrderEvent)
        {
            lock (symbolLocks)
            {
                int eventOrderID = customOrderEvent.OrderId;
                if (customOrderEvent.Status == OrderStatus.PartiallyFilled)
                {
                    if (eventOrderID == trade.EntryOrder.OrderId)
                    {
                        if (trade.Side == TradeSide.Long)
                        {
                            trade.HoldingQuantity += customOrderEvent.FillQuantity;
                            Console.WriteLine($"Trade {trade.EntryOrder.OrderId} Partially Filled, Holding Quantity = {trade.HoldingQuantity}");
                        }
                        else if (trade.Side == TradeSide.Short)
                        {
                            trade.HoldingQuantity -= customOrderEvent.FillQuantity;
                            Console.WriteLine($"Trade {trade.EntryOrder.OrderId} Partially Filled, Holding Quantity = {trade.HoldingQuantity}");
                        }
                        decimal quantity = trade.HoldingQuantity * -1;
                        trade.StopLossOrder = StopMarketOrder(trade.Symbol, quantity, trade.StopLossPrice, tag:"Stop Loss");
                        trade.TargetOrder = LimitOrder(trade.Symbol, quantity, trade.TargetPrice, tag:"Target");
                        Console.WriteLine($"Stop Loss Order {trade.StopLossOrder.OrderId} and Target Order {trade.TargetOrder.OrderId} Created");
                    }
                    else if (eventOrderID == trade.StopLossOrder.OrderId)
                    {
                        if (trade.TargetOrder == null || trade.TargetOrder.Status != OrderStatus.Submitted || trade.TargetOrder.Status != OrderStatus.Filled)
                        {
                            trade.TargetOrder = LimitOrder(trade.Symbol, trade.HoldingQuantity * -1, trade.TargetPrice, tag:"Target");
                            Console.WriteLine($"Target Order {trade.TargetOrder.OrderId} Created");
                        }
                        if (trade.Side == TradeSide.Long)
                        {
                            trade.HoldingQuantity -= customOrderEvent.FillQuantity;
                            trade.TargetOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                            Console.WriteLine($"Trade {trade.StopLossOrder.OrderId} Partially Filled, Holding Quantity = {trade.HoldingQuantity}");
                        }
                        else if (trade.Side == TradeSide.Short)
                        {
                            trade.HoldingQuantity += customOrderEvent.FillQuantity;
                            trade.TargetOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                            Console.WriteLine($"Trade {trade.StopLossOrder.OrderId} Partially Filled, Holding Quantity = {trade.HoldingQuantity}");
                        }
                    }
                    else if (eventOrderID == trade.TargetOrder.OrderId)
                    {
                        if (trade.StopLossOrder == null || trade.StopLossOrder.Status != OrderStatus.Submitted || trade.StopLossOrder.Status != OrderStatus.Filled)
                        {
                            decimal quantity = trade.HoldingQuantity * -1;
                            trade.StopLossOrder = StopMarketOrder(trade.Symbol, quantity, trade.StopLossPrice, tag:"Stop Loss");
                            Console.WriteLine($"Stop Loss Order {trade.StopLossOrder.OrderId} Created");
                        }
                        if (trade.Side == TradeSide.Long)
                        {
                            trade.HoldingQuantity -= customOrderEvent.FillQuantity;
                            trade.StopLossOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                            Console.WriteLine($"Trade {trade.TargetOrder.OrderId} Partially Filled, Holding Quantity = {trade.HoldingQuantity}");
                        }
                        else if (trade.Side == TradeSide.Short)
                        {
                            trade.HoldingQuantity += customOrderEvent.FillQuantity;
                            trade.StopLossOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                            Console.WriteLine($"Trade {trade.TargetOrder.OrderId} Partially Filled, Holding Quantity = {trade.HoldingQuantity}");
                        }
                    }
                }
                else if (customOrderEvent.Status == OrderStatus.Filled)
                {
                    if (eventOrderID == trade.EntryOrder.OrderId)
                    {
                        trade.HoldingQuantity += customOrderEvent.FillQuantity;
                        if (trade.StopLossOrder != null)
                        {
                            trade.StopLossOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                            Console.WriteLine($"Stop Loss Order {trade.StopLossOrder.OrderId} Updated");
                        }
                        else if (trade.StopLossOrder == null)
                        {
                            decimal quantity = trade.HoldingQuantity * -1;
                            trade.StopLossOrder = StopMarketOrder(trade.Symbol, quantity, trade.StopLossPrice, tag:"Stop Loss");
                            Console.WriteLine($"Stop Loss Order {trade.StopLossOrder.OrderId} Created");
                        }
                        if (trade.TargetOrder != null)
                        {
                            trade.TargetOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                            Console.WriteLine($"Target Order {trade.TargetOrder.OrderId} Updated");
                        }
                        else if (trade.TargetOrder == null)
                        {
                            decimal quantity = trade.HoldingQuantity * -1;
                            trade.TargetOrder = LimitOrder(trade.Symbol, quantity, trade.TargetPrice, tag:"Target");
                            Console.WriteLine($"Target Order {trade.TargetOrder.OrderId} Created");
                        }
                    }
                    else if (eventOrderID == trade.StopLossOrder.OrderId)
                    {
                        trade.HoldingQuantity -= customOrderEvent.FillQuantity;
                        if (trade.HoldingQuantity > 0)
                        {
                            trade.TargetOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                        }
                        if (trade.HoldingQuantity == 0 && trade.EntryOrder.Status == OrderStatus.Filled)
                        {
                            trade.Result = Result.Exit;
                            if (trade.TargetOrder != null || trade.TargetOrder.Status != OrderStatus.Filled)
                            {
                                trade.TargetOrder.Cancel();
                            }
                            TradableProductsDict[customOrderEvent.Symbol].ManageResult(eventOrderID, trade);
                        }
                    }
                    else if (eventOrderID == trade.TargetOrder.OrderId)
                    {
                        trade.HoldingQuantity -= customOrderEvent.FillQuantity;
                        if (trade.HoldingQuantity > 0)
                        {
                            trade.StopLossOrder.Update(new UpdateOrderFields
                            {
                                Quantity = trade.HoldingQuantity
                            });
                        }
                        if (trade.HoldingQuantity == 0 && trade.EntryOrder.Status == OrderStatus.Filled)
                        {
                            trade.Result = Result.Exit;
                            if (trade.StopLossOrder != null || trade.StopLossOrder.Status != OrderStatus.Filled)
                            {
                                trade.StopLossOrder.Cancel();
                            }
                            TradableProductsDict[customOrderEvent.Symbol].ManageResult(eventOrderID, trade);
                        }
                    }
                    else if (eventOrderID == trade.ExitOrder.OrderId)
                    {
                        trade.HoldingQuantity -= customOrderEvent.FillQuantity;
                        trade.Result = Result.Exit;
                        if (trade.EntryOrder.Status != OrderStatus.Filled)
                        {
                            trade.EntryOrder.Cancel();
                        }
                        if (trade.StopLossOrder != null)
                        {
                            trade.StopLossOrder.Cancel();
                        }
                        if (trade.TargetOrder != null)
                        {
                            trade.TargetOrder.Cancel();
                        }
                        TradableProductsDict[customOrderEvent.Symbol].ManageResult(eventOrderID, trade);
                    }
                }
                else if (customOrderEvent.Status == OrderStatus.Canceled)
                {
                    if (eventOrderID == trade.EntryOrder.OrderId)
                    {
                        trade.Result = Result.Exit;
                        if (trade.StopLossOrder != null && trade.HoldingQuantity == 0)
                        {
                            trade.StopLossOrder.Cancel();
                        }
                        if (trade.TargetOrder != null && trade.HoldingQuantity == 0)
                        {
                            trade.TargetOrder.Cancel();
                        }
                        TradableProductsDict[customOrderEvent.Symbol].ManageResult(eventOrderID, trade);
                    }
                    else if (eventOrderID == trade.StopLossOrder.OrderId && trade.HoldingQuantity == 0)
                    {
                        decimal quantity = trade.HoldingQuantity * -1;
                        trade.StopLossOrder = StopMarketOrder(trade.Symbol, quantity, trade.StopLossPrice, tag:"Stop Loss");
                    }
                    else if (eventOrderID == trade.TargetOrder.OrderId && trade.HoldingQuantity == 0)
                    {
                        decimal quantity = trade.HoldingQuantity * -1;
                        trade.TargetOrder = LimitOrder(trade.Symbol, quantity, trade.TargetPrice, tag:"Target");
                    }
                }

                customOrderEvent.Processed = true;
            }
        }

        //Trade Class: Contains all the information about a trade and Stores data for ML methods
        public class Trade
        {
            public Symbol Symbol {get; set;}
            public TradeSide Side {get; set;}
            public OrderTicket EntryOrder {get; set;}
            public decimal StopLossPrice {get; set;}
            public decimal TargetPrice {get; set;}
            public Result Result {get; set;}
            public int Strength {get; set;}
            public TrendDirection? Trend {get; set;}
            public OrderTicket ExitOrder {get; set;}
            public OrderTicket StopLossOrder {get; set;}
            public OrderTicket TargetOrder {get; set;}
            public int BarSinceEntry {get; set;}
            public decimal SignalBarHigh {get; set;}
            public decimal SignalBarLow {get; set;}
            public decimal HoldingQuantity {get; set;}
            public int ID {get; set;}

            //Save Rolling Window Data and Pre Entry Variables of the trade for ML methods
            public Dictionary<string, List<QuoteBar>> BarRollingWindows {get; set;}
            public Dictionary<string, List<decimal>> DecimalIndicatorRollingWindows {get; set;}
            public Dictionary<string, List<IndicatorDataPoint>> DataPointIndicatorRollingWindows {get; set;}
            public TradableProduct TradableProduct {get; set;}
            
            public Trade(TradableProduct Product, Symbol symbol, OrderTicket EntryOrder, decimal stopLossPrice, decimal targetPrice)
            {
                Result = Result.InProgress;
                this.HoldingQuantity = 0;
                this.EntryOrder = EntryOrder;
                this.ID = EntryOrder.OrderId;
                Symbol = symbol;
                this.Side = EntryOrder.Quantity > 0 ? TradeSide.Long : TradeSide.Short;
                this.TradableProduct = Product;
                Strength = Product.Strength;
                Trend = Product.Trend;
                BarSinceEntry = 0;
                this.SignalBarHigh = Product.SignalBarHigh;
                this.SignalBarLow = Product.SignalBarLow;   
            }
        }

        //Tradeable Product Class: Manages Trades Internally by Symbol and provides methods for managing trades and Analysing the product
        public class TradableProduct
        {
            public Forex Product {get; set;}
            public Symbol Symbol {get; set;}
            public TrendDirection Trend {get; set;}
            public int Strength {get; set;}
            internal IEnumerable<QuoteBar> ResolutionHistory {get; set;}
            public bool Watching {get; set;}
            internal RollingWindow<QuoteBar> ResolutionWindow {get; set;}
            public RollingWindow<QuoteBar> MinuteWindow {get; set;}   
            public RollingWindow<QuoteBar> DailyWindow {get; set;}  
            public RollingWindow<QuoteBar> WeeklyWindow {get; set;}    
            internal QuoteBarConsolidator mConsolidator {get; set;}
            internal QuoteBarConsolidator dConsolidator {get; set;}
            internal QuoteBarConsolidator wConsolidator {get; set;}
            internal bool MinuteBarClosed {get; set;}
            internal bool DailyBarClosed {get; set;}
            internal bool WeeklyBarClosed {get; set;}
            public bool BullishSignalBar {get; set;}   
            public bool BearishSignalBar {get; set;}    
            public Dictionary<int, Trade> Trades { get; set; } = new Dictionary<int, Trade>();
            public Dictionary<int, Trade> HistoricalTrades {get; set;} = new Dictionary<int, Trade>();
            public bool Subscribed {get; set;}
            public QCAlgorithm Algorithm {get; set;}
            public decimal SignalBarHigh {get; set;}
            public decimal SignalBarLow {get; set;}
            internal SequentialConsolidator oneDayBar { get; set; }
            internal SequentialConsolidator oneWeekBar { get; set; }
            public bool TrailingStopEnabled {get; set;}
            public decimal TradeSize {get; set;}
            public int MinimumTrendStrengthPercent {get; set;}
            public bool NewTradeAllowed {get; set;}
            public RollingWindow<IndicatorDataPoint> RSI {get; set;}
            //public RollingWindow<decimal> AverageBandwidth {get; set;}
            public RollingWindow<IndicatorDataPoint> BollingerBands {get; set;}
            public BollingerBands BollingerBand {get; set;}
            public RelativeStrengthIndex RelativeStrengthIndex {get; set;}
            public int TradeNumber {get; set;}
            public int MaxTrades {get; set;}
            public Trade LatestTrade {get; set;}
            public Dictionary<string, CustomOrderEvent> OrderEventsDict = new Dictionary<string, CustomOrderEvent>();

            public TradableProduct(QCAlgorithm Algorithm, Forex Forex, Symbol Symbol, int Periods, bool TrailingStopEnabled, decimal TradeSize, int MinimumTrendStrengthPercent)
            {
                this.Algorithm = Algorithm;
                this.Product = Forex;
                this.Symbol = Symbol;
                Watching = false;
                MinuteBarClosed = false;
                BullishSignalBar = false;
                BearishSignalBar = false;
                this.TradeSize = TradeSize;
                ResolutionWindow = new RollingWindow<QuoteBar>(Periods);
                Trades = new();  
                HistoricalTrades = new();
                Subscribed = false;   
                this.TrailingStopEnabled = TrailingStopEnabled;
                this.MinimumTrendStrengthPercent = MinimumTrendStrengthPercent;
                NewTradeAllowed = true;
                TradeNumber = 0;
                MaxTrades = 1;
   
                Console.WriteLine($"Created {this.Symbol} Tradable Product");                    
            }
            
            public void Liquidate()
            {
                if (Trades == null || !Trades.Any())
                    return;

                foreach (var trade in Trades)
                {
                    trade.Value.Result = Result.Exit;
                    Exit(trade.Key, trade.Value);
                }
            }

            public void Exit(int key, Trade trade)
            {
                if (trade.Result != Result.Exit)
                    return;

                trade.ExitOrder = Algorithm.MarketOrder(trade.Symbol, trade.EntryOrder.Quantity * -1);

                ManageResult(key, trade);
            }

            // ManageResult: Label trades as Win, Loss, or BreakEven
            public void ManageResult(int key, Trade trade)
            {
                if (trade.Result != Result.Exit)
                    return;

                decimal entryFillQuantity = trade.EntryOrder.QuantityFilled;
                decimal stopLossFillQuantity = trade.StopLossOrder?.QuantityFilled ?? 0m;
                decimal targetFillQuantity = trade.TargetOrder?.QuantityFilled ?? 0m;
                decimal exitFillQuantity = trade.ExitOrder?.QuantityFilled ?? 0m;

                decimal entryAverageFillPrice = trade.EntryOrder.AverageFillPrice;
                decimal stopLossAverageFillPrice = trade.StopLossOrder?.AverageFillPrice ?? 0m;
                decimal targetAverageFillPrice = trade.TargetOrder?.AverageFillPrice ?? 0m;
                decimal exitAverageFillPrice = trade.ExitOrder?.AverageFillPrice ?? 0m;

                // Calculate filled quantities
                decimal totalFilledQuantity = stopLossFillQuantity + targetFillQuantity + exitFillQuantity;

                // Calculate average fill price
                decimal averageFillPrice = (  (stopLossAverageFillPrice * stopLossFillQuantity)
                                            + (targetAverageFillPrice * targetFillQuantity)
                                            + (exitAverageFillPrice * exitFillQuantity)) / totalFilledQuantity;
                
                decimal entryPrice = trade.EntryOrder.AverageFillPrice;
                
                // Calculate the result based on the filled quantities and average fill price
                trade.Result = CalculateResult(trade.Side, entryPrice, averageFillPrice);

                SaveHistoricalTrade(trade);
                string logMsg = $"{trade.Symbol} {trade.Side} {trade.Result}";
                Algorithm.Log(logMsg);
                Console.WriteLine(logMsg);
            }

            private Result CalculateResult(TradeSide side, decimal entryAverageFillPrice, decimal averageFillPrice)
            {
                if (side == TradeSide.Long)
                {
                    if (averageFillPrice > entryAverageFillPrice)
                        return Result.Win;
                    else if (averageFillPrice < entryAverageFillPrice)
                        return Result.Loss;
                    else
                        return Result.BreakEven;
                }
                else if (side == TradeSide.Short)
                {
                    if (averageFillPrice < entryAverageFillPrice)
                        return Result.Win;
                    else if (averageFillPrice > entryAverageFillPrice)
                        return Result.Loss;
                    else
                        return Result.BreakEven;
                }
            
                // Handle the case when the side is not Long or Short
                // Add appropriate error handling here
                return Result.Error;
            }

            // SaveHistoricalTrade: Move trade from Trades to HistoricalTrades for record keeping and ML training
            public void SaveHistoricalTrade(Trade trade)
            {
                if (!Trades.ContainsKey(trade.EntryOrder.OrderId))
                {
                    throw new Exception("Unable to save historical trade, trade not found in Trades Dictionary");
                }
                
                var HistoricalTrade = trade;
                this.HistoricalTrades.Add(trade.EntryOrder.OrderId, HistoricalTrade);
                this.Trades.Remove(trade.EntryOrder.OrderId);
                TradeNumber--;

                Console.WriteLine($"Saved Historical Trade for {this.Symbol}");
            }
         
            public void TrailStops(Dictionary<int, Trade> Trades)
            {
                foreach (var trade in Trades)
                {   
                    if (!TrailingStopEnabled)
                        return;

                    if (Algorithm.Portfolio[this.Symbol].IsLong)
                    {
                        var Distance = trade.Value.EntryOrder.AverageFillPrice - trade.Value.StopLossPrice;
                        var newStopPrice = Math.Round(trade.Value.EntryOrder.AverageFillPrice - Distance, 5);
                        if (newStopPrice > trade.Value.StopLossPrice)
                        {
                            trade.Value.StopLossPrice = newStopPrice;

                            if (trade.Value.StopLossOrder != null && trade.Value.StopLossOrder.Status == OrderStatus.Submitted)
                            {
                                trade.Value.StopLossOrder.Update(new UpdateOrderFields
                                {
                                    StopPrice = trade.Value.StopLossPrice
                                });
                            }

                            Algorithm.Log($"{this.Symbol} Stop Loss Updated to {trade.Value.StopLossPrice}");
                            Console.WriteLine($"{this.Symbol} Stop Loss Updated to {trade.Value.StopLossPrice}");
                        }
                    }
                    if (Algorithm.Portfolio[this.Symbol].IsShort)
                    {
                        var Distance = trade.Value.StopLossPrice - trade.Value.EntryOrder.AverageFillPrice;
                        var newStopPrice = Math.Round(trade.Value.EntryOrder.AverageFillPrice + Distance, 5);
                        if (newStopPrice < trade.Value.StopLossPrice)
                        {
                            trade.Value.StopLossPrice = newStopPrice;

                            if (trade.Value.StopLossOrder != null && trade.Value.StopLossOrder.Status == OrderStatus.Submitted)
                            {
                                trade.Value.StopLossOrder.Update(new UpdateOrderFields
                                {
                                    StopPrice = trade.Value.StopLossPrice
                                });
                            }

                            Algorithm.Log($"{this.Symbol} Stop Loss Updated to {trade.Value.StopLossPrice}");
                            Console.WriteLine($"{this.Symbol} Stop Loss Updated to {trade.Value.StopLossPrice}");
                        }
                    }
                }
            }

            //Store windows at trade entry to use for later training of ML model using the saved data
            public void StoreWindows(Trade trade)
            {
                trade.BarRollingWindows = new(4);
                trade.DecimalIndicatorRollingWindows = new(3);
                trade.DataPointIndicatorRollingWindows = new(1);

                trade.BarRollingWindows.Add("ResolutionWindow", ResolutionWindow.ToList());
                trade.BarRollingWindows.Add("MinuteWindow", MinuteWindow.ToList());
                trade.BarRollingWindows.Add("DailyWindow", DailyWindow.ToList());
                trade.BarRollingWindows.Add("WeeklyWindow", WeeklyWindow.ToList());
                //trade.DecimalIndicatorRollingWindows.Add(AverageBandwidth.ToList(), "AverageBandwidth");
                trade.DataPointIndicatorRollingWindows.Add("BollingerBands", BollingerBands.ToList());
                trade.DataPointIndicatorRollingWindows.Add("RSI", RSI.ToList());
            }
            public void EntryMonitor() //Need to make it so can take opposing trade if i am long or short and new trades not allowed
            {
                if (TradeNumber > MaxTrades)
                    return;
                if (Algorithm.IsWarmingUp) 
                    return;
                if (!this.MinuteWindow.IsReady || !this.DailyWindow.IsReady || !this.WeeklyWindow.IsReady || !this.ResolutionWindow.IsReady || !this.RSI.IsReady || !this.NewTradeAllowed)
                    return;
                if (this.Strength < MinimumTrendStrengthPercent )
                    return;

                //Check our strongest Trends and set positions       
                if (this.Trend == TrendDirection.Bullish && this.BullishSignalBar && RSI[0].Value < 35)
                {
                    if (Algorithm.Portfolio[this.Symbol].IsShort)
                    {      
                        this.Liquidate();           
                    }
                    TradeNumber++;
                    var Price = MinuteWindow[0].Close;
                    var entry = Algorithm.MarketOrder(this.Symbol, TradeSize, true, tag:"Entry");
                    decimal stopPrice = Math.Round(Price * 0.9975m, 5);
                    decimal limitPrice = Math.Round(Price * 1.005m, 5);
                    Trade trade = new Trade(this, this.Symbol, entry, stopPrice, limitPrice); 
                    
                    StoreWindows(trade);
                    Trades.Add(trade.EntryOrder.OrderId, trade);     

                    Console.WriteLine($"{trade.Symbol}, Trade {trade.Result}, {trade.EntryOrder.Quantity}, Trend {this.Trend} {this.Strength}");  
                    Algorithm.Log($"{trade.Symbol}, Trade {trade.Result}, {trade.EntryOrder.Quantity}, Trend {this.Trend} {this.Strength}");               
                }

                if (this.Trend == TrendDirection.Bearish && this.BearishSignalBar && RSI[0].Value > 65)
                {
                    if (Algorithm.Portfolio[this.Symbol].IsLong)
                    {
                        this.Liquidate();                      
                    }
                    TradeNumber++;
                    var Price = MinuteWindow[0].Close;
                    decimal stopPrice = Math.Round(Price * 1.0025m, 5);
                    decimal limitPrice = Math.Round(Price * 0.995m, 5);
                    var entry = Algorithm.MarketOrder(this.Symbol, -TradeSize, true, tag:"Entry");
                    Trade trade = new Trade(this, this.Symbol, entry, stopPrice, limitPrice);
                     
                    StoreWindows(trade);
                    Trades.Add(trade.EntryOrder.OrderId, trade);

                    Console.WriteLine($"{trade.Symbol}, Trade {trade.Result}, {trade.EntryOrder.Quantity}, Trend {this.Trend} {this.Strength}");  
                    Algorithm.Log($"{trade.Symbol}, Trade {trade.Result}, {trade.EntryOrder.Quantity}, Trend {this.Trend} {this.Strength}"); 
  
                }      
            }

            public void SignalBars(RollingWindow<QuoteBar> rollingWindow)
            {
                if (!this.MinuteWindow.IsReady || !this.MinuteBarClosed)
                        return;

                    if (this.MinuteWindow[0].Low < this.MinuteWindow[1].Low && this.MinuteWindow[0].Close > this.MinuteWindow[1].High 
                        && this.MinuteWindow[1].Close < this.MinuteWindow[1].Open)
                    {
                        this.BullishSignalBar = true;
                        this.BearishSignalBar = false;
                        this.SignalBarHigh = this.MinuteWindow[0].High; //try removing this and using opposing high as target
                        this.SignalBarLow = this.MinuteWindow[0].Low;
                    }
                    if (this.MinuteWindow[0].High > this.MinuteWindow[1].High && this.MinuteWindow[0].Close < this.MinuteWindow[1].Low 
                        && this.MinuteWindow[1].Close > this.MinuteWindow[1].Open)
                    {
                        this.BullishSignalBar = false;
                        this.BearishSignalBar = true;
                        this.SignalBarHigh = this.MinuteWindow[0].High;
                        this.SignalBarLow = this.MinuteWindow[0].Low;
                    }
                    if (this.BullishSignalBar)
                    {
                        if (this.MinuteWindow[0].Close < this.SignalBarLow)
                        {
                            this.BullishSignalBar = false;
                        }
                    }
                    if (this.BearishSignalBar)
                    {
                        if (this.MinuteWindow[0].Close > this.SignalBarHigh)
                        {
                            this.BearishSignalBar = false;
                        }
                    }
            }
            public decimal LowestLow(IEnumerable<QuoteBar> quoteBars, int periods)
            {
                var lastXBars = quoteBars.TakeLast(periods);
                var lowestLow = lastXBars.Min(bar => bar.Low);
                return lowestLow;
            }

            public decimal HighestHigh(IEnumerable<QuoteBar> quoteBars, int periods)
            {
                var lastXBars = quoteBars.TakeLast(periods);
                var highestHigh = lastXBars.Max(bar => bar.High);
                return highestHigh;
            }
            public void Analyse(int lookBackPeriod, int MinimumTrendStrengthPercent)
            {
                TrendAndStrength(lookBackPeriod, MinimumTrendStrengthPercent);
                Algorithm.Log($"{this.Symbol} Analysis Successful");
            }

            public void Subscribe()
            {
                Subscriber();
                Algorithm.Log($"{this.Symbol} Subscribe Successful");
            }
            public void Unsubscribe()
            {
                Unsubscriber();
                Algorithm.Log($"{this.Symbol} UnSubscribe Successful");
            }

            public void ActivateWindows(int Periods)
            {   
                MinuteWindow = new RollingWindow<QuoteBar>(50);
                DailyWindow = new RollingWindow<QuoteBar>(31);
                WeeklyWindow = new RollingWindow<QuoteBar>(4);
                RSI = new RollingWindow<IndicatorDataPoint>(15);
                BollingerBands = new RollingWindow<IndicatorDataPoint>(50);
                //AverageBandwidth = new RollingWindow<decimal>(100);
                BollingerBand = Algorithm.BB(this.Symbol, 30, 2);
                RelativeStrengthIndex = Algorithm.RSI(this.Symbol, 14);
                
                mConsolidator = new QuoteBarConsolidator(TimeSpan.FromMinutes(15)); //change minutes per bar here
                dConsolidator = new QuoteBarConsolidator(TimeSpan.FromDays(1)); //change days per bar here
                wConsolidator = new QuoteBarConsolidator(TimeSpan.FromDays(5)); //change days per bar here
                    
                var oneCountConsolidator = new QuoteBarConsolidator(1); //change days per bar here
                var fiveCountConsolidator = new QuoteBarConsolidator(5); //change days per bar here
                oneDayBar = new SequentialConsolidator(dConsolidator, oneCountConsolidator);
                oneWeekBar = new SequentialConsolidator(wConsolidator, fiveCountConsolidator);

                mConsolidator.DataConsolidated += mBarHandler;  
                dConsolidator.DataConsolidated += (sender, consolidated) => dBarHandler(sender, (QuoteBar) consolidated);
                wConsolidator.DataConsolidated += (sender, consolidated) => wBarHandler(sender, (QuoteBar) consolidated);
                
                foreach (var bar in this.ResolutionHistory){
                    this.ResolutionWindow.Add(bar);
                }
                this.ResolutionHistory = null;
            }
            internal void mBarHandler(object sender, QuoteBar consolidated)
            {
                this.MinuteWindow.Add(consolidated); 
                MinuteBarClosed = true; 
                SignalBars(this.MinuteWindow);
                EntryMonitor(); 
                TrailStops(Trades);
            }
            internal void dBarHandler(object sender, QuoteBar consolidated)
            {
                this.DailyWindow.Add(consolidated); 
                DailyBarClosed = true;    
            }
            internal void wBarHandler(object sender, QuoteBar consolidated)
            {
                this.WeeklyWindow.Add(consolidated); 
                WeeklyBarClosed = true;    
            }

            internal void Subscriber()
            {
                Algorithm.SubscriptionManager.AddConsolidator(this.Symbol, this.mConsolidator);
                Algorithm.SubscriptionManager.AddConsolidator(this.Symbol, this.oneDayBar);
                Algorithm.SubscriptionManager.AddConsolidator(this.Symbol,this.oneWeekBar);
                Algorithm.RegisterIndicator(Symbol ,RelativeStrengthIndex, mConsolidator);
                Algorithm.RegisterIndicator(Symbol ,BollingerBand, mConsolidator);
     
                RelativeStrengthIndex.Updated += (sender, updated) => RSI.Add(updated);
                BollingerBand.Updated += (sender, updated) => BollingerBands.Add(updated);
                 
                this.Subscribed = true;
            }

            internal void Unsubscriber() //Currently not unsubscribing symbols because History only updates once per day, therefore need to watch all symbols
            {
                Algorithm.SubscriptionManager.RemoveConsolidator(this.Symbol, this.mConsolidator);
                Algorithm.SubscriptionManager.RemoveConsolidator(this.Symbol, this.oneDayBar);
                Algorithm.SubscriptionManager.RemoveConsolidator(this.Symbol,this.oneWeekBar);
                this.Subscribed = false; 
            }
            //Determine Trend by "How many bars for a new high or low as a percentage of total bars"
            internal void TrendAndStrength(int lookBackPeriod, int MinimumTrendStrengthPercent)
            {
                if (!this.MinuteWindow.IsReady)
                    return;

                decimal minimumTrendStrength = MinimumTrendStrengthPercent / 100m;
                int bearTrendBars = 0;
                int bullTrendBars = 0;
                decimal high = decimal.MinValue;
                decimal low = decimal.MaxValue;

                if (lookBackPeriod > this.MinuteWindow.Count)
                    return;

                int trendStrengthCriteria = (int)Math.Round(lookBackPeriod * minimumTrendStrength);

                for (int i = (lookBackPeriod - 1); i >= 0; i--){
                    if (this.MinuteWindow[i].High > high)
                    {
                        high = this.MinuteWindow[i].High;
                        bullTrendBars++;
                        bearTrendBars = 0;  // Reset bear trend bars count as a higher high indicates a possible bullish trend
                    }
                    if (this.MinuteWindow[i].Low < low)
                    {
                        low = this.MinuteWindow[i].Low;
                        bearTrendBars++;
                        bullTrendBars = 0;  // Reset bull trend bars count as a lower low indicates a possible bearish trend
                    }
                }

                if (bullTrendBars >= trendStrengthCriteria)
                {
                    this.Trend = TrendDirection.Bullish;
                    this.Strength = CalculateStrength(bullTrendBars, lookBackPeriod);
                }
                else if (bearTrendBars >= trendStrengthCriteria)
                {
                    this.Trend = TrendDirection.Bearish;
                    this.Strength = CalculateStrength(bearTrendBars, lookBackPeriod);
                }
                else 
                {
                    this.Trend = TrendDirection.Range;
                    this.Strength = 0;
                }
                
                this.Watching = IsWatching(MinimumTrendStrengthPercent);
                //Console.WriteLine($"{this.Symbol} Trend: {this.TrendDirection} Strength: {this.Strength}");
            }

            private int CalculateStrength(int trendBars, int lookBackPeriod)
            {
                return (int)Math.Round(((double)trendBars / lookBackPeriod) * 100);
            }

            private bool IsWatching(int MinimumTrendStrengthPercent)
            {
                if (this.Trend == TrendDirection.Range || this.Trend == TrendDirection.Error)
                    return false;

                if (this.Strength >= MinimumTrendStrengthPercent)
                    return true;
                else
                {
                    this.Strength = 0;
                    return false;   
                }
            }
            // private decimal AverageBollingerBandwidth(int lookBackPeriod)
            // {
            //     var averageBandwidthList = BollingerBands.Take(lookBackPeriod);
            //     //var CurrentAverage = BollingerBands.;

            //     return CurrentAverage;
            // }
        }
    }   
}