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