Overall Statistics
Total Trades
1890
Average Win
0.23%
Average Loss
-0.24%
Compounding Annual Return
-0.263%
Drawdown
10.400%
Expectancy
-0.009
Net Profit
-2.595%
Sharpe Ratio
-0.046
Probabilistic Sharpe Ratio
0.003%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.97
Alpha
-0.001
Beta
-0.004
Annual Standard Deviation
0.03
Annual Variance
0.001
Information Ratio
-0.655
Tracking Error
0.148
Treynor Ratio
0.382
Total Fees
$4284.35
Estimated Strategy Capacity
$120000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Selection;
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// Framework algorithm that uses the <see cref="EmaCrossUniverseSelectionModel"/> to
    /// select the universe based on a moving average cross.
    /// </summary>
    public class EmaCrossUniverseSelectionFrameworkAlgorithm : QCAlgorithm
    {
        public override void Initialize()
        {
            SetStartDate(2012, 12, 19);
            SetCash(100000);

            var fastSMAPeriod = 20;
            var slowSMAPeriod= 60;
            var atrPeriod = 20;
            var atrMultiplier = 3;
            Resolution resolution = Resolution.Hour;

            UniverseSettings.Leverage = 2.0m;
            UniverseSettings.Resolution = resolution;

            SetUniverseSelection(new ManualUniverseSelectionModel(QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA)));
            SetAlpha(new EmaCrossAlphaModel(fastSMAPeriod,slowSMAPeriod,resolution));
            SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
            AddRiskManagement(new ATRTrailingStopRiskManagementModel(atrPeriod,atrMultiplier,resolution));
        }
    }

    public class ATRTrailingStopRiskManagementModel : RiskManagementModel
    {
        //declare variables
        private readonly int _period;
        private readonly decimal _multiplier;
        private readonly Resolution _resolution;
        private readonly Dictionary<Symbol, HoldingsState> _holdingsState = new Dictionary<Symbol, HoldingsState>();
        private readonly Dictionary<Symbol, SymbolData> _symbolData = new Dictionary<Symbol, SymbolData>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ATRTrailingStopRiskManagementModel"/> class
        /// </summary>
        /// <param name="period">Period of the average true range indicator</param>
        /// <param name="multiplier">The number of average true ranges away from the price to calculate stop value</param>
        /// <param name="resolution">The resolution of ATR indicator</param>
        public ATRTrailingStopRiskManagementModel(int period = 14, decimal multiplier = 3m, Resolution resolution = Resolution.Minute)
        {
            _period = period;
            _multiplier = multiplier;
            _resolution = resolution;
        }

        /// <summary>
        /// Manages the algorithm's risk at each time step
        /// </summary>
        /// <param name="algorithm">The algorithm instance</param>
        /// <param name="targets">The current portfolio targets to be assessed for risk</param>
        public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets)
        {
            foreach (var kvp in algorithm.Securities)
            {
                var symbol = kvp.Key;
                var security = kvp.Value;

                // fetch our symbol data containing our ATR indicator
                SymbolData data;
                if (!_symbolData.TryGetValue(symbol, out data))
                {
                    //exit loop if data not available
                    continue;
                }

                // Remove holdingsState if not invested
                if (!security.Invested)
                {
                    _holdingsState.Remove(symbol);
                    //no need to manage risk if not invested
                    continue;
                }

                PositionSide position = security.Holdings.IsLong ? PositionSide.Long : PositionSide.Short;
                HoldingsState state;

                // Add newly invested security (if doesn't exist) or reset holdings state (if position changed)
                if (!_holdingsState.TryGetValue(symbol, out state) || position != state.Position)
                {  
                    decimal initialStopValue = security.Close + ((_multiplier * data.ATR) * (position == PositionSide.Long ? 1 : -1));
                    _holdingsState[symbol] = state = new HoldingsState(position, initialStopValue);  
                }
            
                // liquidate if stop loss triggered
                if (StopLossTriggered(security, state))
                {
                    _holdingsState.Remove(symbol);
                    // liquidate    
                    yield return new PortfolioTarget(security.Symbol, 0);
                }
                
                //Calculate new stop value
                state.StopValue = position == PositionSide.Long
                    ? Math.Max(state.StopValue, security.Close - (_multiplier * data.ATR))
                    : Math.Min(state.StopValue, security.Close + (_multiplier * data.ATR));
                
            }  
        }

        /// <summary>
        /// Boolean function returns true if the stop loss value is triggered by the CLOSE of the bar
        /// </summary>
        /// <param name="security">Current security in the loop</param>
        /// <param name="state">State of the current trade in the loop</param>
        protected virtual bool StopLossTriggered (Security security, HoldingsState state )
        {
            return state.Position == PositionSide.Long 
                ? state.StopValue >= security.Low
                : state.StopValue <= security.High;
        }

        /// <summary>
        /// Helper class used to store holdings state for the <see cref="TrailingStopRiskManagementModel"/>
        /// in <see cref="ManageRisk"/>
        /// </summary>
        protected class HoldingsState
        {
            public PositionSide Position;
            public decimal StopValue;

            public HoldingsState(PositionSide position, decimal stopValue)
            {
                Position = position;
                StopValue = stopValue;
            }
        }

        /// <summary>
        /// Event fired each time the we add/remove securities from the data feed
        /// </summary>
        /// <param name="algorithm">The algorithm instance that experienced the change in securities</param>
        /// <param name="changes">The security additions and removals from the algorithm</param>
        public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
        {
            foreach (var added in changes.AddedSecurities)
            {
                // initialize new securities
                if (!_symbolData.ContainsKey(added.Symbol))
                {
                    _symbolData[added.Symbol] = new SymbolData(algorithm, added, _period, _resolution);
                }
            }

            foreach (var removed in changes.RemovedSecurities)
            {
                // clean up data from removed securities
                SymbolData data;
                if (_symbolData.TryGetValue(removed.Symbol, out data))
                {
                    if (IsSafeToRemove(algorithm, removed.Symbol))
                    {
                        _symbolData.Remove(removed.Symbol);
                        algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator);
                    }
                }
            }
        }

        /// <summary>
        /// Determines if it's safe to remove the associated symbol data
        /// </summary>
        protected virtual bool IsSafeToRemove(QCAlgorithm algorithm, Symbol symbol)
        {
            // confirm the security isn't currently a member of any universe
            return !algorithm.UniverseManager.Any(kvp => kvp.Value.ContainsMember(symbol));
        }

        /// <summary>
        /// Symbol Data for this Execution Model
        /// </summary>
        protected class SymbolData
        {
            /// <summary>
            /// Security
            /// </summary>
            public Security Security { get; }

            /// <summary>
            /// Average True Range
            /// </summary>
            public AverageTrueRange ATR { get; }

            /// <summary>
            /// Data Consolidator
            /// </summary>
            public IDataConsolidator Consolidator { get; }

            /// <summary>
            /// Initialize an instance of <see cref="SymbolData"/>
            /// </summary>
            /// <param name="algorithm">Algorithm for this security</param>
            /// <param name="security">The security we are using</param>
            /// <param name="period">Period of the ATR</param>
            /// <param name="resolution">Resolution for this symbol</param>
            public SymbolData(QCAlgorithm algorithm, Security security, int period, Resolution resolution)
            {
                Security = security;
                Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution);

                var atrName = algorithm.CreateIndicatorName(security.Symbol, "ATR" + period, resolution);
                ATR = new AverageTrueRange(atrName, period);
                algorithm.RegisterIndicator(security.Symbol, ATR, Consolidator);
                algorithm.WarmUpIndicator(security.Symbol, ATR, resolution);
                
            }
        }

    }

}