Overall Statistics
Total Trades
444
Average Win
0.72%
Average Loss
-0.59%
Compounding Annual Return
17.818%
Drawdown
20.700%
Expectancy
0.595
Net Profit
109.357%
Sharpe Ratio
1.257
Loss Rate
28%
Win Rate
72%
Profit-Loss Ratio
1.22
Alpha
0.021
Beta
0.85
Annual Standard Deviation
0.137
Annual Variance
0.019
Information Ratio
-0.104
Tracking Error
0.059
Treynor Ratio
0.203
Total Fees
$536.43
namespace QuantConnect 
{   
    /*
    *   QuantConnect University: Futures Example
    *
    *   QuantConnect allows importing generic data sources! This example demonstrates importing a futures
    *   data from the popular open data source Quandl.
    *
    *   QuantConnect has a special deal with Quandl giving you access to Stevens Continuous Futurs (SCF) for free. 
    *   If you'd like to download SCF for local backtesting, you can download it through Quandl.com.
    */
    public class DualMomentumSectorRotation : QCAlgorithm
    {
        // we'll use this to tell us when the month has ended
        DateTime LastRotationTime = DateTime.MinValue;
        TimeSpan RotationInterval = TimeSpan.FromDays(30);
        
       
        
        List<string> GEMSymbols = new List<string>
        {
            "SPY",
            "BIL",
            "AGG"
        };
        
        // these are the growth symbols we'll rotate through
        List<string> SectorSymbols = new List<string>
        {
            "XLV", //healthcare
            "XLK", //technology
            "XLI", //industrial
            "XLU", //utilities
            "XLF", //financials
            "XLY", //consumerdisc
            "XLP", //consumerstap
            "XLB", //basic materials
            "XLE", // energy
            "PSR", //real estate
            "IYZ", // communications
        };
        
        // we'll hold some computed data in these guys
        // List<SymbolData> SectorSymbolData = new List<SymbolData>();
        Dictionary<string, SymbolData> SectorSymbolData = new Dictionary<string, SymbolData>();
        Dictionary<string, SymbolData> GEMSymbolData = new Dictionary<string, SymbolData>();
        List<string> strongSectors = new List<string>();

        public override void Initialize()
        {
            //SetStartDate(2003, 7, 1);
            SetStartDate(2010, 7, 1);
            SetEndDate(2015, 1, 1); 
            SetCash(25000);
            
            // define our daily trade bar consolidator. we can access the daily bar
            // from the DataConsolidated events
            var dailyConsolidator = new TradeBarConsolidator(TimeSpan.FromDays(1));
            
            // attach our event handler. the event handler is a function that will be called each time we produce
            // a new consolidated piece of data.
            dailyConsolidator.DataConsolidated += OnDataDaily;
            
            foreach (var symbol in SectorSymbols)
            {
                AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
                Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.TotalReturn);
                Securities[symbol].SetLeverage(1);
                var momentum = MOMP(symbol, 252, Resolution.Daily);
                var ma = SMA(symbol, 252/12*10, Resolution.Daily);	// 10-month Simple Moving Average
                var close = SMA(symbol, 1, Resolution.Daily);		// Store the most recent close 
                SubscriptionManager.AddConsolidator(symbol, dailyConsolidator);
                
                SectorSymbolData.Add(symbol, new SymbolData
                {
                    Symbol = symbol,
                    MomScore = momentum,
                    MovingAvg = ma,
                    Close = close
                });
            }
            
            foreach (var symbol in GEMSymbols)
            {
                AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
                Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.TotalReturn);
                Securities[symbol].SetLeverage(1);
                var momentum = MOMP(symbol, 252, Resolution.Daily);
                SubscriptionManager.AddConsolidator(symbol, dailyConsolidator);
                
