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