Overall Statistics |
Total Trades 30 Average Win 0.34% Average Loss -0.28% Compounding Annual Return 77.018% Drawdown 1.400% Expectancy 0.067 Net Profit 0.418% Sharpe Ratio 14.757 Probabilistic Sharpe Ratio 0% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.21 Alpha 0.242 Beta -1.634 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio 12.8 Tracking Error 0.076 Treynor Ratio -0.424 Total Fees $30.00 Estimated Strategy Capacity $0 Lowest Capacity Asset AMZN R735QTJ8XC9X |
/* This program was developed by Quantify and is property of the client Usage and marketing of this program is permitted. Quantify Developer(s): Conor Flynn Date Created: Client: Client ID: If you find a bug or an inconsistantcy please contact your assigned developer. Contact: cflynn@quantify-co.com To request a new copy of your program please contact support: Contact: support@quantify-co.com Note: Client ID is required for client verification upon requesting a new copy */ 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 = 2022; private int _startMonth = 2; private int _startDay = 26; // 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 typehttps://www.quantconnect.com/project/10886062#optimization-view-tab // determines whether securities are selected by QC's universe function // or manually by the user // manual = false; QC universe = true private readonly bool _universeSelectionType = false; // number of securities for the universe selection to select private readonly int _stockCount = 5; // use default values for universe private readonly bool _useDefaultSymbolBase = false; // 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.Tick, // data resolution 512, // 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 1, // 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; // ================================================================================================================= // 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; 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); // 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 return (from stock in coarse where !stock.HasFundamentalData 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]; if(!sd.EmaH.IsReady || !sd.EmaL.IsReady) return; DataPoint dp = new DataPoint(); dp.High = sd.EmaH; dp.Low = sd.EmaL; dp.Bar = bar; sd.Zzhl.Update(dp); if(!sd.Zzhl.IsReady) return; if(sd.Direction != 1 && sd.Zzhl.Switch && sd.Zzhl.Direction) { SetHoldings(sd.Symbol, 1); sd.Direction = 1; } else if(sd.Direction != -1 && sd.Zzhl.Switch && !sd.Zzhl.Direction) { SetHoldings(sd.Symbol, -1); sd.Direction = -1; } Plot("ZZHL", "SWITCH", sd.Zzhl.Switch ? 1 : 0); Plot("ZZHL", "DIRECTION", sd.Zzhl.Direction ? 2 : -2); } // 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; // direction public int Direction = 0; // ZigZagHighLow public ZigZagHighLow Zzhl; // High EMA public ExponentialMovingAverage EmaH; // Low Ema public ExponentialMovingAverage EmaL; public SymbolData(ClientAlgorithm algorithm, Symbol symbol, SymbolBase symbolBase) { Algorithm = algorithm; Symbol = symbol; SymbolBase = symbolBase; Zzhl = new ZigZagHighLow(algorithm, 0.01m, 0.05m, 5, 2.0m); EmaH = algorithm.EMA(symbol, 5, symbolBase.SymbolResolution, Field.High); EmaL = algorithm.EMA(symbol, 5, symbolBase.SymbolResolution, Field.Low); } 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); } } } }
namespace QuantConnect.Indicators { public class ZigZagHighLow { private readonly QCAlgorithm _algorithm; private readonly decimal _percentageReversal; private readonly decimal _absoluteReversal; private readonly AverageTrueRange _atr; private readonly decimal _atrReversal; private readonly decimal _tickSize; private bool _switch = false; private bool _direction = false; private decimal _value = 0.0m; // 0 = init, 1 = undefined, 2 = downtrend, 3 = uptrend private int _state; private decimal _maxPriceH; private decimal _minPriceL; private decimal _prevMaxH; private decimal _prevMinL; private bool _newMax; private bool _newMin; private bool _priorDownConditional = false; private bool _priorUpConditional = false; public bool Switch => _switch; public bool Direction => _direction; public decimal Value => _value; public bool IsReady => _atr.IsReady; public int WarmUpPeriod => _atr.WarmUpPeriod; public ZigZagHighLow(decimal percentageReversal, decimal absoluteReversal, int atrLength, decimal atrReversal, decimal tickSize = 0) { _percentageReversal = percentageReversal; _atr = new AverageTrueRange(atrLength); _atrReversal = atrReversal; if(tickSize != 0) _absoluteReversal = absoluteReversal; else _absoluteReversal = absoluteReversal * tickSize; _state = 0; _maxPriceH = 0; _minPriceL = 0; _prevMaxH = 0; _prevMinL = 0; _newMax = true; _newMin = true; } public ZigZagHighLow(QCAlgorithm algorithm, decimal percentageReversal, decimal absoluteReversal, int atrLength, decimal atrReversal, decimal tickSize = 0) { _algorithm = algorithm; _percentageReversal = percentageReversal; _atr = new AverageTrueRange(atrLength); _atrReversal = atrReversal; if(tickSize != 0) _absoluteReversal = absoluteReversal; else _absoluteReversal = absoluteReversal * tickSize; _state = 0; _maxPriceH = 0; _minPriceL = 0; _prevMaxH = 0; _prevMinL = 0; _newMax = true; _newMin = true; } public void Update(DataPoint data) { _atr.Update(data.Bar); if(!_atr.IsReady) return; decimal _hlPivot = (_percentageReversal / 100) + (_atr / data.Bar.Close * _atrReversal); _algorithm.Debug($"{_algorithm.Time}-> piv={_hlPivot} atr={_atr} dl={data.Low} dh={data.High} bo={data.Bar.Open} bh={data.Bar.High} bl={data.Bar.Low} bc={data.Bar.Close}"); // initialize data points if(_state == 0) { _maxPriceH = data.High; _minPriceL = data.Low; _newMax = true; _newMin = true; _state = 1; } // if undefined else if(_state == 1) { // check for uptrend if(data.High >= _prevMaxH) { _state = 3; _maxPriceH = data.High; _minPriceL = _prevMinL; _newMax = true; _newMin = false; } else if(data.Low <= _prevMinL) { _state = 2; _maxPriceH = _prevMaxH; _minPriceL = data.Low; _newMax = false; _newMin = true; } else { _state = 1; _maxPriceH = _prevMaxH; _minPriceL = _prevMinL; _newMax = false; _newMin = false; } } // if down trend else if(_state == 2) { if(data.High >= _prevMinL + (_prevMinL * _hlPivot) + _absoluteReversal) { _state = 3; _maxPriceH = data.High; _minPriceL = _prevMinL; _newMax = true; _newMin = false; } else { _state = 2; _maxPriceH = _prevMaxH; _newMax = false; if(data.Low <= _prevMinL) { _minPriceL = data.Low; _newMin = true; } else { _minPriceL = _prevMinL; _newMin = false; } } } // if up trend else if(_state == 3) { if(data.Low <= _prevMaxH - (_prevMaxH * _hlPivot) - _absoluteReversal) { _state = 2; _maxPriceH = _prevMaxH; _minPriceL = data.Low; _newMax = false; _newMin = true; } else { _state = 3; if(data.High >= _prevMaxH) { _maxPriceH = data.High; _newMax = true; } else { _maxPriceH = _prevMaxH; _newMax = false; } _minPriceL = _prevMinL; _newMin = false; } } _prevMaxH = _maxPriceH; _prevMinL = _minPriceL; // calculate switch var lowPoint = _state == 2 && data.Low == _minPriceL; var highPoint = _state == 3 && data.High == _maxPriceH; var downConditional = lowPoint || (_state == 3 && data.High < _maxPriceH); var upConditional = highPoint || (_state == 2 && data.Low > _minPriceL); if(downConditional && _priorDownConditional) { _switch = true; _direction = false; } else if(upConditional && _priorUpConditional) { _switch = true; _direction = true; } else { _switch = false; } _priorDownConditional = downConditional; _priorUpConditional = upConditional; } } public struct DataPoint { public TradeBar Bar; public decimal High; public decimal Low; } }