                GEMSymbolData.Add(symbol, new SymbolData
                {
                    Symbol = symbol,
                    MomScore = momentum
                });
            }
        }
           
        private bool first = true;

        //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
        private void OnData(TradeBars data)
        {
            var _momSPY = GEMSymbolData["SPY"].MomScore;
            var _momTbill = GEMSymbolData["BIL"].MomScore;
            var Bonds = GEMSymbolData["AGG"].Symbol;

            if (first)
            {
                first = false;
                LastRotationTime = Time;
                return;
            }
        }
        
        private void OnDataDaily(object sender, TradeBar consolidated)
        {
            var _momSPY = GEMSymbolData["SPY"].MomScore;
            var _momTbill = GEMSymbolData["BIL"].MomScore;
            var Bonds = GEMSymbolData["AGG"].Symbol;
            List<string> longSectors1 = new List<string>();
            List<string> longSectors2 = new List<string>();
            decimal holdingPercent = 1m;
            decimal bondHoldings = 1m;
            
            if (!_momSPY.IsReady)
            {
                if (Portfolio["SPY"].Quantity == 0) 
                {
                    SetHoldings("SPY", holdingPercent);
                }
            }  
           
            if (first)
            {
                
                first = false;
                LastRotationTime = consolidated.Time;
                return;
            }

            var delta = Time.Subtract(LastRotationTime);
            
            // Check whether we have exceeded the rotation interval, time to assess rotation 
            if (delta > RotationInterval)
            {
                // Easy rebalance for backtest
	            foreach(string symbol in Securities.Keys)
	            {
	                Log("Liquidating " + Portfolio[symbol].Quantity + "of " + symbol);
	                Liquidate(symbol);
	            }
	            
                LastRotationTime = Time;
                
                List<SymbolData> sectorSymbolDataList = new List<SymbolData>();
                foreach (var x in SectorSymbolData) sectorSymbolDataList.Add(x.Value);
                
                var orderedMomScores = sectorSymbolDataList.OrderByDescending(x => x.MomScore.Current.Value).ToList();
                int numberOfSectors = 4;
                
                for (int i = 0; i < numberOfSectors; i++)
                {
                    strongSectors.Add(orderedMomScores[i].Symbol);
                    Log("Strong Symbol #" + i + "is " + orderedMomScores[i].Symbol);
                }
                
                
                foreach (var x in orderedMomScores)
                {
                    Log(">>SCORE>>" + x.Symbol + ">>" + x.MomScore);
                }
                
                
                //
                // Modify this section of code 
                // Loop through each ETF in strongSectors
                // 		If TMOM and MA rules -> record ETF in longSectors1
                //		If TMOM rule only -> record ETF in longSectors2
                //		If MA rule only -> record ETF in longSectors2
                // Set bondHoldings = 100%
                // Loop through each ETF in longSectors1
                //		Allocate holdings to the ETF equivalent to 1 / NumberOfSectors 
                //		Reduce bondHoldings percentage by 1 / NumberOfSectors 
                // Loop through each ETF in longSectors2
                //		Allocate holdings to the ETF equivalent to half of 1 / NumberOfSectors 
                //		Reduce bondHoldings percentage by half of 1 / NumberOfSectors
                // Allocate remaining holdings to bonds (percentage is indicated in bondHoldings variable)
                
                // Loop through each ETF in strongSectors 
                foreach (string etf in strongSectors)
                {
                	bool tmomRule = SectorSymbolData[etf].MomScore > _momTbill;
					bool maRule = SectorSymbolData[etf].Close > SectorSymbolData[etf].MovingAvg;
                	
                	if (tmomRule && maRule)
                		longSectors1.Add(etf);
                	if (tmomRule && !maRule)
                		longSectors2.Add(etf);
                	else if (!tmomRule && maRule)
                		longSectors2.Add(etf);
                }
                
                // Loop through each ETF in longSectors1
                foreach (string etf in longSectors1)
                {
                	SetHoldings(etf, holdingPercent * (1m / numberOfSectors));
                	bondHoldings -= (1m / numberOfSectors);
                }
                
                // Loop through each ETF in longSectors2
                foreach (string etf in longSectors2)
                {
                	SetHoldings(etf, holdingPercent * (0.5m / numberOfSectors));
                	bondHoldings -= (0.5m / numberOfSectors);
                }
                
                // Allocate remaining holdings to bonds 
                SetHoldings(Bonds, bondHoldings);
                
/*                if (_momSPY < 0)
                {
                    SetHoldings(Bonds, holdingPercent);
                    Log("Holding Percent is " + holdingPercent);
                    Log("Set Holdings to " + Portfolio[Bonds].Quantity + "of " + Bonds);
                } else 
                {
                    foreach (var etf in strongSectors)
                    {
                       SetHoldings(etf, holdingPercent * (1m / numberOfSectors));
                       Log("Count of strongSectors is " + numberOfSectors);
                    } 
                } 
 */               
                strongSectors.Clear();
            }
        }
        
		public MMomentumPercent MMOMP(string symbol, int period, int periodexc, Resolution? resolution = null)
		{
			var mmomp = new MMomentumPercent(period, periodexc);
			RegisterIndicator(symbol, mmomp, resolution);
			return mmomp;
		}
    }
    
    class SymbolData
    {
        public string Symbol;
        public MomentumPercent MomScore { get; set; }
        public SimpleMovingAverage MovingAvg { get; set; }
        // Quick and dirty - store most recent close here 
        public SimpleMovingAverage Close { get; set; }
    }
    
    
    public class MMomentumPercent : WindowIndicator<IndicatorDataPoint>
    {
    	private int periodexc = 21; 	// Number of most recent days to exclude from calculation 
    	
    	public MMomentumPercent(int period, int periodexc)
            : base("MMOMP" + period, period)
        {
        }
    	
		protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
        {
            if (window.Samples <= window.Size)
            {
                // keep returning the delta from the first item put in there to init
                return (window[periodexc] - window[window.Count - 1]) / window[periodexc];
            }

            return (window[periodexc] - window.MostRecentlyRemoved) / window[periodexc];
        }    	
    }
    
}