See More

/* * 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 QuantConnect.Brokerages; using QuantConnect.Scheduling; using QuantConnect.Util; namespace QuantConnect.Algorithm { public partial class QCAlgorithm { private readonly Dictionary _pythonIndicators = new Dictionary(); public 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 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 /// 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 public Security AddData(PyObject type, string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillDataForward = false, decimal leverage = 1.0m) { return AddData(type.CreateType(), ticker, resolution, timeZone, fillDataForward, 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 /// public Security AddData(PyObject type, Symbol underlying, Resolution? resolution, DateTimeZone timeZone, bool fillDataForward = false, decimal leverage = 1.0m) { return AddData(type.CreateType(), underlying, resolution, timeZone, fillDataForward, 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 public Security AddData(Type dataType, string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillDataForward = 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, fillDataForward, 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, fillDataForward, 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 /// public Security AddData(Type dataType, Symbol underlying, Resolution? resolution = null, DateTimeZone timeZone = null, bool fillDataForward = false, decimal leverage = 1.0m) { var symbol = QuantConnect.Symbol.CreateBase(dataType, underlying, Market.USA); return AddDataImpl(dataType, symbol, resolution, timeZone, fillDataForward, leverage); } /// /// Creates and adds a new Future Option contract to the algorithm. /// /// The canonical symbol (i.e. Symbol returned from ) /// Filter to apply to option contracts loaded as part of the universe /// The new security, containing a as its underlying. /// The symbol provided is not canonical. 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 fillDataForward, decimal leverage) { var alias = symbol.ID.Symbol; SymbolCache.Set(alias, symbol); if (timeZone != null) { // user set time zone MarketHoursDatabase.SetEntryAlwaysOpen(Market.USA, alias, SecurityType.Base, timeZone); } //Add this new generic data as a tradeable security: var config = SubscriptionManager.SubscriptionDataConfigService.Add( dataType, symbol, resolution, fillDataForward, isCustomData: true, extendedMarketHours: true); var security = Securities.CreateSecurity(symbol, config, leverage, addToSymbolCache: false); AddToUserDefinedUniverse(security, new List { config }); return security; } /// /// 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 public Universe AddUniverse(PyObject pyObject) { Func, object> coarseFunc; Universe universe; 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 coarseFunc)) { return AddUniverse(coarseFunc.ConvertToUniverseSelectionSymbolDelegate()); } 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 public Universe AddUniverse(PyObject pyObject, PyObject pyfine) { Func, object> coarseFunc; Func, object> fineFunc; Universe universe; if (pyObject.TryConvert(out 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 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 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 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 public Universe AddUniverse(PyObject T, string name, PyObject selector) { return AddUniverse(T.CreateType(), SecurityType.Equity, name, Resolution.Daily, Market.USA, 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, Market.USA and UniverseSettings /// /// The data type /// A unique name for this universe /// The epected resolution of the universe data /// Function delegate that performs selection on the universe data public Universe AddUniverse(PyObject T, string name, Resolution resolution, PyObject selector) { return AddUniverse(T.CreateType(), SecurityType.Equity, name, resolution, Market.USA, 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, and Market.USA /// /// The data type /// A unique name for this universe /// The epected resolution of the universe data /// The settings used for securities added by this universe /// Function delegate that performs selection on the universe data public Universe AddUniverse(PyObject T, string name, Resolution resolution, UniverseSettings universeSettings, PyObject selector) { return AddUniverse(T.CreateType(), SecurityType.Equity, name, resolution, Market.USA, 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 public Universe AddUniverse(PyObject T, string name, UniverseSettings universeSettings, PyObject selector) { return AddUniverse(T.CreateType(), SecurityType.Equity, name, Resolution.Daily, Market.USA, 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 epected resolution of the universe data /// The market for selected symbols /// Function delegate that performs selection on the universe data public Universe AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, 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 epected 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 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 epected 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 public Universe AddUniverse(Type dataType, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector) { var marketHoursDbEntry = MarketHoursDatabase.GetEntry(market, name, securityType); var dataTimeZone = marketHoursDbEntry.DataTimeZone; var exchangeTimeZone = marketHoursDbEntry.ExchangeHours.TimeZone; var symbol = QuantConnect.Symbol.Create(name, securityType, market, baseDataType: dataType); var config = new SubscriptionDataConfig(dataType, symbol, resolution, dataTimeZone, exchangeTimeZone, false, false, true, true, isFilteredSubscription: false); var selector = pySelector.ConvertToDelegate, object>>(); return AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer, baseDatas => { var result = selector(baseDatas); return ReferenceEquals(result, Universe.Unchanged) ? Universe.Unchanged : ((object[])result) .Select(x => x is Symbol ? (Symbol)x : QuantConnect.Symbol.Create((string)x, securityType, market, baseDataType: dataType)); } )); } /// /// 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 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) 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) 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) public void RegisterIndicator(Symbol symbol, PyObject indicator, PyObject pyObject, PyObject selector = null) { try { // First check if this is just a regular IDataConsolidator IDataConsolidator dataConsolidator; if (!pyObject.TryConvert(out dataConsolidator)) { // If not then try and wrap it as a custom Python consolidator dataConsolidator = new DataConsolidatorPythonWrapper(pyObject); } RegisterIndicator(symbol, indicator, dataConsolidator, selector); return; } 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); } } catch { throw new ArgumentException("Invalid third argument, should be either a valid consolidator or timedelta object"); } } } /// /// 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) public void RegisterIndicator(Symbol symbol, PyObject indicator, IDataConsolidator consolidator, PyObject selector = null) { IndicatorBase indicatorDataPoint; IndicatorBase indicatorDataBar; IndicatorBase indicatorTradeBar; if (indicator.TryConvert(out indicatorDataPoint)) { RegisterIndicator(symbol, indicatorDataPoint, consolidator, selector?.ConvertToDelegate>()); return; } else if (indicator.TryConvert(out indicatorDataBar)) { RegisterIndicator(symbol, indicatorDataBar, consolidator, selector?.ConvertToDelegate>()); return; } else if (indicator.TryConvert(out indicatorTradeBar)) { RegisterIndicator(symbol, indicatorTradeBar, consolidator, selector?.ConvertToDelegate>()); return; } RegisterIndicator(symbol, WrapPythonIndicator(indicator), consolidator, selector?.ConvertToDelegate>()); } /// /// Plot a chart using string series name, with value. /// /// Name of the plot series /// PyObject with the value to plot /// public void Plot(string series, PyObject pyObject) { using (Py.GIL()) { try { var value = (((dynamic)pyObject).Current.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 /// 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 /// 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 /// 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 /// 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 /// 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 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 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 pyfilter = PythonUtil.ToFunc(filter); var filteredIdentity = new FilteredIdentity(name, pyfilter); 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 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 pyfilter = PythonUtil.ToFunc(filter); var filteredIdentity = new FilteredIdentity(name, pyfilter); 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 /// A python dictionary with pandas DataFrame containing the requested historical data public PyObject History(PyObject tickers, int periods, Resolution? resolution = null) { var symbols = tickers.ConvertToSymbolEnumerable(); return PandasConverter.GetDataFrame(History(symbols, periods, resolution)); } /// /// 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 /// A python dictionary with pandas DataFrame containing the requested historical data public PyObject History(PyObject tickers, TimeSpan span, Resolution? resolution = null) { var symbols = tickers.ConvertToSymbolEnumerable(); return PandasConverter.GetDataFrame(History(symbols, span, resolution)); } /// /// Gets the historical data for the specified symbol between the specified dates. The symbol 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 /// A python dictionary with pandas DataFrame containing the requested historical data public PyObject History(PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null) { var symbols = tickers.ConvertToSymbolEnumerable(); return PandasConverter.GetDataFrame(History(symbols, start, end, resolution)); } /// /// 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 /// pandas.DataFrame containing the requested historical data public PyObject History(PyObject type, PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null) { var symbols = tickers.ConvertToSymbolEnumerable(); var requestedType = type.CreateType(); var requests = symbols.Select(x => { var security = Securities[x]; var config = security.Subscriptions.OrderByDescending(s => s.Resolution) .FirstOrDefault(s => s.Type.BaseType == requestedType.BaseType); if (config == null) return null; return _historyRequestFactory.CreateHistoryRequest(config, start, end, GetExchangeHours(x), resolution); }); return PandasConverter.GetDataFrame(History(requests.Where(x => x != null)).Memoize()); } /// /// 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 /// pandas.DataFrame containing the requested historical data public PyObject History(PyObject type, PyObject tickers, int periods, Resolution? resolution = null) { var symbols = tickers.ConvertToSymbolEnumerable(); var requestedType = type.CreateType(); var requests = symbols.Select(x => { var security = Securities[x]; var config = security.Subscriptions.OrderByDescending(s => s.Resolution) .FirstOrDefault(s => s.Type.BaseType == requestedType.BaseType); if (config == null) return null; var res = GetResolution(x, resolution); var exchange = GetExchangeHours(x); var start = _historyRequestFactory.GetStartTimeAlgoTz(x, periods, res, exchange, config.DataTimeZone); return _historyRequestFactory.CreateHistoryRequest(config, start, Time, exchange, res); }); return PandasConverter.GetDataFrame(History(requests.Where(x => x != null)).Memoize()); } /// /// 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 /// pandas.DataFrame containing the requested historical data public PyObject History(PyObject type, PyObject tickers, TimeSpan span, Resolution? resolution = null) { return History(type, tickers, Time - span, Time, resolution); } /// /// 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 /// pandas.DataFrame containing the requested historical data public PyObject History(PyObject type, Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null) { var security = Securities[symbol]; // verify the types match var requestedType = type.CreateType(); var config = security.Subscriptions.OrderByDescending(s => s.Resolution) .FirstOrDefault(s => s.Type.BaseType == requestedType.BaseType); if (config == null) { var actualType = security.Subscriptions.Select(x => x.Type.Name).DefaultIfEmpty("[None]").FirstOrDefault(); throw new ArgumentException("The specified security is not of the requested type. Symbol: " + symbol.ToString() + " Requested Type: " + requestedType.Name + " Actual Type: " + actualType); } var request = _historyRequestFactory.CreateHistoryRequest(config, start, end, GetExchangeHours(symbol), resolution); return PandasConverter.GetDataFrame(History(request).Memoize()); } /// /// 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 /// pandas.DataFrame containing the requested historical data public PyObject History(PyObject type, Symbol symbol, int periods, Resolution? resolution = null) { if (resolution == Resolution.Tick) throw new ArgumentException("History functions that accept a 'periods' parameter can not be used with Resolution.Tick"); var res = GetResolution(symbol, resolution); var marketHours = GetMarketHours(symbol); var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, res, marketHours.ExchangeHours, marketHours.DataTimeZone); return History(type, symbol, start, Time, resolution); } /// /// 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 /// pandas.DataFrame containing the requested historical data public PyObject History(PyObject type, Symbol symbol, TimeSpan span, Resolution? resolution = null) { return History(type, symbol, Time - span, Time, resolution); } /// /// Sets the specified function as the benchmark, this function provides the value of /// the benchmark at each date/time requested /// /// The benchmark producing function 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 public void SetBrokerageModel(PyObject model) { IBrokerageModel brokerageModel; if (!model.TryConvert(out brokerageModel)) { brokerageModel = new BrokerageModelPythonWrapper(model); } SetBrokerageModel(brokerageModel); } /// /// Sets the security initializer function, used to initialize/configure securities after creation /// /// The security initializer function or class 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 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 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)) { foreach (PyObject pyKey in headers) { 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 /// /// public void Debug(PyObject message) { Debug(message.ToSafeString()); } /// /// Send a string error message to the Console. /// /// Message to display in errors grid /// /// public void Error(PyObject message) { Error(message.ToSafeString()); } /// /// Added another method for logging if user guessed. /// /// String message to log. /// /// public void Log(PyObject message) { Log(message.ToSafeString()); } /// /// Terminate the algorithm after processing the current event handler. /// /// Exit message to display on quitting public void Quit(PyObject message) { Quit(message.ToSafeString()); } /// /// 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 public IDataConsolidator Consolidate(Symbol symbol, Resolution period, PyObject handler) { return Consolidate(symbol, period.ToTimeSpan(), 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 public IDataConsolidator Consolidate(Symbol symbol, Resolution period, TickType? tickType, PyObject handler) { return Consolidate(symbol, period.ToTimeSpan(), tickType, handler); } /// /// 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 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 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, null, 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 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 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 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 private 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 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; } } /// /// Wraps a custom python indicator and save its reference to _pythonIndicators dictionary /// /// The python implementation of /// that wraps the python implementation private PythonIndicator WrapPythonIndicator(PyObject pyObject) { PythonIndicator pythonIndicator; if (!_pythonIndicators.TryGetValue(pyObject.Handle, out pythonIndicator)) { pyObject.TryConvert(out pythonIndicator); pythonIndicator?.SetIndicator(pyObject); if (pythonIndicator == null) { pythonIndicator = new PythonIndicator(pyObject); } // Save to prevent future additions _pythonIndicators.Add(pyObject.Handle, pythonIndicator); } return pythonIndicator; } } }