Lean  $LEAN_TAG$
AlgorithmPythonWrapper.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using NodaTime;
17 using Python.Runtime;
22 using QuantConnect.Data;
27 using QuantConnect.Orders;
28 using QuantConnect.Python;
33 using System;
34 using System.Collections.Concurrent;
35 using System.Collections.Generic;
36 using QuantConnect.Storage;
42 
44 {
45  /// <summary>
46  /// Creates and wraps the algorithm written in python.
47  /// </summary>
49  {
50  private readonly PyObject _algorithm;
51  private readonly dynamic _onData;
52  private readonly dynamic _onMarginCall;
53  private readonly IAlgorithm _baseAlgorithm;
54 
55  // QCAlgorithm methods that might be implemented in the python algorithm:
56  // We keep them to avoid the BasePythonWrapper caching and eventual lookup overhead since these methods are called quite frequently
57  private dynamic _onBrokerageDisconnect;
58  private dynamic _onBrokerageMessage;
59  private dynamic _onBrokerageReconnect;
60  private dynamic _onSplits;
61  private dynamic _onDividends;
62  private dynamic _onDelistings;
63  private dynamic _onSymbolChangedEvents;
64  private dynamic _onEndOfDay;
65  private dynamic _onMarginCallWarning;
66  private dynamic _onOrderEvent;
67  private dynamic _onCommand;
68  private dynamic _onAssignmentOrderEvent;
69  private dynamic _onSecuritiesChanged;
70  private dynamic _onFrameworkSecuritiesChanged;
71 
72  /// <summary>
73  /// True if the underlying python algorithm implements "OnEndOfDay"
74  /// </summary>
75  public bool IsOnEndOfDayImplemented { get; }
76 
77  /// <summary>
78  /// True if the underlying python algorithm implements "OnEndOfDay(symbol)"
79  /// </summary>
80  public bool IsOnEndOfDaySymbolImplemented { get; }
81 
82  /// <summary>
83  /// <see cref = "AlgorithmPythonWrapper"/> constructor.
84  /// Creates and wraps the algorithm written in python.
85  /// </summary>
86  /// <param name="moduleName">Name of the module that can be found in the PYTHONPATH</param>
87  public AlgorithmPythonWrapper(string moduleName)
88  : base(false)
89  {
90  try
91  {
92  using (Py.GIL())
93  {
94  Logging.Log.Trace($"AlgorithmPythonWrapper(): Python version {PythonEngine.Version}: Importing python module {moduleName}");
95 
96  var module = Py.Import(moduleName);
97 
98  Logging.Log.Trace($"AlgorithmPythonWrapper(): {moduleName} successfully imported.");
99 
100  var pyList = module.Dir();
101  foreach (var name in pyList)
102  {
103  Type type;
104  var attr = module.GetAttr(name.ToString());
105  var repr = attr.Repr().GetStringBetweenChars('\'', '\'');
106 
107  if (repr.StartsWith(moduleName) && // Must be defined in the module
108  attr.TryConvert(out type, true) && // Must be a Type
109  typeof(QCAlgorithm).IsAssignableFrom(type)) // Must inherit from QCAlgorithm
110  {
111  Logging.Log.Trace("AlgorithmPythonWrapper(): Creating IAlgorithm instance.");
112 
113  _algorithm = attr.Invoke();
114  SetPythonInstance(_algorithm);
115  var dynAlgorithm = _algorithm as dynamic;
116 
117  // Set pandas
118  dynAlgorithm.SetPandasConverter();
119 
120  // IAlgorithm reference for LEAN internal C# calls (without going from C# to Python and back)
121  _baseAlgorithm = dynAlgorithm.AsManagedObject(type);
122 
123  // determines whether OnData method was defined or inherits from QCAlgorithm
124  // If it is not, OnData from the base class will not be called
125  _onData = _algorithm.GetPythonMethod("OnData");
126 
127  _onMarginCall = _algorithm.GetPythonMethod("OnMarginCall");
128 
129  PyObject endOfDayMethod = _algorithm.GetPythonMethod("OnEndOfDay");
130  if (endOfDayMethod != null)
131  {
132  // Since we have a EOD method implemented
133  // Determine which one it is by inspecting its arg count
134  var argCount = endOfDayMethod.GetPythonArgCount();
135  switch (argCount)
136  {
137  case 0: // EOD()
139  break;
140  case 1: // EOD(Symbol)
142  break;
143  }
144 
145  // Its important to note that even if both are implemented
146  // python will only use the last implemented, meaning only one will
147  // be used and seen.
148  }
149 
150  // Initialize the python methods
151  _onBrokerageDisconnect = _algorithm.GetMethod("OnBrokerageDisconnect");
152  _onBrokerageMessage = _algorithm.GetMethod("OnBrokerageMessage");
153  _onBrokerageReconnect = _algorithm.GetMethod("OnBrokerageReconnect");
154  _onSplits = _algorithm.GetMethod("OnSplits");
155  _onDividends = _algorithm.GetMethod("OnDividends");
156  _onDelistings = _algorithm.GetMethod("OnDelistings");
157  _onSymbolChangedEvents = _algorithm.GetMethod("OnSymbolChangedEvents");
158  _onEndOfDay = _algorithm.GetMethod("OnEndOfDay");
159  _onCommand = _algorithm.GetMethod("OnCommand");
160  _onMarginCallWarning = _algorithm.GetMethod("OnMarginCallWarning");
161  _onOrderEvent = _algorithm.GetMethod("OnOrderEvent");
162  _onAssignmentOrderEvent = _algorithm.GetMethod("OnAssignmentOrderEvent");
163  _onSecuritiesChanged = _algorithm.GetMethod("OnSecuritiesChanged");
164  _onFrameworkSecuritiesChanged = _algorithm.GetMethod("OnFrameworkSecuritiesChanged");
165  }
166  attr.Dispose();
167  }
168  module.Dispose();
169  pyList.Dispose();
170  // If _algorithm could not be set, throw exception
171  if (_algorithm == null)
172  {
173  throw new Exception("Please ensure that one class inherits from QCAlgorithm.");
174  }
175  }
176  }
177  catch (Exception e)
178  {
179  // perform exception interpretation for error in module import
180  var interpreter = StackExceptionInterpreter.CreateFromAssemblies(AppDomain.CurrentDomain.GetAssemblies());
181  e = interpreter.Interpret(e, interpreter);
182 
183  throw new Exception($"AlgorithmPythonWrapper(): {interpreter.GetExceptionMessageHeader(e)}");
184  }
185  }
186 
187  /// <summary>
188  /// AlgorithmId for the backtest
189  /// </summary>
190  public string AlgorithmId => _baseAlgorithm.AlgorithmId;
191 
192  /// <summary>
193  /// Gets the function used to define the benchmark. This function will return
194  /// the value of the benchmark at a requested date/time
195  /// </summary>
196  public IBenchmark Benchmark => _baseAlgorithm.Benchmark;
197 
198  /// <summary>
199  /// Gets the brokerage message handler used to decide what to do
200  /// with each message sent from the brokerage
201  /// </summary>
203  {
204  get
205  {
206  return _baseAlgorithm.BrokerageMessageHandler;
207  }
208 
209  set
210  {
212  }
213  }
214 
215  /// <summary>
216  /// Gets the brokerage model used to emulate a real brokerage
217  /// </summary>
218  public IBrokerageModel BrokerageModel => _baseAlgorithm.BrokerageModel;
219 
220  /// <summary>
221  /// Gets the brokerage name.
222  /// </summary>
223  public BrokerageName BrokerageName => _baseAlgorithm.BrokerageName;
224 
225  /// <summary>
226  /// Gets the risk free interest rate model used to get the interest rates
227  /// </summary>
228  public IRiskFreeInterestRateModel RiskFreeInterestRateModel => _baseAlgorithm.RiskFreeInterestRateModel;
229 
230  /// <summary>
231  /// Debug messages from the strategy:
232  /// </summary>
233  public ConcurrentQueue<string> DebugMessages => _baseAlgorithm.DebugMessages;
234 
235  /// <summary>
236  /// Get Requested Backtest End Date
237  /// </summary>
238  public DateTime EndDate => _baseAlgorithm.EndDate;
239 
240  /// <summary>
241  /// Error messages from the strategy:
242  /// </summary>
243  public ConcurrentQueue<string> ErrorMessages => _baseAlgorithm.ErrorMessages;
244 
245  /// <summary>
246  /// Gets or sets the history provider for the algorithm
247  /// </summary>
249  {
250  get
251  {
252  return _baseAlgorithm.HistoryProvider;
253  }
254 
255  set
256  {
257  SetHistoryProvider(value);
258  }
259  }
260 
261  /// <summary>
262  /// Gets whether or not this algorithm is still warming up
263  /// </summary>
264  public bool IsWarmingUp => _baseAlgorithm.IsWarmingUp;
265 
266  /// <summary>
267  /// Algorithm is running on a live server.
268  /// </summary>
269  public bool LiveMode => _baseAlgorithm.LiveMode;
270 
271  /// <summary>
272  /// Algorithm running mode.
273  /// </summary>
274  public AlgorithmMode AlgorithmMode => _baseAlgorithm.AlgorithmMode;
275 
276  /// <summary>
277  /// Deployment target, either local or cloud.
278  /// </summary>
279  public DeploymentTarget DeploymentTarget => _baseAlgorithm.DeploymentTarget;
280 
281  /// <summary>
282  /// Log messages from the strategy:
283  /// </summary>
284  public ConcurrentQueue<string> LogMessages => _baseAlgorithm.LogMessages;
285 
286  /// <summary>
287  /// Public name for the algorithm.
288  /// </summary>
289  /// <remarks>Not currently used but preserved for API integrity</remarks>
290  public string Name
291  {
292  get
293  {
294  return _baseAlgorithm.Name;
295  }
296  set
297  {
298  _baseAlgorithm.Name = value;
299  }
300  }
301 
302  /// <summary>
303  /// A list of tags associated with the algorithm or the backtest, useful for categorization
304  /// </summary>
305  public HashSet<string> Tags
306  {
307  get
308  {
309  return _baseAlgorithm.Tags;
310  }
311  set
312  {
313  _baseAlgorithm.Tags = value;
314  }
315  }
316 
317  /// <summary>
318  /// Event fired algorithm's name is changed
319  /// </summary>
320  public event AlgorithmEvent<string> NameUpdated
321  {
322  add
323  {
324  _baseAlgorithm.NameUpdated += value;
325  }
326 
327  remove
328  {
329  _baseAlgorithm.NameUpdated -= value;
330  }
331  }
332 
333  /// <summary>
334  /// Event fired when the tag collection is updated
335  /// </summary>
336  public event AlgorithmEvent<HashSet<string>> TagsUpdated
337  {
338  add
339  {
340  _baseAlgorithm.TagsUpdated += value;
341  }
342 
343  remove
344  {
345  _baseAlgorithm.TagsUpdated -= value;
346  }
347  }
348 
349  /// <summary>
350  /// Notification manager for storing and processing live event messages
351  /// </summary>
352  public NotificationManager Notify => _baseAlgorithm.Notify;
353 
354  /// <summary>
355  /// Security portfolio management class provides wrapper and helper methods for the Security.Holdings class such as
356  /// IsLong, IsShort, TotalProfit
357  /// </summary>
358  /// <remarks>Portfolio is a wrapper and helper class encapsulating the Securities[].Holdings objects</remarks>
359  public SecurityPortfolioManager Portfolio => _baseAlgorithm.Portfolio;
360 
361  /// <summary>
362  /// Gets the run time error from the algorithm, or null if none was encountered.
363  /// </summary>
364  public Exception RunTimeError
365  {
366  get
367  {
368  return _baseAlgorithm.RunTimeError;
369  }
370 
371  set
372  {
373  SetRunTimeError(value);
374  }
375  }
376 
377  /// <summary>
378  /// Customizable dynamic statistics displayed during live trading:
379  /// </summary>
380  public ConcurrentDictionary<string, string> RuntimeStatistics => _baseAlgorithm.RuntimeStatistics;
381 
382  /// <summary>
383  /// Gets schedule manager for adding/removing scheduled events
384  /// </summary>
385  public ScheduleManager Schedule => _baseAlgorithm.Schedule;
386 
387  /// <summary>
388  /// Security object collection class stores an array of objects representing representing each security/asset
389  /// we have a subscription for.
390  /// </summary>
391  /// <remarks>It is an IDictionary implementation and can be indexed by symbol</remarks>
392  public SecurityManager Securities => _baseAlgorithm.Securities;
393 
394  /// <summary>
395  /// Gets an instance that is to be used to initialize newly created securities.
396  /// </summary>
397  public ISecurityInitializer SecurityInitializer => _baseAlgorithm.SecurityInitializer;
398 
399  /// <summary>
400  /// Gets the Trade Builder to generate trades from executions
401  /// </summary>
402  public ITradeBuilder TradeBuilder => _baseAlgorithm.TradeBuilder;
403 
404  /// <summary>
405  /// Gets the user settings for the algorithm
406  /// </summary>
407  public IAlgorithmSettings Settings => _baseAlgorithm.Settings;
408 
409  /// <summary>
410  /// Gets the option chain provider, used to get the list of option contracts for an underlying symbol
411  /// </summary>
412  public IOptionChainProvider OptionChainProvider => _baseAlgorithm.OptionChainProvider;
413 
414  /// <summary>
415  /// Gets the future chain provider, used to get the list of future contracts for an underlying symbol
416  /// </summary>
417  public IFutureChainProvider FutureChainProvider => _baseAlgorithm.FutureChainProvider;
418 
419  /// <summary>
420  /// Gets the object store, used for persistence
421  /// </summary>
422  public ObjectStore ObjectStore => _baseAlgorithm.ObjectStore;
423 
424  /// <summary>
425  /// Returns the current Slice object
426  /// </summary>
427  public Slice CurrentSlice => _baseAlgorithm.CurrentSlice;
428 
429  /// <summary>
430  /// Algorithm start date for backtesting, set by the SetStartDate methods.
431  /// </summary>
432  public DateTime StartDate => _baseAlgorithm.StartDate;
433 
434  /// <summary>
435  /// Gets or sets the current status of the algorithm
436  /// </summary>
437  public AlgorithmStatus Status
438  {
439  get
440  {
441  return _baseAlgorithm.Status;
442  }
443 
444  set
445  {
446  SetStatus(value);
447  }
448  }
449 
450  /// <summary>
451  /// Set the state of a live deployment
452  /// </summary>
453  /// <param name="status">Live deployment status</param>
454  public void SetStatus(AlgorithmStatus status) => _baseAlgorithm.SetStatus(status);
455 
456  /// <summary>
457  /// Set the available <see cref="TickType"/> supported by each <see cref="SecurityType"/> in <see cref="SecurityManager"/>
458  /// </summary>
459  /// <param name="availableDataTypes">>The different <see cref="TickType"/> each <see cref="Security"/> supports</param>
460  public void SetAvailableDataTypes(Dictionary<SecurityType, List<TickType>> availableDataTypes) => _baseAlgorithm.SetAvailableDataTypes(availableDataTypes);
461 
462  /// <summary>
463  /// Sets the option chain provider, used to get the list of option contracts for an underlying symbol
464  /// </summary>
465  /// <param name="optionChainProvider">The option chain provider</param>
466  public void SetOptionChainProvider(IOptionChainProvider optionChainProvider) => _baseAlgorithm.SetOptionChainProvider(optionChainProvider);
467 
468  /// <summary>
469  /// Sets the future chain provider, used to get the list of future contracts for an underlying symbol
470  /// </summary>
471  /// <param name="futureChainProvider">The future chain provider</param>
472  public void SetFutureChainProvider(IFutureChainProvider futureChainProvider) => _baseAlgorithm.SetFutureChainProvider(futureChainProvider);
473 
474  /// <summary>
475  /// Event fired when an algorithm generates a insight
476  /// </summary>
477  public event AlgorithmEvent<GeneratedInsightsCollection> InsightsGenerated
478  {
479  add
480  {
481  _baseAlgorithm.InsightsGenerated += value;
482  }
483 
484  remove
485  {
486  _baseAlgorithm.InsightsGenerated -= value;
487  }
488  }
489 
490  /// <summary>
491  /// Gets the time keeper instance
492  /// </summary>
493  public ITimeKeeper TimeKeeper => _baseAlgorithm.TimeKeeper;
494 
495  /// <summary>
496  /// Data subscription manager controls the information and subscriptions the algorithms recieves.
497  /// Subscription configurations can be added through the Subscription Manager.
498  /// </summary>
500 
501  /// <summary>
502  /// The project id associated with this algorithm if any
503  /// </summary>
504  public int ProjectId
505  {
506  set
507  {
508  _baseAlgorithm.ProjectId = value;
509  }
510  get
511  {
512  return _baseAlgorithm.ProjectId;
513  }
514  }
515 
516  /// <summary>
517  /// Current date/time in the algorithm's local time zone
518  /// </summary>
519  public DateTime Time => _baseAlgorithm.Time;
520 
521  /// <summary>
522  /// Gets the time zone of the algorithm
523  /// </summary>
524  public DateTimeZone TimeZone => _baseAlgorithm.TimeZone;
525 
526  /// <summary>
527  /// Security transaction manager class controls the store and processing of orders.
528  /// </summary>
529  /// <remarks>The orders and their associated events are accessible here. When a new OrderEvent is recieved the algorithm portfolio is updated.</remarks>
530  public SecurityTransactionManager Transactions => _baseAlgorithm.Transactions;
531 
532  /// <summary>
533  /// Gets the collection of universes for the algorithm
534  /// </summary>
536 
537  /// <summary>
538  /// Gets the subscription settings to be used when adding securities via universe selection
539  /// </summary>
541 
542  /// <summary>
543  /// Current date/time in UTC.
544  /// </summary>
545  public DateTime UtcTime => _baseAlgorithm.UtcTime;
546 
547  /// <summary>
548  /// Gets the account currency
549  /// </summary>
550  public string AccountCurrency => _baseAlgorithm.AccountCurrency;
551 
552  /// <summary>
553  /// Gets the insight manager
554  /// </summary>
555  public InsightManager Insights => _baseAlgorithm.Insights;
556 
557  /// <summary>
558  /// Sets the statistics service instance to be used by the algorithm
559  /// </summary>
560  /// <param name="statisticsService">The statistics service instance</param>
561  public void SetStatisticsService(IStatisticsService statisticsService) => _baseAlgorithm.SetStatisticsService(statisticsService);
562 
563  /// <summary>
564  /// The current statistics for the running algorithm.
565  /// </summary>
566  public StatisticsResults Statistics => _baseAlgorithm.Statistics;
567 
568  /// <summary>
569  /// SignalExport - Allows sending export signals to different 3rd party API's. For example, it allows to send signals
570  /// to Collective2, CrunchDAO and Numerai API's
571  /// </summary>
572  public SignalExportManager SignalExport => ((QCAlgorithm)_baseAlgorithm).SignalExport;
573 
574  /// <summary>
575  /// Set a required SecurityType-symbol and resolution for algorithm
576  /// </summary>
577  /// <param name="securityType">SecurityType Enum: Equity, Commodity, FOREX or Future</param>
578  /// <param name="symbol">Symbol Representation of the MarketType, e.g. AAPL</param>
579  /// <param name="resolution">The <see cref="Resolution"/> of market data, Tick, Second, Minute, Hour, or Daily.</param>
580  /// <param name="market">The market the requested security belongs to, such as 'usa' or 'fxcm'</param>
581  /// <param name="fillForward">If true, returns the last available data even if none in that timeslice.</param>
582  /// <param name="leverage">leverage for this security</param>
583  /// <param name="extendedMarketHours">Use extended market hours data</param>
584  /// <param name="dataMappingMode">The contract mapping mode to use for the security</param>
585  /// <param name="dataNormalizationMode">The price scaling mode to use for the security</param>
586  public Security AddSecurity(SecurityType securityType, string symbol, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours,
587  DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null)
588  => _baseAlgorithm.AddSecurity(securityType, symbol, resolution, market, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
589 
590 
591  /// <summary>
592  /// Set a required SecurityType-symbol and resolution for algorithm
593  /// </summary>
594  /// <param name="symbol">The security Symbol</param>
595  /// <param name="resolution">Resolution of the MarketType required: MarketData, Second or Minute</param>
596  /// <param name="fillForward">If true, returns the last available data even if none in that timeslice.</param>
597  /// <param name="leverage">leverage for this security</param>
598  /// <param name="extendedMarketHours">Use extended market hours data</param>
599  /// <param name="dataMappingMode">The contract mapping mode to use for the security</param>
600  /// <param name="dataNormalizationMode">The price scaling mode to use for the security</param>
601  /// <param name="contractDepthOffset">The continuous contract desired offset from the current front month.
602  /// For example, 0 (default) will use the front month, 1 will use the back month contract</param>
603  /// <returns>The new Security that was added to the algorithm</returns>
604  public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false,
605  DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0)
606  => _baseAlgorithm.AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset);
607 
608  /// <summary>
609  /// Creates and adds a new single <see cref="Future"/> contract to the algorithm
610  /// </summary>
611  /// <param name="symbol">The futures contract symbol</param>
612  /// <param name="resolution">The <see cref="Resolution"/> of market data, Tick, Second, Minute, Hour, or Daily. Default is <see cref="Resolution.Minute"/></param>
613  /// <param name="fillForward">If true, returns the last available data even if none in that timeslice. Default is <value>true</value></param>
614  /// <param name="leverage">The requested leverage for this equity. Default is set by <see cref="SecurityInitializer"/></param>
615  /// <param name="extendedMarketHours">Use extended market hours data</param>
616  /// <returns>The new <see cref="Future"/> security</returns>
617  public Future AddFutureContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = 0m,
618  bool extendedMarketHours = false)
619  => _baseAlgorithm.AddFutureContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
620 
621  /// <summary>
622  /// Creates and adds a new single <see cref="Option"/> contract to the algorithm
623  /// </summary>
624  /// <param name="symbol">The option contract symbol</param>
625  /// <param name="resolution">The <see cref="Resolution"/> of market data, Tick, Second, Minute, Hour, or Daily. Default is <see cref="Resolution.Minute"/></param>
626  /// <param name="fillForward">If true, returns the last available data even if none in that timeslice. Default is <value>true</value></param>
627  /// <param name="leverage">The requested leverage for this equity. Default is set by <see cref="SecurityInitializer"/></param>
628  /// <param name="extendedMarketHours">Use extended market hours data</param>
629  /// <returns>The new <see cref="Option"/> security</returns>
630  public Option AddOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = 0m, bool extendedMarketHours = false)
631  => _baseAlgorithm.AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
632 
633  /// <summary>
634  /// Invoked at the end of every time step. This allows the algorithm
635  /// to process events before advancing to the next time step.
636  /// </summary>
637  public void OnEndOfTimeStep()
638  {
639  _baseAlgorithm.OnEndOfTimeStep();
640  }
641 
642  /// <summary>
643  /// Send debug message
644  /// </summary>
645  /// <param name="message">String message</param>
646  public void Debug(string message) => _baseAlgorithm.Debug(message);
647 
648  /// <summary>
649  /// Send an error message for the algorithm
650  /// </summary>
651  /// <param name="message">String message</param>
652  public void Error(string message) => _baseAlgorithm.Error(message);
653 
654  /// <summary>
655  /// Add a Chart object to algorithm collection
656  /// </summary>
657  /// <param name="chart">Chart object to add to collection.</param>
658  public void AddChart(Chart chart) => _baseAlgorithm.AddChart(chart);
659 
660  /// <summary>
661  /// Get the chart updates since the last request:
662  /// </summary>
663  /// <param name="clearChartData"></param>
664  /// <returns>List of Chart Updates</returns>
665  public IEnumerable<Chart> GetChartUpdates(bool clearChartData = false) => _baseAlgorithm.GetChartUpdates(clearChartData);
666 
667  /// <summary>
668  /// Gets whether or not this algorithm has been locked and fully initialized
669  /// </summary>
670  public bool GetLocked() => _baseAlgorithm.GetLocked();
671 
672  /// <summary>
673  /// Gets a read-only dictionary with all current parameters
674  /// </summary>
675  public IReadOnlyDictionary<string, string> GetParameters() => _baseAlgorithm.GetParameters();
676 
677  /// <summary>
678  /// Gets the parameter with the specified name. If a parameter with the specified name does not exist,
679  /// the given default value is returned if any, else null
680  /// </summary>
681  /// <param name="name">The name of the parameter to get</param>
682  /// <param name="defaultValue">The default value to return</param>
683  /// <returns>The value of the specified parameter, or defaultValue if not found or null if there's no default value</returns>
684  public string GetParameter(string name, string defaultValue = null) => _baseAlgorithm.GetParameter(name, defaultValue);
685 
686  /// <summary>
687  /// Gets the parameter with the specified name parsed as an integer. If a parameter with the specified name does not exist,
688  /// or the conversion is not possible, the given default value is returned
689  /// </summary>
690  /// <param name="name">The name of the parameter to get</param>
691  /// <param name="defaultValue">The default value to return</param>
692  /// <returns>The value of the specified parameter, or defaultValue if not found or null if there's no default value</returns>
693  public int GetParameter(string name, int defaultValue) => _baseAlgorithm.GetParameter(name, defaultValue);
694 
695  /// <summary>
696  /// Gets the parameter with the specified name parsed as a double. If a parameter with the specified name does not exist,
697  /// or the conversion is not possible, the given default value is returned
698  /// </summary>
699  /// <param name="name">The name of the parameter to get</param>
700  /// <param name="defaultValue">The default value to return</param>
701  /// <returns>The value of the specified parameter, or defaultValue if not found or null if there's no default value</returns>
702  public double GetParameter(string name, double defaultValue) => _baseAlgorithm.GetParameter(name, defaultValue);
703 
704  /// <summary>
705  /// Gets the parameter with the specified name parsed as a decimal. If a parameter with the specified name does not exist,
706  /// or the conversion is not possible, the given default value is returned
707  /// </summary>
708  /// <param name="name">The name of the parameter to get</param>
709  /// <param name="defaultValue">The default value to return</param>
710  /// <returns>The value of the specified parameter, or defaultValue if not found or null if there's no default value</returns>
711  public decimal GetParameter(string name, decimal defaultValue) => _baseAlgorithm.GetParameter(name, defaultValue);
712 
713  /// <summary>
714  /// Initialise the Algorithm and Prepare Required Data:
715  /// </summary>
716  public void Initialize()
717  {
718  InvokeMethod(nameof(Initialize));
719  }
720 
721  /// <summary>
722  /// Liquidate your portfolio holdings
723  /// </summary>
724  /// <param name="symbol">Specific asset to liquidate, defaults to all</param>
725  /// <param name="asynchronous">Flag to indicate if the symbols should be liquidated asynchronously</param>
726  /// <param name="tag">Custom tag to know who is calling this</param>
727  /// <param name="orderProperties">Order properties to use</param>
728  public List<OrderTicket> Liquidate(Symbol symbol = null, bool asynchronous = false, string tag = "Liquidated", IOrderProperties orderProperties = null) => _baseAlgorithm.Liquidate(symbol, asynchronous, tag, orderProperties);
729 
730  /// <summary>
731  /// Save entry to the Log
732  /// </summary>
733  /// <param name="message">String message</param>
734  public void Log(string message) => _baseAlgorithm.Log(message);
735 
736  /// <summary>
737  /// Brokerage disconnected event handler. This method is called when the brokerage connection is lost.
738  /// </summary>
739  public void OnBrokerageDisconnect()
740  {
741  _onBrokerageDisconnect();
742  }
743 
744  /// <summary>
745  /// Brokerage message event handler. This method is called for all types of brokerage messages.
746  /// </summary>
747  public void OnBrokerageMessage(BrokerageMessageEvent messageEvent)
748  {
749  _onBrokerageMessage(messageEvent);
750  }
751 
752  /// <summary>
753  /// Brokerage reconnected event handler. This method is called when the brokerage connection is restored after a disconnection.
754  /// </summary>
755  public void OnBrokerageReconnect()
756  {
757  _onBrokerageReconnect();
758  }
759 
760  /// <summary>
761  /// v3.0 Handler for all data types
762  /// </summary>
763  /// <param name="slice">The current slice of data</param>
764  public void OnData(Slice slice)
765  {
766  if (_onData != null)
767  {
768  using (Py.GIL())
769  {
770  _onData(slice);
771  }
772  }
773  }
774 
775  /// <summary>
776  /// Used to send data updates to algorithm framework models
777  /// </summary>
778  /// <param name="slice">The current data slice</param>
779  public void OnFrameworkData(Slice slice)
780  {
781  _baseAlgorithm.OnFrameworkData(slice);
782  }
783 
784  /// <summary>
785  /// Event handler to be called when there's been a split event
786  /// </summary>
787  /// <param name="splits">The current time slice splits</param>
788  public void OnSplits(Splits splits)
789  {
790  _onSplits(splits);
791  }
792 
793  /// <summary>
794  /// Event handler to be called when there's been a dividend event
795  /// </summary>
796  /// <param name="dividends">The current time slice dividends</param>
797  public void OnDividends(Dividends dividends)
798  {
799  _onDividends(dividends);
800  }
801 
802  /// <summary>
803  /// Event handler to be called when there's been a delistings event
804  /// </summary>
805  /// <param name="delistings">The current time slice delistings</param>
806  public void OnDelistings(Delistings delistings)
807  {
808  _onDelistings(delistings);
809  }
810 
811  /// <summary>
812  /// Event handler to be called when there's been a symbol changed event
813  /// </summary>
814  /// <param name="symbolsChanged">The current time slice symbol changed events</param>
815  public void OnSymbolChangedEvents(SymbolChangedEvents symbolsChanged)
816  {
817  _onSymbolChangedEvents(symbolsChanged);
818  }
819 
820  /// <summary>
821  /// Call this event at the end of the algorithm running.
822  /// </summary>
823  public void OnEndOfAlgorithm()
824  {
826  }
827 
828  /// <summary>
829  /// 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).
830  /// </summary>
831  /// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks>
832  /// <remarks>Deprecated because different assets have different market close times,
833  /// and because Python does not support two methods with the same name</remarks>
834  [Obsolete("This method is deprecated. Please use this overload: OnEndOfDay(Symbol symbol)")]
835  public void OnEndOfDay()
836  {
837  try
838  {
839  _onEndOfDay();
840  }
841  // If OnEndOfDay is not defined in the script, but OnEndOfDay(Symbol) is, a python exception occurs
842  // Only throws if there is an error in its implementation body
843  catch (PythonException exception)
844  {
845  if (!exception.Message.Contains("OnEndOfDay() missing 1 required positional argument"))
846  {
847  _baseAlgorithm.SetRunTimeError(exception);
848  }
849  }
850  }
851 
852  /// <summary>
853  /// 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).
854  /// </summary>
855  /// <remarks>
856  /// This method is left for backwards compatibility and is invoked via <see cref="OnEndOfDay(Symbol)"/>, if that method is
857  /// override then this method will not be called without a called to base.OnEndOfDay(string)
858  /// </remarks>
859  /// <param name="symbol">Asset symbol for this end of day event. Forex and equities have different closing hours.</param>
860  public void OnEndOfDay(Symbol symbol)
861  {
862  try
863  {
864  _onEndOfDay(symbol);
865  }
866  // If OnEndOfDay(Symbol) is not defined in the script, but OnEndOfDay is, a python exception occurs
867  // Only throws if there is an error in its implementation body
868  catch (PythonException exception)
869  {
870  if (!exception.Message.Contains("OnEndOfDay() takes 1 positional argument but 2 were given"))
871  {
872  _baseAlgorithm.SetRunTimeError(exception);
873  }
874  }
875  }
876 
877  /// <summary>
878  /// Margin call event handler. This method is called right before the margin call orders are placed in the market.
879  /// </summary>
880  /// <param name="requests">The orders to be executed to bring this algorithm within margin limits</param>
881  public void OnMarginCall(List<SubmitOrderRequest> requests)
882  {
883  using (Py.GIL())
884  {
885  var result = InvokeMethod(nameof(OnMarginCall), requests);
886 
887  if (_onMarginCall != null)
888  {
889  // If the method does not return or returns a non-iterable PyObject, throw an exception
890  if (result == null || !result.IsIterable())
891  {
892  throw new Exception("OnMarginCall must return a non-empty list of SubmitOrderRequest");
893  }
894 
895  requests.Clear();
896 
897  using var iterator = result.GetIterator();
898  foreach (PyObject pyRequest in iterator)
899  {
900  SubmitOrderRequest request;
901  if (TryConvert(pyRequest, out request))
902  {
903  requests.Add(request);
904  }
905  }
906 
907  // If the PyObject is an empty list or its items are not SubmitOrderRequest objects, throw an exception
908  if (requests.Count == 0)
909  {
910  throw new Exception("OnMarginCall must return a non-empty list of SubmitOrderRequest");
911  }
912  }
913  }
914  }
915 
916  /// <summary>
917  /// Margin call warning event handler. This method is called when Portfolio.MarginRemaining is under 5% of your Portfolio.TotalPortfolioValue
918  /// </summary>
919  public void OnMarginCallWarning()
920  {
921  _onMarginCallWarning();
922  }
923 
924  /// <summary>
925  /// EXPERTS ONLY:: [-!-Async Code-!-]
926  /// New order event handler: on order status changes (filled, partially filled, cancelled etc).
927  /// </summary>
928  /// <param name="newEvent">Event information</param>
929  public void OnOrderEvent(OrderEvent newEvent)
930  {
931  _onOrderEvent(newEvent);
932  }
933 
934  /// <summary>
935  /// Generic untyped command call handler
936  /// </summary>
937  /// <param name="data">The associated data</param>
938  /// <returns>True if success, false otherwise. Returning null will disable command feedback</returns>
939  public bool? OnCommand(dynamic data)
940  {
941  return _onCommand(data);
942  }
943 
944  /// <summary>
945  /// Will submit an order request to the algorithm
946  /// </summary>
947  /// <param name="request">The request to submit</param>
948  /// <remarks>Will run order prechecks, which include making sure the algorithm is not warming up, security is added and has data among others</remarks>
949  /// <returns>The order ticket</returns>
951  {
952  return _baseAlgorithm.SubmitOrderRequest(request);
953  }
954 
955  /// <summary>
956  /// Option assignment event handler. On an option assignment event for short legs the resulting information is passed to this method.
957  /// </summary>
958  /// <param name="assignmentEvent">Option exercise event details containing details of the assignment</param>
959  /// <remarks>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</remarks>
960  public void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
961  {
962  _onAssignmentOrderEvent(assignmentEvent);
963  }
964 
965  /// <summary>
966  /// Event fired each time the we add/remove securities from the data feed
967  /// </summary>
968  /// <param name="changes">Security additions/removals for this time step</param>
970  {
971  _onSecuritiesChanged(changes);
972  }
973 
974  /// <summary>
975  /// Used to send security changes to algorithm framework models
976  /// </summary>
977  /// <param name="changes">Security additions/removals for this time step</param>
979  {
980  _onFrameworkSecuritiesChanged(changes);
981  }
982 
983  /// <summary>
984  /// Called by setup handlers after Initialize and allows the algorithm a chance to organize
985  /// the data gather in the Initialize method
986  /// </summary>
987  public void PostInitialize()
988  {
989  _baseAlgorithm.PostInitialize();
990  }
991 
992  /// <summary>
993  /// Called when the algorithm has completed initialization and warm up.
994  /// </summary>
995  public void OnWarmupFinished()
996  {
998  }
999 
1000  /// <summary>
1001  /// Removes the security with the specified symbol. This will cancel all
1002  /// open orders and then liquidate any existing holdings
1003  /// </summary>
1004  /// <param name="symbol">The symbol of the security to be removed</param>
1005  public bool RemoveSecurity(Symbol symbol) => _baseAlgorithm.RemoveSecurity(symbol);
1006 
1007  /// <summary>
1008  /// Set the algorithm Id for this backtest or live run. This can be used to identify the order and equity records.
1009  /// </summary>
1010  /// <param name="algorithmId">unique 32 character identifier for backtest or live server</param>
1011  public void SetAlgorithmId(string algorithmId) => _baseAlgorithm.SetAlgorithmId(algorithmId);
1012 
1013  /// <summary>
1014  /// Sets the implementation used to handle messages from the brokerage.
1015  /// The default implementation will forward messages to debug or error
1016  /// and when a <see cref="BrokerageMessageType.Error"/> occurs, the algorithm
1017  /// is stopped.
1018  /// </summary>
1019  /// <param name="handler">The message handler to use</param>
1020  public void SetBrokerageMessageHandler(IBrokerageMessageHandler handler) => _baseAlgorithm.SetBrokerageMessageHandler(handler);
1021 
1022  /// <summary>
1023  /// Sets the brokerage model used to resolve transaction models, settlement models,
1024  /// and brokerage specified ordering behaviors.
1025  /// </summary>
1026  /// <param name="brokerageModel">The brokerage model used to emulate the real
1027  /// brokerage</param>
1028  public void SetBrokerageModel(IBrokerageModel brokerageModel) => _baseAlgorithm.SetBrokerageModel(brokerageModel);
1029 
1030  /// <summary>
1031  /// Sets the account currency cash symbol this algorithm is to manage, as well
1032  /// as the starting cash in this currency if given
1033  /// </summary>
1034  /// <remarks>Has to be called during <see cref="Initialize"/> before
1035  /// calling <see cref="SetCash(decimal)"/> or adding any <see cref="Security"/></remarks>
1036  /// <param name="accountCurrency">The account currency cash symbol to set</param>
1037  /// <param name="startingCash">The account currency starting cash to set</param>
1038  public void SetAccountCurrency(string accountCurrency, decimal? startingCash = null) => _baseAlgorithm.SetAccountCurrency(accountCurrency, startingCash);
1039 
1040  /// <summary>
1041  /// Set the starting capital for the strategy
1042  /// </summary>
1043  /// <param name="startingCash">decimal starting capital, default $100,000</param>
1044  public void SetCash(decimal startingCash) => _baseAlgorithm.SetCash(startingCash);
1045 
1046  /// <summary>
1047  /// Set the cash for the specified symbol
1048  /// </summary>
1049  /// <param name="symbol">The cash symbol to set</param>
1050  /// <param name="startingCash">Decimal cash value of portfolio</param>
1051  /// <param name="conversionRate">The current conversion rate for the</param>
1052  public void SetCash(string symbol, decimal startingCash, decimal conversionRate = 0) => _baseAlgorithm.SetCash(symbol, startingCash, conversionRate);
1053 
1054  /// <summary>
1055  /// Set the DateTime Frontier: This is the master time and is
1056  /// </summary>
1057  /// <param name="time"></param>
1058  public void SetDateTime(DateTime time) => _baseAlgorithm.SetDateTime(time);
1059 
1060  /// <summary>
1061  /// Set the start date for the backtest
1062  /// </summary>
1063  /// <param name="start">Datetime Start date for backtest</param>
1064  /// <remarks>Must be less than end date and within data available</remarks>
1065  public void SetStartDate(DateTime start) => _baseAlgorithm.SetStartDate(start);
1066 
1067  /// <summary>
1068  /// Set the end date for a backtest.
1069  /// </summary>
1070  /// <param name="end">Datetime value for end date</param>
1071  /// <remarks>Must be greater than the start date</remarks>
1072  public void SetEndDate(DateTime end) => _baseAlgorithm.SetEndDate(end);
1073 
1074  /// <summary>
1075  /// Get the last known price using the history provider.
1076  /// Useful for seeding securities with the correct price
1077  /// </summary>
1078  /// <param name="security"><see cref="Security"/> object for which to retrieve historical data</param>
1079  /// <returns>A single <see cref="BaseData"/> object with the last known price</returns>
1080  public BaseData GetLastKnownPrice(Security security) => _baseAlgorithm.GetLastKnownPrice(security);
1081 
1082  /// <summary>
1083  /// Set the runtime error
1084  /// </summary>
1085  /// <param name="exception">Represents error that occur during execution</param>
1086  public void SetRunTimeError(Exception exception) => _baseAlgorithm.SetRunTimeError(exception);
1087 
1088  /// <summary>
1089  /// Sets <see cref="IsWarmingUp"/> to false to indicate this algorithm has finished its warm up
1090  /// </summary>
1091  public void SetFinishedWarmingUp()
1092  {
1093  _baseAlgorithm.SetFinishedWarmingUp();
1094 
1095  // notify the algorithm
1096  OnWarmupFinished();
1097  }
1098 
1099  /// <summary>
1100  /// Set the historical data provider
1101  /// </summary>
1102  /// <param name="historyProvider">Historical data provider</param>
1103  public void SetHistoryProvider(IHistoryProvider historyProvider) => _baseAlgorithm.SetHistoryProvider(historyProvider);
1104 
1105  /// <summary>
1106  /// Set live mode state of the algorithm run: Public setter for the algorithm property LiveMode.
1107  /// </summary>
1108  /// <param name="live">Bool live mode flag</param>
1109  public void SetLiveMode(bool live) => _baseAlgorithm.SetLiveMode(live);
1110 
1111  /// <summary>
1112  /// Sets the algorithm running mode
1113  /// </summary>
1114  /// <param name="algorithmMode">Algorithm mode</param>
1115  public void SetAlgorithmMode(AlgorithmMode algorithmMode) => _baseAlgorithm.SetAlgorithmMode(algorithmMode);
1116 
1117  /// <summary>
1118  /// Sets the algorithm deployment target
1119  /// </summary>
1120  /// <param name="deploymentTarget">Deployment target</param>
1121  public void SetDeploymentTarget(DeploymentTarget deploymentTarget) => _baseAlgorithm.SetDeploymentTarget(deploymentTarget);
1122 
1123  /// <summary>
1124  /// Set the algorithm as initialized and locked. No more cash or security changes.
1125  /// </summary>
1126  public void SetLocked() => _baseAlgorithm.SetLocked();
1127 
1128  /// <summary>
1129  /// Set the maximum number of orders the algorithm is allowed to process.
1130  /// </summary>
1131  /// <param name="max">Maximum order count int</param>
1132  public void SetMaximumOrders(int max) => _baseAlgorithm.SetMaximumOrders(max);
1133 
1134  /// <summary>
1135  /// Sets the parameters from the dictionary
1136  /// </summary>
1137  /// <param name="parameters">Dictionary containing the parameter names to values</param>
1138  public void SetParameters(Dictionary<string, string> parameters) => _baseAlgorithm.SetParameters(parameters);
1139 
1140  /// <summary>
1141  /// Tries to convert a PyObject into a C# object
1142  /// </summary>
1143  /// <typeparam name="T">Type of the C# object</typeparam>
1144  /// <param name="pyObject">PyObject to be converted</param>
1145  /// <param name="result">C# object that of type T</param>
1146  /// <returns>True if successful conversion</returns>
1147  private bool TryConvert<T>(PyObject pyObject, out T result)
1148  {
1149  result = default(T);
1150  var type = (Type)pyObject.GetPythonType().AsManagedObject(typeof(Type));
1151 
1152  if (type == typeof(T))
1153  {
1154  result = (T)pyObject.AsManagedObject(typeof(T));
1155  }
1156 
1157  return type == typeof(T);
1158  }
1159 
1160  /// <summary>
1161  /// Returns a <see cref = "string"/> that represents the current <see cref = "AlgorithmPythonWrapper"/> object.
1162  /// </summary>
1163  /// <returns></returns>
1164  public override string ToString()
1165  {
1166  if (_algorithm == null)
1167  {
1168  return base.ToString();
1169  }
1170  using (Py.GIL())
1171  {
1172  return _algorithm.Repr();
1173  }
1174  }
1175 
1176  /// <summary>
1177  /// Sets the current slice
1178  /// </summary>
1179  /// <param name="slice">The Slice object</param>
1180  public void SetCurrentSlice(Slice slice)
1181  {
1182  _baseAlgorithm.SetCurrentSlice(new PythonSlice(slice));
1183  }
1184 
1185  /// <summary>
1186  /// Provide the API for the algorithm.
1187  /// </summary>
1188  /// <param name="api">Initiated API</param>
1189  public void SetApi(IApi api) => _baseAlgorithm.SetApi(api);
1190 
1191  /// <summary>
1192  /// Sets the object store
1193  /// </summary>
1194  /// <param name="objectStore">The object store</param>
1195  public void SetObjectStore(IObjectStore objectStore) => _baseAlgorithm.SetObjectStore(objectStore);
1196 
1197  /// <summary>
1198  /// Determines if the Symbol is shortable at the brokerage
1199  /// </summary>
1200  /// <param name="symbol">Symbol to check if shortable</param>
1201  /// <param name="shortQuantity">Order's quantity to check if it is currently shortable, taking into account current holdings and open orders</param>
1202  /// <param name="updateOrderId">Optionally the id of the order being updated. When updating an order
1203  /// we want to ignore it's submitted short quantity and use the new provided quantity to determine if we
1204  /// can perform the update</param>
1205  /// <returns>True if the symbol can be shorted by the requested quantity</returns>
1206  public bool Shortable(Symbol symbol, decimal shortQuantity, int? updateOrderId = null)
1207  {
1208  return _baseAlgorithm.Shortable(symbol, shortQuantity, updateOrderId);
1209  }
1210 
1211  /// <summary>
1212  /// Gets the quantity shortable for the given asset
1213  /// </summary>
1214  /// <returns>
1215  /// Quantity shortable for the given asset. Zero if not
1216  /// shortable, or a number greater than zero if shortable.
1217  /// </returns>
1218  public long ShortableQuantity(Symbol symbol)
1219  {
1220  return _baseAlgorithm.ShortableQuantity(symbol);
1221  }
1222 
1223  /// <summary>
1224  /// Converts the string 'ticker' symbol into a full <see cref="Symbol"/> object
1225  /// This requires that the string 'ticker' has been added to the algorithm
1226  /// </summary>
1227  /// <param name="ticker">The ticker symbol. This should be the ticker symbol
1228  /// as it was added to the algorithm</param>
1229  /// <returns>The symbol object mapped to the specified ticker</returns>
1230  public Symbol Symbol(string ticker) => _baseAlgorithm.Symbol(ticker);
1231 
1232  /// <summary>
1233  /// For the given symbol will resolve the ticker it used at the current algorithm date
1234  /// </summary>
1235  /// <param name="symbol">The symbol to get the ticker for</param>
1236  /// <returns>The mapped ticker for a symbol</returns>
1237  public string Ticker(Symbol symbol) => _baseAlgorithm.Ticker(symbol);
1238 
1239  /// <summary>
1240  /// Sets name to the currently running backtest
1241  /// </summary>
1242  /// <param name="name">The name for the backtest</param>
1243  public void SetName(string name)
1244  {
1245  _baseAlgorithm.SetName(name);
1246  }
1247 
1248  /// <summary>
1249  /// Adds a tag to the algorithm
1250  /// </summary>
1251  /// <param name="tag">The tag to add</param>
1252  public void AddTag(string tag)
1253  {
1254  _baseAlgorithm.AddTag(tag);
1255  }
1256 
1257  /// <summary>
1258  /// Sets the tags for the algorithm
1259  /// </summary>
1260  /// <param name="tags">The tags</param>
1261  public void SetTags(HashSet<string> tags)
1262  {
1263  _baseAlgorithm.SetTags(tags);
1264  }
1265 
1266  /// <summary>
1267  /// Run a callback command instance
1268  /// </summary>
1269  /// <param name="command">The callback command instance</param>
1270  /// <returns>The command result</returns>
1271  public CommandResultPacket RunCommand(CallbackCommand command) => _baseAlgorithm.RunCommand(command);
1272 
1273  }
1274 }