Overall Statistics
Total Trades
1435
Average Win
0.29%
Average Loss
-0.27%
Compounding Annual Return
5.968%
Drawdown
35.000%
Expectancy
0.060
Net Profit
58.596%
Sharpe Ratio
0.416
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.07
Alpha
0.047
Beta
0.132
Annual Standard Deviation
0.142
Annual Variance
0.02
Information Ratio
-0.135
Tracking Error
0.221
Treynor Ratio
0.447
Total Fees
$1489.20
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp
{

    public class YEARMOMENTUMMODEL : QCAlgorithm
    {

        //Set this number anywhere betweet 0.001 and 0.01 to decide how strict the rebalancing difference has to be. 
        double rebalancingStrictness = 0.005;
        //This is how far a position can go from it default allocation so 0.5 would mean that if the defauly is 2%, the position would be allocated anywhere between 1% and 3%.. 
        double allocationSmoothingStrictness = 0.7;
        //Can leverage your risk
        double leverage = 1.5;

        //List<string> _symbols = new List<string>() { "BAL", "BNO", "CORN", "COW", "DIA", "EWG", "EWH", "EWJ", "EWT", "FXA", "FXB", "FXC", "FXE", "FXF", "FXI", "FXY", "GLD", "IWD", "JJC", "JJG", "JO", "PALL", "PPLT", "QQQQ", "SGG", "SLV", "SOYB", "SPY", "UGA", "UHN", "UNG", "USO", "UUP", "VGK", "VXX", "WEAT" };
        //FXE causes QC to break down. 
        //List of all tickers
        List<string> _symbols = new List<string>() { "BAL", "BNO", "CORN", "COW", "DIA", "EWG", "EWH", "EWJ", "EWT", "FXA", "FXB", "FXC", "FXF", "FXI", "FXY", "GLD", "IWD", "JJC", "JJG", "JO", "PALL", "PPLT", "QQQQ", "SLV", "SOYB", "SPY", "UGA", "UHN", "UNG", "USO", "UUP", "VGK", "VXX", "WEAT" };
        //Dictionary of stocks and their stored values. 
        Dictionary<string, StoredStock> stockData = new Dictionary<string, StoredStock>() { };
        //Used to save the current date to ensure algorithm does not run twice/day.
        DateTime now;
        public bool firstRun = true;
        //Ensures algorithm only begins after this data. It should be the same as the date in SetStartDate
        DateTime start = new DateTime(2010, 1, 1);

        //Initialize the data and resolution you require for your strategy:
        public override void Initialize()
        {
            //Initialize
            //Set start and end date (YYYY,MM,DD)
            SetStartDate(2010, 1, 1);
            SetEndDate(2016, 7, 5);
            //Set the amount of cash to start with
            SetCash(25000);
            //Warm up period should be rather large to store old stock prices.
            SetWarmup(365);
            //Loads data list on first run
            InitializeStockData();
            //Updates average true range
            UpdateATR();
        }

        /// <summary>
        /// Main function. Executed every day/minute and runs the trading algorith.
        /// </summary>
        /// <param name="data"></param>
        public void OnData(TradeBars data)
        {
            //Print header for CSV file in Log
            FirstRunHeaderLogger();

            //Only run code once/day by checking if this day already executed
            if (!stockData["SPY"].ATR.IsReady || now.Date == Time.Date) return;

            //Update prices and save them
            UpdateValues(data);

            //Make sure we are in test period
            if (Time.Date > start && Time.DayOfWeek == DayOfWeek.Friday)
            {
                //Sell all stocks who currently trade at a price lower than 250 days ago               
                //SellTradingFunction(data);
                //Buy all stocks whos current price is above 250 days ago (Based on average true range allocation)
                SellBuyTradingFuction(data);
            }
            now = Time.Date;
        }

        /// <summary>
        /// Prints header to log file
        /// </summary>
        private void FirstRunHeaderLogger()
        {
            if (firstRun == true)
            {
                var header = string.Format
                ("{0},{1},{2},{3},{4},{5}",
                "Date",
                "Stock",
                "New Allocation",
                "ATR",
                "Old Price",
                "Current Price"
                );
                Log(header);
                firstRun = false;
            }
        }

        /// <summary>
        /// Buys all stocks that currently trade at a higher price than 250 days back. Uses an atr based allocation.
        /// Higher ATR leads to a lower allcoation.
        /// Average allocation = 1/35
        /// Highest and lowest is the half or 50% more than the average allocation. 
        /// IMPORTANT: Need to add trade logic to prevent small transaction size rebalancing.
        /// </summary>
        /// <param name="data"></param>
        private void SellBuyTradingFuction(TradeBars data)
        {
            var universeToTrade = stockData.Where(f => f.Value.OldValues.Count > 10);
            List<decimal> ATRSCORES = universeToTrade.Select(x => x.Value.ATR.Current.Value).ToList();

            if (universeToTrade.Count() != 0)
            {
                var allocation = ((decimal)1 / _symbols.Count())*(decimal)leverage;

                foreach (var stock in universeToTrade)
                {
                    var smoothedAdjuster = 0.0m;
                    try
                    {
                        var max = allocation * (decimal)allocationSmoothingStrictness;
                        smoothedAdjuster = (max-(-max)) / ((ATRSCORES.Max() - ATRSCORES.Min())) * (stock.Value.ATR.Current.Value - ATRSCORES.Max()) + (max);
                    }
                    catch (Exception err99)
                    {
                        smoothedAdjuster = allocation / 2;
                    }

                    var percentage = allocation - smoothedAdjuster;

                    if (stock.Value.CompareValue.price > data[stock.Key].Close)
                    {
                        percentage = percentage * -1;
                    }

                    if (stock.Value.lastallocation == 0 || Math.Abs(stock.Value.lastallocation - (double)percentage) > rebalancingStrictness)
                    {
                        stock.Value.lastallocation = (double)percentage;

                        SetHoldings(stock.Key, percentage);

                        LineLogger(data, stock, percentage);
                    }
                }
            }
        }

        /// <summary>
        /// Prints a one line to the LOG everytime a trade is executed.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="stock"></param>
        /// <param name="smoothedAdjuster"></param>
        private void LineLogger(TradeBars data, KeyValuePair<string, StoredStock> stock, decimal smoothedAdjuster)
        {
            var line = string.Format
            ("{0},{1},{2},{3},{4},{5}",
            Time.Date,
            stock.Key,
            smoothedAdjuster * 100 + "%",
            stock.Value.ATR.Current.Value,
            stock.Value.CompareValue.price,
            data[stock.Key].Close
            );
            Log(line);
        }

        /// <summary>
        /// Updates the 14 day exponential average true range
        /// </summary>
        private void UpdateATR()
        {
            foreach (var stock in stockData)
            {
                var todayATR = ATR(stock.Key, 14, MovingAverageType.Exponential, Resolution.Daily);
                stock.Value.ATR = todayATR;
            }
        }

        /// <summary>
        /// Initializes all the data in the stockData dictionary
        /// </summary>
        private void InitializeStockData()
        {
            if (stockData.Count() == 0)
            {
                foreach (var stock in _symbols)
                {
                    AddSecurity(SecurityType.Equity, stock, Resolution.Daily,true,(decimal)leverage + 1,false);
                    var tobeadded = new StoredStock();
                    var ticker = stock;
                    stockData.Add(ticker, tobeadded);
                }
            }
        }

        /// <summary>
        /// Updates the prices into the dictionary
        /// </summary>
        /// <param name="dataset"></param>
        public void UpdateValues(TradeBars dataset)
        {
            var date = Time.Date;

            foreach (var stock in stockData.ToList())
            {
                if (dataset.ContainsKey(stock.Key))
                {
                    try
                    {
                        OldValue data = new OldValue();
                        data.date = date;
                        data.price = Securities[stock.Key].Price;
                        stock.Value.OldValues.Add(data);
                        stock.Value.CompareValue.price = stock.Value.OldValues.OrderBy(x => x.date).FirstOrDefault().price;
                        stock.Value.CompareValue.date = stock.Value.OldValues.OrderBy(x => x.date).FirstOrDefault().date;

                        if (stock.Value.OldValues.Count() > 250)
                        {
                            stock.Value.OldValues.Remove(stock.Value.OldValues.OrderBy(x => x.date).FirstOrDefault());
                        }
                    }
                    catch (Exception err)
                    {
                        //What should happen here?
                    };
                }
            }
        }

        /// <summary>
        /// Class saving a sample of an old price
        /// </summary>
        class OldValue
        {
            public DateTime date;
            public decimal price;
        }

        /// <summary>
        /// The value of the stockData dictionary for each stock
        /// </summary>
        class StoredStock
        {
            public double lastallocation;
            public AverageTrueRange ATR;
            public OldValue CompareValue = new OldValue() { };
            public List<OldValue> OldValues = new List<OldValue> { };
        }
    }
}