Lean  $LEAN_TAG$
SignalExportManager.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 
18 using System.Collections.Generic;
19 
21 {
22  /// <summary>
23  /// Class manager to send portfolio targets to different 3rd party API's
24  /// For example, it allows Collective2, CrunchDAO and Numerai signal export providers
25  /// </summary>
26  public class SignalExportManager
27  {
28  /// <summary>
29  /// List of signal export providers
30  /// </summary>
31  private List<ISignalExportTarget> _signalExports;
32 
33  /// <summary>
34  /// Algorithm being ran
35  /// </summary>
36  private readonly IAlgorithm _algorithm;
37 
38  /// <summary>
39  /// Flag to indicate if the user has tried to send signals with live mode off
40  /// </summary>
41  private bool _isLiveWarningModeLog;
42 
43  /// <summary>
44  /// SignalExportManager Constructor, obtains the entry information needed to send signals
45  /// and initializes the fields to be used
46  /// </summary>
47  /// <param name="algorithm">Algorithm being run</param>
48  public SignalExportManager(IAlgorithm algorithm)
49  {
50  _algorithm = algorithm;
51  _isLiveWarningModeLog = false;
52  }
53 
54  /// <summary>
55  /// Adds one or more new signal exports providers
56  /// </summary>
57  /// <param name="signalExports">One or more signal export provider</param>
58  public void AddSignalExportProviders(params ISignalExportTarget[] signalExports)
59  {
60  _signalExports ??= new List<ISignalExportTarget>();
61 
62  _signalExports.AddRange(signalExports);
63  }
64 
65  /// <summary>
66  /// Sets the portfolio targets from the algorihtm's Portfolio and sends them with the
67  /// algorithm being ran to the signal exports providers already set
68  /// </summary>
69  /// <returns>True if the target list could be obtained from the algorithm's Portfolio and they
70  /// were successfully sent to the signal export providers</returns>
72  {
73  if (!GetPortfolioTargets(out PortfolioTarget[] targets))
74  {
75  return false;
76  }
77  var result = SetTargetPortfolio(targets);
78  return result;
79  }
80 
81  /// <summary>
82  /// Obtains an array of portfolio targets from algorithm's Portfolio and returns them.
83  /// See <see cref="PortfolioTarget.Percent(IAlgorithm, Symbol, decimal, bool)"/> for more
84  /// information about how each symbol quantity was calculated
85  /// </summary>
86  /// <param name="targets">An array of portfolio targets from the algorithm's Portfolio</param>
87  /// <returns>True if TotalPortfolioValue was bigger than zero, false otherwise</returns>
88  protected bool GetPortfolioTargets(out PortfolioTarget[] targets)
89  {
90  var portfolio = _algorithm.Portfolio;
91  targets = new PortfolioTarget[portfolio.Values.Count];
92  var index = 0;
93 
94  var totalPortfolioValue = portfolio.TotalPortfolioValue;
95  if (totalPortfolioValue <= 0)
96  {
97  _algorithm.Error("Total portfolio value was less than or equal to 0");
98  return false;
99  }
100 
101  foreach (var holding in portfolio.Values)
102  {
103  var security = _algorithm.Securities[holding.Symbol];
104  var marginParameters = MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, holding.Quantity);
105  var adjustedPercent = security.BuyingPowerModel.GetMaintenanceMargin(marginParameters) / totalPortfolioValue;
106  // See PortfolioTarget.Percent:
107  // we normalize the target buying power by the leverage so we work in the land of margin
108  var holdingPercent = adjustedPercent * security.BuyingPowerModel.GetLeverage(security);
109 
110  // FreePortfolioValue is used for orders not to be rejected due to volatility when using SetHoldings and CalculateOrderQuantity
111  // Then, we need to substract its value from the TotalPortfolioValue and obtain again the holding percentage for our holding
112  var adjustedHoldingPercent = (holdingPercent * totalPortfolioValue) / _algorithm.Portfolio.TotalPortfolioValueLessFreeBuffer;
113  if (holding.Quantity < 0)
114  {
115  adjustedHoldingPercent *= -1;
116  }
117 
118  targets[index] = new PortfolioTarget(holding.Symbol, adjustedHoldingPercent);
119  ++index;
120  }
121 
122  return true;
123  }
124 
125  /// <summary>
126  /// Sets the portfolio targets with the given entries and sends them with the algorithm
127  /// being ran to the signal exports providers set, as long as the algorithm is in live mode
128  /// </summary>
129  /// <param name="portfolioTargets">One or more portfolio targets to be sent to the defined signal export providers</param>
130  /// <returns>True if the portfolio targets could be sent to the different signal export providers successfully, false otherwise</returns>
131  public bool SetTargetPortfolio(params PortfolioTarget[] portfolioTargets)
132  {
133  if (!_algorithm.LiveMode)
134  {
135  if (!_isLiveWarningModeLog)
136  {
137  _algorithm.Debug("Portfolio targets are only sent in live mode");
138  _isLiveWarningModeLog = true;
139  }
140 
141  return true;
142  }
143 
144  if (portfolioTargets == null || portfolioTargets.Length == 0)
145  {
146  _algorithm.Debug("No portfolio target given");
147  return false;
148  }
149 
150  var targets = new List<PortfolioTarget>(portfolioTargets);
151  var signalExportTargetParameters = new SignalExportTargetParameters
152  {
153  Targets = targets,
154  Algorithm = _algorithm
155  };
156 
157  var result = true;
158  foreach (var signalExport in _signalExports)
159  {
160  result &= signalExport.Send(signalExportTargetParameters);
161  }
162 
163  return result;
164  }
165  }
166 }