Overall Statistics
Total Trades
83
Average Win
2.57%
Average Loss
-1.32%
Compounding Annual Return
-2.198%
Drawdown
14.700%
Expectancy
-0.066
Net Profit
-4.355%
Sharpe Ratio
-0.122
Loss Rate
68%
Win Rate
32%
Profit-Loss Ratio
1.95
Alpha
-0.009
Beta
-0.017
Annual Standard Deviation
0.121
Annual Variance
0.015
Information Ratio
-1.144
Tracking Error
0.301
Treynor Ratio
0.857
Total Fees
$83.00
namespace QuantConnect
{
    public class SecurityObject
    {
    	// Fields
    	
        private string _ticker;
        private decimal _weightedholding = 0.0m;
        private MovingAverageConvergenceDivergence _macd = null;
        private decimal _priorfast = 0.0m;
        private decimal _priorslow = 0.0m;
        private decimal _priorsignal = 0.0m;

		// Properties

        public string Ticker
        {
            get { return _ticker; }
            set
            {
                _ticker = value;
            }
        }
        
        public decimal WeightedHolding
        {
            get { return _weightedholding; }
            set
            {
                _weightedholding = value;
            }
        }
        
        public MovingAverageConvergenceDivergence MACD
        {
            get { return _macd; }
            set
            {
                _macd = value;
            }
        }
        
        public decimal PriorFast
        {
            get { return _priorfast; }
            set
            {
                _priorfast = value;
            }
        }
        
        public decimal PriorSlow
        {
            get { return _priorslow; }
            set
            {
                _priorslow = value;
            }
        }
        
        public decimal PriorSignal
        {
            get { return _priorsignal; }
            set
            {
                _priorsignal = value;
            }
        }
        
        // Methods
        
        public void AssignPriorValues()
        {
        	PriorFast = MACD.Fast;
        	PriorSlow = MACD.Slow;
        	PriorSignal = MACD.Signal;
        }
        
        public bool IsWorthBuying()
        {
        	bool satisfied  = false;
        	
        	decimal currentmacd = MACD.Fast - MACD.Slow;
        	decimal currentsignal = MACD.Signal;
        	decimal priormacd = PriorFast - PriorSlow;
        	decimal priorsignal = PriorSignal;
        	
        	if (currentsignal < currentmacd && 
        		priorsignal > priormacd)
        	{
        		satisfied = true;
        	}
        	
        	return satisfied;
        }
        
        public bool IsWorthSelling()
        {
        	bool satisfied = false;
        	
        	decimal currentmacd = MACD.Fast - MACD.Slow;
        	decimal currentsignal = MACD.Signal;
        	decimal priormacd = PriorFast - PriorSlow;
        	decimal priorsignal = PriorSignal;
        	
        	if (currentsignal > currentmacd &&
        		priorsignal < priormacd)
        	{
        		satisfied = true;
        	}
        	
        	return satisfied;
        }
    }
}
namespace QuantConnect
{   
    public class MovingAverageConvergenceDivergenceAlgo : QCAlgorithm
    {
    	// Notes
    	
    	/*
    	SUMMARY:
    	This project captures the moving average convergence divergence (MACD) of a universe of weighted assets.
    	The MACD is characterized by a "slow" 26-day exponential moving average, a "fast" 12-day exponential moving
    	average.  The MACD line is the difference between the fast and slow moving averages.  Overlaid on top of the MACD
    	is a "signal" 9-day moving average of the MACD line.  Both lines are overlaid on top of each other.
    	
    	RULES:
    	If the signal line crosses downward over the MACD line, and the MACD line is at or below 0.0 in value, we sell the asset.
    	If the signal line crosses upward over the MACD line, and the MACD line is at or above 0.0 in value, we buy.
    	
    	THOUGHTS:
    	-Instead of just selling, we could attempt to sell and then short the asset.
    	-Need to revamp how we think about weighted holdings in portfolio
    	*/
    	
        // User-editable Fields

        public  Dictionary<string, decimal> _tickerDictionary = new Dictionary<string, decimal> // ticker, weighted holdings
	    {
	        { "SPY", 1.0m }
	    };
		private int _warmupdays = 30;

        // Class Fields

        private DateTime _lastRebalanceTime = DateTime.MinValue;
        private TimeSpan _rebalanceInterval = TimeSpan.FromDays(1);
        private List<SecurityObject> _consideredSecuritiesList = new List<SecurityObject>();
        private List<SecurityObject> _winningSecuritiesList = new List<SecurityObject>();
        private List<SecurityObject> _losingSecuritiesList = new List<SecurityObject>();
        private bool _firstOnDataCall = true;

        // Initializer

