Overall Statistics
Total Trades
8946
Average Win
0.59%
Average Loss
-0.47%
Compounding Annual Return
766.987%
Drawdown
33.800%
Expectancy
0.242
Net Profit
13013.014%
Sharpe Ratio
7.247
Probabilistic Sharpe Ratio
99.953%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
1.25
Alpha
4.163
Beta
0.524
Annual Standard Deviation
0.589
Annual Variance
0.346
Information Ratio
6.935
Tracking Error
0.587
Treynor Ratio
8.133
Total Fees
$412150.80
Estimated Strategy Capacity
$7600000.00
Lowest Capacity Asset
BVXV W03ZQ9L0Y1K5
//Copyright HardingSoftware.com. Granted to the public domain.
//Use entirely at your own risk.
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;

namespace QuantConnect.Algorithm.CSharp
{
    public class Flock : QCAlgorithm
    {
		List<StockData> HighDollarVolumeStocks = new List<StockData>();
		int TotalHighDollarVolumeStocks = 250;
		int TotalStocksToHold = 10;
		Resolution Resolution = Resolution.Daily;
		int Period = 5;
		decimal Leverage = 0.99m;
		decimal Threshold = 0.0m;
		
        public override void Initialize()
        {
            UniverseSettings.Resolution = Resolution;

            SetStartDate(2019, 9, 27);
            SetCash(100000);

            AddUniverse(coarse =>
            {
            	return (from stock in coarse
            			where stock.Symbol.ToString().Substring(0, 3) != "GME"
            			where stock.Symbol.ToString().Substring(0, 3) != "AMC"
            			where stock.Symbol.ToString().Substring(0, 4) != "UVXY"
            			//where stock.HasFundamentalData == false
            			orderby stock.DollarVolume descending  
            			select stock.Symbol).Take(TotalHighDollarVolumeStocks);
            });
        }

        public void OnData(TradeBars data)
        {
            foreach (StockData stockData in HighDollarVolumeStocks)
            {
            	if (data.ContainsKey(stockData.Symbol))
            	{
	            	TradeBar bar = data[stockData.Symbol];
	            	stockData.Candles.Add(bar);
	            	if (stockData.Candles.Count > Period)
	            	{
	            		stockData.Candles.RemoveAt(0);
	            	}
            	}
            }
            
            foreach (StockData stockData1 in HighDollarVolumeStocks)
            {
        	    List<TradeBar> candles1 = stockData1.Candles;
                decimal[] prices1 = candles1.Select(x => x.Close).ToArray();
                decimal averagePrice1 = prices1.Average();
                decimal[] normalizedPrices1 = prices1.Select(x => x / averagePrice1).ToArray();
                decimal sumRatios = 0;
                foreach (StockData stockData2 in HighDollarVolumeStocks)
                {
                	if (stockData1 != stockData2)
                	{
	                    List<TradeBar> candles2 = stockData2.Candles;
	                    decimal[] prices2 = candles2.Select(x => x.Close).ToArray();
	                    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.Last() - normalizedPrices2.Last();
	                    if (differenceRange != 0)
	                    {
		                    decimal ratio = currentDifference / differenceRange;
		                    sumRatios += ratio;
	                    }
                	}
                }
                stockData1.AverageRatio = sumRatios / (HighDollarVolumeStocks.Count - 1);
            }
            
            List<StockData> stocksToHold = HighDollarVolumeStocks.OrderByDescending(x => Math.Abs(x.AverageRatio)).Take(TotalStocksToHold).ToList();
            
            foreach (var security in Portfolio.Values)
            {
            	if (Portfolio[security.Symbol].Invested)
            	{
	                if (stocksToHold.Exists(x => x.Symbol == security.Symbol) == false)
	                {
	                    Liquidate(security.Symbol);
	                }
            	}
            }
            
            foreach (StockData stockData in stocksToHold)
            {
                if (stockData.AverageRatio < -Threshold && Portfolio[stockData.Symbol].Quantity <= 0)
                {
                    SetHoldings(stockData.Symbol, Leverage / (decimal)TotalStocksToHold);
                }
                else if (stockData.AverageRatio > Threshold && Portfolio[stockData.Symbol].Quantity >= 0)
                {
					SetHoldings(stockData.Symbol, -Leverage / (decimal)TotalStocksToHold);
                }
            }

        }

		public class StockData
		{
			public Symbol Symbol;
			public List<TradeBar> Candles = new List<TradeBar>();
            public decimal AverageRatio;
		}

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            foreach (var security in changes.RemovedSecurities)
            {
            	StockData stockData = HighDollarVolumeStocks.Find(x => x.Symbol == security.Symbol);
            	if (stockData != null)
            	{
                	HighDollarVolumeStocks.Remove(stockData);
            	}
            }
            foreach (var security in changes.AddedSecurities)
            {
            	StockData stockData = new StockData();
            	stockData.Symbol = security.Symbol;
            	stockData.Candles = History(stockData.Symbol, Period, Resolution).ToList();
				HighDollarVolumeStocks.Add(stockData);
            }
        }
        
    }
}