Overall Statistics
Total Trades
204
Average Win
1.98%
Average Loss
-0.97%
Compounding Annual Return
-84.304%
Drawdown
47.800%
Expectancy
-0.643
Net Profit
-47.774%
Sharpe Ratio
-2.512
Probabilistic Sharpe Ratio
0.000%
Loss Rate
88%
Win Rate
12%
Profit-Loss Ratio
2.03
Alpha
-0.706
Beta
-0.171
Annual Standard Deviation
0.304
Annual Variance
0.092
Information Ratio
-3.09
Tracking Error
0.355
Treynor Ratio
4.466
Total Fees
$377.40
namespace QuantConnect {

    //
    //	Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all
    //	files use "public partial class" if you want to split up your algorithm namespace into multiple files.
    //

    //public partial class BasicTemplateAlgorithm : QCAlgorithm, IAlgorithm
    //{
    //  Extension functions can go here...(ones that need access to QCAlgorithm functions e.g. Debug, Log etc.)
    //}

    public static class ExtensionMethods
    {
        public static bool In(this int num, int[] numbers)
        {
            bool containsNumber = false;

            foreach (int n in numbers) // go over every number in the list
            {
                if (n == num) // check if it matches
                {
                    containsNumber = true;
                    break; // no need to check any further
                }
            }
            return containsNumber;
        }
    }

}
namespace QuantConnect
{
     public class POP_BBTrendAlgorithm_NoFramework : QCAlgorithm
    {
    	#region Private Variables
        private Symbol _symbol;
        private BollingerBands _bBands;
        private Identity _self;

        private AverageTrueRange _atr;
        private TradeBarConsolidator _consolidator;

        private decimal _entryPrice = 0;
        private decimal _stopPrice = 0;
        private decimal _targetPrice = 0;
        private decimal _stopAtrFactor = 1.6m;
        private decimal _targetAtrFactor = 3.0m;

        private OrderTicket _stopOrder = null;
        private OrderTicket _targetOrder = null;

        //0 is the previous bar, 1 is the bar before last
        private decimal[] _previousClose = new decimal[2] { 0, 0 };
        private decimal[] _previousBBUpper = new decimal[2] { 0, 0 };
        private decimal[] _previousBBLower = new decimal[2] { 0, 0 };
		#endregion
		
        public override void Initialize()
        {
            SetStartDate(2020, 6, 15);
            SetEndDate(2020, 10, 20);
            SetCash(10000);

            var future = AddFuture(Futures.Currencies.EUR);
			/***
			Note 1: Need to filter this for quarterly expiration dates (not front month)
			Take only March, June, Sept, Dec
			***/

            //Initialize Indicators
            _bBands = new BollingerBands(200, 1.5m, MovingAverageType.Exponential);
            _atr = new AverageTrueRange(14, MovingAverageType.Wilders);
            _self = new Identity("/6E");

            // Add a custom chart to track the EMA cross
            var chart = new Chart("EMA Cross");
            chart.AddSeries(new Series("BollingerBand", SeriesType.Line, 0));
            chart.AddSeries(new Series("Close", SeriesType.Line, 0));
            AddChart(chart);
            var atrChart = new Chart("ATR");
            atrChart.AddSeries(new Series("ATR", SeriesType.Line, 0));
        }

        #region On Data Arrival Events
        public override void OnData(Slice slice)
        {
        	/*****
        	NOTE 2: Since I can't figure out how to do this using SetFilter, taking approach similar
        	to the lab
        	*****/
        	
        	//Don't check all day long, only when the day changes
            if (slice.Time.Hour == 0 && slice.Time.Minute < 1)
            {
                foreach (var chain in slice.FutureChains)
                {
                    var allContracts = chain.Value.ToList();
                    if (allContracts.Count == 0) continue;

                    var filteredContracts = allContracts.Where(x => x.Symbol.ID.Date.Month.In(new int[4] { 3, 6, 9, 12 }));
                    if (filteredContracts.Count() == 0) continue;

                    var quarterlyContract = filteredContracts.FirstOrDefault();
                    if (_symbol != quarterlyContract.Symbol)
                    {
                        //remove _symbol
                        RemoveSymbol();
                        //add liquidcontract as the new _symbol
                        AddSymbol(quarterlyContract.Symbol);
                    }
                }
            }
        }

        private void OnDataConsolidated(object sender, TradeBar consolidated)
        {
        	/*******
        	NOTE 3: Even with warming up indicators, the indicator values were differing
        	from ThinkOrSwim values, so giving it 5 more days to catch up before making
        	decision
        	********/
            if (Time < new DateTime(2020, 6, 20))
            {
                _previousClose[1] = _previousClose[0];
                _previousBBLower[1] = _previousBBLower[0];
                _previousBBUpper[1] = _previousBBUpper[0];

                _previousBBLower[0] = _bBands.LowerBand;
                _previousBBUpper[0] = _bBands.UpperBand;
                _previousClose[0] = consolidated.Close;
                return;
            }

            try
            {
                SecurityHolding holding;
                
                if (Portfolio.TryGetValue(_symbol, out holding))
                {
                	
                	//When the close crosses 200 EMA in wrong direction liquidate
                    if (holding.Invested &&
                           ((consolidated.Close < _bBands.MiddleBand && holding.Quantity > 0)
                             || (consolidated.Close > _bBands.MiddleBand && holding.Quantity < 0)
                           )
                    )
                    {
                        SetHoldings(_symbol, 0.0m, tag: "Liquidate");
                    }

                    // Buy future when close crosses above upper bollinger band
                    if (_previousClose[0] > _previousBBUpper[0]
                        && ((consolidated.Close + consolidated.Open) / 2 > _bBands.UpperBand)
                        && _previousClose[1] < _previousBBUpper[1] && !holding.Invested)
                    {
                        SetHoldings(_symbol, 0.7m, tag: "Go Long");
                    }
                    //Sell future contract when close crosses below lower bollinger band
                    else if (_previousClose[0] < _previousBBLower[0]
                        && ((consolidated.Close + consolidated.Open) / 2 < _bBands.UpperBand)
                        && _previousClose[1] > _previousBBLower[1] && !holding.Invested)
                    {
                        SetHoldings(_symbol, -0.7m, tag: "Go Short");
                    }
                }

				//Keep a rolling window of last two values
                _previousClose[1] = _previousClose[0];
                _previousBBLower[1] = _previousBBLower[0];
                _previousBBUpper[1] = _previousBBUpper[0];

                _previousBBLower[0] = _bBands.LowerBand;
                _previousBBUpper[0] = _bBands.UpperBand;
                _previousClose[0] = consolidated.Close;
                
                PlotIndicators();
            }
            catch (Exception ex)
            {
                //Console.WriteLine(ex.Message + "\r\n" + ex.StackTrace);
            }
        }
        #endregion

