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; } } }