/*
* 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;
}
set
{
_baseAlgorithm.Name = value;
}
}
///
/// 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 IOptionChainProvider OptionChainProvider
{
get
{
return _baseAlgorithm.OptionChainProvider;
}
}
///
/// 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 void SetOptionChainProvider(IOptionChainProvider optionChainProvider)
{
_baseAlgorithm.SetOptionChainProvider(optionChainProvider);
}
///
/// 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())
{
var pyRequests = _algorithm.OnMarginCall(requests) as PyObject;
// If the method does not return or returns a non-iterable PyObject, throw an exception
if (pyRequests == null || !pyRequests.IsIterable())
{
throw new Exception("OnMarginCall must return a non-empty list of SubmitOrderRequest");
}
requests.Clear();
foreach (PyObject pyRequest in pyRequests)
{
SubmitOrderRequest request;
if (TryConvert(pyRequest, out request))
{
requests.Add(request);
}
}
// If the PyObject is an empty list or its items are not SubmitOrderRequest objects, throw an exception
if (requests.Count == 0)
{
throw new Exception("OnMarginCall must return a non-empty list of SubmitOrderRequest");
}
}
}
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);
}
}
///
/// Tries to convert a PyObject into a C# object
///
/// Type of the C# object
/// PyObject to be converted
/// C# object that of type T
/// True if successful conversion
private bool TryConvert(PyObject pyObject, out T result)
{
result = default(T);
var type = (Type)pyObject.GetPythonType().AsManagedObject(typeof(Type));
if (type == typeof(T))
{
result = (T)pyObject.AsManagedObject(typeof(T));
}
return type == typeof(T);
}
///
/// Returns a that represents the current object.
///
///
public override string ToString()
{
return _algorithm == null ? base.ToString() : _algorithm.Repr();
}
}
}