Overall Statistics
Total Trades
33
Average Win
15.76%
Average Loss
-5.23%
Compounding Annual Return
29.169%
Drawdown
17.100%
Expectancy
1.431
Net Profit
665.057%
Sharpe Ratio
0.898
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
3.01
Alpha
0.324
Beta
-0.081
Annual Standard Deviation
0.352
Annual Variance
0.124
Information Ratio
0.531
Tracking Error
0.425
Treynor Ratio
-3.9
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Models;
using QuantConnect.Indicators;

namespace QuantConnect.Rotation
{
    public class GlobalRotation : QCAlgorithm
    {
        // we'll use this to tell us when the month has ended
        DateTime LastRotationTime = DateTime.MinValue;
        TimeSpan RotationInterval = TimeSpan.FromDays(30);

        // these are the growth symbols we'll rotate through
        List<string> GrowthSymbols = new List<string>
        {
            "MDY", // US S&P mid cap 400
            "IEV", // iShares S&P europe 350
            "EEM", // iShared MSCI emerging markets
            "ILF", // iShares S&P latin america
            "EPP"  // iShared MSCI Pacific ex-Japan
        };

        // these are the safety symbols we go to when things are looking bad for growth
        List<string> SafetySymbols = new List<string>
        {
            "EDV", // Vangaurd TSY 25yr+
            "SHY"  // Barclays Low Duration TSY
        };

        // we'll hold some computed data in these guys
        List<SymbolData> SymbolData = new List<SymbolData>();

        public override void Initialize()
        {
            SetCash(25000);
            SetStartDate(2007, 1, 1);

            foreach (var symbol in GrowthSymbols.Union(SafetySymbols))
            {
                // ideally we would use daily data
                AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
                var oneMonthPerformance = MOM(symbol, 30, Resolution.Daily);
                var threeMonthPerformance = MOM(symbol, 90, Resolution.Daily);

                SymbolData.Add(new SymbolData
                {
                    Symbol = symbol,
                    OneMonthPerformance = oneMonthPerformance,
                    ThreeMonthPerformance = threeMonthPerformance
                });
            }
        }

        private bool first = true;
        public void OnData(TradeBars data)
        {
            try
            {
                // the first time we come through here we'll need to do some things such as allocation
                // and initializing our symbol data
                if (first)
                {
                    first = false;
                    LastRotationTime = data.Time;
                    return;
                }

                var delta = data.Time.Subtract(LastRotationTime);
                if (delta > RotationInterval)
                {
                    LastRotationTime = data.Time;

                    // pick which one is best from growth and safety symbols
                    var orderedObjScores = SymbolData.OrderByDescending(x => x.ObjectiveScore).ToList();
                    foreach (var orderedObjScore in orderedObjScores)
                    {
                        Log(">>SCORE>>" + orderedObjScore.Symbol + ">>" + orderedObjScore.ObjectiveScore);
                    }
                    var bestGrowth = orderedObjScores.First();

                    if (bestGrowth.ObjectiveScore > 0)
                    {
                        if (Portfolio[bestGrowth.Symbol].Quantity == 0)
                        {
                            Log("PREBUY>>LIQUIDATE>>");
                            Liquidate();
                        }
                        Log(">>BUY>>" + bestGrowth.Symbol + "@" + (100 * bestGrowth.OneMonthPerformance).ToString("00.00"));
                        decimal qty = Portfolio.Cash / Securities[bestGrowth.Symbol].Close;
                        Order(bestGrowth.Symbol, qty, OrderType.Market);
                    }
                    else
                    {
                        // if no one has a good objective score then let's hold cash this month to be safe
                        Log(">>LIQUIDATE>>CASH");
                        Liquidate();
                    }
                }
            }
            catch (Exception ex)
            {
                Error("OnTradeBar: " + ex.Message + "\r\n\r\n" + ex.StackTrace);
            }
        }

        /// <summary>
        /// Creates a new Momentum indicator
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="period"></param>
        /// <param name="resolution"></param>
        /// <returns></returns>
        public Momentum MOM(string symbol, int period, Resolution? resolution = null)
        {
            string name = CreateIndicatorName(symbol, "MOM" + period, resolution);
            var momentum = new Momentum(name, period);
            RegisterIndicator(symbol, momentum, resolution);
            return momentum;
        }
        /// <summary>
        /// Creates a new name for an indicator created with the convenience functions (SMA, EMA, ect...)
        /// </summary>
        /// <param name="symbol">The symbol this indicator is registered to</param>
        /// <param name="type">The indicator type, for example, 'SMA5'</param>
        /// <param name="resolution">The resolution requested</param>
        /// <returns>A unique for the given parameters</returns>
        private static string CreateIndicatorName(string symbol, string type, Resolution? resolution)
        {
            string res;
            switch (resolution)
            {
                case Resolution.Tick:
                    res = "_tick";
                    break;
                case Resolution.Second:
                    res = "_sec";
                    break;
                case Resolution.Minute:
                    res = "_min";
                    break;
                case Resolution.Hour:
                    res = "_hr";
                    break;
                case Resolution.Daily:
                    res = "_day";
                    break;
                case null:
                    res = string.Empty;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("resolution");
            }

            return string.Format("{0}({1}{2})", type, symbol.ToUpper(), res);
        }
    }

    class SymbolData
    {
        public string Symbol;

        public Momentum OneMonthPerformance { get; set; }
        public Momentum ThreeMonthPerformance { get; set; }

        public decimal ObjectiveScore
        {
            get
            {
                // we weight the one month performance higher
                decimal weight1 = 100;
                decimal weight2 = 75;

                return (weight1 * OneMonthPerformance + weight2 * ThreeMonthPerformance) / (weight1 + weight2);
            }
        }
    }
}
/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/
namespace QuantConnect.Indicators
{
    /// <summary>
    /// This indicator computes the n-period change in a value
    /// </summary>
    public class Momentum : WindowIndicator
    {

        /// <summary>
        /// Creates a new Momentum indicator with the specified period
        /// </summary>
        /// <param name="period">The period over which to perform to computation</param>
        public Momentum(int period)
            : base("MOM" + period, period)
        {
        }

        /// <summary>
        /// Creates a new Momentum indicator with the specified period
        /// </summary>
        /// <param name="name">The name of this indicator</param>
        /// <param name="period">The period over which to perform to computation</param>
        public Momentum(string name, int period)
            : base(name, period)
        {
        }

        /// <summary>
        ///     Computes the next value for this indicator from the given state.
        /// </summary>
        /// <param name="window">The window of data held in this indicator</param>
        /// <param name="previousValue">The previous value of this indicator</param>
        /// <param name="input">The input value to this indicator on this time step</param>
        /// <returns>A new value for this indicator</returns>
        protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint previousValue, IndicatorDataPoint input)
        {
            if (!window.IsReady)
            {
                // keep returning the delta from the first item put in there to init
                return input - window[window.Count - 1];
            }

            return input - window.MostRecentlyRemoved;
        }
    }
}