Overall Statistics
Total Trades
630
Average Win
1.72%
Average Loss
-2.50%
Compounding Annual Return
-100%
Drawdown
56.600%
Expectancy
-0.480
Net Profit
-39.920%
Sharpe Ratio
-0.335
Probabilistic Sharpe Ratio
0%
Loss Rate
69%
Win Rate
31%
Profit-Loss Ratio
0.69
Alpha
0
Beta
0
Annual Standard Deviation
2.983
Annual Variance
8.901
Information Ratio
-0.335
Tracking Error
2.983
Treynor Ratio
0
Total Fees
$1962.85
using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Securities;
using System.Collections.Generic;
using QuantConnect.Data.Consolidators;
using QuantConnect.Orders;
using System.Linq;

//TODO:
//figure out how to tell when an order was fulfilled
//Notes:
//https://www.quantconnect.com/docs/algorithm-reference/trading-and-orders
//https://github.com/QuantConnect/Lean/blob/master/Algorithm.CSharp/OrderTicketDemoAlgorithm.cs
//https://github.com/QuantConnect/Lean/blob/master/Algorithm.CSharp/MarketOnOpenOnCloseAlgorithm.cs

namespace QuantConnect.Algorithm.CSharp
{
    public class BasicTemplateFuturesConsolidationAlgorithm : QCAlgorithm
    {
        TradeBar openingTradeBar;
        QuoteBar openingBar;
        decimal entryPrice;
        decimal d = 0.01m;
        string positionType = "";
        Boolean inTrade = false;
        private const string RootSP500 = Futures.Indices.SP500EMini;
        public Symbol SP500 = QuantConnect.Symbol.Create(RootSP500, SecurityType.Future, Market.CME);
        private HashSet<Symbol> _futureContracts = new HashSet<Symbol>();

        public override void Initialize()
        {
            SetStartDate(2013, 10, 8);
            SetEndDate(2013, 10, 11);
            SetCash(1000000);
            AddSecurity(RootSP500);

            var futureSP500 = AddFuture(RootSP500, Resolution.Minute);
            // set our expiry filter for this future chain
            // SetFilter method accepts TimeSpan objects or integer for days.
            // The following statements yield the same filtering criteria
            futureSP500.SetFilter(0, 180);
            // futureSP500.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(182));

            //Schedule.On(DateRules.EveryDay(RootSP500), TimeRules.At(13, 30), ClosePositions);
            //Schedule.On(DateRules.EveryDay(RootSP500), TimeRules.At(16, 00), ClosePositions);


            SetBenchmark(x => 0);
        }

        void ClosePositions()
        {
            openingBar = null;
            Liquidate(RootSP500);
            inTrade = false;
        }

        public override void OnData(Slice slice)
        {

            //var stopPrice = close * 1.001m;
            //var limitPrice = close - 0.03m;
            //stopPrice = close * .999m;
            //limitPrice = close + 0.03m;

            foreach (var chain in slice.FutureChains)
            {
                var contract = (
                        from futuresContract in chain.Value.OrderBy(x => x.Expiry)
                        where futuresContract.Expiry > Time.Date.AddDays(90)
                        select futuresContract
                        ).FirstOrDefault();


                {

                    //Securities.Add(contract.Symbol);

                    foreach (var cntrct in chain.Value)
                    {
                        if (!_futureContracts.Contains(cntrct.Symbol))
                        {
                            _futureContracts.Add(cntrct.Symbol);

                            if (!Securities.ContainsKey(cntrct.Symbol.ID.ToString()))
                            //if (!Securities.ContainsKey(cntrct.Symbol))
                            {
                                AddSecurity(cntrct.Symbol);

                                //if (!Securities.ContainsKey(quoteBar.Symbol.ID.ToString()))
                                //{
                                //    AddSecurity(quoteBar.Symbol);
                                //}


                            }

                            var consolidator = new QuoteBarConsolidator(TimeSpan.FromMinutes(1));
                            consolidator.DataConsolidated += OnDataConsolidated;
                            SubscriptionManager.AddConsolidator(cntrct.Symbol, consolidator);

                            Log("Added new consolidator for " + cntrct.Symbol.Value);
                        }

                    }
                }
            }

        }

