Lean  $LEAN_TAG$
QCAlgorithm.Python.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 QuantConnect.Data;
20 using System;
22 using NodaTime;
23 using System.Collections.Generic;
24 using QuantConnect.Python;
25 using Python.Runtime;
28 using System.Linq;
29 using Newtonsoft.Json;
32 using QuantConnect.Util;
34 using QuantConnect.Orders;
36 using QuantConnect.Api;
37 
38 namespace QuantConnect.Algorithm
39 {
40  public partial class QCAlgorithm
41  {
42  private readonly Dictionary<IntPtr, PythonIndicator> _pythonIndicators = new Dictionary<IntPtr, PythonIndicator>();
43 
44  /// <summary>
45  /// PandasConverter for this Algorithm
46  /// </summary>
47  public virtual PandasConverter PandasConverter { get; private set; }
48 
49  /// <summary>
50  /// Sets pandas converter
51  /// </summary>
52  public void SetPandasConverter()
53  {
55  }
56 
57  /// <summary>
58  /// AddData a new user defined data source, requiring only the minimum config options.
59  /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time).
60  /// This method is meant for custom data types that require a ticker, but have no underlying Symbol.
61  /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data
62  /// </summary>
63  /// <param name="type">Data source type</param>
64  /// <param name="ticker">Key/Ticker for data</param>
65  /// <param name="resolution">Resolution of the data</param>
66  /// <returns>The new <see cref="Security"/></returns>
67  [DocumentationAttribute(AddingData)]
68  public Security AddData(PyObject type, string ticker, Resolution? resolution = null)
69  {
70  return AddData(type, ticker, resolution, null, false, 1m);
71  }
72 
73  /// <summary>
74  /// AddData a new user defined data source, requiring only the minimum config options.
75  /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time).
76  /// This adds a Symbol to the `Underlying` property in the custom data Symbol object.
77  /// Use this method when adding custom data with a ticker from the past, such as "AOL"
78  /// before it became "TWX", or if you need to filter using custom data and place trades on the
79  /// Symbol associated with the custom data.
80  /// </summary>
81  /// <param name="type">Data source type</param>
82  /// <param name="underlying">The underlying symbol for the custom data</param>
83  /// <param name="resolution">Resolution of the data</param>
84  /// <returns>The new <see cref="Security"/></returns>
85  /// <remarks>
86  /// We include three optional unused object parameters so that pythonnet chooses the intended method
87  /// correctly. Previously, calling the overloaded method that accepts a string would instead call this method.
88  /// Adding the three unused parameters makes it choose the correct method when using a string or Symbol. This is
89  /// due to pythonnet's method precedence, as viewable here: https://github.com/QuantConnect/pythonnet/blob/9e29755c54e6008cb016e3dd9d75fbd8cd19fcf7/src/runtime/methodbinder.cs#L215
90  /// </remarks>
91  [DocumentationAttribute(AddingData)]
92  public Security AddData(PyObject type, Symbol underlying, Resolution? resolution = null)
93  {
94  return AddData(type, underlying, resolution, null, false, 1m);
95  }
96 
97  /// <summary>
98  /// AddData a new user defined data source, requiring only the minimum config options.
99  /// This method is meant for custom data types that require a ticker, but have no underlying Symbol.
100  /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data
101  /// </summary>
102  /// <param name="type">Data source type</param>
103  /// <param name="ticker">Key/Ticker for data</param>
104  /// <param name="resolution">Resolution of the Data Required</param>
105  /// <param name="timeZone">Specifies the time zone of the raw data</param>
106  /// <param name="fillForward">When no data available on a tradebar, return the last data that was generated</param>
107  /// <param name="leverage">Custom leverage per security</param>
108  /// <returns>The new <see cref="Security"/></returns>
109  [DocumentationAttribute(AddingData)]
110  public Security AddData(PyObject type, string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m)
111  {
112  return AddData(type.CreateType(), ticker, resolution, timeZone, fillForward, leverage);
113  }
114 
115  /// <summary>
116  /// AddData a new user defined data source, requiring only the minimum config options.
117  /// This adds a Symbol to the `Underlying` property in the custom data Symbol object.
118  /// Use this method when adding custom data with a ticker from the past, such as "AOL"
119  /// before it became "TWX", or if you need to filter using custom data and place trades on the
120  /// Symbol associated with the custom data.
121  /// </summary>
122  /// <param name="type">Data source type</param>
123  /// <param name="underlying">The underlying symbol for the custom data</param>
124  /// <param name="resolution">Resolution of the Data Required</param>
125  /// <param name="timeZone">Specifies the time zone of the raw data</param>
126  /// <param name="fillForward">When no data available on a tradebar, return the last data that was generated</param>
127  /// <param name="leverage">Custom leverage per security</param>
128  /// <returns>The new <see cref="Security"/></returns>
129  /// <remarks>
130  /// We include three optional unused object parameters so that pythonnet chooses the intended method
131  /// correctly. Previously, calling the overloaded method that accepts a string would instead call this method.
132  /// Adding the three unused parameters makes it choose the correct method when using a string or Symbol. This is
133  /// due to pythonnet's method precedence, as viewable here: https://github.com/QuantConnect/pythonnet/blob/9e29755c54e6008cb016e3dd9d75fbd8cd19fcf7/src/runtime/methodbinder.cs#L215
134  /// </remarks>
135  [DocumentationAttribute(AddingData)]
136  public Security AddData(PyObject type, Symbol underlying, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m)
137  {
138  return AddData(type.CreateType(), underlying, resolution, timeZone, fillForward, leverage);
139  }
140 
141  /// <summary>
142  /// AddData a new user defined data source, requiring only the minimum config options.
143  /// This method is meant for custom data types that require a ticker, but have no underlying Symbol.
144  /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data
145  /// </summary>
146  /// <param name="dataType">Data source type</param>
147  /// <param name="ticker">Key/Ticker for data</param>
148  /// <param name="resolution">Resolution of the Data Required</param>
149  /// <param name="timeZone">Specifies the time zone of the raw data</param>
150  /// <param name="fillForward">When no data available on a tradebar, return the last data that was generated</param>
151  /// <param name="leverage">Custom leverage per security</param>
152  /// <returns>The new <see cref="Security"/></returns>
153  [DocumentationAttribute(AddingData)]
154  public Security AddData(Type dataType, string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m)
155  {
156  // NOTE: Invoking methods on BaseData w/out setting the symbol may provide unexpected behavior
157  var baseInstance = dataType.GetBaseDataInstance();
158  if (!baseInstance.RequiresMapping())
159  {
160  var symbol = new Symbol(
161  SecurityIdentifier.GenerateBase(dataType, ticker, Market.USA, baseInstance.RequiresMapping()),
162  ticker);
163  return AddDataImpl(dataType, symbol, resolution, timeZone, fillForward, leverage);
164  }
165  // If we need a mappable ticker and we can't find one in the SymbolCache, throw
166  Symbol underlying;
167  if (!SymbolCache.TryGetSymbol(ticker, out underlying))
168  {
169  throw new InvalidOperationException($"The custom data type {dataType.Name} requires mapping, but the provided ticker is not in the cache. " +
170  $"Please add this custom data type using a Symbol or perform this call after " +
171  $"a Security has been added using AddEquity, AddForex, AddCfd, AddCrypto, AddFuture, AddOption or AddSecurity. " +
172  $"An example use case can be found in CustomDataAddDataRegressionAlgorithm");
173  }
174 
175  return AddData(dataType, underlying, resolution, timeZone, fillForward, leverage);
176  }
177 
178  /// <summary>
179  /// AddData a new user defined data source, requiring only the minimum config options.
180  /// This adds a Symbol to the `Underlying` property in the custom data Symbol object.
181  /// Use this method when adding custom data with a ticker from the past, such as "AOL"
182  /// before it became "TWX", or if you need to filter using custom data and place trades on the
183  /// Symbol associated with the custom data.
184  /// </summary>
185  /// <param name="dataType">Data source type</param>
186  /// <param name="underlying"></param>
187  /// <param name="resolution">Resolution of the Data Required</param>
188  /// <param name="timeZone">Specifies the time zone of the raw data</param>
189  /// <param name="fillForward">When no data available on a tradebar, return the last data that was generated</param>
190  /// <param name="leverage">Custom leverage per security</param>
191  /// <returns>The new <see cref="Security"/></returns>
192  /// <remarks>
193  /// We include three optional unused object parameters so that pythonnet chooses the intended method
194  /// correctly. Previously, calling the overloaded method that accepts a string would instead call this method.
195  /// Adding the three unused parameters makes it choose the correct method when using a string or Symbol. This is
196  /// due to pythonnet's method precedence, as viewable here: https://github.com/QuantConnect/pythonnet/blob/9e29755c54e6008cb016e3dd9d75fbd8cd19fcf7/src/runtime/methodbinder.cs#L215
197  /// </remarks>
198  [DocumentationAttribute(AddingData)]
199  public Security AddData(Type dataType, Symbol underlying, Resolution? resolution = null, DateTimeZone timeZone = null, bool fillForward = false, decimal leverage = 1.0m)
200  {
201  var symbol = QuantConnect.Symbol.CreateBase(dataType, underlying, underlying.ID.Market);
202  return AddDataImpl(dataType, symbol, resolution, timeZone, fillForward, leverage);
203  }
204 
205  /// <summary>
206  /// AddData a new user defined data source including symbol properties and exchange hours,
207  /// all other vars are not required and will use defaults.
208  /// This overload reflects the C# equivalent for custom properties and market hours
209  /// </summary>
210  /// <param name="type">Data source type</param>
211  /// <param name="ticker">Key/Ticker for data</param>
212  /// <param name="properties">The properties of this new custom data</param>
213  /// <param name="exchangeHours">The Exchange hours of this symbol</param>
214  /// <param name="resolution">Resolution of the Data Required</param>
215  /// <param name="fillForward">When no data available on a tradebar, return the last data that was generated</param>
216  /// <param name="leverage">Custom leverage per security</param>
217  /// <returns>The new <see cref="Security"/></returns>
218  [DocumentationAttribute(AddingData)]
219  public Security AddData(PyObject type, string ticker, SymbolProperties properties, SecurityExchangeHours exchangeHours, Resolution? resolution = null, bool fillForward = false, decimal leverage = 1.0m)
220  {
221  // Get the right key for storage of base type symbols
222  var dataType = type.CreateType();
223  var key = SecurityIdentifier.GenerateBaseSymbol(dataType, ticker);
224 
225  // Add entries to our Symbol Properties DB and MarketHours DB
226  SetDatabaseEntries(key, properties, exchangeHours);
227 
228  // Then add the data
229  return AddData(dataType, ticker, resolution, null, fillForward, leverage);
230  }
231 
232  /// <summary>
233  /// Creates and adds a new Future Option contract to the algorithm.
234  /// </summary>
235  /// <param name="futureSymbol">The Future canonical symbol (i.e. Symbol returned from <see cref="AddFuture"/>)</param>
236  /// <param name="optionFilter">Filter to apply to option contracts loaded as part of the universe</param>
237  /// <returns>The new Option security, containing a Future as its underlying.</returns>
238  /// <exception cref="ArgumentException">The symbol provided is not canonical.</exception>
239  [DocumentationAttribute(AddingData)]
240  public void AddFutureOption(Symbol futureSymbol, PyObject optionFilter)
241  {
242  Func<OptionFilterUniverse, OptionFilterUniverse> optionFilterUniverse;
243  if (!optionFilter.TryConvertToDelegate(out optionFilterUniverse))
244  {
245  throw new ArgumentException("Option contract universe filter provided is not a function");
246  }
247 
248  AddFutureOption(futureSymbol, optionFilterUniverse);
249  }
250 
251  /// <summary>
252  /// Adds the provided final Symbol with/without underlying set to the algorithm.
253  /// This method is meant for custom data types that require a ticker, but have no underlying Symbol.
254  /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data
255  /// </summary>
256  /// <param name="dataType">Data source type</param>
257  /// <param name="symbol">Final symbol that includes underlying (if any)</param>
258  /// <param name="resolution">Resolution of the Data required</param>
259  /// <param name="timeZone">Specifies the time zone of the raw data</param>
260  /// <param name="fillForward">When no data available on a tradebar, return the last data that was generated</param>
261  /// <param name="leverage">Custom leverage per security</param>
262  /// <returns>The new <see cref="Security"/></returns>
263  private Security AddDataImpl(Type dataType, Symbol symbol, Resolution? resolution, DateTimeZone timeZone, bool fillForward, decimal leverage)
264  {
265  var alias = symbol.ID.Symbol;
266  SymbolCache.Set(alias, symbol);
267 
268  if (timeZone != null)
269  {
270  // user set time zone
271  MarketHoursDatabase.SetEntryAlwaysOpen(symbol.ID.Market, alias, SecurityType.Base, timeZone);
272  }
273 
274  //Add this new generic data as a tradeable security:
276  dataType,
277  symbol,
278  resolution,
279  fillForward,
280  isCustomData: true,
281  extendedMarketHours: true);
282  var security = Securities.CreateSecurity(symbol, config, leverage, addToSymbolCache: false);
283 
284  return AddToUserDefinedUniverse(security, new List<SubscriptionDataConfig> { config });
285  }
286 
287  /// <summary>
288  /// Creates a new universe and adds it to the algorithm. This is for coarse fundamental US Equity data and
289  /// will be executed on day changes in the NewYork time zone (<see cref="TimeZones.NewYork"/>)
290  /// </summary>
291  /// <param name="pyObject">Defines an initial coarse selection</param>
292  [DocumentationAttribute(Universes)]
293  public Universe AddUniverse(PyObject pyObject)
294  {
295  Func<IEnumerable<Fundamental>, object> fundamentalSelector;
296  Universe universe;
297 
298  if (pyObject.TryCreateType(out var type))
299  {
300  return AddUniverse(pyObject, null, null);
301  }
302  // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
303  else if (pyObject.TryConvert(out universe))
304  {
305  return AddUniverse(universe);
306  }
307  else if (pyObject.TryConvert(out universe, allowPythonDerivative: true))
308  {
309  return AddUniverse(new UniversePythonWrapper(pyObject));
310  }
311  else if (pyObject.TryConvertToDelegate(out fundamentalSelector))
312  {
313  return AddUniverse(FundamentalUniverse.USA(fundamentalSelector));
314  }
315  else
316  {
317  using (Py.GIL())
318  {
319  throw new ArgumentException($"QCAlgorithm.AddUniverse: {pyObject.Repr()} is not a valid argument.");
320  }
321  }
322  }
323 
324  /// <summary>
325  /// Creates a new universe and adds it to the algorithm. This is for coarse and fine fundamental US Equity data and
326  /// will be executed on day changes in the NewYork time zone (<see cref="TimeZones.NewYork"/>)
327  /// </summary>
328  /// <param name="pyObject">Defines an initial coarse selection or a universe</param>
329  /// <param name="pyfine">Defines a more detailed selection with access to more data</param>
330  [DocumentationAttribute(Universes)]
331  public Universe AddUniverse(PyObject pyObject, PyObject pyfine)
332  {
333  Func<IEnumerable<CoarseFundamental>, object> coarseFunc;
334  Func<IEnumerable<FineFundamental>, object> fineFunc;
335 
336  try
337  {
338  // this is due to a pythonNet limitation even if defining 'AddUniverse(IDateRule, PyObject)'
339  // it will chose this method instead
340  IDateRule dateRule;
341  using (Py.GIL())
342  {
343  dateRule = pyObject.As<IDateRule>();
344  }
345 
346  if (pyfine.TryConvertToDelegate(out coarseFunc))
347  {
348  return AddUniverse(dateRule, coarseFunc.ConvertToUniverseSelectionSymbolDelegate());
349  }
350  }
351  catch (InvalidCastException)
352  {
353  // pass
354  }
355 
356  if (pyObject.TryCreateType(out var type))
357  {
358  return AddUniverse(pyObject, null, pyfine);
359  }
360  else if (pyObject.TryConvert(out Universe universe) && pyfine.TryConvertToDelegate(out fineFunc))
361  {
362  return AddUniverse(universe, fineFunc.ConvertToUniverseSelectionSymbolDelegate());
363  }
364  else if (pyObject.TryConvertToDelegate(out coarseFunc) && pyfine.TryConvertToDelegate(out fineFunc))
365  {
366  return AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate(),
367  fineFunc.ConvertToUniverseSelectionSymbolDelegate());
368  }
369  else
370  {
371  using (Py.GIL())
372  {
373  throw new ArgumentException($"QCAlgorithm.AddUniverse: {pyObject.Repr()} or {pyfine.Repr()} is not a valid argument.");
374  }
375  }
376  }
377 
378  /// <summary>
379  /// Creates a new universe and adds it to the algorithm. This can be used to return a list of string
380  /// symbols retrieved from anywhere and will loads those symbols under the US Equity market.
381  /// </summary>
382  /// <param name="name">A unique name for this universe</param>
383  /// <param name="resolution">The resolution this universe should be triggered on</param>
384  /// <param name="pySelector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
385  [DocumentationAttribute(Universes)]
386  public Universe AddUniverse(string name, Resolution resolution, PyObject pySelector)
387  {
388  var selector = pySelector.ConvertToDelegate<Func<DateTime, object>>();
389  return AddUniverse(name, resolution, selector.ConvertToUniverseSelectionStringDelegate());
390  }
391 
392  /// <summary>
393  /// Creates a new universe and adds it to the algorithm. This can be used to return a list of string
394  /// symbols retrieved from anywhere and will loads those symbols under the US Equity market.
395  /// </summary>
396  /// <param name="name">A unique name for this universe</param>
397  /// <param name="pySelector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
398  [DocumentationAttribute(Universes)]
399  public Universe AddUniverse(string name, PyObject pySelector)
400  {
401  var selector = pySelector.ConvertToDelegate<Func<DateTime, object>>();
402  return AddUniverse(name, selector.ConvertToUniverseSelectionStringDelegate());
403  }
404 
405  /// <summary>
406  /// Creates a new user defined universe that will fire on the requested resolution during market hours.
407  /// </summary>
408  /// <param name="securityType">The security type of the universe</param>
409  /// <param name="name">A unique name for this universe</param>
410  /// <param name="resolution">The resolution this universe should be triggered on</param>
411  /// <param name="market">The market of the universe</param>
412  /// <param name="universeSettings">The subscription settings used for securities added from this universe</param>
413  /// <param name="pySelector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
414  [DocumentationAttribute(Universes)]
415  public Universe AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector)
416  {
417  var selector = pySelector.ConvertToDelegate<Func<DateTime, object>>();
418  return AddUniverse(securityType, name, resolution, market, universeSettings, selector.ConvertToUniverseSelectionStringDelegate());
419  }
420 
421  /// <summary>
422  /// Creates a new universe and adds it to the algorithm. This will use the default universe settings
423  /// specified via the <see cref="UniverseSettings"/> property. This universe will use the defaults
424  /// of SecurityType.Equity, Resolution.Daily, Market.USA, and UniverseSettings
425  /// </summary>
426  /// <param name="T">The data type</param>
427  /// <param name="name">A unique name for this universe</param>
428  /// <param name="selector">Function delegate that performs selection on the universe data</param>
429  [DocumentationAttribute(Universes)]
430  public Universe AddUniverse(PyObject T, string name, PyObject selector)
431  {
432  return AddUniverse(T.CreateType(), null, name, null, null, null, selector);
433  }
434 
435  /// <summary>
436  /// Creates a new universe and adds it to the algorithm. This will use the default universe settings
437  /// specified via the <see cref="UniverseSettings"/> property. This universe will use the defaults
438  /// of SecurityType.Equity, Market.USA and UniverseSettings
439  /// </summary>
440  /// <param name="T">The data type</param>
441  /// <param name="name">A unique name for this universe</param>
442  /// <param name="resolution">The expected resolution of the universe data</param>
443  /// <param name="selector">Function delegate that performs selection on the universe data</param>
444  [DocumentationAttribute(Universes)]
445  public Universe AddUniverse(PyObject T, string name, Resolution resolution, PyObject selector)
446  {
447  return AddUniverse(T.CreateType(), null, name, resolution, null, null, selector);
448  }
449 
450  /// <summary>
451  /// Creates a new universe and adds it to the algorithm. This will use the default universe settings
452  /// specified via the <see cref="UniverseSettings"/> property. This universe will use the defaults
453  /// of SecurityType.Equity, and Market.USA
454  /// </summary>
455  /// <param name="T">The data type</param>
456  /// <param name="name">A unique name for this universe</param>
457  /// <param name="resolution">The expected resolution of the universe data</param>
458  /// <param name="universeSettings">The settings used for securities added by this universe</param>
459  /// <param name="selector">Function delegate that performs selection on the universe data</param>
460  [DocumentationAttribute(Universes)]
461  public Universe AddUniverse(PyObject T, string name, Resolution resolution, UniverseSettings universeSettings, PyObject selector)
462  {
463  return AddUniverse(T.CreateType(), null, name, resolution, null, universeSettings, selector);
464  }
465 
466  /// <summary>
467  /// Creates a new universe and adds it to the algorithm. This will use the default universe settings
468  /// specified via the <see cref="UniverseSettings"/> property. This universe will use the defaults
469  /// of SecurityType.Equity, Resolution.Daily, and Market.USA
470  /// </summary>
471  /// <param name="T">The data type</param>
472  /// <param name="name">A unique name for this universe</param>
473  /// <param name="universeSettings">The settings used for securities added by this universe</param>
474  /// <param name="selector">Function delegate that performs selection on the universe data</param>
475  [DocumentationAttribute(Universes)]
476  public Universe AddUniverse(PyObject T, string name, UniverseSettings universeSettings, PyObject selector)
477  {
478  return AddUniverse(T.CreateType(), null, name, null, null, universeSettings, selector);
479  }
480 
481  /// <summary>
482  /// Creates a new universe and adds it to the algorithm. This will use the default universe settings
483  /// specified via the <see cref="UniverseSettings"/> property.
484  /// </summary>
485  /// <param name="T">The data type</param>
486  /// <param name="securityType">The security type the universe produces</param>
487  /// <param name="name">A unique name for this universe</param>
488  /// <param name="resolution">The expected resolution of the universe data</param>
489  /// <param name="market">The market for selected symbols</param>
490  /// <param name="selector">Function delegate that performs selection on the universe data</param>
491  [DocumentationAttribute(Universes)]
492  public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, PyObject selector)
493  {
494  return AddUniverse(T.CreateType(), securityType, name, resolution, market, null, selector);
495  }
496 
497  /// <summary>
498  /// Creates a new universe and adds it to the algorithm
499  /// </summary>
500  /// <param name="T">The data type</param>
501  /// <param name="securityType">The security type the universe produces</param>
502  /// <param name="name">A unique name for this universe</param>
503  /// <param name="resolution">The expected resolution of the universe data</param>
504  /// <param name="market">The market for selected symbols</param>
505  /// <param name="universeSettings">The subscription settings to use for newly created subscriptions</param>
506  /// <param name="selector">Function delegate that performs selection on the universe data</param>
507  [DocumentationAttribute(Universes)]
508  public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject selector)
509  {
510  return AddUniverse(T.CreateType(), securityType, name, resolution, market, universeSettings, selector);
511  }
512 
513  /// <summary>
514  /// Creates a new universe and adds it to the algorithm
515  /// </summary>
516  /// <param name="dataType">The data type</param>
517  /// <param name="securityType">The security type the universe produces</param>
518  /// <param name="name">A unique name for this universe</param>
519  /// <param name="resolution">The expected resolution of the universe data</param>
520  /// <param name="market">The market for selected symbols</param>
521  /// <param name="universeSettings">The subscription settings to use for newly created subscriptions</param>
522  /// <param name="pySelector">Function delegate that performs selection on the universe data</param>
523  [DocumentationAttribute(Universes)]
524  public Universe AddUniverse(Type dataType, SecurityType? securityType = null, string name = null, Resolution? resolution = null, string market = null, UniverseSettings universeSettings = null, PyObject pySelector = null)
525  {
526  if (market.IsNullOrEmpty())
527  {
528  market = Market.USA;
529  }
530  securityType ??= SecurityType.Equity;
531  Func<IEnumerable<BaseData>, IEnumerable<Symbol>> wrappedSelector = null;
532  if (pySelector != null)
533  {
534  var selector = pySelector.ConvertToDelegate<Func<IEnumerable<IBaseData>, object>>();
535  wrappedSelector = baseDatas =>
536  {
537  var result = selector(baseDatas);
538  if (ReferenceEquals(result, Universe.Unchanged))
539  {
540  return Universe.Unchanged;
541  }
542  return ((object[])result).Select(x => x is Symbol symbol ? symbol : QuantConnect.Symbol.Create((string)x, securityType.Value, market, baseDataType: dataType));
543  };
544  }
545  return AddUniverseSymbolSelector(dataType, name, resolution, market, universeSettings, wrappedSelector);
546  }
547 
548  /// <summary>
549  /// Creates a new universe selection model and adds it to the algorithm. This universe selection model will chain to the security
550  /// changes of a given <see cref="Universe"/> selection output and create a new <see cref="OptionChainUniverse"/> for each of them
551  /// </summary>
552  /// <param name="universe">The universe we want to chain an option universe selection model too</param>
553  /// <param name="optionFilter">The option filter universe to use</param>
554  [DocumentationAttribute(Universes)]
555  public void AddUniverseOptions(PyObject universe, PyObject optionFilter)
556  {
557  Func<OptionFilterUniverse, OptionFilterUniverse> convertedOptionChain;
558  Universe universeToChain;
559 
560  if (universe.TryConvert(out universeToChain) && optionFilter.TryConvertToDelegate(out convertedOptionChain))
561  {
562  AddUniverseOptions(universeToChain, convertedOptionChain);
563  }
564  else
565  {
566  using (Py.GIL())
567  {
568  throw new ArgumentException($"QCAlgorithm.AddChainedEquityOptionUniverseSelectionModel: {universe.Repr()} or {optionFilter.Repr()} is not a valid argument.");
569  }
570  }
571  }
572 
573  /// <summary>
574  /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
575  /// from the consolidator.
576  /// </summary>
577  /// <param name="symbol">The symbol to register against</param>
578  /// <param name="indicator">The indicator to receive data from the consolidator</param>
579  /// <param name="resolution">The resolution at which to send data to the indicator, null to use the same resolution as the subscription</param>
580  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
581  [DocumentationAttribute(Indicators)]
582  [DocumentationAttribute(ConsolidatingData)]
583  public void RegisterIndicator(Symbol symbol, PyObject indicator, Resolution? resolution = null, PyObject selector = null)
584  {
585  RegisterIndicator(symbol, indicator, ResolveConsolidator(symbol, resolution), selector);
586  }
587 
588  /// <summary>
589  /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
590  /// from the consolidator.
591  /// </summary>
592  /// <param name="symbol">The symbol to register against</param>
593  /// <param name="indicator">The indicator to receive data from the consolidator</param>
594  /// <param name="resolution">The resolution at which to send data to the indicator, null to use the same resolution as the subscription</param>
595  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
596  [DocumentationAttribute(Indicators)]
597  [DocumentationAttribute(ConsolidatingData)]
598  public void RegisterIndicator(Symbol symbol, PyObject indicator, TimeSpan? resolution = null, PyObject selector = null)
599  {
600  RegisterIndicator(symbol, indicator, ResolveConsolidator(symbol, resolution), selector);
601  }
602 
603  /// <summary>
604  /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
605  /// from the consolidator.
606  /// </summary>
607  /// <param name="symbol">The symbol to register against</param>
608  /// <param name="indicator">The indicator to receive data from the consolidator</param>
609  /// <param name="pyObject">The python object that it is trying to register with, could be consolidator or a timespan</param>
610  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
611  [DocumentationAttribute(Indicators)]
612  [DocumentationAttribute(ConsolidatingData)]
613  public void RegisterIndicator(Symbol symbol, PyObject indicator, PyObject pyObject, PyObject selector = null)
614  {
615  // First check if this is just a regular IDataConsolidator
616  IDataConsolidator dataConsolidator;
617  if (pyObject.TryConvert(out dataConsolidator))
618  {
619  RegisterIndicator(symbol, indicator, dataConsolidator, selector);
620  return;
621  }
622 
623  try
624  {
625  dataConsolidator = new DataConsolidatorPythonWrapper(pyObject);
626  }
627  catch
628  {
629  // Finally, since above didn't work, just try it as a timespan
630  // Issue #4668 Fix
631  using (Py.GIL())
632  {
633  try
634  {
635  // tryConvert does not work for timespan
636  TimeSpan? timeSpan = pyObject.As<TimeSpan>();
637  if (timeSpan != default(TimeSpan))
638  {
639  RegisterIndicator(symbol, indicator, timeSpan, selector);
640  return;
641  }
642  }
643  catch (Exception e)
644  {
645  throw new ArgumentException("Invalid third argument, should be either a valid consolidator or timedelta object. The following exception was thrown: ", e);
646  }
647  }
648  }
649 
650  RegisterIndicator(symbol, indicator, dataConsolidator, selector);
651  }
652 
653  /// <summary>
654  /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
655  /// from the consolidator.
656  /// </summary>
657  /// <param name="symbol">The symbol to register against</param>
658  /// <param name="indicator">The indicator to receive data from the consolidator</param>
659  /// <param name="consolidator">The consolidator to receive raw subscription data</param>
660  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
661  [DocumentationAttribute(Indicators)]
662  [DocumentationAttribute(ConsolidatingData)]
663  public void RegisterIndicator(Symbol symbol, PyObject indicator, IDataConsolidator consolidator, PyObject selector = null)
664  {
665  // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
666  var convertedIndicator = ConvertPythonIndicator(indicator);
667  switch (convertedIndicator)
668  {
669  case PythonIndicator pythonIndicator:
670  RegisterIndicator(symbol, pythonIndicator, consolidator,
671  selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
672  break;
673 
674  case IndicatorBase<IndicatorDataPoint> dataPointIndicator:
675  RegisterIndicator(symbol, dataPointIndicator, consolidator,
676  selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
677  break;
678 
679  case IndicatorBase<IBaseDataBar> baseDataBarIndicator:
680  RegisterIndicator(symbol, baseDataBarIndicator, consolidator,
681  selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
682  break;
683 
684  case IndicatorBase<TradeBar> tradeBarIndicator:
685  RegisterIndicator(symbol, tradeBarIndicator, consolidator,
686  selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
687  break;
688 
689  case IndicatorBase<IBaseData> baseDataIndicator:
690  RegisterIndicator(symbol, baseDataIndicator, consolidator,
691  selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
692  break;
693 
694  case IndicatorBase<BaseData> baseDataIndicator:
695  RegisterIndicator(symbol, baseDataIndicator, consolidator,
696  selector?.ConvertToDelegate<Func<IBaseData, BaseData>>());
697  break;
698 
699  default:
700  // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it
701  throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported.");
702  }
703  }
704 
705  /// <summary>
706  /// Warms up a given indicator with historical data
707  /// </summary>
708  /// <param name="symbol">The symbol whose indicator we want</param>
709  /// <param name="indicator">The indicator we want to warm up</param>
710  /// <param name="resolution">The resolution</param>
711  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
712  [DocumentationAttribute(Indicators)]
713  [DocumentationAttribute(HistoricalData)]
714  public void WarmUpIndicator(Symbol symbol, PyObject indicator, Resolution? resolution = null, PyObject selector = null)
715  {
716  // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
717  WarmUpIndicator([symbol], indicator, resolution, selector);
718  }
719 
720  /// <summary>
721  /// Warms up a given indicator with historical data
722  /// </summary>
723  /// <param name="symbol">The symbol or symbols to retrieve historical data for</param>
724  /// <param name="indicator">The indicator we want to warm up</param>
725  /// <param name="resolution">The resolution</param>
726  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
727  [DocumentationAttribute(Indicators)]
728  [DocumentationAttribute(HistoricalData)]
729  public void WarmUpIndicator(PyObject symbol, PyObject indicator, Resolution? resolution = null, PyObject selector = null)
730  {
731  // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
732  var symbols = symbol.ConvertToSymbolEnumerable();
733  WarmUpIndicator(symbols, indicator, resolution, selector);
734  }
735 
736  /// <summary>
737  /// Warms up a given indicator with historical data
738  /// </summary>
739  /// <param name="symbols">The symbols to retrieve historical data for</param>
740  /// <param name="indicator">The indicator we want to warm up</param>
741  /// <param name="resolution">The resolution</param>
742  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
743  private void WarmUpIndicator(IEnumerable<Symbol> symbols, PyObject indicator, Resolution? resolution = null, PyObject selector = null)
744  {
745  // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
746  var convertedIndicator = ConvertPythonIndicator(indicator);
747  switch (convertedIndicator)
748  {
749  case PythonIndicator pythonIndicator:
750  WarmUpIndicator(symbols, pythonIndicator, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
751  break;
752 
753  case IndicatorBase<IndicatorDataPoint> dataPointIndicator:
754  WarmUpIndicator(symbols, dataPointIndicator, resolution, selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
755  break;
756 
757  case IndicatorBase<IBaseDataBar> baseDataBarIndicator:
758  WarmUpIndicator(symbols, baseDataBarIndicator, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
759  break;
760 
761  case IndicatorBase<TradeBar> tradeBarIndicator:
762  WarmUpIndicator(symbols, tradeBarIndicator, resolution, selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
763  break;
764 
765  case IndicatorBase<IBaseData> baseDataIndicator:
766  WarmUpIndicator(symbols, baseDataIndicator, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
767  break;
768 
769  case IndicatorBase<BaseData> baseDataIndicator:
770  WarmUpIndicator(symbols, baseDataIndicator, resolution, selector?.ConvertToDelegate<Func<IBaseData, BaseData>>());
771  break;
772 
773  default:
774  // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it
775  throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported.");
776  }
777  }
778 
779  /// <summary>
780  /// Warms up a given indicator with historical data
781  /// </summary>
782  /// <param name="symbol">The symbol whose indicator we want</param>
783  /// <param name="indicator">The indicator we want to warm up</param>
784  /// <param name="period">The necessary period to warm up the indicator</param>
785  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
786  [DocumentationAttribute(Indicators)]
787  [DocumentationAttribute(HistoricalData)]
788  public void WarmUpIndicator(Symbol symbol, PyObject indicator, TimeSpan period, PyObject selector = null)
789  {
790  WarmUpIndicator([symbol], indicator, period, selector);
791  }
792 
793  /// <summary>
794  /// Warms up a given indicator with historical data
795  /// </summary>
796  /// <param name="symbol">The symbol or symbols to retrieve historical data for</param>
797  /// <param name="indicator">The indicator we want to warm up</param>
798  /// <param name="period">The necessary period to warm up the indicator</param>
799  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
800  [DocumentationAttribute(Indicators)]
801  [DocumentationAttribute(HistoricalData)]
802  public void WarmUpIndicator(PyObject symbol, PyObject indicator, TimeSpan period, PyObject selector = null)
803  {
804  var symbols = symbol.ConvertToSymbolEnumerable();
805  WarmUpIndicator(symbols, indicator, period, selector);
806  }
807 
808  /// <summary>
809  /// Warms up a given indicator with historical data
810  /// </summary>
811  /// <param name="symbols">The symbols to retrieve historical data for</param>
812  /// <param name="indicator">The indicator we want to warm up</param>
813  /// <param name="period">The necessary period to warm up the indicator</param>
814  /// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
815  private void WarmUpIndicator(IEnumerable<Symbol> symbols, PyObject indicator, TimeSpan period, PyObject selector = null)
816  {
817  var convertedIndicator = ConvertPythonIndicator(indicator);
818  switch (convertedIndicator)
819  {
820  case PythonIndicator pythonIndicator:
821  WarmUpIndicator(symbols, pythonIndicator, period, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
822  break;
823 
824  case IndicatorBase<IndicatorDataPoint> dataPointIndicator:
825  WarmUpIndicator(symbols, dataPointIndicator, period, selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
826  break;
827 
828  case IndicatorBase<IBaseDataBar> baseDataBarIndicator:
829  WarmUpIndicator(symbols, baseDataBarIndicator, period, selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
830  break;
831 
832  case IndicatorBase<TradeBar> tradeBarIndicator:
833  WarmUpIndicator(symbols, tradeBarIndicator, period, selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
834  break;
835 
836  case IndicatorBase<IBaseData> baseDataIndicator:
837  WarmUpIndicator(symbols, baseDataIndicator, period, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
838  break;
839 
840  case IndicatorBase<BaseData> baseDataIndicator:
841  WarmUpIndicator(symbols, baseDataIndicator, period, selector?.ConvertToDelegate<Func<IBaseData, BaseData>>());
842  break;
843 
844  default:
845  // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it
846  throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported.");
847  }
848  }
849 
850  /// <summary>
851  /// Plot a chart using string series name, with value.
852  /// </summary>
853  /// <param name="series">Name of the plot series</param>
854  /// <param name="pyObject">PyObject with the value to plot</param>
855  /// <seealso cref="Plot(string,decimal)"/>
856  [DocumentationAttribute(Charting)]
857  public void Plot(string series, PyObject pyObject)
858  {
859  using (Py.GIL())
860  {
861  if (pyObject.TryConvert(out IndicatorBase indicator, true))
862  {
863  Plot(series, indicator);
864  }
865  else
866  {
867  try
868  {
869  var value = (((dynamic)pyObject).Value as PyObject).GetAndDispose<decimal>();
870  Plot(series, value);
871  }
872  catch
873  {
874  var pythonType = pyObject.GetPythonType().Repr();
875  throw new ArgumentException($"QCAlgorithm.Plot(): The last argument should be a QuantConnect Indicator object, {pythonType} was provided.");
876  }
877  }
878  }
879  }
880 
881  /// <summary>
882  /// Plots the value of each indicator on the chart
883  /// </summary>
884  /// <param name="chart">The chart's name</param>
885  /// <param name="first">The first indicator to plot</param>
886  /// <param name="second">The second indicator to plot</param>
887  /// <param name="third">The third indicator to plot</param>
888  /// <param name="fourth">The fourth indicator to plot</param>
889  /// <seealso cref="Plot(string,string,decimal)"/>
890  [DocumentationAttribute(Charting)]
891  public void Plot(string chart, Indicator first, Indicator second = null, Indicator third = null, Indicator fourth = null)
892  {
893  Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
894  }
895 
896  /// <summary>
897  /// Plots the value of each indicator on the chart
898  /// </summary>
899  /// <param name="chart">The chart's name</param>
900  /// <param name="first">The first indicator to plot</param>
901  /// <param name="second">The second indicator to plot</param>
902  /// <param name="third">The third indicator to plot</param>
903  /// <param name="fourth">The fourth indicator to plot</param>
904  /// <seealso cref="Plot(string,string,decimal)"/>
905  [DocumentationAttribute(Charting)]
906  public void Plot(string chart, BarIndicator first, BarIndicator second = null, BarIndicator third = null, BarIndicator fourth = null)
907  {
908  Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
909  }
910 
911  /// <summary>
912  /// Plots the value of each indicator on the chart
913  /// </summary>
914  /// <param name="chart">The chart's name</param>
915  /// <param name="first">The first indicator to plot</param>
916  /// <param name="second">The second indicator to plot</param>
917  /// <param name="third">The third indicator to plot</param>
918  /// <param name="fourth">The fourth indicator to plot</param>
919  /// <seealso cref="Plot(string,string,decimal)"/>
920  [DocumentationAttribute(Charting)]
921  public void Plot(string chart, TradeBarIndicator first, TradeBarIndicator second = null, TradeBarIndicator third = null, TradeBarIndicator fourth = null)
922  {
923  Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
924  }
925 
926  /// <summary>
927  /// Automatically plots each indicator when a new value is available
928  /// </summary>
929  [DocumentationAttribute(Charting)]
930  [DocumentationAttribute(Indicators)]
931  public void PlotIndicator(string chart, PyObject first, PyObject second = null, PyObject third = null, PyObject fourth = null)
932  {
933  var array = GetIndicatorArray(first, second, third, fourth);
934  PlotIndicator(chart, array[0], array[1], array[2], array[3]);
935  }
936 
937  /// <summary>
938  /// Automatically plots each indicator when a new value is available
939  /// </summary>
940  [DocumentationAttribute(Charting)]
941  [DocumentationAttribute(Indicators)]
942  public void PlotIndicator(string chart, bool waitForReady, PyObject first, PyObject second = null, PyObject third = null, PyObject fourth = null)
943  {
944  var array = GetIndicatorArray(first, second, third, fourth);
945  PlotIndicator(chart, waitForReady, array[0], array[1], array[2], array[3]);
946  }
947 
948  /// <summary>
949  /// Creates a new FilteredIdentity indicator for the symbol The indicator will be automatically
950  /// updated on the symbol's subscription resolution
951  /// </summary>
952  /// <param name="symbol">The symbol whose values we want as an indicator</param>
953  /// <param name="selector">Selects a value from the BaseData, if null defaults to the .Value property (x => x.Value)</param>
954  /// <param name="filter">Filters the IBaseData send into the indicator, if null defaults to true (x => true) which means no filter</param>
955  /// <param name="fieldName">The name of the field being selected</param>
956  /// <returns>A new FilteredIdentity indicator for the specified symbol and selector</returns>
957  [DocumentationAttribute(Indicators)]
958  public FilteredIdentity FilteredIdentity(Symbol symbol, PyObject selector = null, PyObject filter = null, string fieldName = null)
959  {
960  var resolution = GetSubscription(symbol).Resolution;
961  return FilteredIdentity(symbol, resolution, selector, filter, fieldName);
962  }
963 
964  /// <summary>
965  /// Creates a new FilteredIdentity indicator for the symbol The indicator will be automatically
966  /// updated on the symbol's subscription resolution
967  /// </summary>
968  /// <param name="symbol">The symbol whose values we want as an indicator</param>
969  /// <param name="resolution">The desired resolution of the data</param>
970  /// <param name="selector">Selects a value from the BaseData, if null defaults to the .Value property (x => x.Value)</param>
971  /// <param name="filter">Filters the IBaseData send into the indicator, if null defaults to true (x => true) which means no filter</param>
972  /// <param name="fieldName">The name of the field being selected</param>
973  /// <returns>A new FilteredIdentity indicator for the specified symbol and selector</returns>
974  [DocumentationAttribute(Indicators)]
975  public FilteredIdentity FilteredIdentity(Symbol symbol, Resolution resolution, PyObject selector = null, PyObject filter = null, string fieldName = null)
976  {
977  var name = CreateIndicatorName(symbol, fieldName ?? "close", resolution);
978  var pyselector = PythonUtil.ToFunc<IBaseData, IBaseDataBar>(selector);
979  var pyfilter = PythonUtil.ToFunc<IBaseData, bool>(filter);
980  var filteredIdentity = new FilteredIdentity(name, pyfilter);
981  RegisterIndicator(symbol, filteredIdentity, resolution, pyselector);
982  return filteredIdentity;
983  }
984 
985  /// <summary>
986  /// Creates a new FilteredIdentity indicator for the symbol The indicator will be automatically
987  /// updated on the symbol's subscription resolution
988  /// </summary>
989  /// <param name="symbol">The symbol whose values we want as an indicator</param>
990  /// <param name="resolution">The desired resolution of the data</param>
991  /// <param name="selector">Selects a value from the BaseData, if null defaults to the .Value property (x => x.Value)</param>
992  /// <param name="filter">Filters the IBaseData send into the indicator, if null defaults to true (x => true) which means no filter</param>
993  /// <param name="fieldName">The name of the field being selected</param>
994  /// <returns>A new FilteredIdentity indicator for the specified symbol and selector</returns>
995  [DocumentationAttribute(Indicators)]
996  public FilteredIdentity FilteredIdentity(Symbol symbol, TimeSpan resolution, PyObject selector = null, PyObject filter = null, string fieldName = null)
997  {
998  var name = $"{symbol}({fieldName ?? "close"}_{resolution.ToStringInvariant(null)})";
999  var pyselector = PythonUtil.ToFunc<IBaseData, IBaseDataBar>(selector);
1000  var pyfilter = PythonUtil.ToFunc<IBaseData, bool>(filter);
1001  var filteredIdentity = new FilteredIdentity(name, pyfilter);
1002  RegisterIndicator(symbol, filteredIdentity, ResolveConsolidator(symbol, resolution), pyselector);
1003  return filteredIdentity;
1004  }
1005 
1006  /// <summary>
1007  /// Gets the historical data for the specified symbol. The exact number of bars will be returned.
1008  /// The symbol must exist in the Securities collection.
1009  /// </summary>
1010  /// <param name="tickers">The symbols to retrieve historical data for</param>
1011  /// <param name="periods">The number of bars to request</param>
1012  /// <param name="resolution">The resolution to request</param>
1013  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1014  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1015  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1016  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1017  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1018  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1019  /// <param name="flatten">Whether to flatten the resulting data frame.
1020  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1021  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1022  /// and each property of the constituent will be a column in the data frame.</param>
1023  /// <returns>A python dictionary with pandas DataFrame containing the requested historical data</returns>
1024  [DocumentationAttribute(HistoricalData)]
1025  public PyObject History(PyObject tickers, int periods, Resolution? resolution = null, bool? fillForward = null,
1026  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1027  int? contractDepthOffset = null, bool flatten = false)
1028  {
1029  if (tickers.TryConvert<Universe>(out var universe))
1030  {
1031  resolution ??= universe.Configuration.Resolution;
1032  var requests = CreateBarCountHistoryRequests(new[] { universe.Symbol }, universe.DataType, periods, resolution, fillForward, extendedMarketHours,
1033  dataMappingMode, dataNormalizationMode, contractDepthOffset);
1034  // we pass in 'BaseDataCollection' type so we clean up the data frame if we can
1035  return GetDataFrame(History(requests.Where(x => x != null)), flatten, typeof(BaseDataCollection));
1036  }
1037  if (tickers.TryCreateType(out var type))
1038  {
1039  var requests = CreateBarCountHistoryRequests(Securities.Keys, type, periods, resolution, fillForward, extendedMarketHours,
1040  dataMappingMode, dataNormalizationMode, contractDepthOffset);
1041  return GetDataFrame(History(requests.Where(x => x != null)), flatten, type);
1042  }
1043 
1044  var symbols = tickers.ConvertToSymbolEnumerable().ToArray();
1045  var dataType = Extensions.GetCustomDataTypeFromSymbols(symbols);
1046 
1047  return GetDataFrame(
1048  History(symbols, periods, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset),
1049  flatten,
1050  dataType);
1051  }
1052 
1053  /// <summary>
1054  /// Gets the historical data for the specified symbols over the requested span.
1055  /// The symbols must exist in the Securities collection.
1056  /// </summary>
1057  /// <param name="tickers">The symbols to retrieve historical data for</param>
1058  /// <param name="span">The span over which to retrieve recent historical data</param>
1059  /// <param name="resolution">The resolution to request</param>
1060  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1061  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1062  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1063  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1064  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1065  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1066  /// <param name="flatten">Whether to flatten the resulting data frame.
1067  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1068  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1069  /// and each property of the constituent will be a column in the data frame.</param>
1070  /// <returns>A python dictionary with pandas DataFrame containing the requested historical data</returns>
1071  [DocumentationAttribute(HistoricalData)]
1072  public PyObject History(PyObject tickers, TimeSpan span, Resolution? resolution = null, bool? fillForward = null,
1073  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1074  int? contractDepthOffset = null, bool flatten = false)
1075  {
1076  return History(tickers, Time - span, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode,
1077  contractDepthOffset, flatten);
1078  }
1079 
1080  /// <summary>
1081  /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection.
1082  /// </summary>
1083  /// <param name="tickers">The symbols to retrieve historical data for</param>
1084  /// <param name="start">The start time in the algorithm's time zone</param>
1085  /// <param name="end">The end time in the algorithm's time zone</param>
1086  /// <param name="resolution">The resolution to request</param>
1087  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1088  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1089  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1090  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1091  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1092  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1093  /// <param name="flatten">Whether to flatten the resulting data frame.
1094  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1095  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1096  /// and each property of the constituent will be a column in the data frame.</param>
1097  /// <returns>A python dictionary with a pandas DataFrame containing the requested historical data</returns>
1098  [DocumentationAttribute(HistoricalData)]
1099  public PyObject History(PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null, bool? fillForward = null,
1100  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1101  int? contractDepthOffset = null, bool flatten = false)
1102  {
1103  if (tickers.TryConvert<Universe>(out var universe))
1104  {
1105  resolution ??= universe.Configuration.Resolution;
1106  var requests = CreateDateRangeHistoryRequests(new[] { universe.Symbol }, universe.DataType, start, end, resolution, fillForward, extendedMarketHours,
1107  dataMappingMode, dataNormalizationMode, contractDepthOffset);
1108  // we pass in 'BaseDataCollection' type so we clean up the data frame if we can
1109  return GetDataFrame(History(requests.Where(x => x != null)), flatten, typeof(BaseDataCollection));
1110  }
1111  if (tickers.TryCreateType(out var type))
1112  {
1113  var requests = CreateDateRangeHistoryRequests(Securities.Keys, type, start, end, resolution, fillForward, extendedMarketHours,
1114  dataMappingMode, dataNormalizationMode, contractDepthOffset);
1115  return GetDataFrame(History(requests.Where(x => x != null)), flatten, type);
1116  }
1117 
1118  var symbols = tickers.ConvertToSymbolEnumerable().ToArray();
1119  var dataType = Extensions.GetCustomDataTypeFromSymbols(symbols);
1120 
1121  return GetDataFrame(
1122  History(symbols, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset),
1123  flatten,
1124  dataType);
1125  }
1126 
1127  /// <summary>
1128  /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection.
1129  /// </summary>
1130  /// <param name="type">The data type of the symbols</param>
1131  /// <param name="tickers">The symbols to retrieve historical data for</param>
1132  /// <param name="start">The start time in the algorithm's time zone</param>
1133  /// <param name="end">The end time in the algorithm's time zone</param>
1134  /// <param name="resolution">The resolution to request</param>
1135  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1136  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1137  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1138  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1139  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1140  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1141  /// <param name="flatten">Whether to flatten the resulting data frame.
1142  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1143  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1144  /// and each property of the constituent will be a column in the data frame.</param>
1145  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1146  [DocumentationAttribute(HistoricalData)]
1147  public PyObject History(PyObject type, PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null,
1148  bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null,
1149  DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false)
1150  {
1151  var symbols = tickers.ConvertToSymbolEnumerable().ToArray();
1152  var requestedType = type.CreateType();
1153  var requests = CreateDateRangeHistoryRequests(symbols, requestedType, start, end, resolution, fillForward, extendedMarketHours,
1154  dataMappingMode, dataNormalizationMode, contractDepthOffset);
1155  return GetDataFrame(History(requests.Where(x => x != null)), flatten, requestedType);
1156  }
1157 
1158  /// <summary>
1159  /// Gets the historical data for the specified symbols. The exact number of bars will be returned for
1160  /// each symbol. This may result in some data start earlier/later than others due to when various
1161  /// exchanges are open. The symbols must exist in the Securities collection.
1162  /// </summary>
1163  /// <param name="type">The data type of the symbols</param>
1164  /// <param name="tickers">The symbols to retrieve historical data for</param>
1165  /// <param name="periods">The number of bars to request</param>
1166  /// <param name="resolution">The resolution to request</param>
1167  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1168  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1169  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1170  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1171  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1172  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1173  /// <param name="flatten">Whether to flatten the resulting data frame.
1174  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1175  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1176  /// and each property of the constituent will be a column in the data frame.</param>
1177  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1178  [DocumentationAttribute(HistoricalData)]
1179  public PyObject History(PyObject type, PyObject tickers, int periods, Resolution? resolution = null, bool? fillForward = null,
1180  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1181  int? contractDepthOffset = null, bool flatten = false)
1182  {
1183  var symbols = tickers.ConvertToSymbolEnumerable().ToArray();
1184  var requestedType = type.CreateType();
1185  CheckPeriodBasedHistoryRequestResolution(symbols, resolution, requestedType);
1186 
1187  var requests = CreateBarCountHistoryRequests(symbols, requestedType, periods, resolution, fillForward, extendedMarketHours,
1188  dataMappingMode, dataNormalizationMode, contractDepthOffset);
1189 
1190  return GetDataFrame(History(requests.Where(x => x != null)), flatten, requestedType);
1191  }
1192 
1193  /// <summary>
1194  /// Gets the historical data for the specified symbols over the requested span.
1195  /// The symbols must exist in the Securities collection.
1196  /// </summary>
1197  /// <param name="type">The data type of the symbols</param>
1198  /// <param name="tickers">The symbols to retrieve historical data for</param>
1199  /// <param name="span">The span over which to retrieve recent historical data</param>
1200  /// <param name="resolution">The resolution to request</param>
1201  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1202  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1203  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1204  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1205  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1206  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1207  /// <param name="flatten">Whether to flatten the resulting data frame.
1208  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1209  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1210  /// and each property of the constituent will be a column in the data frame.</param>
1211  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1212  [DocumentationAttribute(HistoricalData)]
1213  public PyObject History(PyObject type, PyObject tickers, TimeSpan span, Resolution? resolution = null, bool? fillForward = null,
1214  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1215  int? contractDepthOffset = null, bool flatten = false)
1216  {
1217  return History(type, tickers, Time - span, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode,
1218  contractDepthOffset, flatten);
1219  }
1220 
1221  /// <summary>
1222  /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection.
1223  /// </summary>
1224  /// <param name="type">The data type of the symbols</param>
1225  /// <param name="symbol">The symbol to retrieve historical data for</param>
1226  /// <param name="start">The start time in the algorithm's time zone</param>
1227  /// <param name="end">The end time in the algorithm's time zone</param>
1228  /// <param name="resolution">The resolution to request</param>
1229  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1230  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1231  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1232  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1233  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1234  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1235  /// <param name="flatten">Whether to flatten the resulting data frame.
1236  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1237  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1238  /// and each property of the constituent will be a column in the data frame.</param>
1239  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1240  [DocumentationAttribute(HistoricalData)]
1241  public PyObject History(PyObject type, Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null, bool? fillForward = null,
1242  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1243  int? contractDepthOffset = null, bool flatten = false)
1244  {
1245  return History(type.CreateType(), symbol, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode,
1246  dataNormalizationMode, contractDepthOffset, flatten);
1247  }
1248 
1249  /// <summary>
1250  /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection.
1251  /// </summary>
1252  /// <param name="type">The data type of the symbols</param>
1253  /// <param name="symbol">The symbol to retrieve historical data for</param>
1254  /// <param name="start">The start time in the algorithm's time zone</param>
1255  /// <param name="end">The end time in the algorithm's time zone</param>
1256  /// <param name="resolution">The resolution to request</param>
1257  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1258  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1259  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1260  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1261  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1262  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1263  /// <param name="flatten">Whether to flatten the resulting data frame.
1264  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1265  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1266  /// and each property of the constituent will be a column in the data frame.</param>
1267  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1268  private PyObject History(Type type, Symbol symbol, DateTime start, DateTime end, Resolution? resolution, bool? fillForward,
1269  bool? extendedMarketHours, DataMappingMode? dataMappingMode, DataNormalizationMode? dataNormalizationMode,
1270  int? contractDepthOffset, bool flatten)
1271  {
1272  var requests = CreateDateRangeHistoryRequests(new[] { symbol }, type, start, end, resolution, fillForward,
1273  extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset);
1274  if (requests.IsNullOrEmpty())
1275  {
1276  throw new ArgumentException($"No history data could be fetched. " +
1277  $"This could be due to the specified security not being of the requested type. Symbol: {symbol} Requested Type: {type.Name}");
1278  }
1279 
1280  return GetDataFrame(History(requests), flatten, type);
1281  }
1282 
1283  /// <summary>
1284  /// Gets the historical data for the specified symbols. The exact number of bars will be returned for
1285  /// each symbol. This may result in some data start earlier/later than others due to when various
1286  /// exchanges are open. The symbols must exist in the Securities collection.
1287  /// </summary>
1288  /// <param name="type">The data type of the symbols</param>
1289  /// <param name="symbol">The symbol to retrieve historical data for</param>
1290  /// <param name="periods">The number of bars to request</param>
1291  /// <param name="resolution">The resolution to request</param>
1292  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1293  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1294  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1295  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1296  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1297  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1298  /// <param name="flatten">Whether to flatten the resulting data frame.
1299  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1300  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1301  /// and each property of the constituent will be a column in the data frame.</param>
1302  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1303  [DocumentationAttribute(HistoricalData)]
1304  public PyObject History(PyObject type, Symbol symbol, int periods, Resolution? resolution = null, bool? fillForward = null,
1305  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1306  int? contractDepthOffset = null, bool flatten = false)
1307  {
1308  var managedType = type.CreateType();
1309  resolution = GetResolution(symbol, resolution, managedType);
1310  CheckPeriodBasedHistoryRequestResolution(new[] { symbol }, resolution, managedType);
1311 
1312  var marketHours = GetMarketHours(symbol, managedType);
1313  var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, resolution.Value, marketHours.ExchangeHours,
1314  marketHours.DataTimeZone, managedType, extendedMarketHours);
1315  return History(managedType, symbol, start, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode,
1316  contractDepthOffset, flatten);
1317  }
1318 
1319  /// <summary>
1320  /// Gets the historical data for the specified symbols over the requested span.
1321  /// The symbols must exist in the Securities collection.
1322  /// </summary>
1323  /// <param name="type">The data type of the symbols</param>
1324  /// <param name="symbol">The symbol to retrieve historical data for</param>
1325  /// <param name="span">The span over which to retrieve recent historical data</param>
1326  /// <param name="resolution">The resolution to request</param>
1327  /// <param name="fillForward">True to fill forward missing data, false otherwise</param>
1328  /// <param name="extendedMarketHours">True to include extended market hours data, false otherwise</param>
1329  /// <param name="dataMappingMode">The contract mapping mode to use for the security history request</param>
1330  /// <param name="dataNormalizationMode">The price scaling mode to use for the securities history</param>
1331  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
1332  /// For example, 0 will use the front month, 1 will use the back month contract</param>
1333  /// <param name="flatten">Whether to flatten the resulting data frame.
1334  /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
1335  /// If flatten is true, the resulting data frame will contain one row per universe constituent,
1336  /// and each property of the constituent will be a column in the data frame.</param>
1337  /// <returns>pandas.DataFrame containing the requested historical data</returns>
1338  [DocumentationAttribute(HistoricalData)]
1339  public PyObject History(PyObject type, Symbol symbol, TimeSpan span, Resolution? resolution = null, bool? fillForward = null,
1340  bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null,
1341  int? contractDepthOffset = null, bool flatten = false)
1342  {
1343  return History(type, symbol, Time - span, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode,
1344  contractDepthOffset, flatten);
1345  }
1346 
1347  /// <summary>
1348  /// Sets the specified function as the benchmark, this function provides the value of
1349  /// the benchmark at each date/time requested
1350  /// </summary>
1351  /// <param name="benchmark">The benchmark producing function</param>
1352  [DocumentationAttribute(TradingAndOrders)]
1353  [DocumentationAttribute(SecuritiesAndPortfolio)]
1354  [DocumentationAttribute(Indicators)]
1355  public void SetBenchmark(PyObject benchmark)
1356  {
1357  using (Py.GIL())
1358  {
1359  var pyBenchmark = PythonUtil.ToFunc<DateTime, decimal>(benchmark);
1360  if (pyBenchmark != null)
1361  {
1362  SetBenchmark(pyBenchmark);
1363  return;
1364  }
1365  SetBenchmark((Symbol)benchmark.AsManagedObject(typeof(Symbol)));
1366  }
1367  }
1368 
1369  /// <summary>
1370  /// Sets the brokerage to emulate in backtesting or paper trading.
1371  /// This can be used to set a custom brokerage model.
1372  /// </summary>
1373  /// <param name="model">The brokerage model to use</param>
1374  [DocumentationAttribute(Modeling)]
1375  public void SetBrokerageModel(PyObject model)
1376  {
1377  IBrokerageModel brokerageModel;
1378  if (!model.TryConvert(out brokerageModel))
1379  {
1380  brokerageModel = new BrokerageModelPythonWrapper(model);
1381  }
1382 
1383  SetBrokerageModel(brokerageModel);
1384  }
1385 
1386  /// <summary>
1387  /// Sets the implementation used to handle messages from the brokerage.
1388  /// The default implementation will forward messages to debug or error
1389  /// and when a <see cref="BrokerageMessageType.Error"/> occurs, the algorithm
1390  /// is stopped.
1391  /// </summary>
1392  /// <param name="handler">The message handler to use</param>
1393  [DocumentationAttribute(Modeling)]
1394  [DocumentationAttribute(Logging)]
1395  public void SetBrokerageMessageHandler(PyObject handler)
1396  {
1397  if (!handler.TryConvert(out IBrokerageMessageHandler brokerageMessageHandler))
1398  {
1399  brokerageMessageHandler = new BrokerageMessageHandlerPythonWrapper(handler);
1400  }
1401 
1402  SetBrokerageMessageHandler(brokerageMessageHandler);
1403  }
1404 
1405  /// <summary>
1406  /// Sets the risk free interest rate model to be used in the algorithm
1407  /// </summary>
1408  /// <param name="model">The risk free interest rate model to use</param>
1409  [DocumentationAttribute(Modeling)]
1410  public void SetRiskFreeInterestRateModel(PyObject model)
1411  {
1413  }
1414 
1415  /// <summary>
1416  /// Sets the security initializer function, used to initialize/configure securities after creation
1417  /// </summary>
1418  /// <param name="securityInitializer">The security initializer function or class</param>
1419  [DocumentationAttribute(AddingData)]
1420  [DocumentationAttribute(Modeling)]
1421  public void SetSecurityInitializer(PyObject securityInitializer)
1422  {
1423  var securityInitializer1 = PythonUtil.ToAction<Security>(securityInitializer);
1424  if (securityInitializer1 != null)
1425  {
1426  SetSecurityInitializer(securityInitializer1);
1427  return;
1428  }
1429 
1430  SetSecurityInitializer(new SecurityInitializerPythonWrapper(securityInitializer));
1431  }
1432 
1433  /// <summary>
1434  /// Downloads the requested resource as a <see cref="string"/>.
1435  /// The resource to download is specified as a <see cref="string"/> containing the URI.
1436  /// </summary>
1437  /// <param name="address">A string containing the URI to download</param>
1438  /// <param name="headers">Defines header values to add to the request</param>
1439  /// <returns>The requested resource as a <see cref="string"/></returns>
1440  [DocumentationAttribute(AddingData)]
1441  [DocumentationAttribute(MachineLearning)]
1442  public string Download(string address, PyObject headers) => Download(address, headers, null, null);
1443 
1444  /// <summary>
1445  /// Downloads the requested resource as a <see cref="string"/>.
1446  /// The resource to download is specified as a <see cref="string"/> containing the URI.
1447  /// </summary>
1448  /// <param name="address">A string containing the URI to download</param>
1449  /// <param name="headers">Defines header values to add to the request</param>
1450  /// <param name="userName">The user name associated with the credentials</param>
1451  /// <param name="password">The password for the user name associated with the credentials</param>
1452  /// <returns>The requested resource as a <see cref="string"/></returns>
1453  [DocumentationAttribute(AddingData)]
1454  [DocumentationAttribute(MachineLearning)]
1455  public string Download(string address, PyObject headers, string userName, string password)
1456  {
1457  var dict = new Dictionary<string, string>();
1458 
1459  if (headers != null)
1460  {
1461  using (Py.GIL())
1462  {
1463  // In python algorithms, headers must be a python dictionary
1464  // In order to convert it into a C# Dictionary
1465  if (PyDict.IsDictType(headers))
1466  {
1467  using var iterator = headers.GetIterator();
1468  foreach (PyObject pyKey in iterator)
1469  {
1470  var key = (string)pyKey.AsManagedObject(typeof(string));
1471  var value = (string)headers.GetItem(pyKey).AsManagedObject(typeof(string));
1472  dict.Add(key, value);
1473  }
1474  }
1475  else
1476  {
1477  throw new ArgumentException($"QCAlgorithm.Fetch(): Invalid argument. {headers.Repr()} is not a dict");
1478  }
1479  }
1480  }
1481  return Download(address, dict, userName, password);
1482  }
1483 
1484  /// <summary>
1485  /// Send a debug message to the web console:
1486  /// </summary>
1487  /// <param name="message">Message to send to debug console</param>
1488  /// <seealso cref="Log(PyObject)"/>
1489  /// <seealso cref="Error(PyObject)"/>
1490  [DocumentationAttribute(Logging)]
1491  public void Debug(PyObject message)
1492  {
1493  Debug(message.ToSafeString());
1494  }
1495 
1496  /// <summary>
1497  /// Send a string error message to the Console.
1498  /// </summary>
1499  /// <param name="message">Message to display in errors grid</param>
1500  /// <seealso cref="Debug(PyObject)"/>
1501  /// <seealso cref="Log(PyObject)"/>
1502  [DocumentationAttribute(Logging)]
1503  public void Error(PyObject message)
1504  {
1505  Error(message.ToSafeString());
1506  }
1507 
1508  /// <summary>
1509  /// Added another method for logging if user guessed.
1510  /// </summary>
1511  /// <param name="message">String message to log.</param>
1512  /// <seealso cref="Debug(PyObject)"/>
1513  /// <seealso cref="Error(PyObject)"/>
1514  [DocumentationAttribute(Logging)]
1515  public void Log(PyObject message)
1516  {
1517  Log(message.ToSafeString());
1518  }
1519 
1520  /// <summary>
1521  /// Terminate the algorithm after processing the current event handler.
1522  /// </summary>
1523  /// <param name="message">Exit message to display on quitting</param>
1524  [DocumentationAttribute(Logging)]
1525  public void Quit(PyObject message)
1526  {
1527  Quit(message.ToSafeString());
1528  }
1529 
1530  /// <summary>
1531  /// Registers the <paramref name="handler"/> to receive consolidated data for the specified symbol
1532  /// </summary>
1533  /// <param name="symbol">The symbol who's data is to be consolidated</param>
1534  /// <param name="period">The consolidation period</param>
1535  /// <param name="handler">Data handler receives new consolidated data when generated</param>
1536  /// <returns>A new consolidator matching the requested parameters with the handler already registered</returns>
1537  [DocumentationAttribute(ConsolidatingData)]
1538  public IDataConsolidator Consolidate(Symbol symbol, Resolution period, PyObject handler)
1539  {
1540  return Consolidate(symbol, period, null, handler);
1541  }
1542 
1543  /// <summary>
1544  /// Registers the <paramref name="handler"/> to receive consolidated data for the specified symbol
1545  /// </summary>
1546  /// <param name="symbol">The symbol who's data is to be consolidated</param>
1547  /// <param name="period">The consolidation period</param>
1548  /// <param name="tickType">The tick type of subscription used as data source for consolidator. Specify null to use first subscription found.</param>
1549  /// <param name="handler">Data handler receives new consolidated data when generated</param>
1550  /// <returns>A new consolidator matching the requested parameters with the handler already registered</returns>
1551  [DocumentationAttribute(ConsolidatingData)]
1552  public IDataConsolidator Consolidate(Symbol symbol, Resolution period, TickType? tickType, PyObject handler)
1553  {
1554  // resolve consolidator input subscription
1555  var type = GetSubscription(symbol, tickType).Type;
1556 
1557  if (type == typeof(TradeBar))
1558  {
1559  return Consolidate(symbol, period, tickType, handler.ConvertToDelegate<Action<TradeBar>>());
1560  }
1561 
1562  if (type == typeof(QuoteBar))
1563  {
1564  return Consolidate(symbol, period, tickType, handler.ConvertToDelegate<Action<QuoteBar>>());
1565  }
1566 
1567  return Consolidate(symbol, period, tickType, handler.ConvertToDelegate<Action<BaseData>>());
1568  }
1569 
1570  /// <summary>
1571  /// Registers the <paramref name="handler"/> to receive consolidated data for the specified symbol
1572  /// </summary>
1573  /// <param name="symbol">The symbol who's data is to be consolidated</param>
1574  /// <param name="period">The consolidation period</param>
1575  /// <param name="handler">Data handler receives new consolidated data when generated</param>
1576  /// <returns>A new consolidator matching the requested parameters with the handler already registered</returns>
1577  [DocumentationAttribute(ConsolidatingData)]
1578  public IDataConsolidator Consolidate(Symbol symbol, TimeSpan period, PyObject handler)
1579  {
1580  return Consolidate(symbol, period, null, handler);
1581  }
1582 
1583  /// <summary>
1584  /// Registers the <paramref name="handler"/> to receive consolidated data for the specified symbol
1585  /// </summary>
1586  /// <param name="symbol">The symbol who's data is to be consolidated</param>
1587  /// <param name="period">The consolidation period</param>
1588  /// <param name="tickType">The tick type of subscription used as data source for consolidator. Specify null to use first subscription found.</param>
1589  /// <param name="handler">Data handler receives new consolidated data when generated</param>
1590  /// <returns>A new consolidator matching the requested parameters with the handler already registered</returns>
1591  [DocumentationAttribute(ConsolidatingData)]
1592  public IDataConsolidator Consolidate(Symbol symbol, TimeSpan period, TickType? tickType, PyObject handler)
1593  {
1594  // resolve consolidator input subscription
1595  var type = GetSubscription(symbol, tickType).Type;
1596 
1597  if (type == typeof(TradeBar))
1598  {
1599  return Consolidate(symbol, period, tickType, handler.ConvertToDelegate<Action<TradeBar>>());
1600  }
1601 
1602  if (type == typeof(QuoteBar))
1603  {
1604  return Consolidate(symbol, period, tickType, handler.ConvertToDelegate<Action<QuoteBar>>());
1605  }
1606 
1607  return Consolidate(symbol, period, tickType, handler.ConvertToDelegate<Action<BaseData>>());
1608  }
1609 
1610  /// <summary>
1611  /// Registers the <paramref name="handler"/> to receive consolidated data for the specified symbol
1612  /// </summary>
1613  /// <param name="symbol">The symbol who's data is to be consolidated</param>
1614  /// <param name="calendar">The consolidation calendar</param>
1615  /// <param name="handler">Data handler receives new consolidated data when generated</param>
1616  /// <returns>A new consolidator matching the requested parameters with the handler already registered</returns>
1617  [DocumentationAttribute(ConsolidatingData)]
1618  public IDataConsolidator Consolidate(Symbol symbol, Func<DateTime, CalendarInfo> calendar, PyObject handler)
1619  {
1620  return Consolidate(symbol, calendar, null, handler);
1621  }
1622 
1623  /// <summary>
1624  /// Schedules the provided training code to execute immediately
1625  /// </summary>
1626  /// <param name="trainingCode">The training code to be invoked</param>
1627  [DocumentationAttribute(MachineLearning)]
1628  [DocumentationAttribute(ScheduledEvents)]
1629  public ScheduledEvent Train(PyObject trainingCode)
1630  {
1631  return Schedule.TrainingNow(trainingCode);
1632  }
1633 
1634  /// <summary>
1635  /// Schedules the training code to run using the specified date and time rules
1636  /// </summary>
1637  /// <param name="dateRule">Specifies what dates the event should run</param>
1638  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
1639  /// <param name="trainingCode">The training code to be invoked</param>
1640  [DocumentationAttribute(MachineLearning)]
1641  [DocumentationAttribute(ScheduledEvents)]
1642  public ScheduledEvent Train(IDateRule dateRule, ITimeRule timeRule, PyObject trainingCode)
1643  {
1644  return Schedule.Training(dateRule, timeRule, trainingCode);
1645  }
1646 
1647  /// <summary>
1648  /// Registers the <paramref name="handler"/> to receive consolidated data for the specified symbol
1649  /// </summary>
1650  /// <param name="symbol">The symbol who's data is to be consolidated</param>
1651  /// <param name="calendar">The consolidation calendar</param>
1652  /// <param name="tickType">The tick type of subscription used as data source for consolidator. Specify null to use first subscription found.</param>
1653  /// <param name="handler">Data handler receives new consolidated data when generated</param>
1654  /// <returns>A new consolidator matching the requested parameters with the handler already registered</returns>
1655  [DocumentationAttribute(ConsolidatingData)]
1656  public IDataConsolidator Consolidate(Symbol symbol, Func<DateTime, CalendarInfo> calendar, TickType? tickType, PyObject handler)
1657  {
1658  // resolve consolidator input subscription
1659  var type = GetSubscription(symbol, tickType).Type;
1660 
1661  if (type == typeof(TradeBar))
1662  {
1663  return Consolidate(symbol, calendar, tickType, handler.ConvertToDelegate<Action<TradeBar>>());
1664  }
1665 
1666  if (type == typeof(QuoteBar))
1667  {
1668  return Consolidate(symbol, calendar, tickType, handler.ConvertToDelegate<Action<QuoteBar>>());
1669  }
1670 
1671  return Consolidate(symbol, calendar, tickType, handler.ConvertToDelegate<Action<BaseData>>());
1672  }
1673 
1674  /// <summary>
1675  /// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned.
1676  /// The symbol must exist in the Securities collection.
1677  /// </summary>
1678  /// <param name="indicator">The target indicator</param>
1679  /// <param name="symbol">The symbol or symbols to retrieve historical data for</param>
1680  /// <param name="period">The number of bars to request</param>
1681  /// <param name="resolution">The resolution to request</param>
1682  /// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)</param>
1683  /// <returns>pandas.DataFrame of historical data of an indicator</returns>
1684  public IndicatorHistory IndicatorHistory(PyObject indicator, PyObject symbol, int period, Resolution? resolution = null, PyObject selector = null)
1685  {
1686  var symbols = symbol.ConvertToSymbolEnumerable();
1687  var convertedIndicator = ConvertPythonIndicator(indicator);
1688 
1689  switch (convertedIndicator)
1690  {
1691  case PythonIndicator pythonIndicator:
1692  return IndicatorHistory(pythonIndicator, symbols, period, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
1693 
1694  case IndicatorBase<IndicatorDataPoint> dataPointIndicator:
1695  return IndicatorHistory(dataPointIndicator, symbols, period, resolution, selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
1696 
1697  case IndicatorBase<IBaseDataBar> baseDataBarIndicator:
1698  return IndicatorHistory(baseDataBarIndicator, symbols, period, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
1699 
1700  case IndicatorBase<TradeBar> tradeBarIndicator:
1701  return IndicatorHistory(tradeBarIndicator, symbols, period, resolution, selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
1702 
1703  case IndicatorBase<IBaseData> baseDataIndicator:
1704  return IndicatorHistory(baseDataIndicator, symbols, period, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
1705 
1706  case IndicatorBase<BaseData> baseDataIndicator:
1707  return IndicatorHistory(baseDataIndicator, symbols, period, resolution, selector?.ConvertToDelegate<Func<IBaseData, BaseData>>());
1708 
1709  default:
1710  // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it
1711  throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported.");
1712  }
1713  }
1714 
1715  /// <summary>
1716  /// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned.
1717  /// The symbol must exist in the Securities collection.
1718  /// </summary>
1719  /// <param name="indicator">The target indicator</param>
1720  /// <param name="symbol">The symbol or symbols to retrieve historical data for</param>
1721  /// <param name="span">The span over which to retrieve recent historical data</param>
1722  /// <param name="resolution">The resolution to request</param>
1723  /// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)</param>
1724  /// <returns>pandas.DataFrame of historical data of an indicator</returns>
1725  public IndicatorHistory IndicatorHistory(PyObject indicator, PyObject symbol, TimeSpan span, Resolution? resolution = null, PyObject selector = null)
1726  {
1727  return IndicatorHistory(indicator, symbol, Time - span, Time, resolution, selector);
1728  }
1729 
1730  /// <summary>
1731  /// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned.
1732  /// The symbol must exist in the Securities collection.
1733  /// </summary>
1734  /// <param name="indicator">The target indicator</param>
1735  /// <param name="symbol">The symbol or symbols to retrieve historical data for</param>
1736  /// <param name="start">The start time in the algorithm's time zone</param>
1737  /// <param name="end">The end time in the algorithm's time zone</param>
1738  /// <param name="resolution">The resolution to request</param>
1739  /// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)</param>
1740  /// <returns>pandas.DataFrame of historical data of an indicator</returns>
1741  public IndicatorHistory IndicatorHistory(PyObject indicator, PyObject symbol, DateTime start, DateTime end, Resolution? resolution = null, PyObject selector = null)
1742  {
1743  var symbols = symbol.ConvertToSymbolEnumerable();
1744  var convertedIndicator = ConvertPythonIndicator(indicator);
1745 
1746  switch (convertedIndicator)
1747  {
1748  case PythonIndicator pythonIndicator:
1749  return IndicatorHistory(pythonIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
1750 
1751  case IndicatorBase<IndicatorDataPoint> dataPointIndicator:
1752  return IndicatorHistory(dataPointIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
1753 
1754  case IndicatorBase<IBaseDataBar> baseDataBarIndicator:
1755  return IndicatorHistory(baseDataBarIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
1756 
1757  case IndicatorBase<TradeBar> tradeBarIndicator:
1758  return IndicatorHistory(tradeBarIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
1759 
1760  case IndicatorBase<IBaseData> baseDataIndicator:
1761  return IndicatorHistory(baseDataIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
1762 
1763  case IndicatorBase<BaseData> baseDataIndicator:
1764  return IndicatorHistory(baseDataIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate<Func<IBaseData, BaseData>>());
1765 
1766  default:
1767  // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it
1768  throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported.");
1769  }
1770  }
1771 
1772  /// <summary>
1773  /// Gets the historical data of an indicator and convert it into pandas.DataFrame
1774  /// </summary>
1775  /// <param name="indicator">The target indicator</param>
1776  /// <param name="history">Historical data used to calculate the indicator</param>
1777  /// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)</param>
1778  /// <returns>pandas.DataFrame containing the historical data of <paramref name="indicator"/></returns>
1779  public IndicatorHistory IndicatorHistory(PyObject indicator, IEnumerable<Slice> history, PyObject selector = null)
1780  {
1781  var convertedIndicator = ConvertPythonIndicator(indicator);
1782 
1783  switch (convertedIndicator)
1784  {
1785  case PythonIndicator pythonIndicator:
1786  return IndicatorHistory(pythonIndicator, history, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
1787 
1788  case IndicatorBase<IndicatorDataPoint> dataPointIndicator:
1789  return IndicatorHistory(dataPointIndicator, history, selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
1790 
1791  case IndicatorBase<IBaseDataBar> baseDataBarIndicator:
1792  return IndicatorHistory(baseDataBarIndicator, history, selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
1793 
1794  case IndicatorBase<TradeBar> tradeBarIndicator:
1795  return IndicatorHistory(tradeBarIndicator, history, selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
1796 
1797  case IndicatorBase<IBaseData> baseDataIndicator:
1798  return IndicatorHistory(baseDataIndicator, history, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
1799 
1800  case IndicatorBase<BaseData> baseDataIndicator:
1801  return IndicatorHistory(baseDataIndicator, history, selector?.ConvertToDelegate<Func<IBaseData, BaseData>>());
1802 
1803  default:
1804  // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it
1805  throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported.");
1806  }
1807  }
1808 
1809  /// <summary>
1810  /// Liquidate your portfolio holdings
1811  /// </summary>
1812  /// <param name="symbols">List of symbols to liquidate in Python</param>
1813  /// <param name="asynchronous">Flag to indicate if the symbols should be liquidated asynchronously</param>
1814  /// <param name="tag">Custom tag to know who is calling this</param>
1815  /// <param name="orderProperties">Order properties to use</param>
1816  [DocumentationAttribute(TradingAndOrders)]
1817  public List<OrderTicket> Liquidate(PyObject symbols, bool asynchronous = false, string tag = "Liquidated", IOrderProperties orderProperties = null)
1818  {
1819  return Liquidate(symbols.ConvertToSymbolEnumerable(), asynchronous, tag, orderProperties);
1820  }
1821 
1822  /// <summary>
1823  /// Register a command type to be used
1824  /// </summary>
1825  /// <param name="type">The command type</param>
1826  public void AddCommand(PyObject type)
1827  {
1828  // create a test instance to validate interface is implemented accurate
1829  var testInstance = new CommandPythonWrapper(type);
1830 
1831  var wrappedType = Extensions.CreateType(type);
1832  _registeredCommands[wrappedType.Name] = (CallbackCommand command) =>
1833  {
1834  var commandWrapper = new CommandPythonWrapper(type, command.Payload);
1835  return commandWrapper.Run(this);
1836  };
1837  }
1838 
1839 
1840  /// <summary>
1841  /// Get the option chains for the specified symbols at the current time (<see cref="Time"/>)
1842  /// </summary>
1843  /// <param name="symbols">
1844  /// The symbols for which the option chain is asked for.
1845  /// It can be either the canonical options or the underlying symbols.
1846  /// </param>
1847  /// <param name="flatten">
1848  /// Whether to flatten the resulting data frame.
1849  /// See <see cref="History(PyObject, int, Resolution?, bool?, bool?, DataMappingMode?, DataNormalizationMode?, int?, bool)"/>
1850  /// </param>
1851  /// <returns>The option chains</returns>
1852  [DocumentationAttribute(AddingData)]
1853  public OptionChains OptionChains(PyObject symbols, bool flatten = false)
1854  {
1855  return OptionChains(symbols.ConvertToSymbolEnumerable(), flatten);
1856  }
1857 
1858  /// <summary>
1859  /// Get an authenticated link to execute the given command instance
1860  /// </summary>
1861  /// <param name="command">The target command</param>
1862  /// <returns>The authenticated link</returns>
1863  public string Link(PyObject command)
1864  {
1865  var payload = ConvertCommandToPayload(command, out var typeName);
1866  return CommandLink(typeName, payload);
1867  }
1868 
1869  /// <summary>
1870  /// Broadcast a live command
1871  /// </summary>
1872  /// <param name="command">The target command</param>
1873  /// <returns><see cref="RestResponse"/></returns>
1874  public RestResponse BroadcastCommand(PyObject command)
1875  {
1876  var payload = ConvertCommandToPayload(command, out var typeName);
1877  return SendBroadcast(typeName, payload);
1878  }
1879 
1880  /// <summary>
1881  /// Convert the command to a dictionary payload
1882  /// </summary>
1883  /// <param name="command">The target command</param>
1884  /// <param name="typeName">The type of the command</param>
1885  /// <returns>The dictionary payload</returns>
1886  private Dictionary<string, object> ConvertCommandToPayload(PyObject command, out string typeName)
1887  {
1888  using var _ = Py.GIL();
1889 
1890  var strResult = CommandPythonWrapper.Serialize(command);
1891  using var pyType = command.GetPythonType();
1892  typeName = Extensions.CreateType(pyType).Name;
1893 
1894  return JsonConvert.DeserializeObject<Dictionary<string, object>>(strResult);
1895  }
1896 
1897  /// <summary>
1898  /// Gets indicator base type
1899  /// </summary>
1900  /// <param name="type">Indicator type</param>
1901  /// <returns>Indicator base type</returns>
1902  private Type GetIndicatorBaseType(Type type)
1903  {
1904  if (type.BaseType == typeof(object))
1905  {
1906  return type;
1907  }
1908  return GetIndicatorBaseType(type.BaseType);
1909  }
1910 
1911  /// <summary>
1912  /// Converts the sequence of PyObject objects into an array of dynamic objects that represent indicators of the same type
1913  /// </summary>
1914  /// <returns>Array of dynamic objects with indicator</returns>
1915  private dynamic[] GetIndicatorArray(PyObject first, PyObject second = null, PyObject third = null, PyObject fourth = null)
1916  {
1917  using (Py.GIL())
1918  {
1919  var array = new[] { first, second, third, fourth }
1920  .Select(
1921  x =>
1922  {
1923  if (x == null) return null;
1924 
1925  Type type;
1926  return x.GetPythonType().TryConvert(out type)
1927  ? x.AsManagedObject(type)
1928  : WrapPythonIndicator(x);
1929  }
1930  ).ToArray();
1931 
1932  var types = array.Where(x => x != null).Select(x => GetIndicatorBaseType(x.GetType())).Distinct();
1933 
1934  if (types.Count() > 1)
1935  {
1936  throw new Exception("QCAlgorithm.GetIndicatorArray(). All indicators must be of the same type: data point, bar or tradebar.");
1937  }
1938 
1939  return array;
1940  }
1941  }
1942 
1943  /// <summary>
1944  /// Converts the given PyObject into an indicator
1945  /// </summary>
1946  private IndicatorBase ConvertPythonIndicator(PyObject pyIndicator)
1947  {
1948  IndicatorBase convertedIndicator;
1949  if (pyIndicator.TryConvert(out PythonIndicator pythonIndicator))
1950  {
1951  convertedIndicator = WrapPythonIndicator(pyIndicator, pythonIndicator);
1952  }
1953  else if (!pyIndicator.TryConvert(out convertedIndicator))
1954  {
1955  convertedIndicator = WrapPythonIndicator(pyIndicator);
1956  }
1957 
1958  return convertedIndicator;
1959  }
1960 
1961  /// <summary>
1962  /// Wraps a custom python indicator and save its reference to _pythonIndicators dictionary
1963  /// </summary>
1964  /// <param name="pyObject">The python implementation of <see cref="IndicatorBase{IBaseDataBar}"/></param>
1965  /// <param name="convertedPythonIndicator">The C# converted <paramref name="pyObject"/> to avoid re-conversion</param>
1966  /// <returns><see cref="PythonIndicator"/> that wraps the python implementation</returns>
1967  private PythonIndicator WrapPythonIndicator(PyObject pyObject, PythonIndicator convertedPythonIndicator = null)
1968  {
1969  PythonIndicator pythonIndicator;
1970 
1971  if (!_pythonIndicators.TryGetValue(pyObject.Handle, out pythonIndicator))
1972  {
1973  if (convertedPythonIndicator == null)
1974  {
1975  pyObject.TryConvert(out pythonIndicator);
1976  }
1977  else
1978  {
1979  pythonIndicator = convertedPythonIndicator;
1980  }
1981 
1982  if (pythonIndicator == null)
1983  {
1984  pythonIndicator = new PythonIndicator(pyObject);
1985  }
1986  else
1987  {
1988  pythonIndicator.SetIndicator(pyObject);
1989  }
1990 
1991  // Save to prevent future additions
1992  _pythonIndicators.Add(pyObject.Handle, pythonIndicator);
1993  }
1994 
1995  return pythonIndicator;
1996  }
1997 
1998  /// <summary>
1999  /// Converts an enumerable of Slice into a Python Pandas data frame
2000  /// </summary>
2001  protected PyObject GetDataFrame(IEnumerable<Slice> data, bool flatten, Type dataType = null)
2002  {
2003  var history = PandasConverter.GetDataFrame(RemoveMemoizing(data), flatten, dataType);
2004  return flatten ? history : TryCleanupCollectionDataFrame(dataType, history);
2005  }
2006 
2007  /// <summary>
2008  /// Converts an enumerable of BaseData into a Python Pandas data frame
2009  /// </summary>
2010  protected PyObject GetDataFrame<T>(IEnumerable<T> data, bool flatten)
2011  where T : IBaseData
2012  {
2013  var history = PandasConverter.GetDataFrame(RemoveMemoizing(data), flatten: flatten);
2014  return flatten ? history : TryCleanupCollectionDataFrame(typeof(T), history);
2015  }
2016 
2017  private IEnumerable<T> RemoveMemoizing<T>(IEnumerable<T> data)
2018  {
2019  var memoizingEnumerable = data as MemoizingEnumerable<T>;
2020  if (memoizingEnumerable != null)
2021  {
2022  // we don't need the internal buffer which will just generate garbage, so we disable it
2023  // the user will only have access to the final pandas data frame object
2024  memoizingEnumerable.Enabled = false;
2025  }
2026  return data;
2027  }
2028 
2029  private PyObject TryCleanupCollectionDataFrame(Type dataType, PyObject history)
2030  {
2031  if (dataType != null && dataType.IsAssignableTo(typeof(BaseDataCollection)))
2032  {
2033  // clear out the first symbol level since it doesn't make sense, it's the universe generic symbol
2034  // let's directly return the data property which is where all the data points are in a BaseDataCollection, save the user some pain
2035  dynamic dynamic = history;
2036  using (Py.GIL())
2037  {
2038  if (!dynamic.empty)
2039  {
2040  using var columns = new PySequence(dynamic.columns);
2041  using var dataKey = "data".ToPython();
2042  if (columns.Contains(dataKey))
2043  {
2044  history = dynamic["data"];
2045  }
2046  else
2047  {
2048  dynamic.index = dynamic.index.droplevel("symbol");
2049  history = dynamic;
2050  }
2051  }
2052  }
2053  }
2054  return history;
2055  }
2056  }
2057 }