Overall Statistics
Total Orders
5282
Average Win
0.60%
Average Loss
-0.53%
Compounding Annual Return
14.813%
Drawdown
27.300%
Expectancy
0.267
Start Equity
100000
End Equity
4083622.13
Net Profit
3983.622%
Sharpe Ratio
0.542
Sortino Ratio
0.582
Probabilistic Sharpe Ratio
1.130%
Loss Rate
41%
Win Rate
59%
Profit-Loss Ratio
1.14
Alpha
0.069
Beta
0.516
Annual Standard Deviation
0.175
Annual Variance
0.031
Information Ratio
0.256
Tracking Error
0.173
Treynor Ratio
0.184
Total Fees
$71051.38
Estimated Strategy Capacity
$0
Lowest Capacity Asset
PLTR XIAKBH8EIMHX
Portfolio Turnover
5.32%
#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;
    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.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,5%)
        private const decimal RiskPerContractOnPortfolio = 0.005m;

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

        /// 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 = 20;

        ///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(1998, 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);
            security = AddEquity("BIL", Resolution.Daily);
            security = AddEquity("IEF", Resolution.Daily);

            // Set the MarketRegimeFilter
            SimpleMovingAverage spyMovingAverage200 = SMA("SPY", 200, Resolution.Daily);
                        // Set Bonds ROC
            RateOfChange bilROC21 = ROC("BIL",21,Resolution.Daily);
            RateOfChange iefROC21 = ROC("IEF",21,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);
            }
            history = History("BIL", 21, Resolution.Daily);
            foreach (TradeBar tradeBar in history)
            {
                bilROC21.Update(tradeBar.EndTime, tradeBar.Close);
            }
            history = History("IEF", 21, Resolution.Daily);
            foreach (TradeBar tradeBar in history)
            {
                iefROC21.Update(tradeBar.EndTime, tradeBar.Close);
            }

            _marketRegimeFilter = new MarketRegimeFilter(spyMovingAverage200,bilROC21,iefROC21);

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

            //Trade only on Wednesday at opening after 1 minutes
            Schedule.On(DateRules.Every(DayOfWeek.Thursday),
                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 the MarketRegimeIndicator indicator is RiskON, we buy stocks, otherwise not;
                                if (riskON)
                                {
                                    if (quantityHold < numberStocks)
                                    {
                                        // free cash invested in bonds
                                        if (Portfolio.ContainsKey("IEF"))
                                            if (Portfolio["IEF"].Quantity > 0)
                                                Liquidate("IEF");

                                        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)
                        {
                            // free cash invested in bonds
                            if (Portfolio.ContainsKey("IEF"))
                                if (Portfolio["IEF"].Quantity > 0) 
                                    Liquidate("IEF");
                            decimal portfolioCashBalance = Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue;
                            // Do we have cash to trade?
                            if (portfolioCashBalance > (numberStocks * price + (numberStocks * price) * BrokerFee))
                            {
                                Order(symbol, numberStocks);
                            }
                        }
                    }
                }
            }

            // Buy Bonds with remaing cash
            if (_marketRegimeFilter.TradeBonds()){
                if (Securities["IEF"].Price > 0){
                    int numberStocks = (int) Math.Floor((Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue  - (Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue)*BrokerFee) / Securities["IEF"].Price);
                    if (numberStocks > 0) { 
                        Order("IEF", numberStocks);
                    }
                }
            }
            else {
                // we free cash invested in bonds
                if (Portfolio.ContainsKey("IEF")){
                    if (Portfolio["IEF"].Quantity > 0) {
                        Liquidate("IEF");
                    }
                }
            }
        }

        // 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") || (security.Symbol.Value != "IEF") || (security.Symbol.Value != "BIL")))
                    {
                        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);
                    }
                }
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.Framework.Selection
{
    /// <summary>
    /// Defines the QC100 universe as a universe selection model for framework algorithm
    /// </summary>
    public class QC100UniverseSelectionModel : FundamentalUniverseSelectionModel
    {
        private const int _numberOfSymbolsCoarse = 500;
        private const int _numberOfSymbolsFine = 100;

        // rebalances at the start of each month
        private int _lastMonth = -1;
        private readonly Dictionary<Symbol, double> _dollarVolumeBySymbol = new ();

        /// <summary>
        /// Initializes a new default instance of the <see cref="QC100UniverseSelectionModel"/>
        /// </summary>
        public QC100UniverseSelectionModel()
            : base(true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="QC100UniverseSelectionModel"/>
        /// </summary>
        /// <param name="universeSettings">Universe settings defines what subscription properties will be applied to selected securities</param>
        public QC100UniverseSelectionModel(UniverseSettings universeSettings)
            : base(true, universeSettings)
        {
        }

        /// <summary>
        /// Performs coarse selection for the QC100 constituents.
        /// The stocks must have fundamental data
        /// The stock must have positive previous-day close price
        /// The stock must have positive volume on the previous trading day
        /// </summary>
        public override IEnumerable<Symbol> SelectCoarse(QCAlgorithm algorithm, IEnumerable<CoarseFundamental> coarse)
        {
            if (algorithm.Time.Month == _lastMonth)
            {
                return Universe.Unchanged;
            }

            var sortedByDollarVolume =
                (from x in coarse
                 where x.HasFundamentalData && x.Volume > 0 && x.Price > 0
                 orderby x.DollarVolume descending
                 select x).Take(_numberOfSymbolsCoarse).ToList();

            _dollarVolumeBySymbol.Clear();
            foreach (var x in sortedByDollarVolume)
            {
                _dollarVolumeBySymbol[x.Symbol] = x.DollarVolume;
            }

            // If no security has met the QC100 criteria, the universe is unchanged.
            // A new selection will be attempted on the next trading day as _lastMonth is not updated
            if (_dollarVolumeBySymbol.Count == 0)
            {
                return Universe.Unchanged;
            }

            return _dollarVolumeBySymbol.Keys;
        }

        /// <summary>
        /// Performs fine selection for the QC100 constituents
        /// The company's headquarter must in the U.S.
        /// The stock must be traded on either the NYSE or NASDAQ
        /// At least half a year since its initial public offering
        /// The stock's market cap must follow the criteria of the S&P 100 index
        /// </summary>
        public override IEnumerable<Symbol> SelectFine(QCAlgorithm algorithm, IEnumerable<FineFundamental> fine)
        {
            var filteredFine =
                (from x in fine
                 where x.CompanyReference.CountryId == "USA" &&
                       (x.CompanyReference.PrimaryExchangeID == "NYS" || x.CompanyReference.PrimaryExchangeID == "NAS") &&
                       (algorithm.Time - x.SecurityReference.IPODate).Days > 180 &&
                       x.MarketCap > 5000000000m // Adjusted to follow S&P 100 criteria
                 select x).ToList();

            var count = filteredFine.Count;

            // If no security has met the QC100 criteria, the universe is unchanged.
            // A new selection will be attempted on the next trading day as _lastMonth is not updated
            if (count == 0)
            {
                return Universe.Unchanged;
            }

            // Update _lastMonth after all QC100 criteria checks passed
            _lastMonth = algorithm.Time.Month;

            var percent = _numberOfSymbolsFine / (double)count;

            // select stocks with top dollar volume in every single sector
            var topFineBySector =
                (from x in filteredFine
                 // Group by sector
                 group x by x.CompanyReference.IndustryTemplateCode into g
                 let y = from item in g
                         orderby _dollarVolumeBySymbol[item.Symbol] descending
                         select item
                 let c = (int)Math.Ceiling(y.Count() * percent)
                 select new { g.Key, Value = y.Take(c) }
                 ).ToDictionary(x => x.Key, x => x.Value);

            return topFineBySector.SelectMany(x => x.Value)
                .OrderByDescending(x => _dollarVolumeBySymbol[x.Symbol])
                .Take(_numberOfSymbolsFine)
                .Select(x => x.Symbol);
        }
    }
}
#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;

        private RateOfChange _bilROC21;
        private RateOfChange _iefROC21;

        public MarketRegimeFilter(SimpleMovingAverage spyMovingAverage200, RateOfChange bilROC21, RateOfChange iefROC21)
        {
            _spyMovingAverage200 = spyMovingAverage200;
            _bilROC21 = bilROC21;
            _iefROC21 = iefROC21;
        }
        
        // 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;
        }

        // Checks if the iefROC21 is over its bilROC21 and is over 0.
        public bool TradeBonds(){
            
            bool tradeBonds = false;

            if (_iefROC21 > _bilROC21 && _iefROC21 > 0){
                tradeBonds = true;
            }

            if (tradeBonds)
                return true;
            else
                return false;
        }
    }
}