Overall Statistics |
Total Trades 7003 Average Win 0.12% Average Loss -0.07% Compounding Annual Return 20.518% Drawdown 7.600% Expectancy 0.073 Net Profit 45.915% Sharpe Ratio 1.395 Probabilistic Sharpe Ratio 70.241% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 1.86 Alpha 0.144 Beta -0.002 Annual Standard Deviation 0.103 Annual Variance 0.011 Information Ratio -0.364 Tracking Error 0.211 Treynor Ratio -60.336 Total Fees $7395.24 Estimated Strategy Capacity $22000000.00 Lowest Capacity Asset CHV R735QTJ8XC9X |
namespace QuantConnect.Algorithm.CSharp { public class ClientAlgorithm : QCAlgorithm { // BACKTESTING PARAMETERS // ================================================================================================================= // general settings: // set starting cash private int _startingCash = 100000; // backtesting start date time: // date setting variables private int _startYear = 2020; private int _startMonth = 3; private int _startDay = 15; // backtesting end date time: // determines whether there is a specified end date // if false it will go to the current date (if 'true' it will go to the specified date) private bool _enableEndDate = false; // date setting variables private int _endYear = 2022; private int _endMonth = 1; private int _endDay = 4; // universe settings: // universe selection type // determines whether securities are selected by QC's universe function // or manually by the user // manual = false; QC universe = true private readonly bool _universeSelectionType = true; // number of securities for the universe selection to select private readonly int _stockCount = 25; // use default values for universe private readonly bool _useDefaultSymbolBase = true; // stock list for equities // list of equities you want in the universe // used in manual selection of universe // set selectionType = false for activation private readonly SymbolBase[] _universeList = new SymbolBase[]{ new SymbolBase( "AMZN", // symbol ticker Resolution.Minute, // data resolution 30, // data resolution consolidation rate 1.0m // portfolio allocation percentage ), }; // default symbol base settings // used if using QC universe selection or a base is not detected for // an added security private readonly SymbolBase _defaultSymbolBase = new SymbolBase( "<BASE>", // do not modify // parameters to modify: Resolution.Minute, // data resolution 30, // data resolution consolidation rate 1.0m // portfolio allocation percentage ); // position settings: // percent of portfolio to use for trading // must be below 1 private readonly decimal _portfolioAllocation = 1m; // indicator settings: // conditional factors private decimal Factor = 5m; public int ResistanceDivisor = 3; // indicator lengths public static int AtrLength = 12; public static int AdxLength = 12; public static int MedianLength = 3; public static int EmaLength = 20; public static decimal EmaDivisor = 3; // ================================================================================================================= // contains all SymbolBase definitions private Dictionary<string, SymbolBase> _symbolBases = new Dictionary<string, SymbolBase>(); // creates new universe variable setting private Dictionary<Symbol, SymbolData> _universe = new Dictionary<Symbol, SymbolData>(); // security changes variable private SecurityChanges _securityChanges = SecurityChanges.None; // define offset universe to avoid first candle private bool offsetUniverse = true; private int _offset = 50; private int _offsetCounter = 0; public override void Initialize() { // set start date SetStartDate(_startYear, _startMonth, _startDay); // set end date if(_enableEndDate) SetEndDate(_endYear, _endMonth, _endDay); // set starting cash SetCash(_startingCash); // add default symbol base _symbolBases.Add("<BASE>", _defaultSymbolBase); Factor = Convert.ToDecimal(GetParameter("Factor")); AtrLength = Int32.Parse(GetParameter("AtrLength")); EmaDivisor = Convert.ToDecimal(GetParameter("EmaDivisor")); // initialize universe if(!_universeSelectionType) { foreach(SymbolBase sb in _universeList) { _symbolBases.Add(sb.Symbol, sb); AddEquity(sb.Symbol, sb.SymbolResolution); } } else { UniverseSettings.Resolution = _defaultSymbolBase.SymbolResolution; AddUniverse(CoarseFilterFunction, FineFilterFunction); } } // filter based on CoarseFundamental public IEnumerable<Symbol> CoarseFilterFunction(IEnumerable<CoarseFundamental> coarse) { // returns the highest DollarVolume stocks // returns "totalNumberOfStocks" amount of stocks if(_offsetCounter++ % _offset == 0) { return (from stock in coarse orderby stock.DollarVolume descending select stock.Symbol).Take(_stockCount); } return Universe.Unchanged; } // filters out all symbols not contained in the NASDAQ exchange public IEnumerable<Symbol> FineFilterFunction(IEnumerable<FineFundamental> fine) { return (from stock in fine select stock.Symbol).Take(_stockCount); } public void OnDataConsolidated(object sender, TradeBar bar) { if(!_universe.ContainsKey(bar.Symbol)) return; SymbolData sd = _universe[bar.Symbol]; sd.Close.Add(bar.Close); sd.Atr.Update(bar); sd.Adx.Update(bar); if(!sd.IsReady) { sd.Trend.Add(0); sd.TrendUp.Add(bar.Close); sd.TrendDown.Add(bar.Close); return; } int medianLength = 3; ArrayList arr = new ArrayList(medianLength); for(int i = 0; i < medianLength; i++) arr.Add(sd.Close[i]); arr.Sort(); decimal median = (decimal)arr[(int)(medianLength / 2)]; decimal hl2 = (bar.High + bar.Low) / 2; decimal up = hl2 - (Factor * sd.Atr); decimal down = hl2 + (Factor * sd.Atr); decimal upm = median - up; decimal downm = median - down; sd.UpEma.Update(Time, upm); sd.DownEma.Update(Time, downm); if(!sd.UpEma.IsReady || !sd.DownEma.IsReady) return; sd.TrendUp.Add(sd.Close[1] > sd.TrendUp[1] ? Math.Max(up, sd.TrendUp[1]) : up); sd.TrendDown.Add(sd.Close[1] < sd.TrendDown[1] ? Math.Min(down, sd.TrendDown[1]) : down); sd.Trend.Add(sd.Close[0] > sd.TrendDown[1] ? 1 : sd.Close[0] < sd.TrendUp[1] ? -1 : sd.Trend[1]); //buySignal = Trend == 1 and Trend[1] == Trend and Trend[2] != Trend //sellSignal= Trend == -1 and Trend[1] == Trend and Trend[2] != Trend bool buySignal = sd.Trend[0] == 1 && sd.Trend[1] == sd.Trend[0] && sd.Trend[2] != sd.Trend[0]; bool sellSignal = sd.Trend[0] == -1 && sd.Trend[1] == sd.Trend[0] && sd.Trend[2] != sd.Trend[0]; //bool buySignal = upm < sd.UpEma / EmaDivisor; //bool sellSignal = downm < sd.DownEma / EmaDivisor; if(sd.Direction != 1 && buySignal) { SetHoldings(sd.Symbol, 1m / _stockCount); sd.Direction = 1; } else if(sd.Direction != -1 && sellSignal) { SetHoldings(sd.Symbol, -1m / _stockCount); sd.Direction = -1; } } // OnSecuritiesChanged runs when the universe updates current securities public override void OnSecuritiesChanged(SecurityChanges changes) { _securityChanges = changes; // remove stocks from list that get removed from universe foreach (var security in _securityChanges.RemovedSecurities) { if(Securities[security.Symbol].Invested) { Log($"{Time}->Portfolio: Liquidated security {security.Symbol} on universe exit"); Liquidate(security.Symbol); } _universe.Remove(security.Symbol); Log($"{Time}->Universe: Removed security {security.Symbol} from universe"); } // add new securities to universe list foreach(var security in _securityChanges.AddedSecurities) { // creare new SymbolData object SymbolData sd; // if no base exists for symbol use default if(!_symbolBases.ContainsKey(security.Symbol) || _useDefaultSymbolBase) sd = new SymbolData(this, security.Symbol, _symbolBases["<BASE>"]); // otherwise use defined base else sd = new SymbolData(this, security.Symbol, _symbolBases[security.Symbol]); // initialize consolidator and store if needed TickConsolidator tickConsolidator = null; TradeBarConsolidator barConsolidator = null; if(sd.SymbolBase.SymbolResolution == Resolution.Tick) { var consolidator = sd.GetTickConsolidator(); if(consolidator != null) { consolidator.DataConsolidated += OnDataConsolidated; SubscriptionManager.AddConsolidator(sd.Symbol, consolidator); tickConsolidator = consolidator; } } else { var consolidator = sd.GetConsolidator(); if(consolidator != null) { consolidator.DataConsolidated += OnDataConsolidated; SubscriptionManager.AddConsolidator(sd.Symbol, consolidator); barConsolidator = consolidator; } } // add SymbolData to universe _universe.Add(security.Symbol, sd); Log($"{Time}->Universe: Added security {security.Symbol} to universe"); } } public class SymbolBase { public readonly string Symbol; public readonly Resolution SymbolResolution; public readonly int SymbolConsolidationRate; public readonly decimal PortfolioAllocation; public SymbolBase(string symbol = "<BASE>", Resolution symbolResolution = Resolution.Minute, int symbolConsolidationRate = 1, decimal portfolioAllocation = 1.0m) { Symbol = symbol; SymbolResolution = symbolResolution; SymbolConsolidationRate = symbolConsolidationRate; PortfolioAllocation = portfolioAllocation; } } // default class containing all symbol information public class SymbolData { // Variables: // algorithm public readonly ClientAlgorithm Algorithm; // symbol public readonly string Symbol; // symbol base public readonly SymbolBase SymbolBase; public readonly RollingWindow<decimal> Close; public readonly RollingWindow<decimal> Trend; public readonly RollingWindow<decimal> TrendUp; public readonly RollingWindow<decimal> TrendDown; public AverageTrueRange Atr; public AverageDirectionalIndex Adx; public ExponentialMovingAverage UpEma; public ExponentialMovingAverage DownEma; public int Direction = 0; public bool IsReady => /*UpEma.IsReady && DownEma.IsReady &&*/ Adx.IsReady && Atr.IsReady; private readonly static int _length = 3; public SymbolData(ClientAlgorithm algorithm, Symbol symbol, SymbolBase symbolBase) { Algorithm = algorithm; Symbol = symbol; SymbolBase = symbolBase; Close = new RollingWindow<decimal>(_length); Trend = new RollingWindow<decimal>(_length); TrendUp = new RollingWindow<decimal>(_length); TrendDown = new RollingWindow<decimal>(_length); Atr = new AverageTrueRange(ClientAlgorithm.AtrLength); Adx = new AverageDirectionalIndex(ClientAlgorithm.AdxLength); UpEma = new ExponentialMovingAverage(ClientAlgorithm.EmaLength); DownEma = new ExponentialMovingAverage(ClientAlgorithm.EmaLength); } public TradeBarConsolidator GetConsolidator() { TimeSpan timeSpan; switch(SymbolBase.SymbolResolution) { case Resolution.Second: timeSpan = TimeSpan.FromSeconds(SymbolBase.SymbolConsolidationRate); break; case Resolution.Minute: timeSpan = TimeSpan.FromMinutes(SymbolBase.SymbolConsolidationRate); break; case Resolution.Hour: timeSpan = TimeSpan.FromHours(SymbolBase.SymbolConsolidationRate); break; case Resolution.Daily: timeSpan = TimeSpan.FromDays(SymbolBase.SymbolConsolidationRate); break; default: return null; } return new TradeBarConsolidator(timeSpan); } public TickConsolidator GetTickConsolidator() { return new TickConsolidator(SymbolBase.SymbolConsolidationRate); } } } }