/* * 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 NodaTime; using Python.Runtime; using QuantConnect.Algorithm; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Interfaces; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using System; using System.Collections.Concurrent; using System.Collections.Generic; namespace QuantConnect.AlgorithmFactory.Python.Wrappers { /// /// Wrapper for an IAlgorithm instance created in Python. /// All calls to python should be inside a "using (Py.GIL()) {/* Your code here */}" block. /// public class AlgorithmPythonWrapper : IAlgorithm { private readonly PyObject _util; private readonly dynamic _algorithm; private readonly QCAlgorithm _baseAlgorithm; /// /// constructor. /// Creates and wraps the algorithm written in python. /// /// Python module with the algorithm written in Python public AlgorithmPythonWrapper(PyObject module) { _algorithm = null; try { using (Py.GIL()) { if (!module.HasAttr("QCAlgorithm")) { return; } var baseClass = module.GetAttr("QCAlgorithm"); // Load module with util methods _util = ImportUtil(); var moduleName = module.Repr().Split('\'')[1]; foreach (var name in module.Dir()) { var attr = module.GetAttr(name.ToString()); if (attr.IsSubclass(baseClass) && attr.Repr().Contains(moduleName)) { attr.SetAttr("OnPythonData", _util.GetAttr("OnPythonData")); _algorithm = attr.Invoke(); // QCAlgorithm reference for LEAN internal C# calls (without going from C# to Python and back) _baseAlgorithm = (QCAlgorithm)_algorithm; // Set pandas _baseAlgorithm.SetPandas(); return; } } } } catch (Exception e) { Logging.Log.Error(e); } } /// /// Wrapper for in Python /// public string AlgorithmId { get { return _baseAlgorithm.AlgorithmId; } } /// /// Wrapper for in Python /// public IBenchmark Benchmark { get { return _baseAlgorithm.Benchmark; } } /// /// Wrapper for in Python /// public IBrokerageMessageHandler BrokerageMessageHandler { get { return _baseAlgorithm.BrokerageMessageHandler; } set { SetBrokerageMessageHandler(value); } } /// /// Wrapper for in Python /// public IBrokerageModel BrokerageModel { get { return _baseAlgorithm.BrokerageModel; } } /// /// Wrapper for in Python /// public ConcurrentQueue DebugMessages { get { return _baseAlgorithm.DebugMessages; } } /// /// Wrapper for in Python /// public DateTime EndDate { get { return _baseAlgorithm.EndDate; } } /// /// Wrapper for in Python /// public ConcurrentQueue ErrorMessages { get { return _baseAlgorithm.ErrorMessages; } } /// /// Wrapper for in Python /// public IHistoryProvider HistoryProvider { get { return _baseAlgorithm.HistoryProvider; } set { SetHistoryProvider(value); } } /// /// Wrapper for in Python /// public bool IsWarmingUp { get { return _baseAlgorithm.IsWarmingUp; } } /// /// Wrapper for in Python /// public bool LiveMode { get { return _baseAlgorithm.LiveMode; } } /// /// Wrapper for in Python /// public ConcurrentQueue LogMessages { get { return _baseAlgorithm.LogMessages; } } /// /// Wrapper for in Python /// public string Name { get { return _baseAlgorithm.Name; } } /// /// Wrapper for in Python /// public NotificationManager Notify { get { return _baseAlgorithm.Notify; } } /// /// Wrapper for in Python /// public SecurityPortfolioManager Portfolio { get { return _baseAlgorithm.Portfolio; } } /// /// Wrapper for in Python /// public Exception RunTimeError { get { return _baseAlgorithm.RunTimeError; } set { SetRunTimeError(value); } } /// /// Wrapper for in Python /// public ConcurrentDictionary RuntimeStatistics { get { return _baseAlgorithm.RuntimeStatistics; } } /// /// Wrapper for in Python /// public ScheduleManager Schedule { get { return _baseAlgorithm.Schedule; } } /// /// Wrapper for in Python /// public SecurityManager Securities { get { return _baseAlgorithm.Securities; } } /// /// Wrapper for in Python /// public ISecurityInitializer SecurityInitializer { get { return _baseAlgorithm.SecurityInitializer; } } /// /// Wrapper for in Python /// public ITradeBuilder TradeBuilder { get { return _baseAlgorithm.TradeBuilder; } } /// /// Wrapper for in Python /// public AlgorithmSettings Settings { get { return _baseAlgorithm.Settings; } } /// /// Wrapper for in Python /// public DateTime StartDate { get { return _baseAlgorithm.StartDate; } } /// /// Wrapper for in Python /// public AlgorithmStatus Status { get { return _baseAlgorithm.Status; } set { SetStatus(value); } } /// /// Wrapper for in Python /// /// public void SetStatus(AlgorithmStatus value) { _baseAlgorithm.SetStatus(value); } /// /// Wrapper for in Python /// /// public void SetAvailableDataTypes(Dictionary> availableDataTypes) { _baseAlgorithm.SetAvailableDataTypes(availableDataTypes); } /// /// Wrapper for in Python /// public SubscriptionManager SubscriptionManager { get { return _baseAlgorithm.SubscriptionManager; } } /// /// Wrapper for in Python /// public DateTime Time { get { return _baseAlgorithm.Time; } } /// /// Wrapper for in Python /// public DateTimeZone TimeZone { get { return _baseAlgorithm.TimeZone; } } /// /// Wrapper for in Python /// public SecurityTransactionManager Transactions { get { return _baseAlgorithm.Transactions; } } /// /// Wrapper for in Python /// public UniverseManager UniverseManager { get { return _baseAlgorithm.UniverseManager; } } /// /// Wrapper for in Python /// public UniverseSettings UniverseSettings { get { return _baseAlgorithm.UniverseSettings; } } /// /// Wrapper for in Python /// public DateTime UtcTime { get { return _baseAlgorithm.UtcTime; } } /// /// Wrapper for in Python /// /// /// /// /// /// /// /// /// public Security AddSecurity(SecurityType securityType, string symbol, Resolution resolution, string market, bool fillDataForward, decimal leverage, bool extendedMarketHours) { return _baseAlgorithm.AddSecurity(securityType, symbol, resolution, market, fillDataForward, leverage, extendedMarketHours); } /// /// Creates and adds a new single contract to the algorithm /// /// The futures contract symbol /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security public Future AddFutureContract(Symbol symbol, Resolution resolution = Resolution.Minute, bool fillDataForward = true, decimal leverage = 0m) { return _baseAlgorithm.AddFutureContract(symbol, resolution, fillDataForward, leverage); } /// /// Creates and adds a new single contract to the algorithm /// /// The option contract symbol /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security public Option AddOptionContract(Symbol symbol, Resolution resolution = Resolution.Minute, bool fillDataForward = true, decimal leverage = 0m) { return _baseAlgorithm.AddOptionContract(symbol, resolution, fillDataForward, leverage); } /// /// Wrapper for in Python /// /// public void Debug(string message) { _baseAlgorithm.Debug(message); } /// /// Wrapper for in Python /// /// public void Error(string message) { _baseAlgorithm.Error(message); } /// /// Wrapper for in Python /// /// /// public List GetChartUpdates(bool clearChartData = false) { return _baseAlgorithm.GetChartUpdates(clearChartData); } /// /// Wrapper for in Python /// /// public bool GetLocked() { return _baseAlgorithm.GetLocked(); } /// /// Wrapper for in Python /// /// /// public string GetParameter(string name) { return _baseAlgorithm.GetParameter(name); } /// /// Wrapper for in Python /// /// public IEnumerable GetWarmupHistoryRequests() { return _baseAlgorithm.GetWarmupHistoryRequests(); } /// /// Wrapper for in Python /// public void Initialize() { using (Py.GIL()) { _algorithm.Initialize(); } } /// /// Wrapper for in Python /// /// /// /// public List Liquidate(Symbol symbolToLiquidate = null, string tag = "Liquidated") { return _baseAlgorithm.Liquidate(symbolToLiquidate, tag); } /// /// Wrapper for in Python /// /// public void Log(string message) { _baseAlgorithm.Log(message); } /// /// Wrapper for in Python /// public void OnBrokerageDisconnect() { using (Py.GIL()) { _algorithm.OnBrokerageDisconnect(); } } /// /// Wrapper for in Python /// /// public void OnBrokerageMessage(BrokerageMessageEvent messageEvent) { using (Py.GIL()) { _algorithm.OnBrokerageMessage(messageEvent); } } /// /// Wrapper for in Python /// public void OnBrokerageReconnect() { using (Py.GIL()) { _algorithm.OnBrokerageReconnect(); } } /// /// Wrapper for in Python /// public void OnData(Slice slice) { using (Py.GIL()) { if (SubscriptionManager.HasCustomData) { _algorithm.OnPythonData(slice); } else { _algorithm.OnData(slice); } } } /// /// Wrapper for in Python /// public void OnEndOfAlgorithm() { using (Py.GIL()) { _algorithm.OnEndOfAlgorithm(); } } /// /// Wrapper for in Python /// public void OnEndOfDay() { using (Py.GIL()) { _algorithm.OnEndOfDay(); } } /// /// Wrapper for in Python /// /// public void OnEndOfDay(Symbol symbol) { using (Py.GIL()) { _algorithm.OnEndOfDay(symbol); } } /// /// Wrapper for in Python /// /// public void OnMarginCall(List requests) { try { using (Py.GIL()) { _algorithm.OnMarginCall(requests); } } catch (PythonException pythonException) { // Pythonnet generated error due to List conversion if (pythonException.Message.Equals("TypeError : No method matches given arguments")) { _baseAlgorithm.OnMarginCall(requests); } // User code generated error else { throw pythonException; } } } /// /// Wrapper for in Python /// public void OnMarginCallWarning() { using (Py.GIL()) { _algorithm.OnMarginCallWarning(); } } /// /// Wrapper for in Python /// /// public void OnOrderEvent(OrderEvent newEvent) { using (Py.GIL()) { _algorithm.OnOrderEvent(newEvent); } } /// /// Wrapper for in Python /// /// public void OnAssignmentOrderEvent(OrderEvent newEvent) { using (Py.GIL()) { _algorithm.OnAssignmentOrderEvent(newEvent); } } /// /// Wrapper for in Python /// /// public void OnSecuritiesChanged(SecurityChanges changes) { using (Py.GIL()) { _algorithm.OnSecuritiesChanged(changes); } } /// /// Wrapper for in Python /// public void PostInitialize() { _baseAlgorithm.PostInitialize(); } /// /// Wrapper for in Python /// /// /// public bool RemoveSecurity(Symbol symbol) { return _baseAlgorithm.RemoveSecurity(symbol); } /// /// Wrapper for in Python /// /// public void SetAlgorithmId(string algorithmId) { _baseAlgorithm.SetAlgorithmId(algorithmId); } /// /// Wrapper for in Python /// /// public void SetBrokerageMessageHandler(IBrokerageMessageHandler brokerageMessageHandler) { _baseAlgorithm.SetBrokerageMessageHandler(brokerageMessageHandler); } /// /// Wrapper for in Python /// /// public void SetBrokerageModel(IBrokerageModel brokerageModel) { _baseAlgorithm.SetBrokerageModel(brokerageModel); } /// /// Wrapper for in Python /// /// public void SetCash(decimal startingCash) { _baseAlgorithm.SetCash(startingCash); } /// /// Wrapper for in Python /// /// /// /// public void SetCash(string symbol, decimal startingCash, decimal conversionRate) { _baseAlgorithm.SetCash(symbol, startingCash, conversionRate); } /// /// Wrapper for in Python /// /// public void SetDateTime(DateTime time) { _baseAlgorithm.SetDateTime(time); } /// /// Wrapper for in Python /// /// public void SetRunTimeError(Exception exception) { _baseAlgorithm.SetRunTimeError(exception); } /// /// Wrapper for in Python /// public void SetFinishedWarmingUp() { _baseAlgorithm.SetFinishedWarmingUp(); } /// /// Wrapper for in Python /// /// public void SetHistoryProvider(IHistoryProvider historyProvider) { _baseAlgorithm.SetHistoryProvider(historyProvider); } /// /// Wrapper for in Python /// /// public void SetLiveMode(bool live) { _baseAlgorithm.SetLiveMode(live); } /// /// Wrapper for in Python /// public void SetLocked() { _baseAlgorithm.SetLocked(); } /// /// Wrapper for in Python /// /// public void SetMaximumOrders(int max) { _baseAlgorithm.SetMaximumOrders(max); } /// /// Wrapper for in Python /// /// public void SetParameters(Dictionary parameters) { _baseAlgorithm.SetParameters(parameters); } /// /// Creates Util module /// /// PyObject with utils private PyObject ImportUtil() { var code = "from clr import AddReference\n" + "AddReference(\"System\")\n" + "AddReference(\"QuantConnect.Common\")\n" + "import decimal\n" + // OnPythonData call OnData after converting the Slice object "def OnPythonData(self, data):\n" + " self.OnData(PythonSlice(data))\n" + // PythonSlice class "class PythonSlice(dict):\n" + " def __init__(self, slice):\n" + " for data in slice:\n" + " self[data.Key] = Data(data.Value)\n" + " self[data.Key.Value] = Data(data.Value)\n" + // Python Data class: Converts custom data (PythonData) into a python object''' "class Data(object):\n" + " def __init__(self, data):\n" + " members = [attr for attr in dir(data) if not callable(attr) and not attr.startswith(\"__\")]\n" + " for member in members:\n" + " setattr(self, member, getattr(data, member))\n" + " if not hasattr(data, 'GetStorageDictionary'): return\n" + " for kvp in data.GetStorageDictionary():\n" + " name = kvp.Key.replace('-',' ').replace('.',' ').title().replace(' ', '')\n" + " value = decimal.Decimal(kvp.Value) if isinstance(kvp.Value, float) else kvp.Value\n" + " setattr(self, name, value)"; using (Py.GIL()) { return PythonEngine.ModuleFromString("AlgorithmPythonUtil", code); } } /// /// Returns a that represents the current object. /// /// public override string ToString() { return _algorithm == null ? base.ToString() : _algorithm.Repr(); } } }