Overall Statistics
Total Trades
22767
Average Win
0.44%
Average Loss
-0.40%
Compounding Annual Return
-28.040%
Drawdown
83.300%
Expectancy
-0.027
Net Profit
-79.704%
Sharpe Ratio
-0.246
Probabilistic Sharpe Ratio
0.040%
Loss Rate
54%
Win Rate
46%
Profit-Loss Ratio
1.11
Alpha
-0.134
Beta
0.271
Annual Standard Deviation
0.459
Annual Variance
0.211
Information Ratio
-0.405
Tracking Error
0.475
Treynor Ratio
-0.417
Total Fees
$6668.67
Estimated Strategy Capacity
$6200000.00
Lowest Capacity Asset
PINS X3RPXTZRW09X
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Util;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
#endregion

namespace QuantConnect.Algorithm.CSharp
{
    public class Flock : QCAlgorithm
    {
		List<Symbol> HighDollarVolumeStocks = new List<Symbol>();
		int _coarse_count = 250;
		int _stocks_to_hold = 10;
		int _period = 5;
		decimal Leverage = 0.99m;
		decimal Threshold = 0.0m;
        private bool _selection_flag = false;

        private Dictionary<Symbol, RollingWindow<decimal>> _data = new Dictionary<Symbol, RollingWindow<decimal>>();
        private Dictionary<Symbol, float> _weight = new Dictionary<Symbol, float>();
		
        public override void Initialize()
        {
            UniverseSettings.Resolution = Resolution.Minute;

            SetStartDate(2018, 1, 1);
            SetCash(100000);

            AddUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
            Symbol symbol = AddEquity("SPY", Resolution.Minute).Symbol;

            // selection lambda function
            Schedule.On(DateRules.MonthStart(symbol), TimeRules.AfterMarketOpen(symbol), () =>
            {
                _selection_flag = true;
            });
        }

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            foreach (Security security in changes.AddedSecurities){
                security.SetFeeModel(new CustomFeeModel());
                security.SetLeverage(10);
            }
        }

        IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
        {
            // store daily stock prices
            foreach(CoarseFundamental stock in coarse){
                Symbol symbol = stock.Symbol;
                if (_data.ContainsKey(symbol))
                    _data[symbol].Add(stock.AdjustedPrice);
            }

            // if (!_selection_flag)
            //     return Universe.Unchanged;
            
            Symbol[] selected = coarse.Where(x=> x.Symbol.Value != "AMC" && 
                x.Symbol.Value != "GME" && x.Symbol.Value != "UVXY").OrderByDescending(x=>x.DollarVolume).Select(x=>x.Symbol).Take(_coarse_count).ToArray<Symbol>();
            
            foreach (Symbol symbol in selected){
                if (_data.ContainsKey(symbol))
                    continue;
                
                _data[symbol] = new RollingWindow<decimal>((int)_period);
                IEnumerable<TradeBar> history = History<TradeBar>(symbol, _period, Resolution.Daily);
                if (history.IsNullOrEmpty()){
                    Log($"Not enough data for {symbol} yet");
                    continue;
                }

                foreach (TradeBar bar in history) {
                   _data[symbol].Add(bar.Close);
                }
            }

            return selected.Where(x=>_data[x].IsReady).ToList<Symbol>();
        }

        IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
        {
            List<Symbol> fine_symbols = fine.Select(x=>x.Symbol).ToList<Symbol>();

            Dictionary<Symbol, decimal> avg_ratio = new Dictionary<Symbol, decimal>();

            foreach (Symbol symbol1 in fine_symbols)
            {
                decimal[] prices1 = _data[symbol1].ToArray<decimal>();
                decimal averagePrice1 = prices1.Average();
                decimal[] normalizedPrices1 = prices1.Select(x => x / averagePrice1).ToArray();
                decimal sumRatios = 0;

                foreach (Symbol symbol2 in fine_symbols)
                {
                    if (symbol1 != symbol2)
                    {
                        decimal[] prices2 = _data[symbol2].ToArray<decimal>();
                        decimal averagePrice2 = prices2.Average();
                        decimal[] normalizedPrices2 = prices2.Select(x => x / averagePrice2).ToArray();
                        decimal[] differences = normalizedPrices1.Zip(normalizedPrices2, (x, y) => x - y).ToArray();
                        decimal maxDifference = differences.Max();
                        decimal minDifference = differences.Min();
                        decimal differenceRange = maxDifference - minDifference;
                        decimal currentDifference = normalizedPrices1.First() - normalizedPrices2.First();
                        
                        if (differenceRange != 0)
                        {
                            decimal ratio = currentDifference / differenceRange;
                            sumRatios += ratio;
                        }
                    }
                }
                decimal avg_ratio_value = sumRatios / (fine_symbols.Count - 1);
                if (avg_ratio_value != 0)
                    avg_ratio[symbol1] = avg_ratio_value;
            }
            
            Symbol[] _long = new Symbol[] {};

            if (avg_ratio.Count >= _stocks_to_hold)
            {
                _long = avg_ratio.OrderByDescending(x => Math.Abs(x.Value)).ToArray().Take(_stocks_to_hold).Select(x=>x.Key).ToArray<Symbol>();
                foreach(Symbol symbol in _long)
                {
                    if (avg_ratio[symbol] < 0)
                        _weight[symbol] = 1.0f / (float)(_long.Count());
                    else if (avg_ratio[symbol] > 0)
                        _weight[symbol] = -1.0f / (float)(_long.Count());
                }
            }

            return _weight.Keys;
        }

        public void OnData(TradeBars data)
        {
            if(!(Time.Hour == 9 && Time.Minute == 31))
                return;
            
            // liquidate first
            Symbol[] invested = (from x in Portfolio where x.Value.Invested select x.Key).ToArray();
            foreach (Symbol symbol in invested)
                if (!_weight.ContainsKey(symbol))
                    Liquidate(symbol);
                    // MarketOnOpenOrder(symbol, -Portfolio[symbol].Quantity);

            // trade execution
            foreach (KeyValuePair<Symbol, float> item in _weight)
                if(data.ContainsKey(item.Key))
                {
                    SetHoldings(item.Key, item.Value);
                    // decimal q = (Portfolio.TotalPortfolioValue * (decimal)item.Value) / data[item.Key].Value;
                    // MarketOnOpenOrder(item.Key, q);
                }
            
            _weight.Clear();
        }
    }
    
    public class CustomFeeModel : FeeModel {
        public override OrderFee GetOrderFee(OrderFeeParameters parameters) {
            // custom fee math
            var fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005m;
            return new OrderFee(new CashAmount(fee, "USD"));
        }
    }
}