Overall Statistics
Total Trades
9577
Average Win
0.14%
Average Loss
-0.14%
Compounding Annual Return
8.789%
Drawdown
24.100%
Expectancy
0.189
Net Profit
255.047%
Sharpe Ratio
0.411
Sortino Ratio
0.434
Probabilistic Sharpe Ratio
0.981%
Loss Rate
41%
Win Rate
59%
Profit-Loss Ratio
1.01
Alpha
0.001
Beta
0.597
Annual Standard Deviation
0.136
Annual Variance
0.019
Information Ratio
-0.306
Tracking Error
0.119
Treynor Ratio
0.094
Total Fees
$10545.55
Estimated Strategy Capacity
$17000000.00
Lowest Capacity Asset
AIZ SVXZEUFT60DH
Portfolio Turnover
2.83%
#region imports
    using System;
    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;
    using MathNet.Numerics;
    using MathNet.Numerics.LinearAlgebra;
#endregion

//Copied from this forum:
//href https://www.quantconnect.com/forum/discussion/695/adjusted-slope--exponential-slope----annualized-slope--r-squuared--adjusted-slope/p1

namespace QuantConnect.Algorithm.CSharp.Helpers
{
    public class AnnualizedExponentialSlopeIndicator : WindowIndicator<IndicatorDataPoint>
    {
        /// <summary>
        /// Array representing the time.
        /// </summary>
        private readonly double[] t;

        public AnnualizedExponentialSlopeIndicator(int period)
            : base("AESI(" + period + ")", period)
        {
            t = Vector<double>.Build.Dense(period, i => i + 1).ToArray();
        }

        public AnnualizedExponentialSlopeIndicator(string name, int period)
            : base(name, period)
        {
            t = Vector<double>.Build.Dense(period, i => i + 1).ToArray();
        }
        
        protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
        {
            // Until the window is ready, the indicator returns the input value.
            if (window.Samples <= window.Size) return 0m;

            // Sort the window by time, convert the observations to double and transform it to an array
            var series = window
                .OrderBy(i => i.Time)
                .Select(i => Convert.ToDouble(Math.Log(Convert.ToDouble(i.Value))))
                .ToArray();
            // Fit OLS
            // solves y=a + b*x via linear regression
            // http://numerics.mathdotnet.com/Regression.html
            var ols = Fit.Line(x: t, y: series);
            var intercept = ols.Item1;
            var slope = ols.Item2;

            // compute rsquared
            var rsquared = GoodnessOfFit.RSquared(t.Select(x => intercept + slope * x), series);

            // anything this small can be viewed as flat
            if (double.IsNaN(slope) || Math.Abs(slope) < 1e-25) return 0m;

            // trading days per year for us equities
            const int dayCount = 252;

            // annualize dy/dt
            var annualSlope = ((Math.Pow(Math.Exp(slope), dayCount)) - 1) * 100;

            // scale with rsquared
            annualSlope = annualSlope * rsquared;

            if (annualSlope >= (double)decimal.MaxValue || annualSlope <= (double)decimal.MinValue)
            {
                annualSlope = 0;
            }
            return Convert.ToDecimal(annualSlope);
        }
    }
}
#region imports
    using System;
    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



//Copied from this forum:
//href https://www.quantconnect.com/forum/discussion/695/adjusted-slope--exponential-slope----annualized-slope--r-squuared--adjusted-slope/p1

namespace QuantConnect.Algorithm.CSharp.Helpers
{
    public class CustomMomentumIndicator : TradeBarIndicator
    {
        private Symbol _symbol;
        private int _windowSize;
        public readonly AnnualizedExponentialSlopeIndicator AnnualizedSlope;
        public readonly ExponentialMovingAverage MovingAverage;
        public readonly GapIndicator Gap;
        public readonly AverageTrueRange Atr;

        public CustomMomentumIndicator(Symbol symbol, int annualizedSlopeWindow, int movingAverageWindow, int gapWindow, int atrWindow) : base($"CMI({symbol}, {annualizedSlopeWindow}, {movingAverageWindow}, {gapWindow})")
        {
            _symbol = symbol;
            AnnualizedSlope = new AnnualizedExponentialSlopeIndicator(annualizedSlopeWindow);
            MovingAverage = new ExponentialMovingAverage(movingAverageWindow);
            Gap = new GapIndicator(gapWindow);
            Atr = new AverageTrueRange(atrWindow);

            _windowSize = (new int[] { movingAverageWindow, annualizedSlopeWindow, gapWindow, atrWindow }).Max();
        }
        public Symbol Symbol { get { return _symbol; } }

