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")); } } }