Overall Statistics
Total Trades
143
Average Win
7.98%
Average Loss
-4.28%
Compounding Annual Return
16.878%
Drawdown
36.000%
Expectancy
0.817
Net Profit
785.662%
Sharpe Ratio
0.81
Loss Rate
37%
Win Rate
63%
Profit-Loss Ratio
1.87
Alpha
0.092
Beta
4.406
Annual Standard Deviation
0.222
Annual Variance
0.049
Information Ratio
0.72
Tracking Error
0.222
Treynor Ratio
0.041
Total Fees
$2086.99
namespace QuantConnect 
{   
    /*
    *   Author: Tomer Borenstein
    *   My attempt at whipping up an implementation of the "Global Market 
    *   Rotation" strategy. Original description of strategy is linked to 
    *   in the project notes.
    */
    
    public class SGMR : QCAlgorithm
    {
        private string currentMonth;
        private DateTime currentDay;
        private MyMomentum momentumMDY;
        private MyMomentum momentumILF;
        private MyMomentum momentumFEZ;
        private MyMomentum momentumEEM;
        private MyMomentum momentumEPP;
        private MyMomentum momentumTLT;
        string currentETF;
        
        public override void Initialize(){
			
            // start and End Date range for the backtest:
            SetStartDate(2004, 1, 1);         
            SetEndDate(DateTime.Now.Date.AddDays(-1));
            currentMonth = Time.ToString("MMM");
            currentETF = "";
            
            // cash allocation
            SetCash(25000);
            
            // 5 ETFs and a negatively correlated treasury ETF
            AddSecurity(SecurityType.Equity, "MDY", Resolution.Minute);
            AddSecurity(SecurityType.Equity, "ILF", Resolution.Minute);
            AddSecurity(SecurityType.Equity, "FEZ", Resolution.Minute);
            AddSecurity(SecurityType.Equity, "EEM", Resolution.Minute);
            AddSecurity(SecurityType.Equity, "EPP", Resolution.Minute);
            AddSecurity(SecurityType.Equity, "TLT", Resolution.Minute);
            
            // momentum over roughly 3 month period
            momentumMDY = new MyMomentum("MDY", 90);
            momentumILF = new MyMomentum("ILF", 90);
            momentumFEZ = new MyMomentum("FEZ", 90);
            momentumEEM = new MyMomentum("EEM", 90);
            momentumEPP = new MyMomentum("EPP", 90);
            momentumTLT = new MyMomentum("TLT", 90);
        }

        public void OnData(TradeBars data){   
            
            // day tick - add data to MyMomentum objects
            if(currentDay.Date != data.Time.Date){
                currentDay = data.Time;
                if(data.ContainsKey("MDY")){
                    momentumMDY.add(data["MDY"].Close);    
                }
                if(data.ContainsKey("ILF")){
                    momentumILF.add(data["ILF"].Close);    
                }
                if(data.ContainsKey("FEZ")){
                    momentumFEZ.add(data["FEZ"].Close);    
                }
                if(data.ContainsKey("EEM")){
                    momentumEEM.add(data["EEM"].Close);    
                }
                if(data.ContainsKey("EPP")){
                    momentumEPP.add(data["EPP"].Close);    
                }
                if(data.ContainsKey("TLT")){
                    momentumTLT.add(data["TLT"].Close);    
                }
            }
            
            // month tick - use momentum to determine which ETF to invest in
            if(Time.ToString("MMM") != currentMonth){
                currentMonth = Time.ToString("MMM");
                string ETF = bestETF();
                if(currentETF != ETF){
                    Liquidate(); // cash out
                    // fully invest in best ETF if possible
                    if(data.ContainsKey(ETF)){
                        decimal cash = Portfolio.Cash;
                        decimal price = data[ETF].Close;
                        int quantity = (int)Math.Floor(cash/price);
                        Order(ETF, quantity);
                        Debug(Time.ToString("MMM") + ": invested in " + ETF + ".");
                    } else {
                        Debug(Time.ToString("MMM") + ": could not invest in " + ETF + ".");
                    }
                    currentETF = ETF;
                }
            }    
        }
    
        // returns the name of the ETF with the best return
        private string bestETF(){
            MyMomentum[] ETFs = new MyMomentum[]{momentumMDY, 
                                                 momentumILF,
                                                 momentumFEZ,
                                                 momentumEEM,
                                                 momentumEPP,
                                                 momentumTLT};
            string bestFund = "";
            decimal bestReturn = 0m;
            for(int i = 0; i < ETFs.Length; i++){
                MyMomentum mETF = ETFs[i];
                string fund = mETF.getName();
                decimal fundReturn = mETF.getReturn();
                if(bestFund == "" || bestReturn < fundReturn){
                    bestFund = fund;
                    bestReturn = fundReturn;
                }
            }
            return bestFund;
        }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using QuantConnect.Securities;
using QuantConnect.Models;

namespace QuantConnect {

    // Author: Tomer Borenstein
    // MyMomentum - calculates a crude measure of an asset's return
    
    public class MyMomentum {
        private List<decimal> prices;
        private string name;
        private int window;
        
        public MyMomentum(string name, int window){
            this.prices = new List<decimal>();
            this.name = name;
            this.window = window;
        }
        
        public void add(decimal price){
            prices.Add(price);
            if(prices.Count > window){
                prices.RemoveAt(0);
            }
        }
        
        public string getName(){
            return name;
        }
        
        public decimal getReturn(){
            decimal start = prices[0];
            decimal end = prices[prices.Count - 1];
            return (end-start)/start;
        }
    }

}