Lean  $LEAN_TAG$
SecurityPositionGroupBuyingPowerModel.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 
19 {
20  /// <summary>
21  /// Provides an implementation of <see cref="IPositionGroupBuyingPowerModel"/> for groups containing exactly one security
22  /// </summary>
24  {
25  /// <summary>
26  /// Gets the margin currently allocated to the specified holding
27  /// </summary>
28  /// <param name="parameters">An object containing the security</param>
29  /// <returns>The maintenance margin required for the </returns>
31  {
32  // SecurityPositionGroupBuyingPowerModel models buying power the same as non-grouped, so we can simply sum up
33  // the reserved buying power via the security's model. We should really only ever get a single position here,
34  // but it's not incorrect to ask the model for what the reserved buying power would be using default modeling
35  var buyingPower = 0m;
36  foreach (var position in parameters.PositionGroup)
37  {
38  var security = parameters.Portfolio.Securities[position.Symbol];
39  var result = security.BuyingPowerModel.GetMaintenanceMargin(
40  MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, position.Quantity)
41  );
42 
43  buyingPower += result;
44  }
45 
46  return buyingPower;
47  }
48 
49  /// <summary>
50  /// The margin that must be held in order to increase the position by the provided quantity
51  /// </summary>
52  /// <param name="parameters">An object containing the security and quantity</param>
54  {
55  var initialMarginRequirement = 0m;
56  foreach (var position in parameters.PositionGroup)
57  {
58  var security = parameters.Portfolio.Securities[position.Symbol];
59  initialMarginRequirement += security.BuyingPowerModel.GetInitialMarginRequirement(
60  security, position.Quantity
61  );
62  }
63 
64  return initialMarginRequirement;
65  }
66 
67  /// <summary>
68  /// Gets the total margin required to execute the specified order in units of the account currency including fees
69  /// </summary>
70  /// <param name="parameters">An object containing the portfolio, the security and the order</param>
71  /// <returns>The total margin in terms of the currency quoted in the order</returns>
74  )
75  {
76  var initialMarginRequirement = 0m;
77  foreach (var position in parameters.PositionGroup)
78  {
79  // TODO : Support combo order by pull symbol-specific order
80  var security = parameters.Portfolio.Securities[position.Symbol];
81  initialMarginRequirement += security.BuyingPowerModel.GetInitialMarginRequiredForOrder(
82  new InitialMarginRequiredForOrderParameters(parameters.Portfolio.CashBook, security, parameters.Order)
83  );
84  }
85 
86  return initialMarginRequirement;
87  }
88 
89  /// <summary>
90  /// Get the maximum position group order quantity to obtain a position with a given buying power
91  /// percentage. Will not take into account free buying power.
92  /// </summary>
93  /// <param name="parameters">An object containing the portfolio, the position group and the target
94  /// signed buying power percentage</param>
95  /// <returns>Returns the maximum allowed market order quantity and if zero, also the reason</returns>
98  )
99  {
100  if (parameters.PositionGroup.Count != 1)
101  {
102  return parameters.Error(
103  $"{nameof(SecurityPositionGroupBuyingPowerModel)} only supports position groups containing exactly one position."
104  );
105  }
106 
107  var position = parameters.PositionGroup.Single();
108  var security = parameters.Portfolio.Securities[position.Symbol];
109  var result = security.BuyingPowerModel.GetMaximumOrderQuantityForTargetBuyingPower(
110  parameters.Portfolio, security, parameters.TargetBuyingPower, parameters.MinimumOrderMarginPortfolioPercentage
111  );
112 
113  var quantity = result.Quantity / security.SymbolProperties.LotSize;
114  return new GetMaximumLotsResult(quantity, result.Reason, result.IsError);
115  }
116 
117  /// <summary>
118  /// Get the maximum market position group order quantity to obtain a delta in the buying power used by a position group.
119  /// The deltas sign defines the position side to apply it to, positive long, negative short.
120  /// </summary>
121  /// <param name="parameters">An object containing the portfolio, the position group and the delta buying power</param>
122  /// <returns>Returns the maximum allowed market order quantity and if zero, also the reason</returns>
123  /// <remarks>Used by the margin call model to reduce the position by a delta percent.</remarks>
126  )
127  {
128  if (parameters.PositionGroup.Count != 1)
129  {
130  return parameters.Error(
131  $"{nameof(SecurityPositionGroupBuyingPowerModel)} only supports position groups containing exactly one position."
132  );
133  }
134 
135  var position = parameters.PositionGroup.Single();
136  var security = parameters.Portfolio.Securities[position.Symbol];
137  var result = security.BuyingPowerModel.GetMaximumOrderQuantityForDeltaBuyingPower(
139  parameters.Portfolio, security, parameters.DeltaBuyingPower, parameters.MinimumOrderMarginPortfolioPercentage
140  )
141  );
142 
143  var quantity = result.Quantity / security.SymbolProperties.LotSize;
144  return new GetMaximumLotsResult(quantity, result.Reason, result.IsError);
145  }
146 
147  /// <summary>
148  /// Check if there is sufficient buying power for the position group to execute this order.
149  /// </summary>
150  /// <param name="parameters">An object containing the portfolio, the position group and the order</param>
151  /// <returns>Returns buying power information for an order against a position group</returns>
154  )
155  {
156  if (parameters.PositionGroup.Count != 1)
157  {
158  return parameters.Error(
159  $"{nameof(SecurityPositionGroupBuyingPowerModel)} only supports position groups containing exactly one position."
160  );
161  }
162 
163  var position = parameters.PositionGroup.Single();
164  var security = parameters.Portfolio.Securities[position.Symbol];
165  return security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(
166  parameters.Portfolio, security, parameters.Orders.Single()
167  );
168  }
169 
170  /// <summary>
171  /// Additionally check initial margin requirements if the algorithm only has default position groups
172  /// </summary>
175  decimal availableBuyingPower
176  )
177  {
178  // only check initial margin requirements when the algorithm is only using default position groups
179  if (!parameters.Portfolio.Positions.IsOnlyDefaultGroups)
180  {
181  return null;
182  }
183 
184  var symbol = parameters.PositionGroup.Single().Symbol;
185  var security = parameters.Portfolio.Securities[symbol];
186  return security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(
187  parameters.Portfolio, security, parameters.Orders.Single()
188  );
189  }
190  }
191 }