Overall Statistics
using System;
using System.Collections;
using System.Collections.Generic; 

namespace QuantConnect 
{
    using QuantConnect.Securities;
    using QuantConnect.Models; 

    public partial class BasicTemplateAlgorithm : QCAlgorithm, IAlgorithm 
    { 

        string symbol = "MSFT";        
        
        bool initialized = false;
        int numTrackers = 37;
        decimal[] movingAverages;
        decimal[] multipliers;
        bool[,] direction;
        LinkedList<GainTracker>[,] gainTrackers;
        decimal[,] crossPrice;
        int[,] crossCount;
        int[,] barsSinceCross;
        
        int bestI = -1;
        int bestJ = -1;
        
        //int buySize = 100;
        int slowEMAOffset = 3;
        int buySize = 500;
        int minHistoryLength = 60 * 8 * 20;
        //int maxHistoryLength = 60 * 8 * 120;
        long time = 0;
        bool allowShorting = false;
        int stage = 0;
        //Initialize the data and resolution you require for your strategy:
        public override void Initialize() 
        {            
            //Initialize the start, end dates for simulation; cash and data required.
            SetStartDate(2000, 01, 01);
            SetEndDate(DateTime.Now.Date.AddDays(-1)); 
            SetCash(30000); //Starting Cash in USD.
            AddSecurity(SecurityType.Equity, symbol, Resolution.Minute); //Minute, Second or Tick
            SetRunMode(RunMode.Series); //Series or Parallel for intraday strategies.
            
            movingAverages = new decimal[numTrackers];
            multipliers = new decimal[numTrackers];
            direction = new bool[numTrackers, numTrackers];
            crossPrice = new decimal[numTrackers, numTrackers];
            crossCount = new int[numTrackers, numTrackers];
            barsSinceCross = new int[numTrackers, numTrackers];
            gainTrackers = new LinkedList<GainTracker>[numTrackers, numTrackers];
            
            decimal multiplier = 1;
            for (int i = 0; i < numTrackers; i++) {
                multiplier *= .7m;
                multipliers[i] = multiplier;
            }
        }

        //Handle TradeBar Events: a TradeBar occurs on a time-interval (second or minute bars)
        public override void OnTradeBar(Dictionary<string, TradeBar> data) 
        {
            decimal lastPrice = data[symbol].Close;
            decimal fee = Securities[symbol].Model.GetOrderFee(buySize, lastPrice) * 2;
            time++;
            if (initialized) {
                for (int i = 0; i < numTrackers; i++) {
                    movingAverages[i] += (lastPrice - movingAverages[i]) * multipliers[i];
                }
                for (int i = 0; i < numTrackers; i++) {
                    for (int j = i + slowEMAOffset; j < numTrackers; j++) {
                        bool newDirection = movingAverages[i] > movingAverages[j];
                        if (direction[i, j] != newDirection) {
                            decimal diff = (lastPrice - crossPrice[i, j]) * buySize;
                            if (newDirection) {
                                gainTrackers[i, j].AddLast(new GainTracker(-diff - fee, barsSinceCross[i, j]));
                            } else {
                                gainTrackers[i, j].AddLast(new GainTracker(diff - fee, barsSinceCross[i, j]));
                            }
                            if (i == bestI && j == bestJ) {
                                Debug(bestI + ", " + bestJ);
                                Debug("Fee: " + fee);
                                if (allowShorting) {
                                    if (stage == 0) {
                                        if (newDirection) {
                                            stage = 1;
                                            Order(symbol, buySize);
                                        } else {
                                            stage = 2;
                                            Order(symbol, -buySize);
                                        }
                                    } else {
                                        if (newDirection && stage == 2) {
                                            stage = 1;
                                            Order(symbol, 2 * buySize);
                                        } else if (!newDirection && stage == 1) {
                                            stage = 2;
                                            Order(symbol, -2 * buySize);
                                        }
                                    }
                                } else {
                                    if (newDirection) {
                                        if (Portfolio.HoldStock) {
                                            Order(symbol, -buySize);
                                        }
                                    } else {
                                        if (!Portfolio.HoldStock) {
                                            Order(symbol, buySize);
                                        }
                                    }
                                }
                            }
                            direction[i, j] = newDirection;
                            crossPrice[i, j] = lastPrice;
                            crossCount[i, j]++;
                            barsSinceCross[i, j] = 1;
                        } else {
                            barsSinceCross[i, j]++;
                        }
                    }
                }
                if ((allowShorting || !Portfolio.HoldStock) && time > 9600) {
                    decimal maxGain = .06m;
                    bestI = -1;
                    bestJ = -1;
                    for (int i = 0; i < numTrackers; i++) {
                        for (int j = i + slowEMAOffset; j < numTrackers; j++) {
                            if (gainTrackers[i, j].Count > 0) {
                                int totalDuration = 0;
                                decimal profits = 0;
                                foreach (GainTracker g in gainTrackers[i, j]) {
                                    profits += g.profit;
                                    totalDuration += g.time;
                                }
                                decimal gainRate = profits / (decimal)totalDuration;
                                if (gainRate > maxGain && totalDuration > minHistoryLength) {
                                    maxGain = gainRate;
                                    bestI = i;
                                    bestJ = j;
                                }
                                if (gainTrackers[i, j].Count > 30) {
                                    gainTrackers[i, j].RemoveFirst();
                                }
                            }
                        }
                    }
                    Debug("Max Gain: " + maxGain);
                }
            } else {
                for (int i = 0; i < numTrackers; i++) {
                    movingAverages[i] = lastPrice;
                }
                for (int i = 0; i < numTrackers; i++) {
                    for (int j = i + slowEMAOffset; j < numTrackers; j++) {
                        direction[i, j] = movingAverages[i] > movingAverages[j];
                        crossPrice[i, j] = lastPrice;
                        barsSinceCross[i, j] = 1;
                        gainTrackers[i, j] = new LinkedList<GainTracker>();
                    }
                }
                initialized = true;
            }
        }
    }
    public class GainTracker {
        public decimal profit;
        public int time;
        public GainTracker(decimal profit, int time) {
            this.profit = profit;
            this.time = time;
        }
    }
}