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