Overall Statistics
Total Trades
36907
Average Win
0.51%
Average Loss
-0.41%
Compounding Annual Return
46.145%
Drawdown
50.700%
Expectancy
0.174
Net Profit
22822968.099%
Sharpe Ratio
1.382
Probabilistic Sharpe Ratio
92.631%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.25
Alpha
0.306
Beta
0.644
Annual Standard Deviation
0.244
Annual Variance
0.06
Information Ratio
1.242
Tracking Error
0.232
Treynor Ratio
0.524
Total Fees
$0.00
Estimated Strategy Capacity
$4700000.00
Lowest Capacity Asset
IMPP XU3XFV9BKJTX
#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 AdaptableMagentaLlamaDK : QCAlgorithm
    {
        //CONSTANTS and FILTERS///////////////
        //////////////////////////////////////
        private const int TAKE_TOP = 3;

        //Make sure to have an m after the number to make it a decimal
        private const decimal MIN_PRICE = 0m;
        private const decimal MAX_PRICE = 10000000m;

        private List<DateTime> SKIP_DAYS = new List<DateTime>{
            new DateTime(2018,9,20),
            new DateTime(2018,9,21),
            new DateTime(2020,8,04),
            new DateTime(2020,8,05),
            new DateTime(2020,8,06),
            new DateTime(2020,8,07),
            new DateTime(2020,8,08),
            new DateTime(2020,8,09),
            new DateTime(2020,8,10),
            new DateTime(2020,8,11),
            new DateTime(2020,8,12),
            new DateTime(2020,8,13),
            new DateTime(2022,7,25),
            new DateTime(2022,7,27),




        };

        private List<string> SKIP_STOCKS = new List<string>{
            "HYMC XEZF85YZ9C2T",
            "MUDS WSRW88XX4PR9",
            "QPAC VYPRXHXF1KDH",
            "QPAC VZ7HZ6IGF59H",
            "XELA",
            "PHUN"
        };

        //////////////////////////////////////


        public class SymbolData
        {
            public Symbol Symbol { get; set; }
            private int Window = 10;
            private int Offset = 1; //TODO: NOT USING YET, confirm 1 day offset matches
            private List<decimal> Rolling = new List<decimal>();
            public decimal Value = 0;
            public decimal CurrentPrice = 0;

            public SymbolData(Symbol symbol)
            {
                Symbol = symbol;
            }

            public void Update(decimal price, decimal value)
            {
                CurrentPrice = price;

                if (Rolling.Count > Window)
                    Rolling.RemoveAt(0);
                
                Rolling.Add(value);

                Value = Rolling.Median();
            }
        }
        private List<SymbolData> _universe = new List<SymbolData>();

        public override void Initialize()
        {
            SetStartDate(1990, 1, 1);  //Set Start Date
            SetEndDate(2022, 6, 30);    //Set End Date
            SetCash(1000000);          //Set Strategy Cash

            AddUniverseSelection(new CoarseFundamentalUniverseSelectionModel(SelectCoarse));
            AddEquity("SPY", Resolution.Minute); //only used for scheduling
            UniverseSettings.Resolution = Resolution.Minute;
            UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
            SetSecurityInitializer(CustomSecurityInitializer);
    

            // schedule an event to fire on certain days of the week
            Schedule.On(DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday), 
                TimeRules.AfterMarketOpen("SPY", -5), () =>
                {
                    // Log("Morning Liquidate: Fired at: " + Time);
                    Liquidate();
                });
                
            //have to schedule 16 min before the end of the day so that market close orders are accepted
            Schedule.On(DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday), 
                TimeRules.BeforeMarketClose("SPY", 16), () =>
                {
                    // Log("End of Day Buy: Fired at: " + Time);
                    EndOfDayBuy();
                });            
        }

        private IEnumerable<Symbol> SelectCoarse(IEnumerable<CoarseFundamental> coarse)
        {   
            foreach (var c in coarse.Where(c => c.HasFundamentalData))
            {
                if (!_universe.Any(u => u.Symbol == c.Symbol))
                    _universe.Add(new SymbolData(c.Symbol));
                
                _universe.First(u => u.Symbol == c.Symbol).Update(c.Price, c.Volume);
            }

            //UNCOMMENT FOR SELECTED STOCK VALUES
            // foreach (var c in _universe.OrderByDescending(u => u.Value).Take(TAKE_TOP))
            // {
            //     Log($"{c.Symbol} at {c.Value}");
            // }
            // _universe.OrderByDescending(u => u.Value).Take(TAKE_TOP).DoForEach(x => Log($"{x.Symbol} has value {x.Value}"));

            return _universe.Select(c => c.Symbol);
        }

        private void CustomSecurityInitializer(Security security)
        {
            security.SetFeeModel(new ConstantFeeModel(0));
            security.SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted);
            var howmany = GetParameter("howmany");
         
        }
        
        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock data
        public override void OnData(Slice data)
        {
        }

        public void LiquidatePortfolio()
        {

            foreach (var stock in Portfolio.Where(p => p.Value.Invested))
            {
                MarketOnOpenOrder(stock.Value.Symbol, -stock.Value.Quantity);
            }
        }

        public void EndOfDayBuy()
        {
            if (SKIP_DAYS.Contains(Time.Date)) return;

            var currentBuys = _universe
                            .Where(u => u.CurrentPrice >= MIN_PRICE && u.CurrentPrice <= MAX_PRICE)
                            //.Where(u => !SKIP_STOCKS.Contains(u.Symbol))
                            .OrderByDescending(u => u.Value)
                            .Take(TAKE_TOP)
                           // .Select(u => u.Symbol)
                            .ToList();
                            
            var splitPct = 1M / TAKE_TOP;

            foreach(var stock in currentBuys)
            {      
                Log($"{Time.Date} for {stock.Symbol}: MedianVol={stock.Value}");
                var quantity = CalculateOrderQuantity(stock.Symbol, splitPct);
                MarketOnCloseOrder(stock.Symbol, quantity);
            }
        }
    }
}