Lean  $LEAN_TAG$
MarginCallModelPythonWrapper.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 Python.Runtime;
17 using QuantConnect.Orders;
19 using System;
20 using System.Collections.Generic;
21 
22 namespace QuantConnect.Python
23 {
24  /// <summary>
25  /// Provides a margin call model that wraps a <see cref="PyObject"/> object that represents the model responsible for picking which orders should be executed during a margin call
26  /// </summary>
28  {
29  /// <summary>
30  /// Constructor for initialising the <see cref="MarginCallModelPythonWrapper"/> class with wrapped <see cref="PyObject"/> object
31  /// </summary>
32  /// <param name="model">Represents the model responsible for picking which orders should be executed during a margin call</param>
33  public MarginCallModelPythonWrapper(PyObject model)
34  : base(model)
35  {
36  }
37 
38  /// <summary>
39  /// Executes synchronous orders to bring the account within margin requirements.
40  /// </summary>
41  /// <param name="generatedMarginCallOrders">These are the margin call orders that were generated
42  /// by individual security margin models.</param>
43  /// <returns>The list of orders that were actually executed</returns>
44  public List<OrderTicket> ExecuteMarginCall(IEnumerable<SubmitOrderRequest> generatedMarginCallOrders)
45  {
46  using (Py.GIL())
47  {
48  var marginCalls = InvokeMethod(nameof(ExecuteMarginCall), generatedMarginCallOrders);
49 
50  // Since ExecuteMarginCall may return a python list
51  // Need to convert to C# list
52  var tickets = new List<OrderTicket>();
53  using var iterator = marginCalls.GetIterator();
54  foreach (PyObject pyObject in iterator)
55  {
56  OrderTicket ticket;
57  if (pyObject.TryConvert(out ticket))
58  {
59  tickets.Add(ticket);
60  }
61  pyObject.Dispose();
62  }
63  iterator.Dispose();
64  marginCalls.Dispose();
65  return tickets;
66  }
67  }
68 
69  /// <summary>
70  /// Scan the portfolio and the updated data for a potential margin call situation which may get the holdings below zero!
71  /// If there is a margin call, liquidate the portfolio immediately before the portfolio gets sub zero.
72  /// </summary>
73  /// <param name="issueMarginCallWarning">Set to true if a warning should be issued to the algorithm</param>
74  /// <returns>True for a margin call on the holdings.</returns>
75  public List<SubmitOrderRequest> GetMarginCallOrders(out bool issueMarginCallWarning)
76  {
77  using (Py.GIL())
78  {
79  var value = InvokeMethod(nameof(GetMarginCallOrders), false);
80 
81  // Since pythonnet does not support out parameters, the methods return
82  // a tuple where the out parameter comes after the other returned values
83  if (!PyTuple.IsTupleType(value))
84  {
85  throw new ArgumentException($@"{(Instance as dynamic).__class__.__name__}.GetMarginCallOrders(): {
86  Messages.MarginCallModelPythonWrapper.GetMarginCallOrdersMustReturnTuple}");
87  }
88 
89  // In this case, the first item holds the list of margin calls
90  // and the second the out parameter 'issueMarginCallWarning'
91  var marginCallOrders = value[0] as PyObject;
92  issueMarginCallWarning = (value[1] as PyObject).GetAndDispose<bool>();
93 
94  // Since GetMarginCallOrders may return a python list
95  // Need to convert to C# list
96  var requests = new List<SubmitOrderRequest>();
97  using var iterator = marginCallOrders.GetIterator();
98  foreach (PyObject pyObject in iterator)
99  {
100  SubmitOrderRequest request;
101  if (pyObject.TryConvert(out request))
102  {
103  requests.Add(request);
104  }
105  }
106  issueMarginCallWarning |= requests.Count > 0;
107  marginCallOrders.Dispose();
108  (value as PyObject).Dispose();
109  return requests;
110  }
111  }
112  }
113 }