Overall Statistics
Total Trades
1943
Average Win
0.32%
Average Loss
-0.16%
Compounding Annual Return
3.815%
Drawdown
12.300%
Expectancy
0.028
Net Profit
4.326%
Sharpe Ratio
0.412
Loss Rate
65%
Win Rate
35%
Profit-Loss Ratio
1.96
Alpha
0.043
Beta
0.009
Annual Standard Deviation
0.103
Annual Variance
0.011
Information Ratio
0.601
Tracking Error
0.191
Treynor Ratio
4.482
Total Fees
$1943.00
using System;
using System.Collections;
using System.Collections.Generic; 
using QuantConnect.Securities;  
using QuantConnect.Models;   

namespace QuantConnect 
{   
    // Simple Heikin-Ashi adjustable candlestick trading algorithm.
    // Exit point is two opposing trend bars, entry is two consecutive bars in same direction.
    // Only trades in exchange opening hours and liquidates all before closing.
    // See: http://www.investopedia.com/articles/technical/04/092204.asp
    // Like other moving averages. This is a lagging indicator, so the chop will eliminate any gains if used by it self. 
    // However it can be used in conjunction with other indicators to spot trends.
    public class SimpleHeikinAshi : QCAlgorithm
    {
		public enum TrendDirection
        {
            Up,
            Down
        };

        string symbol;
        int rollingWindowSize = 3;
        RollingWindow<TradeBar> history = null;
        TimeSpan barPeriod = TimeSpan.FromMinutes(15); //Adjust for desired bar size

        //Initialize
        public override void Initialize()
        {
            symbol = "SPY";
            SetStartDate(2015, 1, 1);
            SetEndDate(DateTime.Now.Date.AddDays(-1));
            SetCash(25000);
            AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
            history = new RollingWindow<TradeBar>(rollingWindowSize);
            var consolidator = new TradeBarConsolidator(barPeriod);
            consolidator.DataConsolidated += OnDataConsolidated;
            SubscriptionManager.AddConsolidator(symbol, consolidator);
        }


        public void OnDataConsolidated(object sender, TradeBar data)
        {
            var instrument = Securities[symbol];
            var exchange = instrument.Exchange;
            var marketClose = exchange.Hours.GetNextMarketClose(instrument.LocalTime, false);
            var marketOpen = exchange.Hours.GetNextMarketOpen(instrument.LocalTime.AddDays(-1), false);
            var tradeStartAfterOpen = marketOpen.AddMinutes(1);

            TradeBar thisHeikinAshiBar;
            if (!history.Any())
                thisHeikinAshiBar = CalculateHeikinAshiBar(data, null);
            else
                thisHeikinAshiBar = CalculateHeikinAshiBar(data, history[0]);

            history.Add(thisHeikinAshiBar);

            int holdings = Securities[symbol].Holdings.Quantity;

            //Establish first three bars before trading
            if (history.IsReady)
            {
                TrendDirection barTrendDirection = getTrendDirection(thisHeikinAshiBar);
                TrendDirection previousBarTrendDirection1 = getTrendDirection(history[1]);
                TrendDirection previousBarTrendDirection2 = getTrendDirection(history[2]);

                //Wait for the maket open volatility to settle before trading.
                if (exchange.ExchangeOpen && Time > tradeStartAfterOpen && Time <= marketClose)
                {
                    //Exit on one bar trend change
                    if (holdings != 0 && (barTrendDirection != previousBarTrendDirection1 && barTrendDirection != previousBarTrendDirection2))
                    {
                        Liquidate(symbol);
                        Debug("Liquidating " + holdings);
                    }

                    //Two candles treding up - Go long
                    if (barTrendDirection == TrendDirection.Up && previousBarTrendDirection1 == TrendDirection.Up && previousBarTrendDirection2 == TrendDirection.Up && holdings == 0)
                    {
                        Log("BUY  >> " + Securities[symbol].Price);
                        SetHoldings(symbol, 1.0);
                        Debug("Going Long " + holdings);
                    }
                    //Two candles treding down - Go short
                    else if (barTrendDirection == TrendDirection.Down && previousBarTrendDirection1 == TrendDirection.Down && previousBarTrendDirection2 == TrendDirection.Down && holdings == 0)
                    {
                        Log("SHORT >> " + Securities[symbol].Price);
                        SetHoldings(symbol, -1.0);
                        Debug("Going Short " + holdings);
                    }
                }

                //Sell any holdings before market close.
                var oneBeforeClose = marketClose.AddMinutes(-1);
                if (Time == oneBeforeClose)
                {
                    if (!Portfolio.HoldStock)
                    {
                        Liquidate(symbol);
                        history.Reset();
                    }
                }
            }
        }

        public void OnData(TradeBars data)
        { }

        //Returns a TradeBar where OHLC values are smoothed from previous bar.
        public TradeBar CalculateHeikinAshiBar(TradeBar bar, TradeBar previousHeikinAshiBar)
        {
            TradeBar result = (TradeBar)bar.Clone();

            //Average price of the current bar
            result.Close = (bar.Open + bar.High + bar.Low + bar.Close) / 4;

            //Midpoint of the previous bar
            if (previousHeikinAshiBar != null)
                result.Open = (previousHeikinAshiBar.Open + previousHeikinAshiBar.Close) / 2;
            else
                result.Open = (bar.Open + bar.Close) / 2;

            result.High = Math.Max(Math.Max(bar.High, result.Open), result.Close);
            result.Low = Math.Min(Math.Min(bar.Low, result.Open), result.Close);
            return result;
        }

        //Establishes the green/red direction of the candlestick.
        public TrendDirection getTrendDirection(TradeBar bar)
        {
            TrendDirection result = (bar.Open > bar.Close) ? TrendDirection.Down : TrendDirection.Up;

            return result;
        }

        
        
    }
}