/* * 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; using QuantConnect.Algorithm.Framework.Alphas; namespace QuantConnect.AlgorithmFactory.Python.Wrappers { /// /// Creates and wraps the algorithm written in python. /// 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; // write events such that when the base handles an event it // will also invoke event handlers defined on this instance _baseAlgorithm.AlphasGenerated += AlphasGenerated; // Set pandas _baseAlgorithm.SetPandasConverter(); return; } } } } catch (Exception e) { Logging.Log.Error(e); } } /// /// AlgorithmId for the backtest /// public string AlgorithmId { get { return _baseAlgorithm.AlgorithmId; } } /// /// Gets the function used to define the benchmark. This function will return /// the value of the benchmark at a requested date/time /// public IBenchmark Benchmark { get { return _baseAlgorithm.Benchmark; } } /// /// Gets the brokerage message handler used to decide what to do /// with each message sent from the brokerage /// public IBrokerageMessageHandler BrokerageMessageHandler { get { return _baseAlgorithm.BrokerageMessageHandler; } set { SetBrokerageMessageHandler(value); } } /// /// Gets the brokerage model used to emulate a real brokerage /// public IBrokerageModel BrokerageModel { get { return _baseAlgorithm.BrokerageModel; } } /// /// Debug messages from the strategy: /// public ConcurrentQueue DebugMessages { get { return _baseAlgorithm.DebugMessages; } } /// /// Get Requested Backtest End Date /// public DateTime EndDate { get { return _baseAlgorithm.EndDate; } } /// /// Error messages from the strategy: /// public ConcurrentQueue ErrorMessages { get { return _baseAlgorithm.ErrorMessages; } } /// /// Gets or sets the history provider for the algorithm /// public IHistoryProvider HistoryProvider { get { return _baseAlgorithm.HistoryProvider; } set { SetHistoryProvider(value); } } /// /// Gets a flag indicating whether or not this algorithm uses the QCAlgorithmFramework /// public bool IsFrameworkAlgorithm { get { return _baseAlgorithm.IsFrameworkAlgorithm; } } /// /// Gets whether or not this algorithm is still warming up /// public bool IsWarmingUp { get { return _baseAlgorithm.IsWarmingUp; } } /// /// Algorithm is running on a live server. /// public bool LiveMode { get { return _baseAlgorithm.LiveMode; } } /// /// Log messages from the strategy: /// public ConcurrentQueue LogMessages { get { return _baseAlgorithm.LogMessages; } } /// /// Public name for the algorithm. /// /// Not currently used but preserved for API integrity public string Name { get { return _baseAlgorithm.Name; } set { _baseAlgorithm.Name = value; } } /// /// Notification manager for storing and processing live event messages /// public NotificationManager Notify { get { return _baseAlgorithm.Notify; } } /// /// Security portfolio management class provides wrapper and helper methods for the Security.Holdings class such as /// IsLong, IsShort, TotalProfit /// /// Portfolio is a wrapper and helper class encapsulating the Securities[].Holdings objects public SecurityPortfolioManager Portfolio { get { return _baseAlgorithm.Portfolio; } } /// /// Gets the run time error from the algorithm, or null if none was encountered. /// public Exception RunTimeError { get { return _baseAlgorithm.RunTimeError; } set { SetRunTimeError(value); } } /// /// Customizable dynamic statistics displayed during live trading: /// public ConcurrentDictionary RuntimeStatistics { get { return _baseAlgorithm.RuntimeStatistics; } } /// /// Gets schedule manager for adding/removing scheduled events /// public ScheduleManager Schedule { get { return _baseAlgorithm.Schedule; } } /// /// Security object collection class stores an array of objects representing representing each security/asset /// we have a subscription for. /// /// It is an IDictionary implementation and can be indexed by symbol public SecurityManager Securities { get { return _baseAlgorithm.Securities; } } /// /// Gets an instance that is to be used to initialize newly created securities. /// public ISecurityInitializer SecurityInitializer { get { return _baseAlgorithm.SecurityInitializer; } } /// /// Gets the Trade Builder to generate trades from executions /// public ITradeBuilder TradeBuilder { get { return _baseAlgorithm.TradeBuilder; } } /// /// Gets the user settings for the algorithm /// public AlgorithmSettings Settings { get { return _baseAlgorithm.Settings; } } /// /// Gets the option chain provider, used to get the list of option contracts for an underlying symbol /// public IOptionChainProvider OptionChainProvider { get { return _baseAlgorithm.OptionChainProvider; } } /// /// Gets the future chain provider, used to get the list of future contracts for an underlying symbol /// public IFutureChainProvider FutureChainProvider { get { return _baseAlgorithm.FutureChainProvider; } } /// /// Algorithm start date for backtesting, set by the SetStartDate methods. /// public DateTime StartDate { get { return _baseAlgorithm.StartDate; } } /// /// Gets or sets the current status of the algorithm /// public AlgorithmStatus Status { get { return _baseAlgorithm.Status; } set { SetStatus(value); } } /// /// Set the state of a live deployment /// /// Live deployment status public void SetStatus(AlgorithmStatus status) { _baseAlgorithm.SetStatus(status); } /// /// Set the available supported by each in /// /// >The different each supports public void SetAvailableDataTypes(Dictionary> availableDataTypes) { _baseAlgorithm.SetAvailableDataTypes(availableDataTypes); } /// /// Sets the option chain provider, used to get the list of option contracts for an underlying symbol /// /// The option chain provider public void SetOptionChainProvider(IOptionChainProvider optionChainProvider) { _baseAlgorithm.SetOptionChainProvider(optionChainProvider); } /// /// Sets the future chain provider, used to get the list of future contracts for an underlying symbol /// /// The future chain provider public void SetFutureChainProvider(IFutureChainProvider futureChainProvider) { _baseAlgorithm.SetFutureChainProvider(futureChainProvider); } /// /// Event fired when an algorithm generates a alpha /// public event AlgorithmEvent AlphasGenerated; /// /// Data subscription manager controls the information and subscriptions the algorithms recieves. /// Subscription configurations can be added through the Subscription Manager. /// public SubscriptionManager SubscriptionManager { get { return _baseAlgorithm.SubscriptionManager; } } /// /// Current date/time in the algorithm's local time zone /// public DateTime Time { get { return _baseAlgorithm.Time; } } /// /// Gets the time zone of the algorithm /// public DateTimeZone TimeZone { get { return _baseAlgorithm.TimeZone; } } /// /// Security transaction manager class controls the store and processing of orders. /// /// The orders and their associated events are accessible here. When a new OrderEvent is recieved the algorithm portfolio is updated. public SecurityTransactionManager Transactions { get { return _baseAlgorithm.Transactions; } } /// /// Gets the collection of universes for the algorithm /// public UniverseManager UniverseManager { get { return _baseAlgorithm.UniverseManager; } } /// /// Gets the subscription settings to be used when adding securities via universe selection /// public UniverseSettings UniverseSettings { get { return _baseAlgorithm.UniverseSettings; } } /// /// Current date/time in UTC. /// public DateTime UtcTime { get { return _baseAlgorithm.UtcTime; } } /// /// Set a required SecurityType-symbol and resolution for algorithm /// /// SecurityType Enum: Equity, Commodity, FOREX or Future /// Symbol Representation of the MarketType, e.g. AAPL /// Resolution of the MarketType required: MarketData, Second or Minute /// The market the requested security belongs to, such as 'usa' or 'fxcm' /// If true, returns the last available data even if none in that timeslice. /// leverage for this security /// ExtendedMarketHours send in data from 4am - 8pm, not used for FOREX 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); } /// /// Send debug message /// /// String message public void Debug(string message) { _baseAlgorithm.Debug(message); } /// /// Send an error message for the algorithm /// /// String message public void Error(string message) { _baseAlgorithm.Error(message); } /// /// Add a Chart object to algorithm collection /// /// Chart object to add to collection. public void AddChart(Chart chart) { _baseAlgorithm.AddChart(chart); } /// /// Get the chart updates since the last request: /// /// /// List of Chart Updates public List GetChartUpdates(bool clearChartData = false) { return _baseAlgorithm.GetChartUpdates(clearChartData); } /// /// Gets whether or not this algorithm has been locked and fully initialized /// public bool GetLocked() { return _baseAlgorithm.GetLocked(); } /// /// Gets the parameter with the specified name. If a parameter /// with the specified name does not exist, null is returned /// /// The name of the parameter to get /// The value of the specified parameter, or null if not found public string GetParameter(string name) { return _baseAlgorithm.GetParameter(name); } /// /// Gets the history requests required for provide warm up data for the algorithm /// /// public IEnumerable GetWarmupHistoryRequests() { return _baseAlgorithm.GetWarmupHistoryRequests(); } /// /// Initialise the Algorithm and Prepare Required Data: /// public void Initialize() { using (Py.GIL()) { _algorithm.Initialize(); } } /// /// Liquidate your portfolio holdings: /// /// Specific asset to liquidate, defaults to all. /// Custom tag to know who is calling this. /// list of order ids public List Liquidate(Symbol symbolToLiquidate = null, string tag = "Liquidated") { return _baseAlgorithm.Liquidate(symbolToLiquidate, tag); } /// /// Save entry to the Log /// /// String message public void Log(string message) { _baseAlgorithm.Log(message); } /// /// Brokerage disconnected event handler. This method is called when the brokerage connection is lost. /// public void OnBrokerageDisconnect() { using (Py.GIL()) { _algorithm.OnBrokerageDisconnect(); } } /// /// Brokerage message event handler. This method is called for all types of brokerage messages. /// public void OnBrokerageMessage(BrokerageMessageEvent messageEvent) { using (Py.GIL()) { _algorithm.OnBrokerageMessage(messageEvent); } } /// /// Brokerage reconnected event handler. This method is called when the brokerage connection is restored after a disconnection. /// public void OnBrokerageReconnect() { using (Py.GIL()) { _algorithm.OnBrokerageReconnect(); } } /// /// v3.0 Handler for all data types /// /// The current slice of data public void OnData(Slice slice) { using (Py.GIL()) { if (SubscriptionManager.HasCustomData) { _algorithm.OnPythonData(slice); } else { _algorithm.OnData(slice); } } } /// /// Used to send data updates to algorithm framework models /// /// The current data slice public void OnFrameworkData(Slice slice) { using (Py.GIL()) { _algorithm.OnFrameworkData(slice); } } /// /// Call this event at the end of the algorithm running. /// public void OnEndOfAlgorithm() { using (Py.GIL()) { _algorithm.OnEndOfAlgorithm(); } } /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// Method is called 10 minutes before closing to allow user to close out position. public void OnEndOfDay() { try { using (Py.GIL()) { _algorithm.OnEndOfDay(); } } // If OnEndOfDay is not defined in the script, but OnEndOfDay(Symbol) is, a python exception occurs // Only throws if there is an error in its implementation body catch (PythonException exception) { if (!exception.Message.Equals("TypeError : OnEndOfDay() takes exactly 2 arguments (1 given)")) { throw exception; } } } /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// /// This method is left for backwards compatibility and is invoked via , if that method is /// override then this method will not be called without a called to base.OnEndOfDay(string) /// /// Asset symbol for this end of day event. Forex and equities have different closing hours. public void OnEndOfDay(Symbol symbol) { try { using (Py.GIL()) { _algorithm.OnEndOfDay(symbol); } } // If OnEndOfDay(Symbol) is not defined in the script, but OnEndOfDay is, a python exception occurs // Only throws if there is an error in its implementation body catch (PythonException exception) { if (!exception.Message.Equals("TypeError : OnEndOfDay() takes exactly 1 argument (2 given)")) { throw exception; } } } /// /// Margin call event handler. This method is called right before the margin call orders are placed in the market. /// /// The orders to be executed to bring this algorithm within margin limits 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.Contains("TypeError : No method matches given arguments")) { _baseAlgorithm.OnMarginCall(requests); } // User code generated error else { throw pythonException; } } } /// /// Margin call warning event handler. This method is called when Portoflio.MarginRemaining is under 5% of your Portfolio.TotalPortfolioValue /// public void OnMarginCallWarning() { using (Py.GIL()) { _algorithm.OnMarginCallWarning(); } } /// /// EXPERTS ONLY:: [-!-Async Code-!-] /// New order event handler: on order status changes (filled, partially filled, cancelled etc). /// /// Event information public void OnOrderEvent(OrderEvent newEvent) { using (Py.GIL()) { _algorithm.OnOrderEvent(newEvent); } } /// /// Option assignment event handler. On an option assignment event for short legs the resulting information is passed to this method. /// /// Option exercise event details containing details of the assignment /// This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects public void OnAssignmentOrderEvent(OrderEvent assignmentEvent) { using (Py.GIL()) { _algorithm.OnAssignmentOrderEvent(assignmentEvent); } } /// /// Event fired each time the we add/remove securities from the data feed /// /// Security additions/removals for this time step public void OnSecuritiesChanged(SecurityChanges changes) { using (Py.GIL()) { _algorithm.OnSecuritiesChanged(changes); } } /// /// Used to send security changes to algorithm framework models /// /// Security additions/removals for this time step public void OnFrameworkSecuritiesChanged(SecurityChanges changes) { using (Py.GIL()) { _algorithm.OnFrameworkSecuritiesChanged(changes); } } /// /// Called by setup handlers after Initialize and allows the algorithm a chance to organize /// the data gather in the Initialize method /// public void PostInitialize() { _baseAlgorithm.PostInitialize(); } /// /// Called when the algorithm has completed initialization and warm up. /// public void OnWarmupFinished() { using (Py.GIL()) { _algorithm.OnWarmupFinished(); } } /// /// Removes the security with the specified symbol. This will cancel all /// open orders and then liquidate any existing holdings /// /// The symbol of the security to be removed public bool RemoveSecurity(Symbol symbol) { return _baseAlgorithm.RemoveSecurity(symbol); } /// /// Set the algorithm Id for this backtest or live run. This can be used to identify the order and equity records. /// /// unique 32 character identifier for backtest or live server public void SetAlgorithmId(string algorithmId) { _baseAlgorithm.SetAlgorithmId(algorithmId); } /// /// Sets the implementation used to handle messages from the brokerage. /// The default implementation will forward messages to debug or error /// and when a occurs, the algorithm /// is stopped. /// /// The message handler to use public void SetBrokerageMessageHandler(IBrokerageMessageHandler handler) { _baseAlgorithm.SetBrokerageMessageHandler(handler); } /// /// Sets the brokerage model used to resolve transaction models, settlement models, /// and brokerage specified ordering behaviors. /// /// The brokerage model used to emulate the real /// brokerage public void SetBrokerageModel(IBrokerageModel brokerageModel) { _baseAlgorithm.SetBrokerageModel(brokerageModel); } /// /// Set the starting capital for the strategy /// /// decimal starting capital, default $100,000 public void SetCash(decimal startingCash) { _baseAlgorithm.SetCash(startingCash); } /// /// Set the cash for the specified symbol /// /// The cash symbol to set /// Decimal cash value of portfolio /// The current conversion rate for the public void SetCash(string symbol, decimal startingCash, decimal conversionRate) { _baseAlgorithm.SetCash(symbol, startingCash, conversionRate); } /// /// Set the DateTime Frontier: This is the master time and is /// /// public void SetDateTime(DateTime time) { _baseAlgorithm.SetDateTime(time); } /// /// Set the runtime error /// /// Represents error that occur during execution public void SetRunTimeError(Exception exception) { _baseAlgorithm.SetRunTimeError(exception); } /// /// Sets to false to indicate this algorithm has finished its warm up /// public void SetFinishedWarmingUp() { _baseAlgorithm.SetFinishedWarmingUp(); } /// /// Set the historical data provider /// /// Historical data provider public void SetHistoryProvider(IHistoryProvider historyProvider) { _baseAlgorithm.SetHistoryProvider(historyProvider); } /// /// Set live mode state of the algorithm run: Public setter for the algorithm property LiveMode. /// /// Bool live mode flag public void SetLiveMode(bool live) { _baseAlgorithm.SetLiveMode(live); } /// /// Set the algorithm as initialized and locked. No more cash or security changes. /// public void SetLocked() { _baseAlgorithm.SetLocked(); } /// /// Set the maximum number of orders the algortihm is allowed to process. /// /// Maximum order count int public void SetMaximumOrders(int max) { _baseAlgorithm.SetMaximumOrders(max); } /// /// Sets the parameters from the dictionary /// /// Dictionary containing the parameter names to values 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(); } } }