        public void OnDataConsolidated(object sender, QuoteBar quoteBar)
        //public void OnDataConsolidated(TradeBar bar)
        {

            //if (!Securities.Values.Contains())
            //if (!Securities.ContainsKey(quoteBar.Symbol.ID.ToString()))
            //{
            //    AddSecurity()
            //}


            if (Portfolio.Invested)
            {
                if (positionType == "long" && quoteBar.Price >= (entryPrice + 0.50m))
                {
                    var newTicket = MarketOrder(quoteBar.Symbol, 1, asynchronous: false);
                    if (newTicket.Status != OrderStatus.Filled)
                    {
                        Log("Synchronous market order was not filled synchronously!");
                    }

                    inTrade = false;
                }
                else if (positionType == "short" && quoteBar.Price >= (entryPrice - 0.50m))
                {
                    var newTicket = MarketOrder(quoteBar.Symbol, -1, asynchronous: false);
                    if (newTicket.Status != OrderStatus.Filled)
                    {
                        Log("Synchronous market order was not filled synchronously!");
                    }

                    inTrade = false;
                }
            }
            else if (!Portfolio.Invested && openingBar != null && !inTrade && Securities.ContainsKey(quoteBar.Symbol.ID.ToString()))
            {
                if (quoteBar.Close > openingBar.High)
                {
                    StopLimitOrders(quoteBar.Symbol, OrderType.LongOrder, 1, quoteBar.Close, quoteBar.Close + d);


                    inTrade = true;
                    //StopLimitOrder(RootSP500, 1, quoteBar.Close, quoteBar.Close+d, "LONG ORDER!!");
                    positionType = "long";
                }

                if (quoteBar.Close < openingBar.Low)
                {
                    StopLimitOrders(quoteBar.Symbol, OrderType.LongOrder, 1, quoteBar.Close, quoteBar.Close + d);

                    inTrade = true;

                    //StopLimitOrder(RootSP500, -1, quoteBar.Close, quoteBar.Close - d, "SHORT ORDER!!");
                    positionType = "short";
                }
            }


            if (quoteBar.Time.Hour == 9 && quoteBar.Time.Minute <= 30)
            {
                openingBar = quoteBar;
            }

            Log("Portfolio.TotalHoldingsValue: [" + Portfolio.TotalHoldingsValue + "]");
            Log("OnDataConsolidated called");
            Log(quoteBar.ToString());
        }
      
        public enum OrderType
        {
            LongOrder,
            ShortOrder
        }

        public void StopLimitOrders(string symbol, OrderType orderType, int quantity, decimal stopPrice, decimal limitPrice)
        {
            //var stopPrice = close * 1.001m;
            //var limitPrice = close - 0.03m;
            //stopPrice = close * .999m;
            //limitPrice = close + 0.03m;

            Log("Submitting StopLimitOrder");

            OrderTicket newTicket;

            switch (orderType)
            {
                case OrderType.LongOrder:
                   
                    newTicket = StopLimitOrder(symbol, quantity, stopPrice, limitPrice);
                    //_openStopLimitOrders.Add(newTicket);
                    break;
                case OrderType.ShortOrder:

                    newTicket = StopLimitOrder(symbol, -quantity, stopPrice, limitPrice);
                    //_openStopLimitOrders.Add(newTicket);
                    break;
            }
        }

        private bool TimeIs(int day, int hour, int minute)
        {
            return Time.Day == day && Time.Hour == hour && Time.Minute == minute;
        }

        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            var order = Transactions.GetOrderById(orderEvent.OrderId);
            Console.WriteLine("{0}: {1}: {2}", Time, order.Type, orderEvent);

            if (orderEvent.Quantity == 0)
            {
                throw new Exception("OrderEvent quantity is Not expected to be 0, it should hold the current order Quantity");
            }
            if (orderEvent.Quantity != order.Quantity)
            {
                throw new Exception("OrderEvent quantity should hold the current order Quantity");
            }
            if (order is LimitOrder && orderEvent.LimitPrice == 0
                || order is StopLimitOrder && orderEvent.LimitPrice == 0)
            {
                throw new Exception("OrderEvent LimitPrice is Not expected to be 0 for LimitOrder and StopLimitOrder");
            }
            if (order is StopMarketOrder && orderEvent.StopPrice == 0)
            {
                throw new Exception("OrderEvent StopPrice is Not expected to be 0 for StopMarketOrder");
            }
        }

        private bool CheckPairOrdersForFills(OrderTicket longOrder, OrderTicket shortOrder)
        {
            if (longOrder.Status == OrderStatus.Filled)
            {
                Log(shortOrder.OrderType + ": Cancelling short order, long order is filled.");
                shortOrder.Cancel("Long filled.");
                return true;
            }
            if (shortOrder.Status == OrderStatus.Filled)
            {
                Log(longOrder.OrderType + ": Cancelling long order, short order is filled.");
                longOrder.Cancel("Short filled");
                return true;
            }
            return false;
        }
    }

}