/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Market; using QuantConnect.Indicators; using System; using QuantConnect.Securities; using NodaTime; using System.Collections.Generic; using QuantConnect.Python; using Python.Runtime; using QuantConnect.Data.UniverseSelection; using QuantConnect.Data.Fundamental; using System.Linq; using Newtonsoft.Json; using QuantConnect.Brokerages; using QuantConnect.Scheduling; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Orders; using QuantConnect.Commands; using QuantConnect.Api; namespace QuantConnect.Algorithm { public partial class QCAlgorithm { private readonly Dictionary _pythonIndicators = new Dictionary(); /// /// PandasConverter for this Algorithm /// public virtual PandasConverter PandasConverter { get; private set; } /// /// Sets pandas converter /// public void SetPandasConverter() { PandasConverter = new PandasConverter(); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time). /// This method is meant for custom data types that require a ticker, but have no underlying Symbol. /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data /// /// Data source type /// Key/Ticker for data /// Resolution of the data /// The new [DocumentationAttribute(AddingData)] public Security AddData(PyObject type, string ticker, Resolution? resolution = null) { return AddData(type, ticker, resolution, null, false, 1m); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time). /// This adds a Symbol to the `Underlying` property in the custom data Symbol object. /// Use this method when adding custom data with a ticker from the past, such as "AOL" /// before it became "TWX", or if you need to filter using custom data and place trades on the /// Symbol associated with the custom data. /// /// Data source type /// The underlying symbol for the custom data /// Resolution of the data /// The new /// /// We include three optional unused object parameters so that pythonnet chooses the intended method /// correctly. Previously, calling the overloaded method that accepts a string would instead call this method. /// Adding the three unused parameters makes it choose the correct method when using a string or Symbol. This is /// due to pythonnet's method precedence, as viewable here: https://github.com/QuantConnect/pythonnet/blob/9e29755c54e6008cb016e3dd9d75fbd8cd19fcf7/src/runtime/methodbinder.cs#L215 /// [DocumentationAttribute(AddingData)] public Security AddData(PyObject type, Symbol underlying, Resolution? resolution = null) { return AddData(type, underlying, resolution, null, false, 1m); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// This method is meant for custom data types that require a ticker, but have no underlying Symbol. /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data /// /// Data source type /// Key/Ticker for data /// Resolution of the Data Required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new [DocumentationAttribute(AddingData)] public Security AddData(PyObject type, string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m) { return AddData(type.CreateType(), ticker, resolution, timeZone, fillForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// This adds a Symbol to the `Underlying` property in the custom data Symbol object. /// Use this method when adding custom data with a ticker from the past, such as "AOL" /// before it became "TWX", or if you need to filter using custom data and place trades on the /// Symbol associated with the custom data. /// /// Data source type /// The underlying symbol for the custom data /// Resolution of the Data Required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new /// /// We include three optional unused object parameters so that pythonnet chooses the intended method /// correctly. Previously, calling the overloaded method that accepts a string would instead call this method. /// Adding the three unused parameters makes it choose the correct method when using a string or Symbol. This is /// due to pythonnet's method precedence, as viewable here: https://github.com/QuantConnect/pythonnet/blob/9e29755c54e6008cb016e3dd9d75fbd8cd19fcf7/src/runtime/methodbinder.cs#L215 /// [DocumentationAttribute(AddingData)] public Security AddData(PyObject type, Symbol underlying, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m) { return AddData(type.CreateType(), underlying, resolution, timeZone, fillForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// This method is meant for custom data types that require a ticker, but have no underlying Symbol. /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data /// /// Data source type /// Key/Ticker for data /// Resolution of the Data Required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new [DocumentationAttribute(AddingData)] public Security AddData(Type dataType, string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m) { // NOTE: Invoking methods on BaseData w/out setting the symbol may provide unexpected behavior var baseInstance = dataType.GetBaseDataInstance(); if (!baseInstance.RequiresMapping()) { var symbol = new Symbol( SecurityIdentifier.GenerateBase(dataType, ticker, Market.USA, baseInstance.RequiresMapping()), ticker); return AddDataImpl(dataType, symbol, resolution, timeZone, fillForward, leverage); } // If we need a mappable ticker and we can't find one in the SymbolCache, throw Symbol underlying; if (!SymbolCache.TryGetSymbol(ticker, out underlying)) { throw new InvalidOperationException($"The custom data type {dataType.Name} requires mapping, but the provided ticker is not in the cache. " + $"Please add this custom data type using a Symbol or perform this call after " + $"a Security has been added using AddEquity, AddForex, AddCfd, AddCrypto, AddFuture, AddOption or AddSecurity. " + $"An example use case can be found in CustomDataAddDataRegressionAlgorithm"); } return AddData(dataType, underlying, resolution, timeZone, fillForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// This adds a Symbol to the `Underlying` property in the custom data Symbol object. /// Use this method when adding custom data with a ticker from the past, such as "AOL" /// before it became "TWX", or if you need to filter using custom data and place trades on the /// Symbol associated with the custom data. /// /// Data source type /// /// Resolution of the Data Required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new /// /// We include three optional unused object parameters so that pythonnet chooses the intended method /// correctly. Previously, calling the overloaded method that accepts a string would instead call this method. /// Adding the three unused parameters makes it choose the correct method when using a string or Symbol. This is /// due to pythonnet's method precedence, as viewable here: https://github.com/QuantConnect/pythonnet/blob/9e29755c54e6008cb016e3dd9d75fbd8cd19fcf7/src/runtime/methodbinder.cs#L215 /// [DocumentationAttribute(AddingData)] public Security AddData(Type dataType, Symbol underlying, Resolution? resolution = null, DateTimeZone timeZone = null, bool fillForward = false, decimal leverage = 1.0m) { var symbol = QuantConnect.Symbol.CreateBase(dataType, underlying, underlying.ID.Market); return AddDataImpl(dataType, symbol, resolution, timeZone, fillForward, leverage); } /// /// AddData a new user defined data source including symbol properties and exchange hours, /// all other vars are not required and will use defaults. /// This overload reflects the C# equivalent for custom properties and market hours /// /// Data source type /// Key/Ticker for data /// The properties of this new custom data /// The Exchange hours of this symbol /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new [DocumentationAttribute(AddingData)] public Security AddData(PyObject type, string ticker, SymbolProperties properties, SecurityExchangeHours exchangeHours, Resolution? resolution = null, bool fillForward = false, decimal leverage = 1.0m) { // Get the right key for storage of base type symbols var dataType = type.CreateType(); var key = SecurityIdentifier.GenerateBaseSymbol(dataType, ticker); // Add entries to our Symbol Properties DB and MarketHours DB SetDatabaseEntries(key, properties, exchangeHours); // Then add the data return AddData(dataType, ticker, resolution, null, fillForward, leverage); } /// /// Creates and adds a new Future Option contract to the algorithm. /// /// The Future canonical symbol (i.e. Symbol returned from ) /// Filter to apply to option contracts loaded as part of the universe /// The new Option security, containing a Future as its underlying. /// The symbol provided is not canonical. [DocumentationAttribute(AddingData)] public void AddFutureOption(Symbol futureSymbol, PyObject optionFilter) { Func optionFilterUniverse; if (!optionFilter.TryConvertToDelegate(out optionFilterUniverse)) { throw new ArgumentException("Option contract universe filter provided is not a function"); } AddFutureOption(futureSymbol, optionFilterUniverse); } /// /// Adds the provided final Symbol with/without underlying set to the algorithm. /// This method is meant for custom data types that require a ticker, but have no underlying Symbol. /// Examples of data sources that meet this criteria are U.S. Treasury Yield Curve Rates and Trading Economics data /// /// Data source type /// Final symbol that includes underlying (if any) /// Resolution of the Data required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new private Security AddDataImpl(Type dataType, Symbol symbol, Resolution? resolution, DateTimeZone timeZone, bool fillForward, decimal leverage) { var alias = symbol.ID.Symbol; SymbolCache.Set(alias, symbol); if (timeZone != null) { // user set time zone MarketHoursDatabase.SetEntryAlwaysOpen(symbol.ID.Market, alias, SecurityType.Base, timeZone); } //Add this new generic data as a tradeable security: var config = SubscriptionManager.SubscriptionDataConfigService.Add( dataType, symbol, resolution, fillForward, isCustomData: true, extendedMarketHours: true); var security = Securities.CreateSecurity(symbol, config, leverage, addToSymbolCache: false); return AddToUserDefinedUniverse(security, new List { config }); } /// /// Creates a new universe and adds it to the algorithm. This is for coarse fundamental US Equity data and /// will be executed on day changes in the NewYork time zone () /// /// Defines an initial coarse selection [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject pyObject) { Func, object> fundamentalSelector; Universe universe; if (pyObject.TryCreateType(out var type)) { return AddUniverse(pyObject, null, null); } // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved else if (pyObject.TryConvert(out universe)) { return AddUniverse(universe); } else if (pyObject.TryConvert(out universe, allowPythonDerivative: true)) { return AddUniverse(new UniversePythonWrapper(pyObject)); } else if (pyObject.TryConvertToDelegate(out fundamentalSelector)) { return AddUniverse(FundamentalUniverse.USA(fundamentalSelector)); } else { using (Py.GIL()) { throw new ArgumentException($"QCAlgorithm.AddUniverse: {pyObject.Repr()} is not a valid argument."); } } } /// /// Creates a new universe and adds it to the algorithm. This is for coarse and fine fundamental US Equity data and /// will be executed on day changes in the NewYork time zone () /// /// Defines an initial coarse selection or a universe /// Defines a more detailed selection with access to more data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject pyObject, PyObject pyfine) { Func, object> coarseFunc; Func, object> fineFunc; try { // this is due to a pythonNet limitation even if defining 'AddUniverse(IDateRule, PyObject)' // it will chose this method instead IDateRule dateRule; using (Py.GIL()) { dateRule = pyObject.As(); } if (pyfine.TryConvertToDelegate(out coarseFunc)) { return AddUniverse(dateRule, coarseFunc.ConvertToUniverseSelectionSymbolDelegate()); } } catch (InvalidCastException) { // pass } if (pyObject.TryCreateType(out var type)) { return AddUniverse(pyObject, null, pyfine); } else if (pyObject.TryConvert(out Universe universe) && pyfine.TryConvertToDelegate(out fineFunc)) { return AddUniverse(universe, fineFunc.ConvertToUniverseSelectionSymbolDelegate()); } else if (pyObject.TryConvertToDelegate(out coarseFunc) && pyfine.TryConvertToDelegate(out fineFunc)) { return AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate(), fineFunc.ConvertToUniverseSelectionSymbolDelegate()); } else { using (Py.GIL()) { throw new ArgumentException($"QCAlgorithm.AddUniverse: {pyObject.Repr()} or {pyfine.Repr()} is not a valid argument."); } } } /// /// Creates a new universe and adds it to the algorithm. This can be used to return a list of string /// symbols retrieved from anywhere and will loads those symbols under the US Equity market. /// /// A unique name for this universe /// The resolution this universe should be triggered on /// Function delegate that accepts a DateTime and returns a collection of string symbols [DocumentationAttribute(Universes)] public Universe AddUniverse(string name, Resolution resolution, PyObject pySelector) { var selector = pySelector.ConvertToDelegate>(); return AddUniverse(name, resolution, selector.ConvertToUniverseSelectionStringDelegate()); } /// /// Creates a new universe and adds it to the algorithm. This can be used to return a list of string /// symbols retrieved from anywhere and will loads those symbols under the US Equity market. /// /// A unique name for this universe /// Function delegate that accepts a DateTime and returns a collection of string symbols [DocumentationAttribute(Universes)] public Universe AddUniverse(string name, PyObject pySelector) { var selector = pySelector.ConvertToDelegate>(); return AddUniverse(name, selector.ConvertToUniverseSelectionStringDelegate()); } /// /// Creates a new user defined universe that will fire on the requested resolution during market hours. /// /// The security type of the universe /// A unique name for this universe /// The resolution this universe should be triggered on /// The market of the universe /// The subscription settings used for securities added from this universe /// Function delegate that accepts a DateTime and returns a collection of string symbols [DocumentationAttribute(Universes)] public Universe AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector) { var selector = pySelector.ConvertToDelegate>(); return AddUniverse(securityType, name, resolution, market, universeSettings, selector.ConvertToUniverseSelectionStringDelegate()); } /// /// Creates a new universe and adds it to the algorithm. This will use the default universe settings /// specified via the property. This universe will use the defaults /// of SecurityType.Equity, Resolution.Daily, Market.USA, and UniverseSettings /// /// The data type /// A unique name for this universe /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject T, string name, PyObject selector) { return AddUniverse(T.CreateType(), null, name, null, null, null, selector); } /// /// Creates a new universe and adds it to the algorithm. This will use the default universe settings /// specified via the property. This universe will use the defaults /// of SecurityType.Equity, Market.USA and UniverseSettings /// /// The data type /// A unique name for this universe /// The expected resolution of the universe data /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject T, string name, Resolution resolution, PyObject selector) { return AddUniverse(T.CreateType(), null, name, resolution, null, null, selector); } /// /// Creates a new universe and adds it to the algorithm. This will use the default universe settings /// specified via the property. This universe will use the defaults /// of SecurityType.Equity, and Market.USA /// /// The data type /// A unique name for this universe /// The expected resolution of the universe data /// The settings used for securities added by this universe /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject T, string name, Resolution resolution, UniverseSettings universeSettings, PyObject selector) { return AddUniverse(T.CreateType(), null, name, resolution, null, universeSettings, selector); } /// /// Creates a new universe and adds it to the algorithm. This will use the default universe settings /// specified via the property. This universe will use the defaults /// of SecurityType.Equity, Resolution.Daily, and Market.USA /// /// The data type /// A unique name for this universe /// The settings used for securities added by this universe /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject T, string name, UniverseSettings universeSettings, PyObject selector) { return AddUniverse(T.CreateType(), null, name, null, null, universeSettings, selector); } /// /// Creates a new universe and adds it to the algorithm. This will use the default universe settings /// specified via the property. /// /// The data type /// The security type the universe produces /// A unique name for this universe /// The expected resolution of the universe data /// The market for selected symbols /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, PyObject selector) { return AddUniverse(T.CreateType(), securityType, name, resolution, market, null, selector); } /// /// Creates a new universe and adds it to the algorithm /// /// The data type /// The security type the universe produces /// A unique name for this universe /// The expected resolution of the universe data /// The market for selected symbols /// The subscription settings to use for newly created subscriptions /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject selector) { return AddUniverse(T.CreateType(), securityType, name, resolution, market, universeSettings, selector); } /// /// Creates a new universe and adds it to the algorithm /// /// The data type /// The security type the universe produces /// A unique name for this universe /// The expected resolution of the universe data /// The market for selected symbols /// The subscription settings to use for newly created subscriptions /// Function delegate that performs selection on the universe data [DocumentationAttribute(Universes)] public Universe AddUniverse(Type dataType, SecurityType? securityType = null, string name = null, Resolution? resolution = null, string market = null, UniverseSettings universeSettings = null, PyObject pySelector = null) { if (market.IsNullOrEmpty()) { market = Market.USA; } securityType ??= SecurityType.Equity; Func, IEnumerable> wrappedSelector = null; if (pySelector != null) { var selector = pySelector.ConvertToDelegate, object>>(); wrappedSelector = baseDatas => { var result = selector(baseDatas); if (ReferenceEquals(result, Universe.Unchanged)) { return Universe.Unchanged; } return ((object[])result).Select(x => x is Symbol symbol ? symbol : QuantConnect.Symbol.Create((string)x, securityType.Value, market, baseDataType: dataType)); }; } return AddUniverseSymbolSelector(dataType, name, resolution, market, universeSettings, wrappedSelector); } /// /// Creates a new universe selection model and adds it to the algorithm. This universe selection model will chain to the security /// changes of a given selection output and create a new for each of them /// /// The universe we want to chain an option universe selection model too /// The option filter universe to use [DocumentationAttribute(Universes)] public void AddUniverseOptions(PyObject universe, PyObject optionFilter) { Func convertedOptionChain; Universe universeToChain; if (universe.TryConvert(out universeToChain) && optionFilter.TryConvertToDelegate(out convertedOptionChain)) { AddUniverseOptions(universeToChain, convertedOptionChain); } else { using (Py.GIL()) { throw new ArgumentException($"QCAlgorithm.AddChainedEquityOptionUniverseSelectionModel: {universe.Repr()} or {optionFilter.Repr()} is not a valid argument."); } } } /// /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates /// from the consolidator. /// /// The symbol to register against /// The indicator to receive data from the consolidator /// The resolution at which to send data to the indicator, null to use the same resolution as the subscription /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(ConsolidatingData)] public void RegisterIndicator(Symbol symbol, PyObject indicator, Resolution? resolution = null, PyObject selector = null) { RegisterIndicator(symbol, indicator, ResolveConsolidator(symbol, resolution), selector); } /// /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates /// from the consolidator. /// /// The symbol to register against /// The indicator to receive data from the consolidator /// The resolution at which to send data to the indicator, null to use the same resolution as the subscription /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(ConsolidatingData)] public void RegisterIndicator(Symbol symbol, PyObject indicator, TimeSpan? resolution = null, PyObject selector = null) { RegisterIndicator(symbol, indicator, ResolveConsolidator(symbol, resolution), selector); } /// /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates /// from the consolidator. /// /// The symbol to register against /// The indicator to receive data from the consolidator /// The python object that it is trying to register with, could be consolidator or a timespan /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(ConsolidatingData)] public void RegisterIndicator(Symbol symbol, PyObject indicator, PyObject pyObject, PyObject selector = null) { // First check if this is just a regular IDataConsolidator IDataConsolidator dataConsolidator; if (pyObject.TryConvert(out dataConsolidator)) { RegisterIndicator(symbol, indicator, dataConsolidator, selector); return; } try { dataConsolidator = new DataConsolidatorPythonWrapper(pyObject); } catch { // Finally, since above didn't work, just try it as a timespan // Issue #4668 Fix using (Py.GIL()) { try { // tryConvert does not work for timespan TimeSpan? timeSpan = pyObject.As(); if (timeSpan != default(TimeSpan)) { RegisterIndicator(symbol, indicator, timeSpan, selector); return; } } catch (Exception e) { throw new ArgumentException("Invalid third argument, should be either a valid consolidator or timedelta object. The following exception was thrown: ", e); } } } RegisterIndicator(symbol, indicator, dataConsolidator, selector); } /// /// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates /// from the consolidator. /// /// The symbol to register against /// The indicator to receive data from the consolidator /// The consolidator to receive raw subscription data /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(ConsolidatingData)] public void RegisterIndicator(Symbol symbol, PyObject indicator, IDataConsolidator consolidator, PyObject selector = null) { // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved var convertedIndicator = ConvertPythonIndicator(indicator); switch (convertedIndicator) { case PythonIndicator pythonIndicator: RegisterIndicator(symbol, pythonIndicator, consolidator, selector?.ConvertToDelegate>()); break; case IndicatorBase dataPointIndicator: RegisterIndicator(symbol, dataPointIndicator, consolidator, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataBarIndicator: RegisterIndicator(symbol, baseDataBarIndicator, consolidator, selector?.ConvertToDelegate>()); break; case IndicatorBase tradeBarIndicator: RegisterIndicator(symbol, tradeBarIndicator, consolidator, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataIndicator: RegisterIndicator(symbol, baseDataIndicator, consolidator, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataIndicator: RegisterIndicator(symbol, baseDataIndicator, consolidator, selector?.ConvertToDelegate>()); break; default: // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported."); } } /// /// Warms up a given indicator with historical data /// /// The symbol whose indicator we want /// The indicator we want to warm up /// The resolution /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(HistoricalData)] public void WarmUpIndicator(Symbol symbol, PyObject indicator, Resolution? resolution = null, PyObject selector = null) { // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved WarmUpIndicator([symbol], indicator, resolution, selector); } /// /// Warms up a given indicator with historical data /// /// The symbol or symbols to retrieve historical data for /// The indicator we want to warm up /// The resolution /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(HistoricalData)] public void WarmUpIndicator(PyObject symbol, PyObject indicator, Resolution? resolution = null, PyObject selector = null) { // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved var symbols = symbol.ConvertToSymbolEnumerable(); WarmUpIndicator(symbols, indicator, resolution, selector); } /// /// Warms up a given indicator with historical data /// /// The symbols to retrieve historical data for /// The indicator we want to warm up /// The resolution /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) private void WarmUpIndicator(IEnumerable symbols, PyObject indicator, Resolution? resolution = null, PyObject selector = null) { // TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved var convertedIndicator = ConvertPythonIndicator(indicator); switch (convertedIndicator) { case PythonIndicator pythonIndicator: WarmUpIndicator(symbols, pythonIndicator, resolution, selector?.ConvertToDelegate>()); break; case IndicatorBase dataPointIndicator: WarmUpIndicator(symbols, dataPointIndicator, resolution, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataBarIndicator: WarmUpIndicator(symbols, baseDataBarIndicator, resolution, selector?.ConvertToDelegate>()); break; case IndicatorBase tradeBarIndicator: WarmUpIndicator(symbols, tradeBarIndicator, resolution, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataIndicator: WarmUpIndicator(symbols, baseDataIndicator, resolution, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataIndicator: WarmUpIndicator(symbols, baseDataIndicator, resolution, selector?.ConvertToDelegate>()); break; default: // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported."); } } /// /// Warms up a given indicator with historical data /// /// The symbol whose indicator we want /// The indicator we want to warm up /// The necessary period to warm up the indicator /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(HistoricalData)] public void WarmUpIndicator(Symbol symbol, PyObject indicator, TimeSpan period, PyObject selector = null) { WarmUpIndicator([symbol], indicator, period, selector); } /// /// Warms up a given indicator with historical data /// /// The symbol or symbols to retrieve historical data for /// The indicator we want to warm up /// The necessary period to warm up the indicator /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) [DocumentationAttribute(Indicators)] [DocumentationAttribute(HistoricalData)] public void WarmUpIndicator(PyObject symbol, PyObject indicator, TimeSpan period, PyObject selector = null) { var symbols = symbol.ConvertToSymbolEnumerable(); WarmUpIndicator(symbols, indicator, period, selector); } /// /// Warms up a given indicator with historical data /// /// The symbols to retrieve historical data for /// The indicator we want to warm up /// The necessary period to warm up the indicator /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) private void WarmUpIndicator(IEnumerable symbols, PyObject indicator, TimeSpan period, PyObject selector = null) { var convertedIndicator = ConvertPythonIndicator(indicator); switch (convertedIndicator) { case PythonIndicator pythonIndicator: WarmUpIndicator(symbols, pythonIndicator, period, selector?.ConvertToDelegate>()); break; case IndicatorBase dataPointIndicator: WarmUpIndicator(symbols, dataPointIndicator, period, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataBarIndicator: WarmUpIndicator(symbols, baseDataBarIndicator, period, selector?.ConvertToDelegate>()); break; case IndicatorBase tradeBarIndicator: WarmUpIndicator(symbols, tradeBarIndicator, period, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataIndicator: WarmUpIndicator(symbols, baseDataIndicator, period, selector?.ConvertToDelegate>()); break; case IndicatorBase baseDataIndicator: WarmUpIndicator(symbols, baseDataIndicator, period, selector?.ConvertToDelegate>()); break; default: // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported."); } } /// /// Plot a chart using string series name, with value. /// /// Name of the plot series /// PyObject with the value to plot /// [DocumentationAttribute(Charting)] public void Plot(string series, PyObject pyObject) { using (Py.GIL()) { if (pyObject.TryConvert(out IndicatorBase indicator, true)) { Plot(series, indicator); } else { try { var value = (((dynamic)pyObject).Value as PyObject).GetAndDispose(); Plot(series, value); } catch { var pythonType = pyObject.GetPythonType().Repr(); throw new ArgumentException($"QCAlgorithm.Plot(): The last argument should be a QuantConnect Indicator object, {pythonType} was provided."); } } } } /// /// Plots the value of each indicator on the chart /// /// The chart's name /// The first indicator to plot /// The second indicator to plot /// The third indicator to plot /// The fourth indicator to plot /// [DocumentationAttribute(Charting)] public void Plot(string chart, Indicator first, Indicator second = null, Indicator third = null, Indicator fourth = null) { Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray()); } /// /// Plots the value of each indicator on the chart /// /// The chart's name /// The first indicator to plot /// The second indicator to plot /// The third indicator to plot /// The fourth indicator to plot /// [DocumentationAttribute(Charting)] public void Plot(string chart, BarIndicator first, BarIndicator second = null, BarIndicator third = null, BarIndicator fourth = null) { Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray()); } /// /// Plots the value of each indicator on the chart /// /// The chart's name /// The first indicator to plot /// The second indicator to plot /// The third indicator to plot /// The fourth indicator to plot /// [DocumentationAttribute(Charting)] public void Plot(string chart, TradeBarIndicator first, TradeBarIndicator second = null, TradeBarIndicator third = null, TradeBarIndicator fourth = null) { Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray()); } /// /// Automatically plots each indicator when a new value is available /// [DocumentationAttribute(Charting)] [DocumentationAttribute(Indicators)] public void PlotIndicator(string chart, PyObject first, PyObject second = null, PyObject third = null, PyObject fourth = null) { var array = GetIndicatorArray(first, second, third, fourth); PlotIndicator(chart, array[0], array[1], array[2], array[3]); } /// /// Automatically plots each indicator when a new value is available /// [DocumentationAttribute(Charting)] [DocumentationAttribute(Indicators)] public void PlotIndicator(string chart, bool waitForReady, PyObject first, PyObject second = null, PyObject third = null, PyObject fourth = null) { var array = GetIndicatorArray(first, second, third, fourth); PlotIndicator(chart, waitForReady, array[0], array[1], array[2], array[3]); } /// /// Creates a new FilteredIdentity indicator for the symbol The indicator will be automatically /// updated on the symbol's subscription resolution /// /// The symbol whose values we want as an indicator /// Selects a value from the BaseData, if null defaults to the .Value property (x => x.Value) /// Filters the IBaseData send into the indicator, if null defaults to true (x => true) which means no filter /// The name of the field being selected /// A new FilteredIdentity indicator for the specified symbol and selector [DocumentationAttribute(Indicators)] public FilteredIdentity FilteredIdentity(Symbol symbol, PyObject selector = null, PyObject filter = null, string fieldName = null) { var resolution = GetSubscription(symbol).Resolution; return FilteredIdentity(symbol, resolution, selector, filter, fieldName); } /// /// Creates a new FilteredIdentity indicator for the symbol The indicator will be automatically /// updated on the symbol's subscription resolution /// /// The symbol whose values we want as an indicator /// The desired resolution of the data /// Selects a value from the BaseData, if null defaults to the .Value property (x => x.Value) /// Filters the IBaseData send into the indicator, if null defaults to true (x => true) which means no filter /// The name of the field being selected /// A new FilteredIdentity indicator for the specified symbol and selector [DocumentationAttribute(Indicators)] public FilteredIdentity FilteredIdentity(Symbol symbol, Resolution resolution, PyObject selector = null, PyObject filter = null, string fieldName = null) { var name = CreateIndicatorName(symbol, fieldName ?? "close", resolution); var pyselector = PythonUtil.ToFunc(selector); var filteredIdentity = new FilteredIdentity(name, filter); RegisterIndicator(symbol, filteredIdentity, resolution, pyselector); return filteredIdentity; } /// /// Creates a new FilteredIdentity indicator for the symbol The indicator will be automatically /// updated on the symbol's subscription resolution /// /// The symbol whose values we want as an indicator /// The desired resolution of the data /// Selects a value from the BaseData, if null defaults to the .Value property (x => x.Value) /// Filters the IBaseData send into the indicator, if null defaults to true (x => true) which means no filter /// The name of the field being selected /// A new FilteredIdentity indicator for the specified symbol and selector [DocumentationAttribute(Indicators)] public FilteredIdentity FilteredIdentity(Symbol symbol, TimeSpan resolution, PyObject selector = null, PyObject filter = null, string fieldName = null) { var name = $"{symbol}({fieldName ?? "close"}_{resolution.ToStringInvariant(null)})"; var pyselector = PythonUtil.ToFunc(selector); var filteredIdentity = new FilteredIdentity(name, filter); RegisterIndicator(symbol, filteredIdentity, ResolveConsolidator(symbol, resolution), pyselector); return filteredIdentity; } /// /// Gets the historical data for the specified symbol. The exact number of bars will be returned. /// The symbol must exist in the Securities collection. /// /// The symbols to retrieve historical data for /// The number of bars to request /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// A python dictionary with pandas DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject tickers, int periods, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { if (tickers.TryConvert(out var universe)) { resolution ??= universe.Configuration.Resolution; var requests = CreateBarCountHistoryRequests(new[] { universe.Symbol }, universe.DataType, periods, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); // we pass in 'BaseDataCollection' type so we clean up the data frame if we can return GetDataFrame(History(requests.Where(x => x != null)), flatten, typeof(BaseDataCollection)); } if (tickers.TryCreateType(out var type)) { var requests = CreateBarCountHistoryRequests(Securities.Keys, type, periods, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); return GetDataFrame(History(requests.Where(x => x != null)), flatten, type); } var symbols = tickers.ConvertToSymbolEnumerable().ToArray(); var dataType = Extensions.GetCustomDataTypeFromSymbols(symbols); return GetDataFrame( History(symbols, periods, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset), flatten, dataType); } /// /// Gets the historical data for the specified symbols over the requested span. /// The symbols must exist in the Securities collection. /// /// The symbols to retrieve historical data for /// The span over which to retrieve recent historical data /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// A python dictionary with pandas DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject tickers, TimeSpan span, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { return History(tickers, Time - span, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset, flatten); } /// /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection. /// /// The symbols to retrieve historical data for /// The start time in the algorithm's time zone /// The end time in the algorithm's time zone /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// A python dictionary with a pandas DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { if (tickers.TryConvert(out var universe)) { resolution ??= universe.Configuration.Resolution; var requests = CreateDateRangeHistoryRequests(new[] { universe.Symbol }, universe.DataType, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); // we pass in 'BaseDataCollection' type so we clean up the data frame if we can return GetDataFrame(History(requests.Where(x => x != null)), flatten, typeof(BaseDataCollection)); } if (tickers.TryCreateType(out var type)) { var requests = CreateDateRangeHistoryRequests(Securities.Keys, type, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); return GetDataFrame(History(requests.Where(x => x != null)), flatten, type); } var symbols = tickers.ConvertToSymbolEnumerable().ToArray(); var dataType = Extensions.GetCustomDataTypeFromSymbols(symbols); return GetDataFrame( History(symbols, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset), flatten, dataType); } /// /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbols to retrieve historical data for /// The start time in the algorithm's time zone /// The end time in the algorithm's time zone /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject type, PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { var symbols = tickers.ConvertToSymbolEnumerable().ToArray(); var requestedType = type.CreateType(); var requests = CreateDateRangeHistoryRequests(symbols, requestedType, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); return GetDataFrame(History(requests.Where(x => x != null)), flatten, requestedType); } /// /// Gets the historical data for the specified symbols. The exact number of bars will be returned for /// each symbol. This may result in some data start earlier/later than others due to when various /// exchanges are open. The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbols to retrieve historical data for /// The number of bars to request /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject type, PyObject tickers, int periods, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { var symbols = tickers.ConvertToSymbolEnumerable().ToArray(); var requestedType = type.CreateType(); CheckPeriodBasedHistoryRequestResolution(symbols, resolution, requestedType); var requests = CreateBarCountHistoryRequests(symbols, requestedType, periods, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); return GetDataFrame(History(requests.Where(x => x != null)), flatten, requestedType); } /// /// Gets the historical data for the specified symbols over the requested span. /// The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbols to retrieve historical data for /// The span over which to retrieve recent historical data /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject type, PyObject tickers, TimeSpan span, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { return History(type, tickers, Time - span, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset, flatten); } /// /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbol to retrieve historical data for /// The start time in the algorithm's time zone /// The end time in the algorithm's time zone /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject type, Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { return History(type.CreateType(), symbol, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset, flatten); } /// /// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbol to retrieve historical data for /// The start time in the algorithm's time zone /// The end time in the algorithm's time zone /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data private PyObject History(Type type, Symbol symbol, DateTime start, DateTime end, Resolution? resolution, bool? fillForward, bool? extendedMarketHours, DataMappingMode? dataMappingMode, DataNormalizationMode? dataNormalizationMode, int? contractDepthOffset, bool flatten) { var requests = CreateDateRangeHistoryRequests(new[] { symbol }, type, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); if (requests.IsNullOrEmpty()) { throw new ArgumentException($"No history data could be fetched. " + $"This could be due to the specified security not being of the requested type. Symbol: {symbol} Requested Type: {type.Name}"); } return GetDataFrame(History(requests), flatten, type); } /// /// Gets the historical data for the specified symbols. The exact number of bars will be returned for /// each symbol. This may result in some data start earlier/later than others due to when various /// exchanges are open. The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbol to retrieve historical data for /// The number of bars to request /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject type, Symbol symbol, int periods, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { var managedType = type.CreateType(); resolution = GetResolution(symbol, resolution, managedType); CheckPeriodBasedHistoryRequestResolution(new[] { symbol }, resolution, managedType); var marketHours = GetMarketHours(symbol, managedType); var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, resolution.Value, marketHours.ExchangeHours, marketHours.DataTimeZone, managedType, extendedMarketHours); return History(managedType, symbol, start, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset, flatten); } /// /// Gets the historical data for the specified symbols over the requested span. /// The symbols must exist in the Securities collection. /// /// The data type of the symbols /// The symbol to retrieve historical data for /// The span over which to retrieve recent historical data /// The resolution to request /// True to fill forward missing data, false otherwise /// True to include extended market hours data, false otherwise /// The contract mapping mode to use for the security history request /// The price scaling mode to use for the securities history /// The continuous contract desired offset from the current front month. /// For example, 0 will use the front month, 1 will use the back month contract /// Whether to flatten the resulting data frame. /// e.g. for universe requests, the each row represents a day of data, and the data is stored in a list in a cell of the data frame. /// If flatten is true, the resulting data frame will contain one row per universe constituent, /// and each property of the constituent will be a column in the data frame. /// pandas.DataFrame containing the requested historical data [DocumentationAttribute(HistoricalData)] public PyObject History(PyObject type, Symbol symbol, TimeSpan span, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null, bool flatten = false) { return History(type, symbol, Time - span, Time, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset, flatten); } /// /// Sets the specified function as the benchmark, this function provides the value of /// the benchmark at each date/time requested /// /// The benchmark producing function [DocumentationAttribute(TradingAndOrders)] [DocumentationAttribute(SecuritiesAndPortfolio)] [DocumentationAttribute(Indicators)] public void SetBenchmark(PyObject benchmark) { using (Py.GIL()) { var pyBenchmark = PythonUtil.ToFunc(benchmark); if (pyBenchmark != null) { SetBenchmark(pyBenchmark); return; } SetBenchmark((Symbol)benchmark.AsManagedObject(typeof(Symbol))); } } /// /// Sets the brokerage to emulate in backtesting or paper trading. /// This can be used to set a custom brokerage model. /// /// The brokerage model to use [DocumentationAttribute(Modeling)] public void SetBrokerageModel(PyObject model) { IBrokerageModel brokerageModel; if (!model.TryConvert(out brokerageModel)) { brokerageModel = new BrokerageModelPythonWrapper(model); } SetBrokerageModel(brokerageModel); } /// /// Sets the implementation used to handle messages from the brokerage. /// The default implementation will forward messages to debug or error /// and when a occurs, the algorithm /// is stopped. /// /// The message handler to use [DocumentationAttribute(Modeling)] [DocumentationAttribute(Logging)] public void SetBrokerageMessageHandler(PyObject handler) { if (!handler.TryConvert(out IBrokerageMessageHandler brokerageMessageHandler)) { brokerageMessageHandler = new BrokerageMessageHandlerPythonWrapper(handler); } SetBrokerageMessageHandler(brokerageMessageHandler); } /// /// Sets the risk free interest rate model to be used in the algorithm /// /// The risk free interest rate model to use [DocumentationAttribute(Modeling)] public void SetRiskFreeInterestRateModel(PyObject model) { SetRiskFreeInterestRateModel(RiskFreeInterestRateModelPythonWrapper.FromPyObject(model)); } /// /// Sets the security initializer function, used to initialize/configure securities after creation /// /// The security initializer function or class [DocumentationAttribute(AddingData)] [DocumentationAttribute(Modeling)] public void SetSecurityInitializer(PyObject securityInitializer) { var securityInitializer1 = PythonUtil.ToAction(securityInitializer); if (securityInitializer1 != null) { SetSecurityInitializer(securityInitializer1); return; } SetSecurityInitializer(new SecurityInitializerPythonWrapper(securityInitializer)); } /// /// Downloads the requested resource as a . /// The resource to download is specified as a containing the URI. /// /// A string containing the URI to download /// Defines header values to add to the request /// The requested resource as a [DocumentationAttribute(AddingData)] [DocumentationAttribute(MachineLearning)] public string Download(string address, PyObject headers) => Download(address, headers, null, null); /// /// Downloads the requested resource as a . /// The resource to download is specified as a containing the URI. /// /// A string containing the URI to download /// Defines header values to add to the request /// The user name associated with the credentials /// The password for the user name associated with the credentials /// The requested resource as a [DocumentationAttribute(AddingData)] [DocumentationAttribute(MachineLearning)] public string Download(string address, PyObject headers, string userName, string password) { var dict = new Dictionary(); if (headers != null) { using (Py.GIL()) { // In python algorithms, headers must be a python dictionary // In order to convert it into a C# Dictionary if (PyDict.IsDictType(headers)) { using var iterator = headers.GetIterator(); foreach (PyObject pyKey in iterator) { var key = (string)pyKey.AsManagedObject(typeof(string)); var value = (string)headers.GetItem(pyKey).AsManagedObject(typeof(string)); dict.Add(key, value); } } else { throw new ArgumentException($"QCAlgorithm.Fetch(): Invalid argument. {headers.Repr()} is not a dict"); } } } return Download(address, dict, userName, password); } /// /// Send a debug message to the web console: /// /// Message to send to debug console /// /// [DocumentationAttribute(Logging)] public void Debug(PyObject message) { Debug(message.ToSafeString()); } /// /// Send a string error message to the Console. /// /// Message to display in errors grid /// /// [DocumentationAttribute(Logging)] public void Error(PyObject message) { Error(message.ToSafeString()); } /// /// Added another method for logging if user guessed. /// /// String message to log. /// /// [DocumentationAttribute(Logging)] public void Log(PyObject message) { Log(message.ToSafeString()); } /// /// Terminate the algorithm after processing the current event handler. /// /// Exit message to display on quitting [DocumentationAttribute(Logging)] public void Quit(PyObject message) { Quit(message.ToSafeString()); } /// /// Creates and registers a consolidator for the following bar types: RenkoBar, VolumeRenkoBar, or RangeBar /// for the specified symbol and threshold. The specified handler will be invoked with each new consolidated bar. /// /// The Python type of the bar (RenkoBar, VolumeRenkoBar, or RangeBar) /// The symbol whose data is to be consolidated /// The size value for the consolidator (e.g., brick size, range size or maxCount) /// The tick type to consolidate. If null, the first matching subscription is used. /// The callback to invoke with each new consolidated bar /// The created and registered instance [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(PyObject type, Symbol symbol, decimal size, TickType? tickType, PyObject handler) { var convertedType = type.CreateType(); if (convertedType == typeof(RenkoBar)) { // size will be used as barSize return Consolidate(symbol, size, tickType, handler.ConvertToDelegate>()); } else if (convertedType == typeof(VolumeRenkoBar)) { // size will be used as barSize return Consolidate(symbol, size, tickType, handler.ConvertToDelegate>()); } else if (convertedType == typeof(RangeBar)) { // size will be used as rangeSize return Consolidate(symbol, (int)size, tickType, handler.ConvertToDelegate>()); } else if (convertedType == typeof(TradeBar)) { // size will be used as maxCount return Consolidate(symbol, (int)size, tickType, handler.ConvertToDelegate>()); } else if (convertedType == typeof(QuoteBar)) { // size will be used as maxCount return Consolidate(symbol, (int)size, tickType, handler.ConvertToDelegate>()); } else { // size will be used as maxCount return Consolidate(symbol, (int)size, tickType, handler.ConvertToDelegate>()); } } /// /// Registers the to receive consolidated data for the specified symbol /// /// The symbol who's data is to be consolidated /// The consolidation period /// Data handler receives new consolidated data when generated /// A new consolidator matching the requested parameters with the handler already registered [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(Symbol symbol, Resolution period, PyObject handler) { return Consolidate(symbol, period, null, handler); } /// /// Registers the to receive consolidated data for the specified symbol /// /// The symbol who's data is to be consolidated /// The consolidation period /// The tick type of subscription used as data source for consolidator. Specify null to use first subscription found. /// Data handler receives new consolidated data when generated /// A new consolidator matching the requested parameters with the handler already registered [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(Symbol symbol, Resolution period, TickType? tickType, PyObject handler) { // resolve consolidator input subscription var type = GetSubscription(symbol, tickType).Type; if (type == typeof(TradeBar)) { return Consolidate(symbol, period, tickType, handler.ConvertToDelegate>()); } if (type == typeof(QuoteBar)) { return Consolidate(symbol, period, tickType, handler.ConvertToDelegate>()); } return Consolidate(symbol, period, tickType, handler.ConvertToDelegate>()); } /// /// Registers the to receive consolidated data for the specified symbol /// /// The symbol who's data is to be consolidated /// The consolidation period /// Data handler receives new consolidated data when generated /// A new consolidator matching the requested parameters with the handler already registered [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(Symbol symbol, TimeSpan period, PyObject handler) { return Consolidate(symbol, period, null, handler); } /// /// Registers the to receive consolidated data for the specified symbol /// /// The symbol who's data is to be consolidated /// The consolidation period /// The tick type of subscription used as data source for consolidator. Specify null to use first subscription found. /// Data handler receives new consolidated data when generated /// A new consolidator matching the requested parameters with the handler already registered [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(Symbol symbol, TimeSpan period, TickType? tickType, PyObject handler) { // resolve consolidator input subscription var type = GetSubscription(symbol, tickType).Type; if (type == typeof(TradeBar)) { return Consolidate(symbol, period, tickType, handler.ConvertToDelegate>()); } if (type == typeof(QuoteBar)) { return Consolidate(symbol, period, tickType, handler.ConvertToDelegate>()); } return Consolidate(symbol, period, tickType, handler.ConvertToDelegate>()); } /// /// Registers the to receive consolidated data for the specified symbol /// /// The symbol who's data is to be consolidated /// The consolidation calendar /// Data handler receives new consolidated data when generated /// A new consolidator matching the requested parameters with the handler already registered [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(Symbol symbol, Func calendar, PyObject handler) { return Consolidate(symbol, calendar, null, handler); } /// /// Schedules the provided training code to execute immediately /// /// The training code to be invoked [DocumentationAttribute(MachineLearning)] [DocumentationAttribute(ScheduledEvents)] public ScheduledEvent Train(PyObject trainingCode) { return Schedule.TrainingNow(trainingCode); } /// /// Schedules the training code to run using the specified date and time rules /// /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The training code to be invoked [DocumentationAttribute(MachineLearning)] [DocumentationAttribute(ScheduledEvents)] public ScheduledEvent Train(IDateRule dateRule, ITimeRule timeRule, PyObject trainingCode) { return Schedule.Training(dateRule, timeRule, trainingCode); } /// /// Registers the to receive consolidated data for the specified symbol /// /// The symbol who's data is to be consolidated /// The consolidation calendar /// The tick type of subscription used as data source for consolidator. Specify null to use first subscription found. /// Data handler receives new consolidated data when generated /// A new consolidator matching the requested parameters with the handler already registered [DocumentationAttribute(ConsolidatingData)] public IDataConsolidator Consolidate(Symbol symbol, Func calendar, TickType? tickType, PyObject handler) { // resolve consolidator input subscription var type = GetSubscription(symbol, tickType).Type; if (type == typeof(TradeBar)) { return Consolidate(symbol, calendar, tickType, handler.ConvertToDelegate>()); } if (type == typeof(QuoteBar)) { return Consolidate(symbol, calendar, tickType, handler.ConvertToDelegate>()); } return Consolidate(symbol, calendar, tickType, handler.ConvertToDelegate>()); } /// /// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned. /// The symbol must exist in the Securities collection. /// /// The target indicator /// The symbol or symbols to retrieve historical data for /// The number of bars to request /// The resolution to request /// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value) /// pandas.DataFrame of historical data of an indicator public IndicatorHistory IndicatorHistory(PyObject indicator, PyObject symbol, int period, Resolution? resolution = null, PyObject selector = null) { var symbols = symbol.ConvertToSymbolEnumerable(); var convertedIndicator = ConvertPythonIndicator(indicator); switch (convertedIndicator) { case PythonIndicator pythonIndicator: return IndicatorHistory(pythonIndicator, symbols, period, resolution, selector?.ConvertToDelegate>()); case IndicatorBase dataPointIndicator: return IndicatorHistory(dataPointIndicator, symbols, period, resolution, selector?.ConvertToDelegate>()); case IndicatorBase baseDataBarIndicator: return IndicatorHistory(baseDataBarIndicator, symbols, period, resolution, selector?.ConvertToDelegate>()); case IndicatorBase tradeBarIndicator: return IndicatorHistory(tradeBarIndicator, symbols, period, resolution, selector?.ConvertToDelegate>()); case IndicatorBase baseDataIndicator: return IndicatorHistory(baseDataIndicator, symbols, period, resolution, selector?.ConvertToDelegate>()); case IndicatorBase baseDataIndicator: return IndicatorHistory(baseDataIndicator, symbols, period, resolution, selector?.ConvertToDelegate>()); default: // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported."); } } /// /// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned. /// The symbol must exist in the Securities collection. /// /// The target indicator /// The symbol or symbols to retrieve historical data for /// The span over which to retrieve recent historical data /// The resolution to request /// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value) /// pandas.DataFrame of historical data of an indicator public IndicatorHistory IndicatorHistory(PyObject indicator, PyObject symbol, TimeSpan span, Resolution? resolution = null, PyObject selector = null) { return IndicatorHistory(indicator, symbol, Time - span, Time, resolution, selector); } /// /// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned. /// The symbol must exist in the Securities collection. /// /// The target indicator /// The symbol or symbols to retrieve historical data for /// The start time in the algorithm's time zone /// The end time in the algorithm's time zone /// The resolution to request /// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value) /// pandas.DataFrame of historical data of an indicator public IndicatorHistory IndicatorHistory(PyObject indicator, PyObject symbol, DateTime start, DateTime end, Resolution? resolution = null, PyObject selector = null) { var symbols = symbol.ConvertToSymbolEnumerable(); var convertedIndicator = ConvertPythonIndicator(indicator); switch (convertedIndicator) { case PythonIndicator pythonIndicator: return IndicatorHistory(pythonIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate>()); case IndicatorBase dataPointIndicator: return IndicatorHistory(dataPointIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate>()); case IndicatorBase baseDataBarIndicator: return IndicatorHistory(baseDataBarIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate>()); case IndicatorBase tradeBarIndicator: return IndicatorHistory(tradeBarIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate>()); case IndicatorBase baseDataIndicator: return IndicatorHistory(baseDataIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate>()); case IndicatorBase baseDataIndicator: return IndicatorHistory(baseDataIndicator, symbols, start, end, resolution, selector?.ConvertToDelegate>()); default: // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported."); } } /// /// Gets the historical data of an indicator and convert it into pandas.DataFrame /// /// The target indicator /// Historical data used to calculate the indicator /// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value) /// pandas.DataFrame containing the historical data of public IndicatorHistory IndicatorHistory(PyObject indicator, IEnumerable history, PyObject selector = null) { var convertedIndicator = ConvertPythonIndicator(indicator); switch (convertedIndicator) { case PythonIndicator pythonIndicator: return IndicatorHistory(pythonIndicator, history, selector?.ConvertToDelegate>()); case IndicatorBase dataPointIndicator: return IndicatorHistory(dataPointIndicator, history, selector?.ConvertToDelegate>()); case IndicatorBase baseDataBarIndicator: return IndicatorHistory(baseDataBarIndicator, history, selector?.ConvertToDelegate>()); case IndicatorBase tradeBarIndicator: return IndicatorHistory(tradeBarIndicator, history, selector?.ConvertToDelegate>()); case IndicatorBase baseDataIndicator: return IndicatorHistory(baseDataIndicator, history, selector?.ConvertToDelegate>()); case IndicatorBase baseDataIndicator: return IndicatorHistory(baseDataIndicator, history, selector?.ConvertToDelegate>()); default: // Shouldn't happen, ConvertPythonIndicator will wrap the PyObject in a PythonIndicator instance if it can't convert it throw new ArgumentException($"Indicator type {indicator.GetPythonType().Name} is not supported."); } } /// /// Liquidate your portfolio holdings /// /// List of symbols to liquidate in Python /// Flag to indicate if the symbols should be liquidated asynchronously /// Custom tag to know who is calling this /// Order properties to use [DocumentationAttribute(TradingAndOrders)] public List Liquidate(PyObject symbols, bool asynchronous = false, string tag = "Liquidated", IOrderProperties orderProperties = null) { return Liquidate(symbols.ConvertToSymbolEnumerable(), asynchronous, tag, orderProperties); } /// /// Register a command type to be used /// /// The command type public void AddCommand(PyObject type) { // create a test instance to validate interface is implemented accurate var testInstance = new CommandPythonWrapper(type); var wrappedType = Extensions.CreateType(type); _registeredCommands[wrappedType.Name] = (CallbackCommand command) => { var commandWrapper = new CommandPythonWrapper(type, command.Payload); return commandWrapper.Run(this); }; } /// /// Get the option chains for the specified symbols at the current time () /// /// /// The symbols for which the option chain is asked for. /// It can be either the canonical options or the underlying symbols. /// /// /// Whether to flatten the resulting data frame. /// See /// /// The option chains [DocumentationAttribute(AddingData)] public OptionChains OptionChains(PyObject symbols, bool flatten = false) { return OptionChains(symbols.ConvertToSymbolEnumerable(), flatten); } /// /// Get an authenticated link to execute the given command instance /// /// The target command /// The authenticated link public string Link(PyObject command) { var payload = ConvertCommandToPayload(command, out var typeName); return CommandLink(typeName, payload); } /// /// Broadcast a live command /// /// The target command /// public RestResponse BroadcastCommand(PyObject command) { var payload = ConvertCommandToPayload(command, out var typeName); return SendBroadcast(typeName, payload); } /// /// Convert the command to a dictionary payload /// /// The target command /// The type of the command /// The dictionary payload private Dictionary ConvertCommandToPayload(PyObject command, out string typeName) { using var _ = Py.GIL(); var strResult = CommandPythonWrapper.Serialize(command); using var pyType = command.GetPythonType(); typeName = Extensions.CreateType(pyType).Name; return JsonConvert.DeserializeObject>(strResult); } /// /// Gets indicator base type /// /// Indicator type /// Indicator base type private Type GetIndicatorBaseType(Type type) { if (type.BaseType == typeof(object)) { return type; } return GetIndicatorBaseType(type.BaseType); } /// /// Converts the sequence of PyObject objects into an array of dynamic objects that represent indicators of the same type /// /// Array of dynamic objects with indicator private dynamic[] GetIndicatorArray(PyObject first, PyObject second = null, PyObject third = null, PyObject fourth = null) { using (Py.GIL()) { var array = new[] { first, second, third, fourth } .Select( x => { if (x == null) return null; Type type; return x.GetPythonType().TryConvert(out type) ? x.AsManagedObject(type) : WrapPythonIndicator(x); } ).ToArray(); var types = array.Where(x => x != null).Select(x => GetIndicatorBaseType(x.GetType())).Distinct(); if (types.Count() > 1) { throw new Exception("QCAlgorithm.GetIndicatorArray(). All indicators must be of the same type: data point, bar or tradebar."); } return array; } } /// /// Converts the given PyObject into an indicator /// private IndicatorBase ConvertPythonIndicator(PyObject pyIndicator) { IndicatorBase convertedIndicator; if (pyIndicator.TryConvert(out PythonIndicator pythonIndicator)) { convertedIndicator = WrapPythonIndicator(pyIndicator, pythonIndicator); } else if (!pyIndicator.TryConvert(out convertedIndicator)) { convertedIndicator = WrapPythonIndicator(pyIndicator); } return convertedIndicator; } /// /// Wraps a custom python indicator and save its reference to _pythonIndicators dictionary /// /// The python implementation of /// The C# converted to avoid re-conversion /// that wraps the python implementation private PythonIndicator WrapPythonIndicator(PyObject pyObject, PythonIndicator convertedPythonIndicator = null) { PythonIndicator pythonIndicator; if (!_pythonIndicators.TryGetValue(pyObject.Handle, out pythonIndicator)) { if (convertedPythonIndicator == null) { pyObject.TryConvert(out pythonIndicator); } else { pythonIndicator = convertedPythonIndicator; } if (pythonIndicator == null) { pythonIndicator = new PythonIndicator(pyObject); } else { pythonIndicator.SetIndicator(pyObject); } // Save to prevent future additions _pythonIndicators.Add(pyObject.Handle, pythonIndicator); } return pythonIndicator; } /// /// Converts an enumerable of Slice into a Python Pandas data frame /// protected PyObject GetDataFrame(IEnumerable data, bool flatten, Type dataType = null) { var history = PandasConverter.GetDataFrame(RemoveMemoizing(data), flatten, dataType); return flatten ? history : TryCleanupCollectionDataFrame(dataType, history); } /// /// Converts an enumerable of BaseData into a Python Pandas data frame /// protected PyObject GetDataFrame(IEnumerable data, bool flatten) where T : IBaseData { var history = PandasConverter.GetDataFrame(RemoveMemoizing(data), flatten: flatten); return flatten ? history : TryCleanupCollectionDataFrame(typeof(T), history); } private IEnumerable RemoveMemoizing(IEnumerable data) { var memoizingEnumerable = data as MemoizingEnumerable; if (memoizingEnumerable != null) { // we don't need the internal buffer which will just generate garbage, so we disable it // the user will only have access to the final pandas data frame object memoizingEnumerable.Enabled = false; } return data; } private PyObject TryCleanupCollectionDataFrame(Type dataType, PyObject history) { if (dataType != null && dataType.IsAssignableTo(typeof(BaseDataCollection))) { // clear out the first symbol level since it doesn't make sense, it's the universe generic symbol // let's directly return the data property which is where all the data points are in a BaseDataCollection, save the user some pain dynamic dynamic = history; using (Py.GIL()) { if (!dynamic.empty) { using var columns = new PySequence(dynamic.columns); using var dataKey = "data".ToPython(); if (columns.Contains(dataKey)) { history = dynamic["data"]; } else { dynamic.index = dynamic.index.droplevel("symbol"); history = dynamic; } } } } return history; } } }