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