Overall Statistics
Total Trades
277
Average Win
0.03%
Average Loss
-0.01%
Compounding Annual Return
230.169%
Drawdown
0.500%
Expectancy
0.751
Net Profit
1.539%
Sharpe Ratio
11.101
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
1.87
Alpha
0.073
Beta
63.418
Annual Standard Deviation
0.07
Annual Variance
0.005
Information Ratio
10.964
Tracking Error
0.069
Treynor Ratio
0.012
Total Fees
$277.00
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;

namespace QuantConnect 
{   
    /*
    *   Basic Template Algorithm
    *
    *   The underlying QCAlgorithm class has many methods which enable you to use QuantConnect.
    *   We have explained some of these here, but the full base class can be found at:
    *   https://github.com/QuantConnect/Lean/tree/master/Algorithm
    */
    public enum PointType { NA = 'N', A = 'A', C = 'C', D = 'D' }
    public enum PointDirection { Up = 'U', Down = 'D' }
    public enum PointStatus { Failed = 'F', Pending = 'P', Success = 'S', Complete = 'C' }
    
    public class Range {
    	public TimeSpan  Begin 	   = new TimeSpan(23,59,59);
    	public TimeSpan  End  	   = new TimeSpan(00,00,00);
    	public decimal   Low  	   =  0xFFFFF;
    	public decimal   High 	   = -0xFFFFF;
    	public bool 	 Completed = false;

		public Range() {
			Low  	   =  0xFFFFF;
	    	High 	   = -0xFFFFF;
	    	Completed = false;
		}
	
		public int Duration() {
			return End.Subtract( Begin ).Minutes;
		}
		
		private void SetPrice(decimal Price)
		{	
			if(Price > High) High = Price;
			if(Price < Low)	 Low  = Price;
		}
		
		public void SetTime(TimeSpan Time) 
		{
			if( Time > End   ) End   = Time;
			if( Time < Begin ) Begin = Time;
		}
		
		public void SetPriceRange(decimal Price, TimeSpan Time)
		{
			SetPrice(Price); 
			SetTime(Time);
		}

    }

    public class Point {
    	public PointType Type = PointType.NA;  // Set to Uninitialized
    	public PointDirection Direction;
    	public PointStatus Status;
    	public Range Range = new Range();
    	public decimal Mean() { return (Range.Low + Range.High)/2; }
    }
    
    public class Position {
    	public string Symbol;
    	public int Quantity;
    	public decimal Price;
    	public Range Opening = new Range();
    	public decimal Point_A = 0;
    	public decimal Point_C = 0;
    	public Point Point   = new Point();
    	public List<OrderTicket> Tickets = new List<OrderTicket>();
    	public bool CrossOpenRange() {
    		//return !((Point.Range.Low < Opening.Low || Point.Range.Low > Opening.High) && (Point.Range.High < Opening.Low || Point.Range.High > Opening.High));
    		return Opening.Low <= Point.Range.High && Point.Range.Low <= Opening.High;
    	}

    }
    
    public class OpeningRange {
    	public string  symbol;
    	public decimal high;
    	public decimal low;
    }
    
    public class BasicTemplateAlgorithm : QCAlgorithm
    {
		List<Position> Positions = new List<Position>();
		Range rangeOpening = new Range();

		public void CancelAndClose(Position position) {
    		if(Portfolio.ContainsKey(position.Symbol))      // Sell or Cover Short (--)
	    		try {
	    		   decimal Quantity = Portfolio[position.Symbol].Quantity;
	    		   if(Quantity!=0)
	               	 MarketOrder(position.Symbol, -Portfolio[position.Symbol].Quantity);
	    		}  catch (Exception e) {
	    		   Debug($"MarketOrder: {e.Message}");
	    		}
    		
           	foreach(OrderTicket ticket in position.Tickets) // Close Open Orders, Cancel all pending orders
           		try {
                  ticket.Cancel();
	           	} catch (Exception e) {
	           	  Debug($"ticket.Cancel() -> {e.Message}");
	           	}
		}
    	
    	public void ClearRanges() {
    	
	    	foreach (Position position in Positions) {
				position.Opening = new Range();
				position.Point = new Point();
				//Debug("Ranges Cleared - Morning");
				position.Point_A = 0;
				position.Point_C = 0;
				position.Tickets.Clear();
			}
    	}