        public override void Initialize()
        {
            SetStartDate(2003, 1, 1);
            SetEndDate(2005, 1, 1);
            SetCash(10000);

            //Chart macdchart = new Chart("MACD");
            //macdchart.AddSeries(new Series("MACD", SeriesType.Line, index:0));
            //macdchart.AddSeries(new Series("Signal", SeriesType.Line, index:0));
            //AddChart(macdchart);

			foreach(KeyValuePair<string, decimal> entry in _tickerDictionary)
            {
            	string ticker = entry.Key;
            	decimal weightedholding = entry.Value;
            	
                AddSecurity(SecurityType.Equity, ticker, Resolution.Minute);
                Securities[ticker].SetDataNormalizationMode(DataNormalizationMode.TotalReturn);
                Securities[ticker].SetLeverage(1);

				MovingAverageConvergenceDivergence _macd = MACD(entry.Key, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily);
			
                _consideredSecuritiesList.Add(new SecurityObject { Ticker = ticker, WeightedHolding = weightedholding, MACD = _macd });
                
                SetWarmup(_warmupdays);
            }
        }

        // Event-Driven Methods

        public void OnData(TradeBars data)
        {
        	// Abandon method if we're warming up the algorithm with historical data
            if (IsWarmingUp) return;
            
            // Abandon method if this is the first time the OnData() method is called (rules out first-of-day data errors)
            if (_firstOnDataCall)
            {
                _firstOnDataCall = false;
                _lastRebalanceTime = this.Time;
                return;
            }
            
			// Assess interval rules
			
            TimeSpan _timeSinceLastRebalance = this.Time.Subtract(_lastRebalanceTime);

            if (_timeSinceLastRebalance > _rebalanceInterval)
           	{
           		BeginningLog();
            	
                _lastRebalanceTime = this.Time;
                
				DefaultWinnersAndLosersToEmptyList();
				
				AssessBuyers();
				
				AssessSellers();
				
				TakeAction();
				
				AssignPriorValuesToSecurities();
            }
        }
        
        public override void OnEndOfDay() 
        {
        	foreach (SecurityObject security in _consideredSecuritiesList)
            {
				double macdpoint = Convert.ToDouble(security.MACD.Fast - security.MACD.Slow);
				double signalpoint = Convert.ToDouble(security.MACD.Signal);
	            Plot(security.Ticker, "MACD", macdpoint);
	            Plot(security.Ticker, "Signal", signalpoint);
            }
        }

        // Rebalance Rules Methods

		public void BeginningLog()
		{
			foreach (SecurityObject security in _consideredSecuritiesList)
			{
				Log(String.Format(">> SECURITY: {0}", security.Ticker));
            	Log(String.Format(">>>>> CURRENT:\tFast: {0}\tSlow: {1}\tSignal: {2}", security.MACD.Fast, security.MACD.Slow, security.MACD.Signal));
            	Log(String.Format(">>>>>   PRIOR:\tFast: {0}\tSlow: {1}\tSignal: {2}", security.PriorFast, security.PriorSlow, security.PriorSignal));
			}
		}

		public void DefaultWinnersAndLosersToEmptyList()
		{
			_winningSecuritiesList = new List<SecurityObject>();
			_losingSecuritiesList = new List<SecurityObject>();
		}
		
		public void AssessBuyers()
		{
        	foreach (SecurityObject security in _consideredSecuritiesList)
            {
                if (security.IsWorthBuying() == true && security.PriorSignal != 0.0m)
                {
                	_winningSecuritiesList.Add(security);
                }
            }
		}
		
		public void AssessSellers()
		{
        	foreach (SecurityObject security in _consideredSecuritiesList)
            {
                if (security.IsWorthSelling() == true && security.PriorSignal != 0.0m)
                {
                	_losingSecuritiesList.Add(security);
                }
            }
		}
		
		public void TakeAction()
		{
			// Calculate Total Weightings
			decimal totalweightings = CalculateTotalWeightings();
			
			Log(String.Format(">> Total weightings: {0}.", totalweightings));
			
			// Sell our losing securities
			
			foreach (SecurityObject security in _losingSecuritiesList)
			{
				if (Portfolio[security.Ticker].HoldStock)
				{
					Log(String.Format(">> Shorting {0}.", security.Ticker));
					decimal percentageofportfolio = _tickerDictionary[security.Ticker] / totalweightings;
					SetHoldings(security.Ticker, 0.0m);
					SetHoldings(security.Ticker, -Convert.ToDouble(percentageofportfolio));
				}
			}
			
			// Buy our winning securities
			
			foreach (SecurityObject security in _winningSecuritiesList)
			{
				Log(String.Format(">> Buying {0}.", security.Ticker));
				decimal percentageofportfolio = _tickerDictionary[security.Ticker] / totalweightings;
				SetHoldings(security.Ticker, 0.0m);
				SetHoldings(security.Ticker, Convert.ToDouble(percentageofportfolio));
			}
			
		}
		
		public void AssignPriorValuesToSecurities()
		{
			foreach (SecurityObject security in _consideredSecuritiesList)
			{
				security.AssignPriorValues();
			}
		}
		
		// Helper Methods
		
		public decimal CalculateTotalWeightings()
		{
			decimal calculatedtotalweightings = 0.0m;
			
			foreach (SecurityObject security in _consideredSecuritiesList)
			{
				calculatedtotalweightings += _tickerDictionary[security.Ticker];
			}
			
			return calculatedtotalweightings;
		}
    }
}