        public override void OnOrderEvent(OrderEvent orderEvent)
        {
			/*******
			NOTE 4: This whole complicated thing is to manage stop and limit orders
			The logic is very simple: Entry price (when close crosses BB) is market order,
			with 3.0 * ATR target and 1.5 * ATR Stop. 
			As one more way to limit risk, if the close is in wrong 
			direction of 200 EMA, close at market.
			This seems to be working, but will appreciate if someone suggests a simpler way
			*******/
			var order = Transactions.GetOrderById(orderEvent.OrderId);
            //if (orderEvent.Status == OrderStatus.Filled)
            //Console.WriteLine("{0};{1};{2};{3};{4};{5};{6};{7}", order.Time, order.Type, order.Id, order.Status, order.Quantity, order.Price, order.Tag, Portfolio[_symbol].Quantity);

			
            if (order.Type == OrderType.Market && orderEvent.Status == OrderStatus.Filled && order.Tag.ToUpper(CultureInfo.InvariantCulture) != "LIQUIDATE")
            {
                if (order.Direction == OrderDirection.Buy)
                {
                    _targetPrice = order.Price + _targetAtrFactor * _atr;
                    _stopPrice = order.Price - _stopAtrFactor * _atr;
                }
                else if (order.Direction == OrderDirection.Sell)
                {
                    _targetPrice = order.Price - _targetAtrFactor * _atr;
                    _stopPrice = order.Price + _stopAtrFactor * _atr;
                }

                _stopOrder = StopMarketOrder(_symbol, -1 * order.Quantity, _stopPrice);
                _targetOrder = LimitOrder(_symbol, -1 * order.Quantity, _targetPrice);
            }

            if (_targetOrder != null && _stopOrder != null && orderEvent.OrderId == _stopOrder.OrderId && orderEvent.Status == OrderStatus.Filled)
            {
                _targetOrder.Cancel();
                _stopOrder = null;
                _targetOrder = null;
            }
            if (_targetOrder != null && _stopOrder != null && orderEvent.OrderId == _targetOrder.OrderId && orderEvent.Status == OrderStatus.Filled)
            {
                _stopOrder.Cancel();
                _stopOrder = null;
                _targetOrder = null;
            }
            if (order.Tag.ToUpper(CultureInfo.InvariantCulture) == "LIQUIDATE")
            {
                if (_stopOrder != null) _stopOrder.Cancel();
                if (_targetOrder != null) _targetOrder.Cancel();
                _stopOrder = null;
                _targetOrder = null;
            }
        }

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            if (changes.RemovedSecurities.Count > 0)
            {

                // We don't need to call Liquidate(_symbol),
                // since its positions are liquidated because the contract has expired.
                RemoveSymbol();
            }

			if (changes.AddedSecurities.Count > 0)
                AddSymbol(changes.AddedSecurities.FirstOrDefault().Symbol);
            else
            {	
            	/****
            	NOTE 5: For some reason 6/16/20, 7/21/20, 8/18, 9/15 and 10/20 it hits this point,
            	so there are no securities working in algorithm at this point. These are all third
            	Tuesdays of the month
            	****/
                Debug("Added Secuirty count 0 on " + Time.ToShortDateString());          
            }
        }

        //public override void OnEndOfAlgorithm()
        //{
        //}

        #region Private Methods
        private void RemoveSymbol()
        {
            if (_symbol != null && _consolidator != null)
            {
                // Remove the consolidator for the previous contract
                // and reset the indicators

                SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
                _bBands.Reset();
                _atr.Reset();
                _self.Reset();
                Liquidate();
            }
        }

        private void AddSymbol(Symbol sym)
        {
            _symbol = sym;

			// Create a new consolidator and register the indicators to it

            _consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(5));
            _consolidator.DataConsolidated += OnDataConsolidated;
            SubscriptionManager.AddConsolidator(_symbol, _consolidator);

            RegisterIndicator(_symbol, _bBands, _consolidator);
            RegisterIndicator(_symbol, _atr, _consolidator);
            RegisterIndicator(_symbol, _self, _consolidator);

            // Warm up the indicators
            WarmUpIndicator(_symbol, _bBands, TimeSpan.FromMinutes(5));
            WarmUpIndicator(_symbol, _atr, TimeSpan.FromMinutes(5));
            
            PlotIndicators();
        }

        private void PlotIndicators()
        {
            Plot("EMA Cross", "UpperBol", _bBands.UpperBand);
            Plot("EMA Cross", "LowerBol", _bBands.LowerBand);
            Plot("EMA Cross", "Close", _self);
            
            Plot("ATR", "ATR", _atr);
        }
        #endregion
    }

}