        public override void Reset()
        {
            AnnualizedSlope.Reset();
            MovingAverage.Reset();
            Gap.Reset();
            Atr.Reset();
            base.Reset();
        }

        protected override decimal ComputeNextValue(TradeBar input)
        {
            AnnualizedSlope.Update(input.EndTime, input.Value);
            MovingAverage.Update(input.EndTime, input.Value);
            Gap.Update(input.EndTime, input.Value);
            Atr.Update(input);

            return AnnualizedSlope;
        }
        /// <summary>
        /// Are the indicators ready to be used?
        /// </summary>
        public override bool IsReady
        {
            get { return AnnualizedSlope.IsReady && MovingAverage.IsReady && Gap.IsReady && Atr.IsReady; }
        }
        /// <summary>
        /// Returns the Window of the indicator required to warm up indicator
        /// </summary>
        public int Window
        {
            get {return _windowSize;}
        }
        public new string ToDetailedString()
        {
            return $"Symbol:{_symbol} Slope:{AnnualizedSlope.ToDetailedString()} Average:{MovingAverage.ToDetailedString()} Gap:{Gap.ToDetailedString()} Atr:{Atr.ToDetailedString()} IsReady:{IsReady}";
        }
    }
}
#region imports
    using System;
    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;
    using MathNet.Numerics;
    using MathNet.Numerics.Statistics;
#endregion



/// <summary>
///  Indicator to indicate the percentage (0.10 = 10%) of which a security gapped over the last period;
/// </summary>
namespace QuantConnect.Algorithm.CSharp.Helpers
{
    public class GapIndicator : WindowIndicator<IndicatorDataPoint>
    {
        public GapIndicator(int period)
            : base("GAP(" + period + ")", period)
        {
        }

        public GapIndicator(string name, int period)
            : base(name, period)
        {
        }
        public override bool IsReady
        {
            get { return Samples >= Period; }
        }

        protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
        {
            if (window.Count < 3) return 0m;

            var diff = new double[window.Count];

            // load input data for regression
            for (int i = 0; i < window.Count - 1; i++)
            {
                diff[i] = (double)((window[i + 1] - window[i]) / (window[i] == 0 ? 1 : window[i].Value));
            }
            return (decimal) diff.MaximumAbsolute();
        }
    }
}
#region imports
    using System;
    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 class MarketRegimeFilter
    {
        private SimpleMovingAverage _spyMovingAverage200;

        public MarketRegimeFilter(SimpleMovingAverage spyMovingAverage200)
        {
            _spyMovingAverage200 = spyMovingAverage200;
        }
        
        // Checks if the SPY is over its 200d SMA
        public bool RiskON(decimal spyPrice){
            
            bool riskonSPY = false;

            if (spyPrice > _spyMovingAverage200){
                riskonSPY = true;
            }

            if (riskonSPY)
                return true;
            else
                return false;
        }
    }
}
#region imports
    using System;
    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.Algorithm.CSharp.Helpers;
    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
{
    /*
    *******************************************************************************************
    2023/01/15 - Updated and Adapted by CabedoVestement based on the previous work of
    Quant Trader (https://www.quantconnect.com/forum/discussion/3136/andreas-f-clenow-momentum)
    *******************************************************************************************
    Momentum strategy according to Andreas F. Clenow book 
    'Stocks on the Move - Beating the Market with Hedge Fund Momentum Strategies'

    ** Objective of the algorithm: **

    The algorithm's objective is to select stocks from the SP500 universe with the fastest rising prices while maintaining limited volatility.

    ** Key Trading Rules: **

    Market Universe: It operates within the SP500 universe.
    Trading Schedule: Trades are executed on Wednesday
    Regular Selling/Buying: Stocks are bought and sold every week.
    Rebalancing: Positions are rebalanced twice a month to manage risk.
    Top Ranking Stocks: The algorithm selects the top 100 (or 20%) ranked stocks from the SP500.
    Moving Average Filter: Stocks below their 100-day moving average are sold.
    Gap Filter: Stocks with a gap of more than 15% over the last 90 days are not bought.
    Index Membership Filter: Stocks that leave the SP500 index are sold.
    Market Regime Filter: Stocks are bought only when the SP500 is above its 200-day moving average.
    Position Sizing: Position sizes are calculated based on a 0.1% risk of the total portfolio value using the Average True Range (ATR) of the last 20 days.
    Momentum Calculation: Momentum is calculated based on the annualized exponential regression slope over the past 90 days.
    Momentum Weighting: Momentum values are weighted for volatility adjustment using the coefficient of determination (R-squared).    
*/

    /// Momentum strategy according to Andreas F. Clenow book titled 
    /// 'Stocks on the Move: Beating the Market with Hedge Fund Momentum Strategies'	
    public class StocksOnTheMoveAlgorithm : QCAlgorithm
    {
        ///Momentum is calculated based on 90 past days annualized exponential regression slope;
        private int _annualizedSlopeWindow = 90;

        /// If the stock is below its 100 days moving average, sell it;
        private int _movingAverageWindow = 100;

        /// ATR window
        private int _atrWindow = 20;

        /// Daily Risk of each trade on the portfolio (0,1%)
        private const decimal RiskPerContractOnPortfolio = 0.001m;

        /// Total number of security symbols in the Universe
        private static int _universeSelectMaxStocks = 500;

        /// Holds our security custom indicators per symbol
        private Dictionary<Symbol, CustomMomentumIndicator> _customIndicators = new Dictionary<QuantConnect.Symbol, CustomMomentumIndicator>(_universeSelectMaxStocks);

        // If the SP500 is above the 200 days moving average we buy stocks, otherwise not; 
        private MarketRegimeFilter _marketRegimeFilter;

        //If the stock is not in the top 100/ 20% ranking, sell it;
        private int _topNStockOfSp500 = 100;

        ///If the stock gapped > 15% over period (90d) Do not buy: Maximum Gap in percentage
        private decimal _maximumGap = 0.15m;
        private int _gapWindow = 90;

        ///Minimum annualized slope before buying stock.
        private decimal _minimumAnnualizedSlope = 0m;

        ///Twice a month rebalance the positions sizes (risk);
        private bool _rebalanceWeek = false;
        public bool RebalanceWeek { get { return _rebalanceWeek; } }

        ///Broker fee to take into account to check if Cash is avalaible
        private const decimal BrokerFee = 0.005m;

        // Debug parameters
        private bool _isLogging = false;
        /// Is debugging set?
        public bool IsLooging { get { return _isLogging; } }
        public new void Log(string message)
        {
            if (IsLooging)
                base.Log(message);
        }


        /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm.
        public override void Initialize()
        {
            _isLogging = false;
            //Set trading window
            SetStartDate(2009, 1, 1);
            SetEndDate(DateTime.Now);

            //Set cash and brokermodel
            SetCash(100000);
            SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);

            //Set Benchmark
            Security security = AddEquity("SPY", Resolution.Daily);
            SetBenchmark(security.Symbol);

            // Set the MarketRegimeFilter
            SimpleMovingAverage spyMovingAverage200 = SMA("SPY", 200, Resolution.Daily);

            //Warm up SMA
            SetWarmUp(200);
            IEnumerable<TradeBar> history = History("SPY", 200, Resolution.Daily);
            foreach (TradeBar tradeBar in history)
            {
                spyMovingAverage200.Update(tradeBar.EndTime, tradeBar.Close);
            }
            _marketRegimeFilter = new MarketRegimeFilter(spyMovingAverage200);

            //Setup universe based on ETF: https://www.quantconnect.com/docs#Universes
            UniverseSettings.Resolution = Resolution.Daily;
            AddUniverse(Universe.ETF("SPY", Market.USA, UniverseSettings));

            //Trade only on Wednesday at opening after 1 minutes
            Schedule.On(DateRules.Every(DayOfWeek.Wednesday),
                TimeRules.AfterMarketOpen("SPY", 1), ScheduledOnWednesday1MinuteAfterMarketOpen);
        }

        // SECURITY RANKING, SELL, REBALANCE AND BUY
        private void ScheduledOnWednesday1MinuteAfterMarketOpen()
        {
            if (IsWarmingUp) return;

            // First, we order by slope and we take top 20% ranked
            var sortedEquityListBySlope = _customIndicators.Where(x => x.Value.IsReady)
            .OrderByDescending(x => x.Value.AnnualizedSlope)
            .Take(_topNStockOfSp500)
            .ToList();
            // Second, we filter by minimum slope, above moving average and max gap
            sortedEquityListBySlope = sortedEquityListBySlope
            .Where(x => x.Value.AnnualizedSlope > _minimumAnnualizedSlope
                && Securities[x.Key].Price > x.Value.MovingAverage
                && x.Value.Gap < _maximumGap).ToList();

            //Sell if security is not in list
            foreach (var security in Portfolio.Values.Where(x => x.Invested))
            {
                var symbolHold = security.Symbol;
                if (!sortedEquityListBySlope.Exists(x => x.Value.Symbol == symbolHold))
                {
                    Liquidate(symbolHold);
                }
            }

            bool riskON = _marketRegimeFilter.RiskON(Securities["SPY"].Price);

            //Twice a month rebalance the positions sizes (risk);
            if (RebalanceWeek) {
                _rebalanceWeek = false;
                var risk = Portfolio.TotalPortfolioValue * RiskPerContractOnPortfolio;

                foreach (var security in Portfolio.Values.Where(x => x.Invested))
                {
                    var symbolHold = security.Symbol;
                    var quantityHold = security.Quantity;
                    var priceHold = Securities[symbolHold].Price;

                    foreach (var customIndicator in sortedEquityListBySlope.Where(x => x.Key == symbolHold))
                    {
                        var numberStocks = (int)Math.Floor(risk / customIndicator.Value.Atr);
                        if (Math.Abs(quantityHold - numberStocks) > 0 && quantityHold > 1)
                        {
                            // Sell or Buy the stocks diff
                            if (quantityHold > numberStocks)
                            {
                                Sell(symbolHold, (quantityHold - numberStocks));
                            }
                            else
                            {
                                if (quantityHold < numberStocks)
                                {
                                    decimal portfolioCashBalance = Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue;
                                    // Do we have cash to trade?
                                    if (portfolioCashBalance > ((numberStocks - quantityHold) * priceHold + (numberStocks - quantityHold) * priceHold * BrokerFee))
                                    {
                                        Order(symbolHold, (numberStocks - quantityHold));
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else { _rebalanceWeek = true; }

            //If the MarketRegimeIndicator indicator is RiskON, we buy stocks, otherwise not;
            if (riskON)
            {
                foreach (var customIndicatorItem in sortedEquityListBySlope)
                {
                    CustomMomentumIndicator customIndicator = customIndicatorItem.Value;
                    var symbol = customIndicator.Symbol;
                    var inPortfolio = false;
                    foreach (var security in Portfolio.Values.Where(x => x.Invested))
                    {
                        if (security.Symbol == symbol)
                        {
                            inPortfolio = true;
                        }
                    }
                    if (!inPortfolio)
                    {
                        var risk = Portfolio.TotalPortfolioValue * RiskPerContractOnPortfolio;
                        var numberStocks = (int)Math.Floor(risk / customIndicator.Atr);
                        var price = Securities[symbol].Price;
                        if (numberStocks > 0)
                        {
                            decimal portfolioCashBalance = Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue;
                            // Do we have cash to trade?
                            if (portfolioCashBalance > (numberStocks * price + (numberStocks * price) * BrokerFee))
                            {
                                Order(symbol, numberStocks);
                            }
                        }
                    }
                }
            }
        }

        // creating custom indicators for each symbol
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            if (changes.AddedSecurities.Count > 0)
            {
                    foreach (Security security in changes.AddedSecurities)
                {
                    if (!_customIndicators.ContainsKey(security.Symbol) && (security.Symbol.Value != "SPY"))
                    {
                        var customIndicator = new CustomMomentumIndicator(security.Symbol, _annualizedSlopeWindow, _movingAverageWindow, _gapWindow, _atrWindow);
                        //warm up indicator
                        var history = History(security.Symbol, customIndicator.Window, Resolution.Daily);
                        foreach (TradeBar tradeBar in history)
                            customIndicator.Update(tradeBar);

                        _customIndicators.Add(security.Symbol, customIndicator);
                        RegisterIndicator(security.Symbol, customIndicator, Resolution.Daily);
                    }
                }
            }
            if (changes.RemovedSecurities.Count > 0)
            {
                foreach (var security in changes.RemovedSecurities)
                {
                    if (security.Invested)
                    {
                        Liquidate(security.Symbol);
                    }
                    if (_customIndicators.ContainsKey(security.Symbol))
                    {
                        _customIndicators.Remove(security.Symbol);
                    }
                }
            }
        }
    }
}