Lean  $LEAN_TAG$
Security.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 System;
17 using System.Collections.Generic;
18 using System.Linq;
19 using System.Dynamic;
20 using System.Reflection;
21 using System.Globalization;
22 
23 using QuantConnect.Data;
31 using QuantConnect.Python;
32 using Python.Runtime;
37 
39 {
40  /// <summary>
41  /// A base vehicle properties class for providing a common interface to all assets in QuantConnect.
42  /// </summary>
43  /// <remarks>
44  /// Security object is intended to hold properties of the specific security asset. These properties can include trade start-stop dates,
45  /// price, market hours, resolution of the security, the holdings information for this security and the specific fill model.
46  /// </remarks>
47  public class Security : DynamicObject, ISecurityPrice
48  {
49  private LocalTimeKeeper _localTimeKeeper;
50 
51  /// <summary>
52  /// Collection of SubscriptionDataConfigs for this security.
53  /// Uses concurrent bag to avoid list enumeration threading issues
54  /// </summary>
55  /// <remarks>Just use a list + lock, not concurrent bag, avoid garbage it creates for features we don't need here. See https://github.com/dotnet/runtime/issues/23103</remarks>
56  private readonly List<SubscriptionDataConfig> _subscriptionsBag;
57 
58  /// <summary>
59  /// This securities <see cref="IShortableProvider"/>
60  /// </summary>
61  public IShortableProvider ShortableProvider { get; private set; }
62 
63  /// <summary>
64  /// A null security leverage value
65  /// </summary>
66  /// <remarks>This value is used to determine when the
67  /// <see cref="SecurityInitializer"/> leverage is used</remarks>
68  public const decimal NullLeverage = 0;
69 
70  /// <summary>
71  /// Gets all the subscriptions for this security
72  /// </summary>
73  public IEnumerable<SubscriptionDataConfig> Subscriptions
74  {
75  get
76  {
77  lock (_subscriptionsBag)
78  {
79  return _subscriptionsBag.ToList();
80  }
81  }
82  }
83 
84  /// <summary>
85  /// <see cref="Symbol"/> for the asset.
86  /// </summary>
87  public Symbol Symbol { get; }
88 
89  /// <summary>
90  /// Gets the Cash object used for converting the quote currency to the account currency
91  /// </summary>
92  public Cash QuoteCurrency
93  {
94  get;
95  }
96 
97  /// <summary>
98  /// Gets the symbol properties for this security
99  /// </summary>
101  {
102  get;
103  }
104 
105  /// <summary>
106  /// Type of the security.
107  /// </summary>
108  /// <remarks>
109  /// QuantConnect currently only supports Equities and Forex
110  /// </remarks>
112 
113  /// <summary>
114  /// Resolution of data requested for this security.
115  /// </summary>
116  /// <remarks>Tick, second or minute resolution for QuantConnect assets.</remarks>
117  [Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
118  public Resolution Resolution { get; private set; }
119 
120  /// <summary>
121  /// Indicates the data will use previous bars when there was no trading in this time period. This was a configurable datastream setting set in initialization.
122  /// </summary>
123  [Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
124  public bool IsFillDataForward { get; private set; }
125 
126  /// <summary>
127  /// Indicates the security will continue feeding data after the primary market hours have closed. This was a configurable setting set in initialization.
128  /// </summary>
129  [Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
130  public bool IsExtendedMarketHours { get; private set; }
131 
132  /// <summary>
133  /// Gets the data normalization mode used for this security
134  /// </summary>
135  [Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
136  public DataNormalizationMode DataNormalizationMode { get; private set; }
137 
138  /// <summary>
139  /// Gets the subscription configuration for this security
140  /// </summary>
141  [Obsolete("This property returns only the first subscription. Use the 'Subscriptions' property for all of this security's subscriptions.")]
143  {
144  get
145  {
146  lock (_subscriptionsBag)
147  {
148  return _subscriptionsBag.FirstOrDefault();
149  }
150  }
151  }
152 
153  /// <summary>
154  /// There has been at least one datapoint since our algorithm started running for us to determine price.
155  /// </summary>
156  public bool HasData => GetLastData() != null;
157 
158  /// <summary>
159  /// Gets or sets whether or not this security should be considered tradable
160  /// </summary>
161  public virtual bool IsTradable
162  {
163  get; set;
164  }
165 
166  /// <summary>
167  /// True if the security has been delisted from exchanges and is no longer tradable
168  /// </summary>
169  public bool IsDelisted { get; set; }
170 
171  /// <summary>
172  /// Data cache for the security to store previous price information.
173  /// </summary>
174  /// <seealso cref="EquityCache"/>
175  /// <seealso cref="ForexCache"/>
176  public SecurityCache Cache
177  {
178  get; set;
179  }
180 
181  /// <summary>
182  /// Holdings class contains the portfolio, cash and processes order fills.
183  /// </summary>
184  /// <seealso cref="EquityHolding"/>
185  /// <seealso cref="ForexHolding"/>
187  {
188  get;
189  set;
190  }
191 
192  /// <summary>
193  /// Exchange class contains the market opening hours, along with pre-post market hours.
194  /// </summary>
195  /// <seealso cref="EquityExchange"/>
196  /// <seealso cref="ForexExchange"/>
198  {
199  get;
200  set;
201  }
202 
203  /// <summary>
204  /// Fee model used to compute order fees for this security
205  /// </summary>
206  public IFeeModel FeeModel
207  {
208  get;
209  set;
210  }
211 
212  /// <summary>
213  /// Fill model used to produce fill events for this security
214  /// </summary>
215  public IFillModel FillModel
216  {
217  get;
218  set;
219  }
220 
221  /// <summary>
222  /// Slippage model use to compute slippage of market orders
223  /// </summary>
225  {
226  get;
227  set;
228  }
229 
230  /// <summary>
231  /// Gets the portfolio model used by this security
232  /// </summary>
234  {
235  get;
236  set;
237  }
238 
239  /// <summary>
240  /// Gets the buying power model used for this security
241  /// </summary>
243  {
244  get;
245  set;
246  }
247 
248  /// <summary>
249  /// Gets the buying power model used for this security, an alias for <see cref="BuyingPowerModel"/>
250  /// </summary>
252  {
253  get { return BuyingPowerModel; }
254  set { BuyingPowerModel = value; }
255  }
256 
257  /// <summary>
258  /// Gets or sets the margin interest rate model
259  /// </summary>
261  {
262  get;
263  set;
264  }
265 
266  /// <summary>
267  /// Gets the settlement model used for this security
268  /// </summary>
270  {
271  get;
272  set;
273  }
274 
275  /// <summary>
276  /// Gets the volatility model used for this security
277  /// </summary>
279  {
280  get;
281  set;
282  }
283 
284  /// <summary>
285  /// Customizable data filter to filter outlier ticks before they are passed into user event handlers.
286  /// By default all ticks are passed into the user algorithms.
287  /// </summary>
288  /// <remarks>TradeBars (seconds and minute bars) are prefiltered to ensure the ticks which build the bars are realistically tradeable</remarks>
289  /// <seealso cref="EquityDataFilter"/>
290  /// <seealso cref="ForexDataFilter"/>
292  {
293  get;
294  set;
295  }
296 
297  /// <summary>
298  /// Customizable price variation model used to define the minimum price variation of this security.
299  /// By default minimum price variation is a constant find in the symbol-properties-database.
300  /// </summary>
301  /// <seealso cref="AdjustedPriceVariationModel"/>
302  /// <seealso cref="SecurityPriceVariationModel"/>
303  /// <seealso cref="EquityPriceVariationModel"/>
305  {
306  get;
307  set;
308  }
309 
310  /// <summary>
311  /// Provides dynamic access to data in the cache
312  /// </summary>
313  public dynamic Data
314  {
315  get;
316  }
317 
318  /// <summary>
319  /// Construct a new security vehicle based on the user options.
320  /// </summary>
321  public Security(SecurityExchangeHours exchangeHours,
322  SubscriptionDataConfig config,
323  Cash quoteCurrency,
324  SymbolProperties symbolProperties,
325  ICurrencyConverter currencyConverter,
326  IRegisteredSecurityDataTypesProvider registeredTypesProvider,
327  SecurityCache cache
328  )
329  : this(config,
330  quoteCurrency,
331  symbolProperties,
332  new SecurityExchange(exchangeHours),
333  cache,
335  new ImmediateFillModel(),
337  new ConstantSlippageModel(0),
339  Securities.VolatilityModel.Null,
340  new SecurityMarginModel(),
341  new SecurityDataFilter(),
343  currencyConverter,
344  registeredTypesProvider,
345  Securities.MarginInterestRateModel.Null
346  )
347  {
348  }
349 
350  /// <summary>
351  /// Construct a new security vehicle based on the user options.
352  /// </summary>
353  public Security(Symbol symbol,
354  SecurityExchangeHours exchangeHours,
355  Cash quoteCurrency,
356  SymbolProperties symbolProperties,
357  ICurrencyConverter currencyConverter,
358  IRegisteredSecurityDataTypesProvider registeredTypesProvider,
359  SecurityCache cache
360  )
361  : this(symbol,
362  quoteCurrency,
363  symbolProperties,
364  new SecurityExchange(exchangeHours),
365  cache,
367  new ImmediateFillModel(),
369  new ConstantSlippageModel(0),
371  Securities.VolatilityModel.Null,
372  new SecurityMarginModel(),
373  new SecurityDataFilter(),
375  currencyConverter,
376  registeredTypesProvider,
377  Securities.MarginInterestRateModel.Null
378  )
379  {
380  }
381 
382  /// <summary>
383  /// Construct a new security vehicle based on the user options.
384  /// </summary>
385  protected Security(Symbol symbol,
386  Cash quoteCurrency,
387  SymbolProperties symbolProperties,
388  SecurityExchange exchange,
389  SecurityCache cache,
390  ISecurityPortfolioModel portfolioModel,
391  IFillModel fillModel,
392  IFeeModel feeModel,
393  ISlippageModel slippageModel,
394  ISettlementModel settlementModel,
395  IVolatilityModel volatilityModel,
396  IBuyingPowerModel buyingPowerModel,
397  ISecurityDataFilter dataFilter,
398  IPriceVariationModel priceVariationModel,
399  ICurrencyConverter currencyConverter,
400  IRegisteredSecurityDataTypesProvider registeredTypesProvider,
401  IMarginInterestRateModel marginInterestRateModel
402  )
403  {
404  if (symbolProperties == null)
405  {
406  throw new ArgumentNullException(nameof(symbolProperties), Messages.Security.ValidSymbolPropertiesInstanceRequired);
407  }
408 
409  if (symbolProperties.QuoteCurrency != quoteCurrency.Symbol)
410  {
411  throw new ArgumentException(Messages.Security.UnmatchingQuoteCurrencies);
412  }
413 
414  Symbol = symbol;
415  _subscriptionsBag = new ();
416  QuoteCurrency = quoteCurrency;
417  SymbolProperties = symbolProperties;
418  IsTradable = true;
419  Cache = cache;
420  Exchange = exchange;
421  DataFilter = dataFilter;
422  PriceVariationModel = priceVariationModel;
423  PortfolioModel = portfolioModel;
424  BuyingPowerModel = buyingPowerModel;
425  FillModel = fillModel;
426  FeeModel = feeModel;
427  SlippageModel = slippageModel;
428  SettlementModel = settlementModel;
429  VolatilityModel = volatilityModel;
430  MarginInterestRateModel = marginInterestRateModel;
431  Holdings = new SecurityHolding(this, currencyConverter);
432  Data = new DynamicSecurityData(registeredTypesProvider, Cache);
434 
435  UpdateSubscriptionProperties();
436  }
437 
438 
439  /// <summary>
440  /// Temporary convenience constructor
441  /// </summary>
443  Cash quoteCurrency,
444  SymbolProperties symbolProperties,
445  SecurityExchange exchange,
446  SecurityCache cache,
447  ISecurityPortfolioModel portfolioModel,
448  IFillModel fillModel,
449  IFeeModel feeModel,
450  ISlippageModel slippageModel,
451  ISettlementModel settlementModel,
452  IVolatilityModel volatilityModel,
453  IBuyingPowerModel buyingPowerModel,
454  ISecurityDataFilter dataFilter,
455  IPriceVariationModel priceVariationModel,
456  ICurrencyConverter currencyConverter,
457  IRegisteredSecurityDataTypesProvider registeredTypesProvider,
458  IMarginInterestRateModel marginInterestRateModel
459  )
460  : this(config.Symbol,
461  quoteCurrency,
462  symbolProperties,
463  exchange,
464  cache,
465  portfolioModel,
466  fillModel,
467  feeModel,
468  slippageModel,
469  settlementModel,
470  volatilityModel,
471  buyingPowerModel,
472  dataFilter,
473  priceVariationModel,
474  currencyConverter,
475  registeredTypesProvider,
476  marginInterestRateModel
477  )
478  {
479  _subscriptionsBag.Add(config);
480  UpdateSubscriptionProperties();
481  }
482 
483  /// <summary>
484  /// Read only property that checks if we currently own stock in the company.
485  /// </summary>
486  public virtual bool HoldStock => Holdings.HoldStock;
487 
488  /// <summary>
489  /// Alias for HoldStock - Do we have any of this security
490  /// </summary>
491  public virtual bool Invested => HoldStock;
492 
493  /// <summary>
494  /// Local time for this market
495  /// </summary>
496  public virtual DateTime LocalTime
497  {
498  get
499  {
500  if (_localTimeKeeper == null)
501  {
502  throw new InvalidOperationException(Messages.Security.SetLocalTimeKeeperMustBeCalledBeforeUsingLocalTime);
503  }
504 
505  return _localTimeKeeper.LocalTime;
506  }
507  }
508 
509  /// <summary>
510  /// Get the current value of the security.
511  /// </summary>
512  public virtual decimal Price => Cache.Price;
513 
514  /// <summary>
515  /// Leverage for this Security.
516  /// </summary>
517  public virtual decimal Leverage => Holdings.Leverage;
518 
519  /// <summary>
520  /// If this uses tradebar data, return the most recent high.
521  /// </summary>
522  public virtual decimal High => Cache.High == 0 ? Price : Cache.High;
523 
524  /// <summary>
525  /// If this uses tradebar data, return the most recent low.
526  /// </summary>
527  public virtual decimal Low => Cache.Low == 0 ? Price : Cache.Low;
528 
529  /// <summary>
530  /// If this uses tradebar data, return the most recent close.
531  /// </summary>
532  public virtual decimal Close => Cache.Close == 0 ? Price : Cache.Close;
533 
534  /// <summary>
535  /// If this uses tradebar data, return the most recent open.
536  /// </summary>
537  public virtual decimal Open => Cache.Open == 0 ? Price : Cache.Open;
538 
539  /// <summary>
540  /// Access to the volume of the equity today
541  /// </summary>
542  public virtual decimal Volume => Cache.Volume;
543 
544  /// <summary>
545  /// Gets the most recent bid price if available
546  /// </summary>
547  public virtual decimal BidPrice => Cache.BidPrice == 0 ? Price : Cache.BidPrice;
548 
549  /// <summary>
550  /// Gets the most recent bid size if available
551  /// </summary>
552  public virtual decimal BidSize => Cache.BidSize;
553 
554  /// <summary>
555  /// Gets the most recent ask price if available
556  /// </summary>
557  public virtual decimal AskPrice => Cache.AskPrice == 0 ? Price : Cache.AskPrice;
558 
559  /// <summary>
560  /// Gets the most recent ask size if available
561  /// </summary>
562  public virtual decimal AskSize => Cache.AskSize;
563 
564  /// <summary>
565  /// Access to the open interest of the security today
566  /// </summary>
567  public virtual long OpenInterest => Cache.OpenInterest;
568 
569  /// <summary>
570  /// Gets the fundamental data associated with the security if there is any, otherwise null.
571  /// </summary>
573  {
574  get
575  {
576  if (Cache.GetData<Fundamentals>() != null)
577  {
578  return Cache.GetData<Fundamentals>();
579  }
580 
581  var coarse = Cache.GetData<CoarseFundamental>();
582  if (coarse == null)
583  {
584  return null;
585  }
586 
587  return new Fundamentals
588  {
589  Symbol = Symbol,
590  Value = coarse.Value,
591  EndTime = coarse.EndTime,
592  DollarVolume = coarse.DollarVolume,
593  DataType = coarse.DataType,
594  Market = coarse.Market,
595  Volume = coarse.Volume
596  };
597  }
598  }
599 
600  /// <summary>
601  /// Get the last price update set to the security if any else null
602  /// </summary>
603  /// <returns>BaseData object for this security</returns>
604  public BaseData GetLastData() => Cache.GetData();
605 
606  /// <summary>
607  /// Sets the <see cref="LocalTimeKeeper"/> to be used for this <see cref="Security"/>.
608  /// This is the source of this instance's time.
609  /// </summary>
610  /// <param name="localTimeKeeper">The source of this <see cref="Security"/>'s time.</param>
611  public virtual void SetLocalTimeKeeper(LocalTimeKeeper localTimeKeeper)
612  {
613  _localTimeKeeper = localTimeKeeper;
614  Exchange.SetLocalDateTimeFrontier(localTimeKeeper.LocalTime);
615 
616  _localTimeKeeper.TimeUpdated += (sender, args) =>
617  {
618  //Update the Exchange/Timer:
619  Exchange.SetLocalDateTimeFrontier(args.Time);
620  };
621  }
622 
623  /// <summary>
624  /// Update any security properties based on the latest market data and time
625  /// </summary>
626  /// <param name="data">New data packet from LEAN</param>
627  public void SetMarketPrice(BaseData data)
628  {
629  //Add new point to cache:
630  if (data == null) return;
631  Cache.AddData(data);
632 
633  UpdateMarketPrice(data);
634  }
635 
636  /// <summary>
637  /// Updates all of the security properties, such as price/OHLCV/bid/ask based
638  /// on the data provided. Data is also stored into the security's data cache
639  /// </summary>
640  /// <param name="data">The security update data</param>
641  /// <param name="dataType">The data type</param>
642  /// <param name="containsFillForwardData">Flag indicating whether
643  /// <paramref name="data"/> contains any fill forward bar or not</param>
644  public void Update(IReadOnlyList<BaseData> data, Type dataType, bool? containsFillForwardData = null)
645  {
646  Cache.AddDataList(data, dataType, containsFillForwardData);
647 
648  UpdateMarketPrice(data[data.Count - 1]);
649  }
650 
651  /// <summary>
652  /// Returns true if the security contains at least one subscription that represents custom data
653  /// </summary>
654  [Obsolete("This method is obsolete. Use the 'SubscriptionDataConfig' exposed by" +
655  " 'SubscriptionManager' and the 'IsCustomData()' extension method")]
656  public bool IsCustomData()
657  {
658  if (Subscriptions == null || !Subscriptions.Any())
659  {
660  return false;
661  }
662 
663  return Subscriptions.Any(x => x.IsCustomData);
664  }
665 
666  /// <summary>
667  /// Set the leverage parameter for this security
668  /// </summary>
669  /// <param name="leverage">Leverage for this asset</param>
670  public void SetLeverage(decimal leverage)
671  {
672  if (Symbol.ID.SecurityType == SecurityType.Future || Symbol.ID.SecurityType.IsOption())
673  {
674  return;
675  }
676 
677  BuyingPowerModel.SetLeverage(this, leverage);
678  }
679 
680  /// <summary>
681  /// Sets the data normalization mode to be used by this security
682  /// </summary>
683  [Obsolete("This method is obsolete. Use the 'SubscriptionDataConfig' exposed by" +
684  " 'SubscriptionManager' and the 'SetDataNormalizationMode()' extension method")]
686  {
687  lock (_subscriptionsBag)
688  {
689  foreach (var subscription in _subscriptionsBag)
690  {
691  subscription.DataNormalizationMode = mode;
692  }
693  UpdateSubscriptionProperties();
694  }
695  }
696 
697  /// <summary>
698  /// This method will refresh the value of the <see cref="DataNormalizationMode"/> property.
699  /// This is required for backward-compatibility.
700  /// TODO: to be deleted with the DataNormalizationMode property
701  /// </summary>
703  {
704  lock (_subscriptionsBag)
705  {
706  DataNormalizationMode = _subscriptionsBag
707  .Select(x => x.DataNormalizationMode)
708  .DefaultIfEmpty(DataNormalizationMode.Adjusted)
709  .FirstOrDefault();
710  }
711  }
712 
713  /// <summary>
714  /// Sets the fee model
715  /// </summary>
716  /// <param name="feelModel">Model that represents a fee model</param>
717  public void SetFeeModel(IFeeModel feelModel)
718  {
719  FeeModel = feelModel;
720  }
721 
722  /// <summary>
723  /// Sets the fee model
724  /// </summary>
725  /// <param name="feelModel">Model that represents a fee model</param>
726  public void SetFeeModel(PyObject feelModel)
727  {
728  FeeModel = new FeeModelPythonWrapper(feelModel);
729  }
730 
731  /// <summary>
732  /// Sets the fill model
733  /// </summary>
734  /// <param name="fillModel">Model that represents a fill model</param>
735  public void SetFillModel(IFillModel fillModel)
736  {
737  FillModel = fillModel;
738  }
739 
740  /// <summary>
741  /// Sets the fill model
742  /// </summary>
743  /// <param name="fillModel">Model that represents a fill model</param>
744  public void SetFillModel(PyObject fillModel)
745  {
746  FillModel = new FillModelPythonWrapper(fillModel);
747  }
748 
749  /// <summary>
750  /// Sets the slippage model
751  /// </summary>
752  /// <param name="slippageModel">Model that represents a slippage model</param>
753  public void SetSlippageModel(ISlippageModel slippageModel)
754  {
755  SlippageModel = slippageModel;
756  }
757 
758  /// <summary>
759  /// Sets the slippage model
760  /// </summary>
761  /// <param name="slippageModel">Model that represents a slippage model</param>
762  public void SetSlippageModel(PyObject slippageModel)
763  {
764  SlippageModel = new SlippageModelPythonWrapper(slippageModel);
765  }
766 
767  /// <summary>
768  /// Sets the volatility model
769  /// </summary>
770  /// <param name="volatilityModel">Model that represents a volatility model</param>
771  public void SetVolatilityModel(IVolatilityModel volatilityModel)
772  {
773  VolatilityModel = volatilityModel;
774  }
775 
776  /// <summary>
777  /// Sets the volatility model
778  /// </summary>
779  /// <param name="volatilityModel">Model that represents a volatility model</param>
780  public void SetVolatilityModel(PyObject volatilityModel)
781  {
782  VolatilityModel = new VolatilityModelPythonWrapper(volatilityModel);
783  }
784 
785  /// <summary>
786  /// Sets the buying power model
787  /// </summary>
788  /// <param name="buyingPowerModel">Model that represents a security's model of buying power</param>
789  public void SetBuyingPowerModel(IBuyingPowerModel buyingPowerModel)
790  {
791  BuyingPowerModel = buyingPowerModel;
792  }
793 
794  /// <summary>
795  /// Sets the buying power model
796  /// </summary>
797  /// <param name="pyObject">Model that represents a security's model of buying power</param>
798  public void SetBuyingPowerModel(PyObject pyObject)
799  {
801  }
802 
803  /// <summary>
804  /// Sets the margin interests rate model
805  /// </summary>
806  /// <param name="marginInterestRateModel">Model that represents a security's model of margin interest rate</param>
807  public void SetMarginInterestRateModel(IMarginInterestRateModel marginInterestRateModel)
808  {
809  MarginInterestRateModel = marginInterestRateModel;
810  }
811 
812  /// <summary>
813  /// Sets the margin interests rate model
814  /// </summary>
815  /// <param name="pyObject">Model that represents a security's model of margin interest rate</param>
816  public void SetMarginInterestRateModel(PyObject pyObject)
817  {
819  }
820 
821  /// <summary>
822  /// Sets the margin model
823  /// </summary>
824  /// <param name="marginModel">Model that represents a security's model of buying power</param>
825  public void SetMarginModel(IBuyingPowerModel marginModel)
826  {
827  MarginModel = marginModel;
828  }
829 
830  /// <summary>
831  /// Sets the margin model
832  /// </summary>
833  /// <param name="pyObject">Model that represents a security's model of buying power</param>
834  public void SetMarginModel(PyObject pyObject)
835  {
837  }
838 
839  /// <summary>
840  /// Set Python Shortable Provider for this <see cref="Security"/>
841  /// </summary>
842  /// <param name="pyObject">Python class that represents a custom shortable provider</param>
843  public void SetShortableProvider(PyObject pyObject)
844  {
845  if (pyObject.TryConvert<IShortableProvider>(out var shortableProvider))
846  {
847  SetShortableProvider(shortableProvider);
848  }
849  else if (Extensions.TryConvert<IShortableProvider>(pyObject, out _, allowPythonDerivative: true))
850  {
852  }
853  else
854  {
855  using (Py.GIL())
856  {
857  throw new Exception($"SetShortableProvider: {pyObject.Repr()} is not a valid argument");
858  }
859  }
860  }
861 
862  /// <summary>
863  /// Set Shortable Provider for this <see cref="Security"/>
864  /// </summary>
865  /// <param name="shortableProvider">Provider to use</param>
866  public void SetShortableProvider(IShortableProvider shortableProvider)
867  {
868  ShortableProvider = shortableProvider;
869  }
870 
871  /// <summary>
872  /// Set Security Data Filter
873  /// </summary>
874  /// <param name="pyObject">Python class that represents a custom Security Data Filter</param>
875  /// <exception cref="ArgumentException"></exception>
876  public void SetDataFilter(PyObject pyObject)
877  {
878  if (pyObject.TryConvert<ISecurityDataFilter>(out var dataFilter))
879  {
880  SetDataFilter(dataFilter);
881  }
882  else if (Extensions.TryConvert<ISecurityDataFilter>(pyObject, out _, allowPythonDerivative: true))
883  {
885  }
886  else
887  {
888  using (Py.GIL())
889  {
890  throw new ArgumentException($"SetDataFilter: {pyObject.Repr()} is not a valid argument");
891  }
892  }
893  }
894 
895  /// <summary>
896  /// Set Security Data Filter
897  /// </summary>
898  /// <param name="dataFilter">Security Data Filter</param>
899  public void SetDataFilter(ISecurityDataFilter dataFilter)
900  {
901  DataFilter = dataFilter;
902  }
903 
904  #region DynamicObject Overrides and Helper Methods
905 
906  /// <summary>
907  /// This is a <see cref="DynamicObject"/> override. Not meant for external use.
908  /// </summary>
909  public override bool TryGetMember(GetMemberBinder binder, out object result)
910  {
911  return Cache.Properties.TryGetValue(binder.Name, out result);
912  }
913 
914  /// <summary>
915  /// This is a <see cref="DynamicObject"/> override. Not meant for external use.
916  /// </summary>
917  public override bool TrySetMember(SetMemberBinder binder, object value)
918  {
919  Cache.Properties[binder.Name] = value;
920  return true;
921  }
922 
923  /// <summary>
924  /// This is a <see cref="DynamicObject"/> override. Not meant for external use.
925  /// </summary>
926  public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
927  {
928  try
929  {
930  result = Cache.Properties.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, Cache.Properties, args,
931  CultureInfo.InvariantCulture);
932  return true;
933  }
934  catch
935  {
936  result = null;
937  return false;
938  }
939  }
940 
941  /// <summary>
942  /// Adds the specified custom property.
943  /// This allows us to use the security object as a dynamic object for quick storage.
944  /// </summary>
945  /// <param name="key">The property key</param>
946  /// <param name="value">The property value</param>
947  public void Add(string key, object value)
948  {
949  Cache.Properties[key] = value;
950  }
951 
952  /// <summary>
953  /// Gets the specified custom property
954  /// </summary>
955  /// <param name="key">The property key</param>
956  /// <param name="value">The property value</param>
957  /// <returns>True if the property is found.</returns>
958  /// <exception cref="InvalidCastException">If the property is found but its value cannot be casted to the speficied type</exception>
959  public bool TryGet<T>(string key, out T value)
960  {
961  if (Cache.Properties.TryGetValue(key, out var obj))
962  {
963  value = CastDynamicPropertyValue<T>(obj);
964  return true;
965  }
966  value = default;
967  return false;
968  }
969 
970  /// <summary>
971  /// Gets the specified custom property
972  /// </summary>
973  /// <param name="key">The property key</param>
974  /// <returns>The property value is found</returns>
975  /// <exception cref="KeyNotFoundException">If the property is not found</exception>
976  public T Get<T>(string key)
977  {
978  return CastDynamicPropertyValue<T>(Cache.Properties[key]);
979  }
980 
981  /// <summary>
982  /// Removes a custom property.
983  /// </summary>
984  /// <param name="key">The property key</param>
985  /// <returns>True if the property is successfully removed</returns>
986  public bool Remove(string key)
987  {
988  return Cache.Properties.Remove(key);
989  }
990 
991  /// <summary>
992  /// Removes a custom property.
993  /// </summary>
994  /// <param name="key">The property key</param>
995  /// <param name="value">The removed property value</param>
996  /// <returns>True if the property is successfully removed</returns>
997  public bool Remove<T>(string key, out T value)
998  {
999  value = default;
1000  var result = Cache.Properties.Remove(key, out object objectValue);
1001  if (result)
1002  {
1003  value = CastDynamicPropertyValue<T>(objectValue);
1004  }
1005  return result;
1006  }
1007 
1008  /// <summary>
1009  /// Removes every custom property that had been set.
1010  /// </summary>
1011  public void Clear()
1012  {
1013  Cache.Properties.Clear();
1014  }
1015 
1016  /// <summary>
1017  /// Gets or sets the specified custom property through the indexer.
1018  /// This is a wrapper around the <see cref="Get{T}(string)"/> and <see cref="Add(string,object)"/> methods.
1019  /// </summary>
1020  /// <param name="key">The property key</param>
1021  public object this[string key]
1022  {
1023  get
1024  {
1025  return Get<object>(key);
1026  }
1027  set
1028  {
1029  Add(key, value);
1030  }
1031  }
1032 
1033  #endregion
1034 
1035  /// <summary>
1036  /// Returns a string that represents the current object.
1037  /// </summary>
1038  /// <returns>
1039  /// A string that represents the current object.
1040  /// </returns>
1041  /// <filterpriority>2</filterpriority>
1042  public override string ToString()
1043  {
1044  return Symbol.ToString();
1045  }
1046 
1047  /// <summary>
1048  /// Adds the specified data subscription to this security.
1049  /// </summary>
1050  /// <param name="subscription">The subscription configuration to add. The Symbol and ExchangeTimeZone properties must match the existing Security object</param>
1051  internal void AddData(SubscriptionDataConfig subscription)
1052  {
1053  lock (_subscriptionsBag)
1054  {
1055  if (subscription.Symbol != Symbol)
1056  {
1057  throw new ArgumentException(Messages.Security.UnmatchingSymbols, $"{nameof(subscription)}.{nameof(subscription.Symbol)}");
1058  }
1059  if (!subscription.ExchangeTimeZone.Equals(Exchange.TimeZone))
1060  {
1061  throw new ArgumentException(Messages.Security.UnmatchingExchangeTimeZones, $"{nameof(subscription)}.{nameof(subscription.ExchangeTimeZone)}");
1062  }
1063  _subscriptionsBag.Add(subscription);
1064  UpdateSubscriptionProperties();
1065  }
1066  }
1067 
1068  /// <summary>
1069  /// Adds the specified data subscriptions to this security.
1070  /// </summary>
1071  /// <param name="subscriptions">The subscription configuration to add. The Symbol and ExchangeTimeZone properties must match the existing Security object</param>
1072  internal void AddData(SubscriptionDataConfigList subscriptions)
1073  {
1074  lock (_subscriptionsBag)
1075  {
1076  foreach (var subscription in subscriptions)
1077  {
1078  if (subscription.Symbol != Symbol)
1079  {
1080  throw new ArgumentException(Messages.Security.UnmatchingSymbols, $"{nameof(subscription)}.{nameof(subscription.Symbol)}");
1081  }
1082  if (!subscription.ExchangeTimeZone.Equals(Exchange.TimeZone))
1083  {
1084  throw new ArgumentException(Messages.Security.UnmatchingExchangeTimeZones, $"{nameof(subscription)}.{nameof(subscription.ExchangeTimeZone)}");
1085  }
1086  _subscriptionsBag.Add(subscription);
1087  }
1088  UpdateSubscriptionProperties();
1089  }
1090  }
1091 
1092  /// <summary>
1093  /// Update market price of this Security
1094  /// </summary>
1095  /// <param name="data">Data to pull price from</param>
1096  protected virtual void UpdateConsumersMarketPrice(BaseData data)
1097  {
1098  if (data is OpenInterest || data.Price == 0m) return;
1100  VolatilityModel.Update(this, data);
1101  }
1102 
1103  /// <summary>
1104  /// Caller should hold the lock on '_subscriptionsBag'
1105  /// </summary>
1106  private void UpdateSubscriptionProperties()
1107  {
1108  Resolution = _subscriptionsBag.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Daily).Min();
1109  IsFillDataForward = _subscriptionsBag.Any(x => x.FillDataForward);
1110  IsExtendedMarketHours = _subscriptionsBag.Any(x => x.ExtendedMarketHours);
1112  }
1113 
1114  /// <summary>
1115  /// Updates consumers market price. It will do nothing if the passed data type is auxiliary.
1116  /// </summary>
1117  private void UpdateMarketPrice(BaseData data)
1118  {
1119  if (data.DataType != MarketDataType.Auxiliary)
1120  {
1122  }
1123  }
1124 
1125  /// <summary>
1126  /// Casts a dynamic property value to the specified type.
1127  /// Useful for cases where the property value is a PyObject and we want to cast it to the underlying type.
1128  /// </summary>
1129  private static T CastDynamicPropertyValue<T>(object obj)
1130  {
1131  T value;
1132  var pyObj = obj as PyObject;
1133  if (pyObj != null)
1134  {
1135  using (Py.GIL())
1136  {
1137  value = pyObj.As<T>();
1138  }
1139  }
1140  else
1141  {
1142  value = (T)obj;
1143  }
1144 
1145  return value;
1146  }
1147  }
1148 }