Lean  $LEAN_TAG$
TrailingStopRiskManagementModel.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15 */
16 
17 using System;
18 using System.Collections.Generic;
20 
22 {
23  /// <summary>
24  /// Provides an implementation of <see cref="IRiskManagementModel"/> that limits the maximum possible loss
25  /// measured from the highest unrealized profit
26  /// </summary>
28  {
29  private readonly decimal _maximumDrawdownPercent;
30  private readonly Dictionary<Symbol, HoldingsState> _trailingAbsoluteHoldingsState = new Dictionary<Symbol, HoldingsState>();
31 
32  /// <summary>
33  /// Initializes a new instance of the <see cref="TrailingStopRiskManagementModel"/> class
34  /// </summary>
35  /// <param name="maximumDrawdownPercent">The maximum percentage relative drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown per security</param>
36  public TrailingStopRiskManagementModel(decimal maximumDrawdownPercent = 0.05m)
37  {
38  _maximumDrawdownPercent = Math.Abs(maximumDrawdownPercent);
39  }
40 
41  /// <summary>
42  /// Manages the algorithm's risk at each time step
43  /// </summary>
44  /// <param name="algorithm">The algorithm instance</param>
45  /// <param name="targets">The current portfolio targets to be assessed for risk</param>
46  public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets)
47  {
48  foreach (var kvp in algorithm.Securities)
49  {
50  var symbol = kvp.Key;
51  var security = kvp.Value;
52 
53  // Remove if not invested
54  if (!security.Invested)
55  {
56  _trailingAbsoluteHoldingsState.Remove(symbol);
57  continue;
58  }
59 
60  var position = security.Holdings.IsLong ? PositionSide.Long : PositionSide.Short;
61  var absoluteHoldingsValue = security.Holdings.AbsoluteHoldingsValue;
62  HoldingsState trailingAbsoluteHoldingsState;
63 
64  // Add newly invested security (if doesn't exist) or reset holdings state (if position changed)
65  if (!_trailingAbsoluteHoldingsState.TryGetValue(symbol, out trailingAbsoluteHoldingsState) ||
66  position != trailingAbsoluteHoldingsState.Position)
67  {
68  _trailingAbsoluteHoldingsState[symbol] = trailingAbsoluteHoldingsState = new HoldingsState(position, security.Holdings.AbsoluteHoldingsCost);
69  }
70 
71  var trailingAbsoluteHoldingsValue = trailingAbsoluteHoldingsState.AbsoluteHoldingsValue;
72 
73  // Check for new max (for long position) or min (for short position) absolute holdings value
74  if ((position == PositionSide.Long && trailingAbsoluteHoldingsValue < absoluteHoldingsValue) ||
75  (position == PositionSide.Short && trailingAbsoluteHoldingsValue > absoluteHoldingsValue))
76  {
77  trailingAbsoluteHoldingsState.AbsoluteHoldingsValue = absoluteHoldingsValue;
78  continue;
79  }
80 
81  var drawdown = Math.Abs((trailingAbsoluteHoldingsValue - absoluteHoldingsValue) / trailingAbsoluteHoldingsValue);
82 
83  if (_maximumDrawdownPercent < drawdown)
84  {
85  // Cancel insights
86  algorithm.Insights.Cancel(new[] { symbol });
87 
88  _trailingAbsoluteHoldingsState.Remove(symbol);
89  // liquidate
90  yield return new PortfolioTarget(symbol, 0);
91  }
92  }
93  }
94 
95  /// <summary>
96  /// Helper class used to store holdings state for the <see cref="TrailingStopRiskManagementModel"/>
97  /// in <see cref="ManageRisk"/>
98  /// </summary>
99  private class HoldingsState
100  {
101  public PositionSide Position;
102  public decimal AbsoluteHoldingsValue;
103 
104  public HoldingsState(PositionSide position, decimal absoluteHoldingsValue)
105  {
106  Position = position;
107  AbsoluteHoldingsValue = absoluteHoldingsValue;
108  }
109  }
110  }
111 }