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