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