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