Overall Statistics |
Total Trades 4439 Average Win 1.07% Average Loss -0.87% Compounding Annual Return 392.109% Drawdown 46.200% Expectancy 0.187 Net Profit 2542.679% Sharpe Ratio 3.976 Probabilistic Sharpe Ratio 93.012% Loss Rate 47% Win Rate 53% Profit-Loss Ratio 1.23 Alpha 2.787 Beta 0.723 Annual Standard Deviation 0.732 Annual Variance 0.535 Information Ratio 3.817 Tracking Error 0.718 Treynor Ratio 4.024 Total Fees $93498.81 Estimated Strategy Capacity $3900000.00 Lowest Capacity Asset LLY R735QTJ8XC9X |
//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 = 50; int TotalStocksToHold = 5; //Resolution Resolution = Resolution.Daily; int Period = 5; decimal Leverage = 0.99m; decimal Threshold = 0.0m; public DateTime LastTime = new DateTime(); public override void Initialize() { UniverseSettings.Resolution = Resolution.Minute; SetStartDate(2019, 12, 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 override void OnEndOfDay() { foreach (StockData stockData in HighDollarVolumeStocks) { if (stockData.MinuteCandles.Count % 390 == 0) { stockData.DailyCandles = Group(stockData.MinuteCandles, 390); } } } public void OnData(TradeBars data) { foreach (StockData stockData in HighDollarVolumeStocks) { if (data.ContainsKey(stockData.Symbol)) { TradeBar bar = data[stockData.Symbol]; stockData.MinuteCandles.Add(bar); if (stockData.MinuteCandles.Count > Period * 390) { stockData.MinuteCandles.RemoveAt(0); } } } if (Time.Day == LastTime.Day) { return; } LastTime = Time; foreach (StockData stockData1 in HighDollarVolumeStocks) { List<TradeBar> candles1 = stockData1.DailyCandles; 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.DailyCandles; 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> MinuteCandles = new List<TradeBar>(); public List<TradeBar> DailyCandles = 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.MinuteCandles = History(stockData.Symbol, Period * 390, Resolution.Minute).ToList(); stockData.DailyCandles = History(stockData.Symbol, Period, Resolution.Daily).ToList(); HighDollarVolumeStocks.Add(stockData); } } public static List<TradeBar> Group(List<TradeBar> candles, int binWidth) { List<TradeBar> outCandles = new List<TradeBar>(); for (int start = 0; start < candles.Count; start += binWidth) { List<TradeBar> group = candles.GetRange(start, binWidth); TradeBar newCandle = new TradeBar(); newCandle.Time = group.First().Time; newCandle.Open = group.First().Open; newCandle.Close = group.Last().Close; newCandle.High = group.Select(x => x.High).Max(); newCandle.Low = group.Select(x => x.Low).Min(); newCandle.Volume = group.Select(x => x.Volume).Sum(); outCandles.Add(newCandle); } return outCandles; } } }