Overall Statistics
Total Trades
17
Average Win
7.40%
Average Loss
-3.20%
Compounding Annual Return
17.412%
Drawdown
15.200%
Expectancy
1.210
Net Profit
37.915%
Sharpe Ratio
1.043
Probabilistic Sharpe Ratio
48.234%
Loss Rate
33%
Win Rate
67%
Profit-Loss Ratio
2.32
Alpha
0.173
Beta
-0.087
Annual Standard Deviation
0.147
Annual Variance
0.022
Information Ratio
-0.265
Tracking Error
0.288
Treynor Ratio
-1.756
Total Fees
$31.76
Estimated Strategy Capacity
$740000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
using QuantConnect.Algorithm.Framework;
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.Market;
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.Custom
{
    public class BtfdAlgo : QCAlgorithm
    {
        //private static Symbol[] _symbolTickers = new [] { "IVV", "IYY", "IJR" };
        private static Symbol[] _symbols = new Symbol[] { QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA) };

        public override void Initialize()
        {
            int startYear = int.Parse(GetParameter("START_YEAR"));
            int endYear = int.Parse(GetParameter("END_YEAR"));
            int days = int.Parse(GetParameter("AVERAGE_DAYS"));
            decimal returnPercentage = decimal.Parse(GetParameter("SELL_SIGNAL_PERCENT"));
           
            SetStartDate(new DateTime(startYear, 1, 1));
            SetEndDate(new DateTime(endYear, 1, 1));
            SetCash(100000);

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

            SetUniverseSelection(new ManualUniverseSelectionModel(_symbols));
            AddAlpha(new BtfdAlpha());
            SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
            SetExecution(new ImmediateExecutionModel());
            AddRiskManagement(new TakeProfitRiskManagement(returnPercentage));
            AddRiskManagement(new MaximumDrawdownPercentPerSecurity());
        }

        private class BtfdAlpha : IAlphaModel, INotifiedSecurityChanges
        {
            private readonly Dictionary<Symbol, SymbolData> _symbols = new Dictionary<Symbol, SymbolData>();
            private readonly int _smaDays;

            public BtfdAlpha(int smaDays = 365)
            {
                _smaDays = smaDays;
            }

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

                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>();
                foreach (var symbol in data.Keys)
                {
                    // First, check if already invested
                    if (!algorithm.Securities[symbol].Invested)
                    {
                        // Not invested, check to buy
                        SymbolData symbolData = _symbols[symbol];
                        var price = data[symbol].Close;
                        var currentSma = symbolData.SMA.Current.Value;
                        if (price < currentSma)
                        {
                            // Prediction is to buy
                            Insight insight = new Insight(symbol, new TimeSpan(30,0,0,0,0), InsightType.Price, InsightDirection.Up);
                            insights.Add(insight);
                        }
                    }
                }
                return insights;
            }
        }

        private class TakeProfitRiskManagement : RiskManagementModel
        {
            private Dictionary<Symbol, decimal> _purchasePrices = new Dictionary<Symbol, decimal>();
            private decimal _returnPercentage;

            public TakeProfitRiskManagement(decimal returnPercentage = 0.1m)
            {
                _returnPercentage = returnPercentage;
            }

            public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets)
            {
                ManagePurchasePrices(algorithm);
                List<PortfolioTarget> portfolioTargets = new List<PortfolioTarget>();
                foreach(var security in algorithm.ActiveSecurities)
                {
                    Symbol symbol = security.Key;
                    if(algorithm.Securities[symbol].Invested)
                    {
                        decimal purchasePrice = _purchasePrices[symbol];
                        decimal currentPrice = algorithm.Securities[symbol].Price;
                        var currentReturn = ((currentPrice - purchasePrice) / purchasePrice) * 100.0m;
                        if(currentReturn >= _returnPercentage)
                        {
                            PortfolioTarget newTarget = new PortfolioTarget(symbol, 0);
                            portfolioTargets.Add(newTarget);
                        }
                    }
                }
                return portfolioTargets;
            }

            private void ManagePurchasePrices(QCAlgorithm algorithm)
            {
                foreach (var security in algorithm.ActiveSecurities)
                {
                    Symbol symbol = security.Key;
                    if (algorithm.Securities[symbol].Invested)
                    {
                        // If we havn't recorded the purchase price, we add it
                        if (!_purchasePrices.ContainsKey(symbol))
                        {
                            decimal purchasePrice = algorithm.Securities[symbol].Price;
                            _purchasePrices.Add(symbol, purchasePrice);
                        }
                    }
                    else
                    {
                        if (!algorithm.Securities[symbol].Invested)
                        {
                            // If the price is not invested, we remove it from the prices dictionary
                            if (_purchasePrices.ContainsKey(symbol))
                            {
                                _purchasePrices.Remove(symbol);
                            }
                        }
                    }
                }

            }
        }


        private class SymbolData
        {
            private readonly Symbol _symbol;
            public Symbol Symbol { get { return _symbol; } }

            private readonly SimpleMovingAverage _sma;
            public SimpleMovingAverage SMA { get { return _sma; } }

            public SymbolData(Symbol symbol, int days, QCAlgorithm algo)
            {
                _symbol = symbol;
                _sma = new SimpleMovingAverage(days);
                algo.RegisterIndicator(symbol, _sma, Resolution.Daily);
            }
        }
    }
}