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 = 14; 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); } } } }