        public override void Initialize() 
        {
        	// backtest parameters	// backtest parameters	// backtest parameters	// BACKTEST DATES 
            SetStartDate(2019,03,18);         
            SetEndDate(2019, 03, 22);
            
            // cash allocation
            SetCash(300000);

			Positions.Add(new Position() { Symbol="WYNN" } );
			Positions.Add(new Position() { Symbol="SPY"  } );
    		Positions.Add(new Position() { Symbol="QQQ"  } );
			Positions.Add(new Position() { Symbol="DIA"  } );
			Positions.Add(new Position() { Symbol="XOP"  } );
            Positions.Add(new Position() { Symbol="SQ"   } );
            Positions.Add(new Position() { Symbol="TGT"  } );
            Positions.Add(new Position() { Symbol="MU"   } );
            Positions.Add(new Position() { Symbol="FDX"  } );
            Positions.Add(new Position() { Symbol="CSX"  } );
            Positions.Add(new Position() { Symbol="FEYE" } );
            Positions.Add(new Position() { Symbol="GE"   } );
            Positions.Add(new Position() { Symbol="PLAY" } );
            Positions.Add(new Position() { Symbol="JACK" } );
            Positions.Add(new Position() { Symbol="TXN"  } );
            Positions.Add(new Position() { Symbol="CRON" } );
            Positions.Add(new Position() { Symbol="WYNN" } );
            Positions.Add(new Position() { Symbol="FIVE" } );
			Positions.Add(new Position() { Symbol="SLB"  } );
            Positions.Add(new Position() { Symbol="GMLP" } );
            Positions.Add(new Position() { Symbol="MO"   } );
            Positions.Add(new Position() { Symbol="T"    } );
            Positions.Add(new Position() { Symbol="RIO"  } );
            Positions.Add(new Position() { Symbol="ROKU" } );
            Positions.Add(new Position() { Symbol="KEM"  } );
            Positions.Add(new Position() { Symbol="CARA" } );
            Positions.Add(new Position() { Symbol="AMD"  } );
			Positions.Add(new Position() { Symbol="MSFT" } );
			Positions.Add(new Position() { Symbol="AAPL" } );
			Positions.Add(new Position() { Symbol="NVDA" } );
			Positions.Add(new Position() { Symbol="CGC"  } );
			Positions.Add(new Position() { Symbol="GLD"  } );
			//Positions.Add(new Position() { Symbol="GOOG" } );

            foreach (Position position in Positions)  {
            	AddEquity(position.Symbol, Resolution.Second);
            }
            

			Schedule.On(DateRules.EveryDay(Positions[0].Symbol), TimeRules.At(09,28), () => ClearRanges() );

            Schedule.On(DateRules.EveryDay(Positions[0].Symbol), TimeRules.At(9,50), ()=> {
            	string msgBody = $"\nQuant Connect Algorithm 'Logical Trader' by John F. Kallie\n\n" +
						$"Algorithm Server Local Time: {DateTime.Now.ToString("hh:mm:ss tt")}\n\n";
            	string msgLine = "";
				List<OpeningRange> OpeningRanges = new List<OpeningRange>();
            	//string payload = "[";
            	foreach (Position position in Positions)
            	{
            		msgLine= $"{position.Symbol.PadRight(4)}\t Opening Range - Low: {position.Opening.Low.ToString("C").PadRight(9)} High: {position.Opening.High.ToString("C").PadRight(9)}" +
            				 $" Spread: {(position.Opening.High-position.Opening.Low).ToString("C").PadRight(8)}  {((position.Opening.High/position.Opening.Low)-1).ToString("P")}\n";
            		
            		//payload+="{\"symbol\": \"" + position.Symbol          + "\", " +
            		//		  "\"low\": \""    + DateTime.Now.ToString("yyyy-mm-dd hh:mm:ss")  + "\", " +
            		//		  "\"low\": \""    + position.Opening.Low     + "\", " +
            		//		  "\"high\": \""   + position.Opening.High    + "\"}, ";
            		
            		OpeningRanges.Add( new OpeningRange() {symbol=position.Symbol, high=position.Opening.High, low=position.Opening.Low}  );
            		
            		Debug(msgLine+$" Range Time: {position.Opening.Begin.ToString()} - {position.Opening.End}");
            		msgBody += msgLine;
            		position.Opening.Completed = true;
            	}
            	//payload+="]";
				Notify.Email("QuantAlert@ApexMicro.com", "QC Alert: Todays Opening Ranges", 
							$"\nQuant Connect Algorithm 'Logical Trader' by John F. Kallie\n\n" +
							$"Algorithm Server Local Time: {DateTime.Now.ToString("yyyy-mm-dd hh:mm:ss tt")}\n\n" 
							+ msgBody+"\n\n"+JsonConvert.SerializeObject(OpeningRanges));

				Notify.Web("https://tradealerts.azurewebsites.net/api/PostOpeningRanges?code=dYQMFetc3PW91/8Aab26xetxFCBmwUS0HpFrJaFOzMomFVxwK30yLA==&clientId=default",
							JsonConvert.SerializeObject(OpeningRanges));
				
            } );
            
            Schedule.On(DateRules.EveryDay(Positions[0].Symbol), TimeRules.BeforeMarketClose(Positions[0].Symbol,1), ()=> {
            	foreach (Position position in Positions) {
            		CancelAndClose(position);
               	}
               	Debug($"{DateTime.Now.ToString("hh:mm:ss tt")} Cancel and Close all positions.");
            } );
         
        }
        
        
        // Override the base class event handler for order events
		public override void OnOrderEvent(OrderEvent orderEvent)
		{
			var order = Transactions.GetOrderById(orderEvent.OrderId);
			Console.WriteLine("{0}: {1}: {2}", Time, order.Type, orderEvent);
		}

