/* * 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 System.Reflection.Emit; using System.Reflection; using QuantConnect.Python; using Python.Runtime; using QuantConnect.Data.UniverseSelection; using QuantConnect.Data.Fundamental; using System.Linq; using QuantConnect.Util; namespace QuantConnect.Algorithm { public partial class QCAlgorithm { 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) /// /// Data source type /// Key/Symbol for data /// Resolution of the data /// The new public Security AddData(PyObject type, string symbol, Resolution resolution = Resolution.Minute) { return AddData(type, symbol, resolution, TimeZones.NewYork, false, 1m); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// Data source type /// Key/Symbol 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 symbol, Resolution resolution, DateTimeZone timeZone, bool fillDataForward = false, decimal leverage = 1.0m) { return AddData(CreateType(type), symbol, resolution, timeZone, fillDataForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// Data source type /// Key/Symbol 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 symbol, Resolution resolution, DateTimeZone timeZone, bool fillDataForward = false, decimal leverage = 1.0m) { var marketHoursDbEntry = MarketHoursDatabase.SetEntryAlwaysOpen(Market.USA, symbol, SecurityType.Base, timeZone); //Add this to the data-feed subscriptions var symbolObject = new Symbol(SecurityIdentifier.GenerateBase(symbol, Market.USA), symbol); var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(Market.USA, symbol, SecurityType.Base, CashBook.AccountCurrency); //Add this new generic data as a tradeable security: var security = SecurityManager.CreateSecurity(dataType, Portfolio, SubscriptionManager, marketHoursDbEntry.ExchangeHours, marketHoursDbEntry.DataTimeZone, symbolProperties, SecurityInitializer, symbolObject, resolution, fillDataForward, leverage, true, false, true, LiveMode); AddToUserDefinedUniverse(security); 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 void AddUniverse(PyObject pycoarse) { var coarse = PythonUtil.ToFunc, object[]>(pycoarse); if (coarse != null) { AddUniverse(c => coarse(c).Select(x => (Symbol)x)); return; } var type = (Type)pycoarse.GetPythonType().AsManagedObject(typeof(Type)); AddUniverse((dynamic)pycoarse.AsManagedObject(type)); } /// /// 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 /// Defines a more detailed selection with access to more data public void AddUniverse(PyObject pycoarse, PyObject pyfine) { var coarse = PythonUtil.ToFunc, object[]>(pycoarse); var fine = PythonUtil.ToFunc, object[]>(pyfine); AddUniverse(c => coarse(c).Select(x => (Symbol)x), f => fine(f).Select(x => (Symbol)x)); } /// /// 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 void AddUniverse(string name, Resolution resolution, PyObject pySelector) { var selector = PythonUtil.ToFunc(pySelector); AddUniverse(name, resolution, d => selector(d).Select(x => (string)x)); } /// /// 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 void AddUniverse(string name, PyObject pySelector) { var selector = PythonUtil.ToFunc(pySelector); AddUniverse(name, d => selector(d).Select(x => (string)x)); } /// /// 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 void AddUniverse(SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject pySelector) { var selector = PythonUtil.ToFunc(pySelector); AddUniverse(securityType, name, resolution, market, universeSettings, d => selector(d).Select(x => (string)x)); } /// /// 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 void AddUniverse(PyObject T, string name, PyObject selector) { AddUniverse(CreateType(T), 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 void AddUniverse(PyObject T, string name, Resolution resolution, PyObject selector) { AddUniverse(CreateType(T), 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 void AddUniverse(PyObject T, string name, Resolution resolution, UniverseSettings universeSettings, PyObject selector) { AddUniverse(CreateType(T), 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 void AddUniverse(PyObject T, string name, UniverseSettings universeSettings, PyObject selector) { AddUniverse(CreateType(T), 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 void AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, PyObject selector) { AddUniverse(CreateType(T), 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 void AddUniverse(PyObject T, SecurityType securityType, string name, Resolution resolution, string market, UniverseSettings universeSettings, PyObject selector) { AddUniverse(CreateType(T), 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 void 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); var config = new SubscriptionDataConfig(dataType, symbol, resolution, dataTimeZone, exchangeTimeZone, false, false, true, true, isFilteredSubscription: false); var selector = PythonUtil.ToFunc, object[]>(pySelector); AddUniverse(new FuncUniverse(config, universeSettings, SecurityInitializer, d => selector(d) .Select(x => x is Symbol ? (Symbol)x : QuantConnect.Symbol.Create((string)x, securityType, market)))); } /// /// 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 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) { object managedObject = null; using (Py.GIL()) { var pythonType = indicator.GetPythonType(); if (pythonType.Repr().Contains("QuantConnect")) { managedObject = indicator.AsManagedObject(pythonType.As()); } else if (!indicator.HasAttr("Update")) { throw new ArgumentException($"Update method must be defined. Please checkout {indicator}"); } } // Lean indicators are directed to other RegisterIndicator overloads if (managedObject != null) { var indicatorDataPoint = managedObject as Indicator; if (indicatorDataPoint != null) { var managedSelector = (Func)selector?.AsManagedObject(typeof(Func)); RegisterIndicator(symbol, indicatorDataPoint, consolidator, managedSelector); } var indicatorDataBar = managedObject as BarIndicator; if (indicatorDataBar != null) { var managedSelector = (Func)selector?.AsManagedObject(typeof(Func)); RegisterIndicator(symbol, indicatorDataBar, consolidator, managedSelector); } var indicatorTradeBar = managedObject as TradeBarIndicator; if (indicatorTradeBar != null) { var managedSelector = (Func)selector?.AsManagedObject(typeof(Func)); RegisterIndicator(symbol, indicatorTradeBar, consolidator, managedSelector); } return; } // register the consolidator for automatic updates via SubscriptionManager SubscriptionManager.AddConsolidator(symbol, consolidator); // attach to the DataConsolidated event so it updates our indicator consolidator.DataConsolidated += (sender, consolidated) => { using (Py.GIL()) { indicator.InvokeMethod("Update", new[] { consolidated.ToPython() }); } }; } /// /// 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 = string.Format("{0}({1}_{2})", symbol, fieldName ?? "close", resolution); 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 = GetSymbolsFromPyObject(tickers); if (symbols == null) return null; 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 = GetSymbolsFromPyObject(tickers); if (symbols == null) return null; 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 = GetSymbolsFromPyObject(tickers); if (symbols == null) return null; 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 = GetSymbolsFromPyObject(tickers); if (symbols == null) return null; var requests = symbols.Select(x => { var security = Securities[x]; var config = security.Subscriptions.OrderByDescending(s => s.Resolution) .FirstOrDefault(s => s.Type.BaseType == CreateType(type).BaseType); if (config == null) return null; return CreateHistoryRequest(config, start, end, 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 = GetSymbolsFromPyObject(tickers); if (symbols == null) return null; var requests = symbols.Select(x => { var security = Securities[x]; var config = security.Subscriptions.OrderByDescending(s => s.Resolution) .FirstOrDefault(s => s.Type.BaseType == CreateType(type).BaseType); if (config == null) return null; Resolution? res = resolution ?? security.Resolution; var start = GetStartTimeAlgoTz(x, periods, resolution).ConvertToUtc(TimeZone); return CreateHistoryRequest(config, start, UtcTime.RoundDown(res.Value.ToTimeSpan()), resolution); }); 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 = CreateType(type); 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 = CreateHistoryRequest(config, start, end, 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 start = GetStartTimeAlgoTz(symbol, periods, resolution); var end = Time.RoundDown((resolution ?? Securities[symbol].Resolution).ToTimeSpan()); return History(type, symbol, start, end, 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) { SetBrokerageModel(new BrokerageModelPythonWrapper(model)); } /// /// 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 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 = null, string userName = null, string password = null) { 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); } /// /// Gets the symbols/string from a PyObject /// /// PyObject containing symbols /// List of symbols public List GetSymbolsFromPyObject(PyObject pyObject) { using (Py.GIL()) { // If not a PyList, convert it into one if (!PyList.IsListType(pyObject)) { var tmp = new PyList(); tmp.Append(pyObject); pyObject = tmp; } var symbols = new List(); foreach (PyObject item in pyObject) { var symbol = (Symbol)item.AsManagedObject(typeof(Symbol)); if (string.IsNullOrWhiteSpace(symbol.Value)) { continue; } symbols.Add(symbol); } return symbols.Count == 0 ? null : symbols; } } /// /// 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()); } /// /// 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; var type = (Type)x.GetPythonType().AsManagedObject(typeof(Type)); return (dynamic)x.AsManagedObject(type); }).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; } } /// /// Creates a type with a given name /// /// Python object /// Type object private Type CreateType(PyObject type) { using (Py.GIL()) { var an = new AssemblyName(type.Repr().Split('.')[1].Replace("\'>", "")); var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule"); return moduleBuilder.DefineType(an.Name, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, // If the type has IsAuthCodeSet member, it is a PythonQuandl type.HasAttr("IsAuthCodeSet") ? typeof(PythonQuandl) : typeof(PythonData)) .CreateType(); } } } }