/* * 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.Algorithm.Framework.Alphas; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Exceptions; using QuantConnect.Interfaces; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Python; 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.Storage; using QuantConnect.Statistics; using QuantConnect.Data.Market; using QuantConnect.Algorithm.Framework.Alphas.Analysis; using QuantConnect.Commands; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; namespace QuantConnect.AlgorithmFactory.Python.Wrappers { /// /// Creates and wraps the algorithm written in python. /// public class AlgorithmPythonWrapper : BasePythonWrapper, IAlgorithm { private readonly dynamic _onData; private readonly dynamic _onMarginCall; private readonly IAlgorithm _baseAlgorithm; // QCAlgorithm methods that might be implemented in the python algorithm: // We keep them to avoid the BasePythonWrapper caching and eventual lookup overhead since these methods are called quite frequently private dynamic _onBrokerageDisconnect; private dynamic _onBrokerageMessage; private dynamic _onBrokerageReconnect; private dynamic _onSplits; private dynamic _onDividends; private dynamic _onDelistings; private dynamic _onSymbolChangedEvents; private dynamic _onEndOfDay; private dynamic _onMarginCallWarning; private dynamic _onOrderEvent; private dynamic _onCommand; private dynamic _onAssignmentOrderEvent; private dynamic _onSecuritiesChanged; private dynamic _onFrameworkSecuritiesChanged; /// /// True if the underlying python algorithm implements "OnEndOfDay" /// public bool IsOnEndOfDayImplemented { get; } /// /// True if the underlying python algorithm implements "OnEndOfDay(symbol)" /// public bool IsOnEndOfDaySymbolImplemented { get; } /// /// constructor. /// Creates and wraps the algorithm written in python. /// /// Name of the module that can be found in the PYTHONPATH public AlgorithmPythonWrapper(string moduleName) : base(false) { try { using (Py.GIL()) { Logging.Log.Trace($"AlgorithmPythonWrapper(): Python version {PythonEngine.Version}: Importing python module {moduleName}"); var module = Py.Import(moduleName); Logging.Log.Trace($"AlgorithmPythonWrapper(): {moduleName} successfully imported."); var pyList = module.Dir(); foreach (var name in pyList) { Type type; var attr = module.GetAttr(name.ToString()); var repr = attr.Repr().GetStringBetweenChars('\'', '\''); if (repr.StartsWith(moduleName) && // Must be defined in the module attr.TryConvert(out type, true) && // Must be a Type typeof(QCAlgorithm).IsAssignableFrom(type)) // Must inherit from QCAlgorithm { Logging.Log.Trace("AlgorithmPythonWrapper(): Creating IAlgorithm instance."); SetPythonInstance(attr.Invoke()); var dynAlgorithm = Instance as dynamic; // Set pandas dynAlgorithm.SetPandasConverter(); // IAlgorithm reference for LEAN internal C# calls (without going from C# to Python and back) _baseAlgorithm = dynAlgorithm.AsManagedObject(type); // determines whether OnData method was defined or inherits from QCAlgorithm // If it is not, OnData from the base class will not be called _onData = Instance.GetPythonMethod("OnData"); _onMarginCall = Instance.GetPythonMethod("OnMarginCall"); using PyObject endOfDayMethod = Instance.GetPythonMethod("OnEndOfDay"); if (endOfDayMethod != null) { // Since we have a EOD method implemented // Determine which one it is by inspecting its arg count var argCount = endOfDayMethod.GetPythonArgCount(); switch (argCount) { case 0: // EOD() IsOnEndOfDayImplemented = true; break; case 1: // EOD(Symbol) IsOnEndOfDaySymbolImplemented = true; break; } // Its important to note that even if both are implemented // python will only use the last implemented, meaning only one will // be used and seen. } // Initialize the python methods _onBrokerageDisconnect = Instance.GetMethod("OnBrokerageDisconnect"); _onBrokerageMessage = Instance.GetMethod("OnBrokerageMessage"); _onBrokerageReconnect = Instance.GetMethod("OnBrokerageReconnect"); _onSplits = Instance.GetMethod("OnSplits"); _onDividends = Instance.GetMethod("OnDividends"); _onDelistings = Instance.GetMethod("OnDelistings"); _onSymbolChangedEvents = Instance.GetMethod("OnSymbolChangedEvents"); _onEndOfDay = Instance.GetMethod("OnEndOfDay"); _onCommand = Instance.GetMethod("OnCommand"); _onMarginCallWarning = Instance.GetMethod("OnMarginCallWarning"); _onOrderEvent = Instance.GetMethod("OnOrderEvent"); _onAssignmentOrderEvent = Instance.GetMethod("OnAssignmentOrderEvent"); _onSecuritiesChanged = Instance.GetMethod("OnSecuritiesChanged"); _onFrameworkSecuritiesChanged = Instance.GetMethod("OnFrameworkSecuritiesChanged"); } attr.Dispose(); } module.Dispose(); pyList.Dispose(); // If _algorithm could not be set, throw exception if (Instance == null) { throw new Exception("Please ensure that one class inherits from QCAlgorithm."); } } } catch (Exception e) { // perform exception interpretation for error in module import var interpreter = StackExceptionInterpreter.CreateFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()); e = interpreter.Interpret(e, interpreter); throw new Exception($"AlgorithmPythonWrapper(): {interpreter.GetExceptionMessageHeader(e)}"); } } /// /// AlgorithmId for the backtest /// public string AlgorithmId => _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 => _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 => _baseAlgorithm.BrokerageModel; /// /// Gets the brokerage name. /// public BrokerageName BrokerageName => _baseAlgorithm.BrokerageName; /// /// Gets the risk free interest rate model used to get the interest rates /// public IRiskFreeInterestRateModel RiskFreeInterestRateModel => _baseAlgorithm.RiskFreeInterestRateModel; /// /// Debug messages from the strategy: /// public ConcurrentQueue DebugMessages => _baseAlgorithm.DebugMessages; /// /// Get Requested Backtest End Date /// public DateTime EndDate => _baseAlgorithm.EndDate; /// /// Error messages from the strategy: /// public ConcurrentQueue ErrorMessages => _baseAlgorithm.ErrorMessages; /// /// Gets or sets the history provider for the algorithm /// public IHistoryProvider HistoryProvider { get { return _baseAlgorithm.HistoryProvider; } set { SetHistoryProvider(value); } } /// /// Gets whether or not this algorithm is still warming up /// public bool IsWarmingUp => _baseAlgorithm.IsWarmingUp; /// /// Algorithm is running on a live server. /// public bool LiveMode => _baseAlgorithm.LiveMode; /// /// Algorithm running mode. /// public AlgorithmMode AlgorithmMode => _baseAlgorithm.AlgorithmMode; /// /// Deployment target, either local or cloud. /// public DeploymentTarget DeploymentTarget => _baseAlgorithm.DeploymentTarget; /// /// Log messages from the strategy: /// public ConcurrentQueue LogMessages => _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; } } /// /// A list of tags associated with the algorithm or the backtest, useful for categorization /// public HashSet Tags { get { return _baseAlgorithm.Tags; } set { _baseAlgorithm.Tags = value; } } /// /// Event fired algorithm's name is changed /// public event AlgorithmEvent NameUpdated { add { _baseAlgorithm.NameUpdated += value; } remove { _baseAlgorithm.NameUpdated -= value; } } /// /// Event fired when the tag collection is updated /// public event AlgorithmEvent> TagsUpdated { add { _baseAlgorithm.TagsUpdated += value; } remove { _baseAlgorithm.TagsUpdated -= value; } } /// /// Notification manager for storing and processing live event messages /// public NotificationManager Notify => _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 => _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 => _baseAlgorithm.RuntimeStatistics; /// /// Gets schedule manager for adding/removing scheduled events /// public ScheduleManager Schedule => _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 => _baseAlgorithm.Securities; /// /// Gets an instance that is to be used to initialize newly created securities. /// public ISecurityInitializer SecurityInitializer => _baseAlgorithm.SecurityInitializer; /// /// Gets the Trade Builder to generate trades from executions /// public ITradeBuilder TradeBuilder => _baseAlgorithm.TradeBuilder; /// /// Gets the user settings for the algorithm /// public IAlgorithmSettings Settings => _baseAlgorithm.Settings; /// /// Gets the option chain provider, used to get the list of option contracts for an underlying symbol /// public IOptionChainProvider OptionChainProvider => _baseAlgorithm.OptionChainProvider; /// /// Gets the future chain provider, used to get the list of future contracts for an underlying symbol /// public IFutureChainProvider FutureChainProvider => _baseAlgorithm.FutureChainProvider; /// /// Gets the object store, used for persistence /// public ObjectStore ObjectStore => _baseAlgorithm.ObjectStore; /// /// Returns the current Slice object /// public Slice CurrentSlice => _baseAlgorithm.CurrentSlice; /// /// Algorithm start date for backtesting, set by the SetStartDate methods. /// public DateTime StartDate => _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 insight /// public event AlgorithmEvent InsightsGenerated { add { _baseAlgorithm.InsightsGenerated += value; } remove { _baseAlgorithm.InsightsGenerated -= value; } } /// /// Gets the time keeper instance /// public ITimeKeeper TimeKeeper => _baseAlgorithm.TimeKeeper; /// /// Data subscription manager controls the information and subscriptions the algorithms recieves. /// Subscription configurations can be added through the Subscription Manager. /// public SubscriptionManager SubscriptionManager => _baseAlgorithm.SubscriptionManager; /// /// The project id associated with this algorithm if any /// public int ProjectId { set { _baseAlgorithm.ProjectId = value; } get { return _baseAlgorithm.ProjectId; } } /// /// Current date/time in the algorithm's local time zone /// public DateTime Time => _baseAlgorithm.Time; /// /// Gets the time zone of the algorithm /// public DateTimeZone TimeZone => _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 => _baseAlgorithm.Transactions; /// /// Gets the collection of universes for the algorithm /// public UniverseManager UniverseManager => _baseAlgorithm.UniverseManager; /// /// Gets the subscription settings to be used when adding securities via universe selection /// public UniverseSettings UniverseSettings => _baseAlgorithm.UniverseSettings; /// /// Current date/time in UTC. /// public DateTime UtcTime => _baseAlgorithm.UtcTime; /// /// Gets the account currency /// public string AccountCurrency => _baseAlgorithm.AccountCurrency; /// /// Gets the insight manager /// public InsightManager Insights => _baseAlgorithm.Insights; /// /// Sets the statistics service instance to be used by the algorithm /// /// The statistics service instance public void SetStatisticsService(IStatisticsService statisticsService) => _baseAlgorithm.SetStatisticsService(statisticsService); /// /// The current statistics for the running algorithm. /// public StatisticsResults Statistics => _baseAlgorithm.Statistics; /// /// SignalExport - Allows sending export signals to different 3rd party API's. For example, it allows to send signals /// to Collective2, CrunchDAO and Numerai API's /// public SignalExportManager SignalExport => ((QCAlgorithm)_baseAlgorithm).SignalExport; /// /// Set a required SecurityType-symbol and resolution for algorithm /// /// SecurityType Enum: Equity, Commodity, FOREX or Future /// Symbol Representation of the MarketType, e.g. AAPL /// The of market data, Tick, Second, Minute, Hour, or Daily. /// 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 /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security public Security AddSecurity(SecurityType securityType, string symbol, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) => _baseAlgorithm.AddSecurity(securityType, symbol, resolution, market, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); /// /// Set a required SecurityType-symbol and resolution for algorithm /// /// The security Symbol /// Resolution of the MarketType required: MarketData, Second or Minute /// If true, returns the last available data even if none in that timeslice. /// leverage for this security /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security /// The continuous contract desired offset from the current front month. /// For example, 0 (default) will use the front month, 1 will use the back month contract /// The new Security that was added to the algorithm public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) => _baseAlgorithm.AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); /// /// 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 /// Use extended market hours data /// The new security public Future AddFutureContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = 0m, bool extendedMarketHours = false) => _baseAlgorithm.AddFutureContract(symbol, resolution, fillForward, leverage, extendedMarketHours); /// /// 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 /// Use extended market hours data /// The new security public Option AddOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = 0m, bool extendedMarketHours = false) => _baseAlgorithm.AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours); /// /// Invoked at the end of every time step. This allows the algorithm /// to process events before advancing to the next time step. /// public void OnEndOfTimeStep() { _baseAlgorithm.OnEndOfTimeStep(); } /// /// 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 IEnumerable GetChartUpdates(bool clearChartData = false) => _baseAlgorithm.GetChartUpdates(clearChartData); /// /// Gets whether or not this algorithm has been locked and fully initialized /// public bool GetLocked() => _baseAlgorithm.GetLocked(); /// /// Gets a read-only dictionary with all current parameters /// public IReadOnlyDictionary GetParameters() => _baseAlgorithm.GetParameters(); /// /// Gets the parameter with the specified name. If a parameter with the specified name does not exist, /// the given default value is returned if any, else null /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value public string GetParameter(string name, string defaultValue = null) => _baseAlgorithm.GetParameter(name, defaultValue); /// /// Gets the parameter with the specified name parsed as an integer. If a parameter with the specified name does not exist, /// or the conversion is not possible, the given default value is returned /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value public int GetParameter(string name, int defaultValue) => _baseAlgorithm.GetParameter(name, defaultValue); /// /// Gets the parameter with the specified name parsed as a double. If a parameter with the specified name does not exist, /// or the conversion is not possible, the given default value is returned /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value public double GetParameter(string name, double defaultValue) => _baseAlgorithm.GetParameter(name, defaultValue); /// /// Gets the parameter with the specified name parsed as a decimal. If a parameter with the specified name does not exist, /// or the conversion is not possible, the given default value is returned /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value public decimal GetParameter(string name, decimal defaultValue) => _baseAlgorithm.GetParameter(name, defaultValue); /// /// Initialise the Algorithm and Prepare Required Data: /// public void Initialize() { InvokeMethod(nameof(Initialize)); } /// /// Liquidate your portfolio holdings /// /// Specific asset to liquidate, defaults to all /// Flag to indicate if the symbols should be liquidated asynchronously /// Custom tag to know who is calling this /// Order properties to use public List Liquidate(Symbol symbol = null, bool asynchronous = false, string tag = "Liquidated", IOrderProperties orderProperties = null) => _baseAlgorithm.Liquidate(symbol, asynchronous, tag, orderProperties); /// /// 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() { _onBrokerageDisconnect(); } /// /// Brokerage message event handler. This method is called for all types of brokerage messages. /// public void OnBrokerageMessage(BrokerageMessageEvent messageEvent) { _onBrokerageMessage(messageEvent); } /// /// Brokerage reconnected event handler. This method is called when the brokerage connection is restored after a disconnection. /// public void OnBrokerageReconnect() { _onBrokerageReconnect(); } /// /// v3.0 Handler for all data types /// /// The current slice of data public void OnData(Slice slice) { if (_onData != null) { using (Py.GIL()) { _onData(slice); } } } /// /// Used to send data updates to algorithm framework models /// /// The current data slice public void OnFrameworkData(Slice slice) { _baseAlgorithm.OnFrameworkData(slice); } /// /// Event handler to be called when there's been a split event /// /// The current time slice splits public void OnSplits(Splits splits) { _onSplits(splits); } /// /// Event handler to be called when there's been a dividend event /// /// The current time slice dividends public void OnDividends(Dividends dividends) { _onDividends(dividends); } /// /// Event handler to be called when there's been a delistings event /// /// The current time slice delistings public void OnDelistings(Delistings delistings) { _onDelistings(delistings); } /// /// Event handler to be called when there's been a symbol changed event /// /// The current time slice symbol changed events public void OnSymbolChangedEvents(SymbolChangedEvents symbolsChanged) { _onSymbolChangedEvents(symbolsChanged); } /// /// Call this event at the end of the algorithm running. /// public void OnEndOfAlgorithm() { InvokeMethod(nameof(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. /// Deprecated because different assets have different market close times, /// and because Python does not support two methods with the same name [Obsolete("This method is deprecated. Please use this overload: OnEndOfDay(Symbol symbol)")] [StubsIgnore] public void OnEndOfDay() { try { _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.Contains("OnEndOfDay() missing 1 required positional argument")) { _baseAlgorithm.SetRunTimeError(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. [StubsAvoidImplicits] public void OnEndOfDay(Symbol symbol) { try { _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.Contains("OnEndOfDay() takes 1 positional argument but 2 were given")) { _baseAlgorithm.SetRunTimeError(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) { using (Py.GIL()) { var result = InvokeMethod(nameof(OnMarginCall), requests); if (_onMarginCall != null) { // If the method does not return or returns a non-iterable PyObject, throw an exception if (result == null || !result.IsIterable()) { throw new Exception("OnMarginCall must return a non-empty list of SubmitOrderRequest"); } requests.Clear(); using var iterator = result.GetIterator(); foreach (PyObject pyRequest in iterator) { 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"); } } } } /// /// Margin call warning event handler. This method is called when Portfolio.MarginRemaining is under 5% of your Portfolio.TotalPortfolioValue /// public void OnMarginCallWarning() { _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) { _onOrderEvent(newEvent); } /// /// Generic untyped command call handler /// /// The associated data /// True if success, false otherwise. Returning null will disable command feedback public bool? OnCommand(dynamic data) { return _onCommand(data); } /// /// Will submit an order request to the algorithm /// /// The request to submit /// Will run order prechecks, which include making sure the algorithm is not warming up, security is added and has data among others /// The order ticket public OrderTicket SubmitOrderRequest(SubmitOrderRequest request) { return _baseAlgorithm.SubmitOrderRequest(request); } /// /// 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) { _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) { _onSecuritiesChanged(changes); } /// /// Used to send security changes to algorithm framework models /// /// Security additions/removals for this time step public void OnFrameworkSecuritiesChanged(SecurityChanges changes) { _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() { InvokeMethod(nameof(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) => _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); /// /// Sets the account currency cash symbol this algorithm is to manage, as well /// as the starting cash in this currency if given /// /// Has to be called during before /// calling or adding any /// The account currency cash symbol to set /// The account currency starting cash to set public void SetAccountCurrency(string accountCurrency, decimal? startingCash = null) => _baseAlgorithm.SetAccountCurrency(accountCurrency, startingCash); /// /// 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 = 0) => _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 start date for the backtest /// /// Datetime Start date for backtest /// Must be less than end date and within data available public void SetStartDate(DateTime start) => _baseAlgorithm.SetStartDate(start); /// /// Set the end date for a backtest. /// /// Datetime value for end date /// Must be greater than the start date public void SetEndDate(DateTime end) => _baseAlgorithm.SetEndDate(end); /// /// Get the last known price using the history provider. /// Useful for seeding securities with the correct price /// /// object for which to retrieve historical data /// A single object with the last known price public BaseData GetLastKnownPrice(Security security) => _baseAlgorithm.GetLastKnownPrice(security); /// /// 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(); // notify the algorithm OnWarmupFinished(); } /// /// 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); /// /// Sets the algorithm running mode /// /// Algorithm mode public void SetAlgorithmMode(AlgorithmMode algorithmMode) => _baseAlgorithm.SetAlgorithmMode(algorithmMode); /// /// Sets the algorithm deployment target /// /// Deployment target public void SetDeploymentTarget(DeploymentTarget deploymentTarget) => _baseAlgorithm.SetDeploymentTarget(deploymentTarget); /// /// 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 algorithm 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); /// /// 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() { if (Instance == null) { return base.ToString(); } using (Py.GIL()) { return Instance.Repr(); } } /// /// Sets the current slice /// /// The Slice object public void SetCurrentSlice(Slice slice) { _baseAlgorithm.SetCurrentSlice(slice); } /// /// Provide the API for the algorithm. /// /// Initiated API public void SetApi(IApi api) => _baseAlgorithm.SetApi(api); /// /// Sets the object store /// /// The object store public void SetObjectStore(IObjectStore objectStore) => _baseAlgorithm.SetObjectStore(objectStore); /// /// Determines if the Symbol is shortable at the brokerage /// /// Symbol to check if shortable /// Order's quantity to check if it is currently shortable, taking into account current holdings and open orders /// Optionally the id of the order being updated. When updating an order /// we want to ignore it's submitted short quantity and use the new provided quantity to determine if we /// can perform the update /// True if the symbol can be shorted by the requested quantity public bool Shortable(Symbol symbol, decimal shortQuantity, int? updateOrderId = null) { return _baseAlgorithm.Shortable(symbol, shortQuantity, updateOrderId); } /// /// Gets the quantity shortable for the given asset /// /// /// Quantity shortable for the given asset. Zero if not /// shortable, or a number greater than zero if shortable. /// public long ShortableQuantity(Symbol symbol) { return _baseAlgorithm.ShortableQuantity(symbol); } /// /// Converts the string 'ticker' symbol into a full object /// This requires that the string 'ticker' has been added to the algorithm /// /// The ticker symbol. This should be the ticker symbol /// as it was added to the algorithm /// The symbol object mapped to the specified ticker public Symbol Symbol(string ticker) => _baseAlgorithm.Symbol(ticker); /// /// For the given symbol will resolve the ticker it used at the current algorithm date /// /// The symbol to get the ticker for /// The mapped ticker for a symbol public string Ticker(Symbol symbol) => _baseAlgorithm.Ticker(symbol); /// /// Sets name to the currently running backtest /// /// The name for the backtest public void SetName(string name) { _baseAlgorithm.SetName(name); } /// /// Adds a tag to the algorithm /// /// The tag to add public void AddTag(string tag) { _baseAlgorithm.AddTag(tag); } /// /// Sets the tags for the algorithm /// /// The tags public void SetTags(HashSet tags) { _baseAlgorithm.SetTags(tags); } /// /// Run a callback command instance /// /// The callback command instance /// The command result public CommandResultPacket RunCommand(CallbackCommand command) => _baseAlgorithm.RunCommand(command); /// /// Dispose of this instance /// public override void Dispose() { using var _ = Py.GIL(); _onBrokerageDisconnect?.Dispose(); _onBrokerageMessage?.Dispose(); _onBrokerageReconnect?.Dispose(); _onSplits?.Dispose(); _onDividends?.Dispose(); _onDelistings?.Dispose(); _onSymbolChangedEvents?.Dispose(); _onEndOfDay?.Dispose(); _onMarginCallWarning?.Dispose(); _onOrderEvent?.Dispose(); _onCommand?.Dispose(); _onAssignmentOrderEvent?.Dispose(); _onSecuritiesChanged?.Dispose(); _onFrameworkSecuritiesChanged?.Dispose(); _onData?.Dispose(); _onMarginCall?.Dispose(); base.Dispose(); } } }