        /* 
        *	New data arrives here.
        *	The "Slice" data represents a slice of time, it has all the data you need for a moment.	
        */ 
        public override void OnData(Slice data) 
        {
        	// slice has lots of useful information
        	TradeBars bars = data.Bars;
        	Splits splits = data.Splits;
        	Dividends dividends = data.Dividends;

			TimeSpan TickTime = TimeSpan.FromSeconds(0);
			decimal Price = -1;
			string Symbol;

        	
        	foreach (Position position in Positions) 
        	{
        		Symbol = position.Symbol;
        		if ( data.ContainsKey(Symbol) )  //&& !data[Symbol].Suspicious )
        		{
		        	try { TickTime = data[Symbol].Time.TimeOfDay; } catch (Exception e) { Debug($"TimeSpan:{e.Message}"); }
					try { Price = data[Symbol].Price; } catch (Exception e) { Debug($"Get Price: {Symbol} Price: {Price} - {e.Message}");  }
        			finally 
        			{ 
        				if (Price>0) 
        				{
			        		if (!position.Opening.Completed)
					        	position.Opening.SetPriceRange(Price, TickTime);
					        else  // Not Opening Range
							{
								if (position.Point.Type != 0  && position.Point.Type != PointType.NA && position.Point.Status != PointStatus.Failed) // Point Set & NOT Failed
								{
									switch (position.Point.Type)	{
										case PointType.A:
											switch (position.Point.Status) {
												case PointStatus.Pending:
													position.Point.Range.SetPriceRange(Price, TickTime);
													if(position.CrossOpenRange())
													{
														position.Point.Status = PointStatus.Failed;
														//Debug($"{position.Symbol} Failed A @ {Price}");
													}
													else  // Pending & NOT Crossed Open
													{
														if(position.Point.Range.Duration() >= 10) {
															position.Point.Status = PointStatus.Success;
															Debug($"{position.Symbol}, Point: A, Dir: {position.Point.Direction}, Duration: {position.Point.Range.Duration()} Status:{position.Point.Status} Low:{position.Point.Range.Low} High:{position.Point.Range.High} Mean:{position.Point.Mean()}");
															// Setup Point C
		
															position.Point_A = position.Point.Mean();
															position.Point.Type = PointType.C;
															position.Point.Status = PointStatus.Pending;
		
															if (position.Point.Direction == PointDirection.Up) {

																
																position.Point.Direction = PointDirection.Down;
																position.Point_C = position.Opening.Low - (position.Point_A - position.Opening.High);

																Debug($"Buy: LimitOrder + {position.Symbol} {position.Point.Mean()}  Stop @ {position.Point_C}");
																
																position.Tickets.Add( StopLimitOrder(position.Symbol, 100, position.Point_C, position.Point.Mean()));  // A UP
																
																Notify.Email("QuantAlert@ApexMicro.com", 
																	$"QC Alert: {Symbol} - A-Up @ {position.Point.Mean()} Stop @ {position.Point_C}",
																	$"\nQuant Connect Algorithm 'Logical Trader' by John F. Kallie\n\n" +
																	$"Algorithm Server Local Time: {DateTime.Now.ToString("hh:mm:ss tt")}\n\n" +
																	$"Buy {Symbol} @ {position.Point.Mean()}\n\n" +
																	$"Opening Range: {position.Opening.Low} : {position.Opening.High}\n\n"+
																	$"A Range:       {position.Point.Range.Low} : {position.Point.Range.High}\n\n");
		
															}
															else // Down
															{

																
																position.Point.Direction = PointDirection.Up;
																position.Point_C = position.Opening.High + (position.Opening.Low - position.Point_A );
																
																Debug($"Short: LimitOrder - {position.Symbol} {position.Point.Mean()} Stop @ {position.Point_C}");
																
																position.Tickets.Add( StopLimitOrder(position.Symbol, -100, position.Point_C, position.Point.Mean())); // A DOWN
																
																Notify.Email("QuantAlert@ApexMicro.com", 
																	$"QC Alert: {Symbol} - A-Down @ {position.Point.Mean()} Stop @ {position.Point_C}",
																	$"\nQuant Connect Algorithm 'Logical Trader' by John F. Kallie\n\n" +
																	$"Algorithm Server Local Time: {DateTime.Now.ToString("hh:mm:ss tt")}\n\n" +
																	$"Short {Symbol} @ {position.Point.Mean()}\n\n" +
																	$"Opening Range:   {position.Opening.Low} : {position.Opening.High}\n\n"+
																	$"A Range:         {position.Point.Range.Low} : {position.Point.Range.High}\n\n");
															}
														}
														else  // Pending, Not Crossed,  Not 10 minutes
															position.Point.Range.SetPriceRange(Price, TickTime);
													}
													break;
												case PointStatus.Success:
													break;
												case PointStatus.Failed:
													break;
												default:
													Debug($"default: {position.Symbol}, Point: A, Direction: {position.Point.Direction}, Status:{position.Point.Status} ");
													break;
											}
											break;
										case PointType.C:
											switch (position.Point.Status) {
												case PointStatus.Pending:
													// Did CrossRange ? Which Range
													switch (position.Point.Direction) {
														case PointDirection.Up:
															if(Price > position.Point_C) {
																Debug($"{Symbol} Point C Up {position.Point_C} - Hit @ {Price} TickTime: {TickTime} || Price Check: {data[Symbol].Price} Time Check: {data[Symbol].Time.TimeOfDay}");
																//CancelAndClose(position);
																
																Notify.Email("QuantAlert@ApexMicro.com", 
																	$"QC Alert: {Symbol} - C-Up Triggered @ {Price}",
																	$"\nQuant Connect Algorithm 'Logical Trader' by John F. Kallie\n\n" +
																	$"TickTime: {TickTime}\n\n" +
																	$"Algorithm Server Local Time: {DateTime.Now.ToString("hh:mm:ss tt")}\n\n" +
																	$"Stop Loss Trigger or COVER and CANCEL any SHORTS of {Symbol}\n\n" +
																	$"Opening Range:   {position.Opening.Low} : {position.Opening.High}\n\n"+
																	$"A Range:         {position.Point.Range.Low} : {position.Point.Range.High}\n\n");
																position.Point.Status = PointStatus.Success; // Just to stop execution
															}
															break;
														case PointDirection.Down:
															if(Price < position.Point_C) {
																Debug($"{position.Symbol} Point C-Down {position.Point_C} - Hit @ {Price}"); // TickTime: {TickTime}");
																//CancelAndClose(position);
																Notify.Email("QuantAlert@ApexMicro.com", 
																	$"QC Alert: {Symbol} - C-Down Triggered @ {Price}",
																	$"\nQuant Connect Algorithm 'Logical Trader' by John F. Kallie\n\n" +
																	$"TickTime: {TickTime}\n\n" +
																	$"Algorithm Server Local Time: {DateTime.Now.ToString("hh:mm:ss tt")}\n\n" +
																	$"Stop Loss Trigger OR SELL and CANCEL any LONGS of {Symbol}\n\n" +
																	$"Opening Range:   {position.Opening.Low} : {position.Opening.High}\n\n"+
																	$"A Range:         {position.Point.Range.Low} : {position.Point.Range.High}\n\n");
																position.Point.Status = PointStatus.Success; // Just to stop execution
															}
															break;
															
														default:
															Debug("Unknown C Direction");
															break;
													}
												break;
												case PointStatus.Success:
													//Debug($"{position.Symbol} - C Success");
													position.Point.Status = PointStatus.Complete;  
													if (TickTime < new TimeSpan(15,00,00)) {  // TODO: Need to make Constant at Top of Page!
														position.Point.Type = PointType.NA;
														position.Point.Status = PointStatus.Failed;
														Debug($"{position.Symbol} - C Success -> Restarting Opening Completed");
													}
												break;
												case PointStatus.Failed:
												break;
												case PointStatus.Complete:
												break;
											}
											break;
										case PointType.D:
											Debug($"{position.Symbol}, Point: D, Status: {position.Point.Status}");
											break;
										default:
											Debug($"Point Type Is Unknown: {position.Point.Type}");
											break;
									}
								}
								else // No Point Set - Opening Completed
								{
									//decimal LastPrice = data[position.Symbol][0].LastPrice;
									if (Price > position.Opening.High)  // Out of Range
										{
											position.Point = new Point() {
												Type = PointType.A,
												Direction = PointDirection.Up,
												Status = PointStatus.Pending,
												Range = new Range() { Low=Price, High=Price, Begin = TickTime, End = TickTime, Completed = false },
											};
										}
										else 
											if (Price < position.Opening.Low) // Out of Range Down
											{
												position.Point = new Point() {
													Type = PointType.A,
													Direction = PointDirection.Down,
													Status = PointStatus.Pending,
													Range = new Range() { Low=Price, High=Price, Begin = TickTime, End = TickTime, Completed = false },
												};
											}
											
								}
							}
        				}
        			}
        		} //  Contained Data of Symbol in data 
        	} // Next Each Position
        }    
    }
}