Overall Statistics |
Total Trades 4104 Average Win 0.50% Average Loss -0.50% Compounding Annual Return 11.526% Drawdown 38.700% Expectancy 0.038 Net Profit 27.964% Sharpe Ratio 0.409 Probabilistic Sharpe Ratio 12.410% Loss Rate 48% Win Rate 52% Profit-Loss Ratio 1.00 Alpha 0.18 Beta -0.026 Annual Standard Deviation 0.428 Annual Variance 0.183 Information Ratio -0.042 Tracking Error 0.474 Treynor Ratio -6.817 Total Fees $9697.92 Estimated Strategy Capacity $0 Lowest Capacity Asset ZIM XLKPYP4NP5R9 |
//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.Consolidators; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Securities; 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.Daily; Resolution = Resolution.Minute; SetStartDate(2019, 9, 27); SetCash(100000); SetBenchmark("SPY"); AddUniverse(coarse => { return (from stock in coarse where stock.Symbol.Value.Substring(0, stock.Symbol.Value.Length) != "GME" where stock.Symbol.Value.Substring(0, stock.Symbol.Value.Length) != "AMC" where stock.Symbol.Value.Substring(0, stock.Symbol.Value.Length) != "UVXY" //where stock.HasFundamentalData == false orderby stock.DollarVolume descending select stock.Symbol).Take(TotalHighDollarVolumeStocks); }); Symbol _spy = AddEquity("SPY").Symbol; } public class SymbolData { public Symbol Symbol; public QCAlgorithm _algo; public IDataConsolidator Consolidator1d; public SymbolData(Symbol symbol, QCAlgorithm algo) { _algo = algo; Symbol = symbol; Consolidator1d = algo.Consolidate(symbol, Resolution.Daily, ((Flock)algo).OnConsolidatedDaily); } } public void OnConsolidatedDaily(TradeBar bar) { StockData stockData = HighDollarVolumeStocks.Find(x => x.Symbol == bar.Symbol); stockData.Candles.Add(bar); if (stockData.Candles.Count > Period) { stockData.Candles.RemoveAt(0); } } public void OnData(TradeBars data) { if (this.Time.Hour == 9 && this.Time.Minute == 31) Rebalance(data); } public void Rebalance(TradeBars data) { foreach (StockData stockData1 in HighDollarVolumeStocks) { if (stockData1.Candles.Count == 0) continue; 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 && stockData2.Candles.Count > 0) { 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); } // We only want to get tradeable stocks List<StockData> tradeableStocks = HighDollarVolumeStocks.Where(x => x.Security.IsTradable && data.ContainsKey(x.Symbol)).ToList(); List<StockData> stocksToHold = tradeableStocks.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 Security Security; 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.Security = security; // Get daily data for calculations stockData.Candles = History(stockData.Symbol, Period, Resolution).ToList(); // Warmup Minute data so we don't get errors History(stockData.Symbol, 390, Resolution.Minute); HighDollarVolumeStocks.Add(stockData); } } } }