Lean  $LEAN_TAG$
SecurityPositionGroupResolver.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 using System.Linq;
17 using System.Collections.Generic;
18 
20 {
21  /// <summary>
22  /// Provides an implementation of <see cref="IPositionGroupResolver"/> that places all positions into a default group of one security.
23  /// </summary>
25  {
26  private readonly IPositionGroupBuyingPowerModel _buyingPowerModel;
27 
28  /// <summary>
29  /// Initializes a new instance of the <see cref="SecurityPositionGroupResolver"/> class
30  /// </summary>
31  /// <param name="buyingPowerModel">The buying power model to use for created groups</param>
33  {
34  _buyingPowerModel = buyingPowerModel;
35  }
36 
37  /// <summary>
38  /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an
39  /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this
40  /// resolver.
41  /// </summary>
42  /// <param name="newPositions">The positions to be grouped</param>
43  /// <param name="currentPositions">The currently grouped positions</param>
44  /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param>
45  /// <returns>True if this resolver can group the specified positions, otherwise false</returns>
46  public bool TryGroup(IReadOnlyCollection<IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup group)
47  {
48  // we can only create default groupings containing a single security
49  if (newPositions.Count != 1)
50  {
51  group = null;
52  return false;
53  }
54 
55  var key = new PositionGroupKey(_buyingPowerModel, newPositions);
56  var position = newPositions.First();
57  group = new PositionGroup(key, position.GetGroupQuantity(), newPositions.ToDictionary(p => p.Symbol));
58  return true;
59  }
60 
61  /// <summary>
62  /// Resolves the position groups that exist within the specified collection of positions.
63  /// </summary>
64  /// <param name="positions">The collection of positions</param>
65  /// <returns>An enumerable of position groups</returns>
67  {
68  var result = new PositionGroupCollection(positions
69  .Select(position => new PositionGroup(_buyingPowerModel, position.GetGroupQuantity(), position)).ToList()
70  );
71 
72  positions.Clear();
73  return result;
74  }
75 
76  /// <summary>
77  /// Determines the position groups that would be evaluated for grouping of the specified
78  /// positions were passed into the <see cref="IPositionGroupResolver.Resolve"/> method.
79  /// </summary>
80  /// <remarks>
81  /// This function allows us to determine a set of impacted groups and run the resolver on just
82  /// those groups in order to support what-if analysis
83  /// </remarks>
84  /// <param name="groups">The existing position groups</param>
85  /// <param name="positions">The positions being changed</param>
86  /// <returns>An enumerable containing the position groups that could be impacted by the specified position changes</returns>
87  public IEnumerable<IPositionGroup> GetImpactedGroups(
89  IReadOnlyCollection<IPosition> positions
90  )
91  {
92  var seen = new HashSet<PositionGroupKey>();
93  foreach (var position in positions)
94  {
95  IReadOnlyCollection<IPositionGroup> groupsForSymbol;
96  if (!groups.TryGetGroups(position.Symbol, out groupsForSymbol))
97  {
98  continue;
99  }
100 
101  foreach (var group in groupsForSymbol)
102  {
103  if (seen.Add(group.Key))
104  {
105  yield return group;
106  }
107  }
108  }
109  }
110  }
111 }