Overall Statistics
Total Trades
1002
Average Win
0.81%
Average Loss
-0.75%
Compounding Annual Return
-3.483%
Drawdown
38.900%
Expectancy
-0.035
Net Profit
-16.260%
Sharpe Ratio
-0.2
Probabilistic Sharpe Ratio
0.155%
Loss Rate
54%
Win Rate
46%
Profit-Loss Ratio
1.08
Alpha
-0.02
Beta
-0.016
Annual Standard Deviation
0.113
Annual Variance
0.013
Information Ratio
-0.781
Tracking Error
0.207
Treynor Ratio
1.397
Total Fees
$5362.12
Estimated Strategy Capacity
$7700000.00
Lowest Capacity Asset
IJR RV0PWMLXVHPH
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QuantConnect.Algorithm.CSharp
{
    public class MacdGoldenCross : QCAlgorithm
    {
        private static Symbol[] _symbols = new Symbol[] { QuantConnect.Symbol.Create("IJR", SecurityType.Equity, Market.USA) };

        public override void Initialize()
        {
            var startYear = 2016;
            var endYear = 2021;
            var warmup = 150;

            SetStartDate(new DateTime(startYear, 1, 1));
            SetEndDate(new DateTime(endYear, 1, 1));
            SetCash(100000);

            UniverseSettings.Resolution = Resolution.Daily;
            SetWarmup(new TimeSpan(warmup, 0, 0, 0));
            EnableAutomaticIndicatorWarmUp = true;

            SetUniverseSelection(new ManualUniverseSelectionModel(_symbols));

            AddAlpha(new GoldenCrossAlpha());

            SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
            SetExecution(new ImmediateExecutionModel());

            //AddRiskManagement(new MaximumUnrealizedProfitPercentPerSecurity(0.1m));
            AddRiskManagement(riskManagement: new MaximumDrawdownPercentPerSecurity(0.05m));

            var spy = AddEquity("SPY", Resolution.Daily);
            AddRiskManagement(new MarketSmaLimit(spy.Symbol, SMA(spy.Symbol, 150, Resolution.Daily)));
        }

        private class SymbolData
        {
            public MovingAverageConvergenceDivergence Macd { get; private set; }

            public SymbolData(Symbol symbol, QCAlgorithm algo, MacdSettings settings)
            {
                Macd = algo.MACD(symbol, settings.Fast, settings.Slow, settings.Signal, MovingAverageType.Exponential, Resolution.Daily);
            }
        }

        private struct MacdSettings
        {
            public int Fast { get; set; }
            public int Slow { get; set; }
            public int Signal { get; set; }
        }

        private class GoldenCrossAlpha : IAlphaModel
        {
            private readonly Dictionary<Symbol, SymbolData> _symbols = new Dictionary<Symbol, SymbolData>();
            private MacdSettings _macdSettings;

            public GoldenCrossAlpha(int fast = 12, int slow = 26, int signal = 9)
            {
                _macdSettings = new MacdSettings()
                {
                    Fast = fast,
                    Slow = slow,
                    Signal = signal
                };
            }

            public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
            {
                if (algorithm.IsWarmingUp) 
                    return;
                foreach (var added in changes.AddedSecurities)
                {
                    if (!_symbols.ContainsKey(added.Symbol))
                    {
                        _symbols.Add(added.Symbol, new SymbolData(added.Symbol, algorithm, _macdSettings));
                    }
                }

                foreach (var removed in changes.RemovedSecurities)
                {
                    if (_symbols.ContainsKey(removed.Symbol))
                    {
                        _symbols.Remove(removed.Symbol);
                    }
                }
            }

            public IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
            {
                List<Insight> insights = new List<Insight>();
                if (algorithm.IsWarmingUp)
                    return insights;
                foreach (var symbol in data.Keys)
                {
                    if (!_symbols.ContainsKey(symbol) || symbol.Value == "SPY")
                        continue;
                    var symbolData = _symbols[symbol];
                    if (!algorithm.Securities[symbol].Invested)
                    {
                        if (symbolData.Macd.Fast > symbolData.Macd.Slow)
                        {
                            insights.Add(new Insight(symbol, new TimeSpan(), InsightType.Price, InsightDirection.Up));
                        }
                    }
                    else
                    {
                        if (symbolData.Macd.Fast < symbolData.Macd.Slow)
                        {
                            insights.Add(new Insight(symbol, new TimeSpan(), InsightType.Price, InsightDirection.Down));
                        }
                    }
                }
                return insights;
            }
        }

        private class MarketSmaLimit : RiskManagementModel
        {
            private readonly Symbol _benchmark;
            private readonly SimpleMovingAverage _sma;

            public MarketSmaLimit(Symbol benchmark, SimpleMovingAverage sma)
            {
                _benchmark = benchmark;
                _sma = sma;
            }

            public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets)
            {
                var results = new List<IPortfolioTarget>();
                var price = algorithm.Portfolio.Securities[_benchmark].Price;
                if (price < _sma.Current.Value)
                {
                    algorithm.Debug("Closing all open positions due to SPY falling below SMA");
                    foreach(var security in algorithm.ActiveSecurities.Values)
                    {
                        if(security.Invested)
                        {
                            results.Add(new PortfolioTarget(security.Symbol, 0));
                        }
                    }
                    return results;
                }
                else
                {
                    return targets;
                }
            }
        }
    }
}