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 
420  if (Symbol.SecurityType != SecurityType.Index)
421  {
422  IsTradable = true;
423  }
424 
425  Cache = cache;
426  Exchange = exchange;
427  DataFilter = dataFilter;
428  PriceVariationModel = priceVariationModel;
429  PortfolioModel = portfolioModel;
430  BuyingPowerModel = buyingPowerModel;
431  FillModel = fillModel;
432  FeeModel = feeModel;
433  SlippageModel = slippageModel;
434  SettlementModel = settlementModel;
435  VolatilityModel = volatilityModel;
436  MarginInterestRateModel = marginInterestRateModel;
437  Holdings = new SecurityHolding(this, currencyConverter);
438  Data = new DynamicSecurityData(registeredTypesProvider, Cache);
440 
441  UpdateSubscriptionProperties();
442  }
443 
444 
445  /// <summary>
446  /// Temporary convenience constructor
447  /// </summary>
449  Cash quoteCurrency,
450  SymbolProperties symbolProperties,
451  SecurityExchange exchange,
452  SecurityCache cache,
453  ISecurityPortfolioModel portfolioModel,
454  IFillModel fillModel,
455  IFeeModel feeModel,
456  ISlippageModel slippageModel,
457  ISettlementModel settlementModel,
458  IVolatilityModel volatilityModel,
459  IBuyingPowerModel buyingPowerModel,
460  ISecurityDataFilter dataFilter,
461  IPriceVariationModel priceVariationModel,
462  ICurrencyConverter currencyConverter,
463  IRegisteredSecurityDataTypesProvider registeredTypesProvider,
464  IMarginInterestRateModel marginInterestRateModel
465  )
466  : this(config.Symbol,
467  quoteCurrency,
468  symbolProperties,
469  exchange,
470  cache,
471  portfolioModel,
472  fillModel,
473  feeModel,
474  slippageModel,
475  settlementModel,
476  volatilityModel,
477  buyingPowerModel,
478  dataFilter,
479  priceVariationModel,
480  currencyConverter,
481  registeredTypesProvider,
482  marginInterestRateModel
483  )
484  {
485  _subscriptionsBag.Add(config);
486  UpdateSubscriptionProperties();
487  }
488 
489  /// <summary>
490  /// Read only property that checks if we currently own stock in the company.
491  /// </summary>
492  public virtual bool HoldStock => Holdings.HoldStock;
493 
494  /// <summary>
495  /// Alias for HoldStock - Do we have any of this security
496  /// </summary>
497  public virtual bool Invested => HoldStock;
498 
499  /// <summary>
500  /// Local time for this market
501  /// </summary>
502  public virtual DateTime LocalTime
503  {
504  get
505  {
506  if (_localTimeKeeper == null)
507  {
508  throw new InvalidOperationException(Messages.Security.SetLocalTimeKeeperMustBeCalledBeforeUsingLocalTime);
509  }
510 
511  return _localTimeKeeper.LocalTime;
512  }
513  }
514 
515  /// <summary>
516  /// Get the current value of the security.
517  /// </summary>
518  public virtual decimal Price => Cache.Price;
519 
520  /// <summary>
521  /// Leverage for this Security.
522  /// </summary>
523  public virtual decimal Leverage => Holdings.Leverage;
524 
525  /// <summary>
526  /// If this uses tradebar data, return the most recent high.
527  /// </summary>
528  public virtual decimal High => Cache.High == 0 ? Price : Cache.High;
529 
530  /// <summary>
531  /// If this uses tradebar data, return the most recent low.
532  /// </summary>
533  public virtual decimal Low => Cache.Low == 0 ? Price : Cache.Low;
534 
535  /// <summary>
536  /// If this uses tradebar data, return the most recent close.
537  /// </summary>
538  public virtual decimal Close => Cache.Close == 0 ? Price : Cache.Close;
539 
540  /// <summary>
541  /// If this uses tradebar data, return the most recent open.
542  /// </summary>
543  public virtual decimal Open => Cache.Open == 0 ? Price : Cache.Open;
544 
545  /// <summary>
546  /// Access to the volume of the equity today
547  /// </summary>
548  public virtual decimal Volume => Cache.Volume;
549 
550  /// <summary>
551  /// Gets the most recent bid price if available
552  /// </summary>
553  public virtual decimal BidPrice => Cache.BidPrice == 0 ? Price : Cache.BidPrice;
554 
555  /// <summary>
556  /// Gets the most recent bid size if available
557  /// </summary>
558  public virtual decimal BidSize => Cache.BidSize;
559 
560  /// <summary>
561  /// Gets the most recent ask price if available
562  /// </summary>
563  public virtual decimal AskPrice => Cache.AskPrice == 0 ? Price : Cache.AskPrice;
564 
565  /// <summary>
566  /// Gets the most recent ask size if available
567  /// </summary>
568  public virtual decimal AskSize => Cache.AskSize;
569 
570  /// <summary>
571  /// Access to the open interest of the security today
572  /// </summary>
573  public virtual long OpenInterest => Cache.OpenInterest;
574 
575  /// <summary>
576  /// Gets the fundamental data associated with the security if there is any, otherwise null.
577  /// </summary>
579  {
580  get
581  {
582  return new Fundamental(LocalTime, Symbol);
583  }
584  }
585 
586  /// <summary>
587  /// Get the last price update set to the security if any else null
588  /// </summary>
589  /// <returns>BaseData object for this security</returns>
590  public BaseData GetLastData() => Cache.GetData();
591 
592  /// <summary>
593  /// Sets the <see cref="LocalTimeKeeper"/> to be used for this <see cref="Security"/>.
594  /// This is the source of this instance's time.
595  /// </summary>
596  /// <param name="localTimeKeeper">The source of this <see cref="Security"/>'s time.</param>
597  public virtual void SetLocalTimeKeeper(LocalTimeKeeper localTimeKeeper)
598  {
599  _localTimeKeeper = localTimeKeeper;
600  Exchange.SetLocalDateTimeFrontierProvider(localTimeKeeper);
601  }
602 
603  /// <summary>
604  /// Update any security properties based on the latest market data and time
605  /// </summary>
606  /// <param name="data">New data packet from LEAN</param>
607  public void SetMarketPrice(BaseData data)
608  {
609  //Add new point to cache:
610  if (data == null) return;
611  Cache.AddData(data);
612 
613  UpdateMarketPrice(data);
614  }
615 
616  /// <summary>
617  /// Updates all of the security properties, such as price/OHLCV/bid/ask based
618  /// on the data provided. Data is also stored into the security's data cache
619  /// </summary>
620  /// <param name="data">The security update data</param>
621  /// <param name="dataType">The data type</param>
622  /// <param name="containsFillForwardData">Flag indicating whether
623  /// <paramref name="data"/> contains any fill forward bar or not</param>
624  public void Update(IReadOnlyList<BaseData> data, Type dataType, bool? containsFillForwardData = null)
625  {
626  Cache.AddDataList(data, dataType, containsFillForwardData);
627 
628  UpdateMarketPrice(data[data.Count - 1]);
629  }
630 
631  /// <summary>
632  /// Returns true if the security contains at least one subscription that represents custom data
633  /// </summary>
634  [Obsolete("This method is obsolete. Use the 'SubscriptionDataConfig' exposed by" +
635  " 'SubscriptionManager' and the 'IsCustomData()' extension method")]
636  public bool IsCustomData()
637  {
638  if (Subscriptions == null || !Subscriptions.Any())
639  {
640  return false;
641  }
642 
643  return Subscriptions.Any(x => x.IsCustomData);
644  }
645 
646  /// <summary>
647  /// Set the leverage parameter for this security
648  /// </summary>
649  /// <param name="leverage">Leverage for this asset</param>
650  public void SetLeverage(decimal leverage)
651  {
652  if (Symbol.ID.SecurityType == SecurityType.Future || Symbol.ID.SecurityType.IsOption())
653  {
654  return;
655  }
656 
657  BuyingPowerModel.SetLeverage(this, leverage);
658  }
659 
660  /// <summary>
661  /// Sets the data normalization mode to be used by this security
662  /// </summary>
663  [Obsolete("This method is obsolete. Use the 'SubscriptionDataConfig' exposed by" +
664  " 'SubscriptionManager' and the 'SetDataNormalizationMode()' extension method")]
666  {
667  lock (_subscriptionsBag)
668  {
669  foreach (var subscription in _subscriptionsBag)
670  {
671  subscription.DataNormalizationMode = mode;
672  }
673  UpdateSubscriptionProperties();
674  }
675  }
676 
677  /// <summary>
678  /// This method will refresh the value of the <see cref="DataNormalizationMode"/> property.
679  /// This is required for backward-compatibility.
680  /// TODO: to be deleted with the DataNormalizationMode property
681  /// </summary>
683  {
684  lock (_subscriptionsBag)
685  {
686  DataNormalizationMode = _subscriptionsBag
687  .Select(x => x.DataNormalizationMode)
688  .DefaultIfEmpty(DataNormalizationMode.Adjusted)
689  .FirstOrDefault();
690  }
691  }
692 
693  /// <summary>
694  /// Sets the fee model
695  /// </summary>
696  /// <param name="feelModel">Model that represents a fee model</param>
697  public void SetFeeModel(IFeeModel feelModel)
698  {
699  FeeModel = feelModel;
700  }
701 
702  /// <summary>
703  /// Sets the fee model
704  /// </summary>
705  /// <param name="feelModel">Model that represents a fee model</param>
706  public void SetFeeModel(PyObject feelModel)
707  {
708  FeeModel = new FeeModelPythonWrapper(feelModel);
709  }
710 
711  /// <summary>
712  /// Sets the fill model
713  /// </summary>
714  /// <param name="fillModel">Model that represents a fill model</param>
715  public void SetFillModel(IFillModel fillModel)
716  {
717  FillModel = fillModel;
718  }
719 
720  /// <summary>
721  /// Sets the fill model
722  /// </summary>
723  /// <param name="fillModel">Model that represents a fill model</param>
724  public void SetFillModel(PyObject fillModel)
725  {
726  FillModel = new FillModelPythonWrapper(fillModel);
727  }
728 
729  /// <summary>
730  /// Sets the settlement model
731  /// </summary>
732  /// <param name="settlementModel"> Model that represents a settlement model</param>
733  public void SetSettlementModel(ISettlementModel settlementModel)
734  {
735  SettlementModel = settlementModel;
736  }
737 
738  /// <summary>
739  /// Sets the settlement model
740  /// </summary>
741  /// <param name="settlementModel">Model that represents a settlement model</param>
742  public void SetSettlementModel(PyObject settlementModel)
743  {
744  SettlementModel = new SettlementModelPythonWrapper(settlementModel);
745  }
746 
747  /// <summary>
748  /// Sets the slippage model
749  /// </summary>
750  /// <param name="slippageModel">Model that represents a slippage model</param>
751  public void SetSlippageModel(ISlippageModel slippageModel)
752  {
753  SlippageModel = slippageModel;
754  }
755 
756  /// <summary>
757  /// Sets the slippage model
758  /// </summary>
759  /// <param name="slippageModel">Model that represents a slippage model</param>
760  public void SetSlippageModel(PyObject slippageModel)
761  {
762  SlippageModel = new SlippageModelPythonWrapper(slippageModel);
763  }
764 
765  /// <summary>
766  /// Sets the volatility model
767  /// </summary>
768  /// <param name="volatilityModel">Model that represents a volatility model</param>
769  public void SetVolatilityModel(IVolatilityModel volatilityModel)
770  {
771  VolatilityModel = volatilityModel;
772  }
773 
774  /// <summary>
775  /// Sets the volatility model
776  /// </summary>
777  /// <param name="volatilityModel">Model that represents a volatility model</param>
778  public void SetVolatilityModel(PyObject volatilityModel)
779  {
780  VolatilityModel = new VolatilityModelPythonWrapper(volatilityModel);
781  }
782 
783  /// <summary>
784  /// Sets the buying power model
785  /// </summary>
786  /// <param name="buyingPowerModel">Model that represents a security's model of buying power</param>
787  public void SetBuyingPowerModel(IBuyingPowerModel buyingPowerModel)
788  {
789  BuyingPowerModel = buyingPowerModel;
790  }
791 
792  /// <summary>
793  /// Sets the buying power model
794  /// </summary>
795  /// <param name="pyObject">Model that represents a security's model of buying power</param>
796  public void SetBuyingPowerModel(PyObject pyObject)
797  {
799  }
800 
801  /// <summary>
802  /// Sets the margin interests rate model
803  /// </summary>
804  /// <param name="marginInterestRateModel">Model that represents a security's model of margin interest rate</param>
805  public void SetMarginInterestRateModel(IMarginInterestRateModel marginInterestRateModel)
806  {
807  MarginInterestRateModel = marginInterestRateModel;
808  }
809 
810  /// <summary>
811  /// Sets the margin interests rate model
812  /// </summary>
813  /// <param name="pyObject">Model that represents a security's model of margin interest rate</param>
814  public void SetMarginInterestRateModel(PyObject pyObject)
815  {
817  }
818 
819  /// <summary>
820  /// Sets the margin model
821  /// </summary>
822  /// <param name="marginModel">Model that represents a security's model of buying power</param>
823  public void SetMarginModel(IBuyingPowerModel marginModel)
824  {
825  MarginModel = marginModel;
826  }
827 
828  /// <summary>
829  /// Sets the margin model
830  /// </summary>
831  /// <param name="pyObject">Model that represents a security's model of buying power</param>
832  public void SetMarginModel(PyObject pyObject)
833  {
835  }
836 
837  /// <summary>
838  /// Set Python Shortable Provider for this <see cref="Security"/>
839  /// </summary>
840  /// <param name="pyObject">Python class that represents a custom shortable provider</param>
841  public void SetShortableProvider(PyObject pyObject)
842  {
843  if (pyObject.TryConvert<IShortableProvider>(out var shortableProvider))
844  {
845  SetShortableProvider(shortableProvider);
846  }
847  else if (Extensions.TryConvert<IShortableProvider>(pyObject, out _, allowPythonDerivative: true))
848  {
850  }
851  else
852  {
853  using (Py.GIL())
854  {
855  throw new Exception($"SetShortableProvider: {pyObject.Repr()} is not a valid argument");
856  }
857  }
858  }
859 
860  /// <summary>
861  /// Set Shortable Provider for this <see cref="Security"/>
862  /// </summary>
863  /// <param name="shortableProvider">Provider to use</param>
864  public void SetShortableProvider(IShortableProvider shortableProvider)
865  {
866  ShortableProvider = shortableProvider;
867  }
868 
869  /// <summary>
870  /// Set Security Data Filter
871  /// </summary>
872  /// <param name="pyObject">Python class that represents a custom Security Data Filter</param>
873  /// <exception cref="ArgumentException"></exception>
874  public void SetDataFilter(PyObject pyObject)
875  {
876  if (pyObject.TryConvert<ISecurityDataFilter>(out var dataFilter))
877  {
878  SetDataFilter(dataFilter);
879  }
880  else if (Extensions.TryConvert<ISecurityDataFilter>(pyObject, out _, allowPythonDerivative: true))
881  {
883  }
884  else
885  {
886  using (Py.GIL())
887  {
888  throw new ArgumentException($"SetDataFilter: {pyObject.Repr()} is not a valid argument");
889  }
890  }
891  }
892 
893  /// <summary>
894  /// Set Security Data Filter
895  /// </summary>
896  /// <param name="dataFilter">Security Data Filter</param>
897  public void SetDataFilter(ISecurityDataFilter dataFilter)
898  {
899  DataFilter = dataFilter;
900  }
901 
902  #region DynamicObject Overrides and Helper Methods
903 
904  /// <summary>
905  /// This is a <see cref="DynamicObject"/> override. Not meant for external use.
906  /// </summary>
907  public override bool TryGetMember(GetMemberBinder binder, out object result)
908  {
909  return Cache.Properties.TryGetValue(binder.Name, out result);
910  }
911 
912  /// <summary>
913  /// This is a <see cref="DynamicObject"/> override. Not meant for external use.
914  /// </summary>
915  public override bool TrySetMember(SetMemberBinder binder, object value)
916  {
917  Cache.Properties[binder.Name] = value;
918  return true;
919  }
920 
921  /// <summary>
922  /// This is a <see cref="DynamicObject"/> override. Not meant for external use.
923  /// </summary>
924  public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
925  {
926  try
927  {
928  result = Cache.Properties.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, Cache.Properties, args,
929  CultureInfo.InvariantCulture);
930  return true;
931  }
932  catch
933  {
934  result = null;
935  return false;
936  }
937  }
938 
939  /// <summary>
940  /// Adds the specified custom property.
941  /// This allows us to use the security object as a dynamic object for quick storage.
942  /// </summary>
943  /// <param name="key">The property key</param>
944  /// <param name="value">The property value</param>
945  public void Add(string key, object value)
946  {
947  Set(key, value);
948  }
949 
950  /// <summary>
951  /// Sets the specified custom property.
952  /// This allows us to use the security object as a dynamic object for quick storage.
953  /// </summary>
954  /// <param name="key">The property key</param>
955  /// <param name="value">The property value</param>
956  public void Set(string key, object value)
957  {
958  Cache.Properties[key] = value;
959  }
960 
961  /// <summary>
962  /// Gets the specified custom property
963  /// </summary>
964  /// <param name="key">The property key</param>
965  /// <param name="value">The property value</param>
966  /// <returns>True if the property is found.</returns>
967  /// <exception cref="InvalidCastException">If the property is found but its value cannot be casted to the speficied type</exception>
968  public bool TryGet<T>(string key, out T value)
969  {
970  if (Cache.Properties.TryGetValue(key, out var obj))
971  {
972  value = CastDynamicPropertyValue<T>(obj);
973  return true;
974  }
975  value = default;
976  return false;
977  }
978 
979  /// <summary>
980  /// Gets the specified custom property
981  /// </summary>
982  /// <param name="key">The property key</param>
983  /// <returns>The property value is found</returns>
984  /// <exception cref="KeyNotFoundException">If the property is not found</exception>
985  public T Get<T>(string key)
986  {
987  return CastDynamicPropertyValue<T>(Cache.Properties[key]);
988  }
989 
990  /// <summary>
991  /// Removes a custom property.
992  /// </summary>
993  /// <param name="key">The property key</param>
994  /// <returns>True if the property is successfully removed</returns>
995  public bool Remove(string key)
996  {
997  return Cache.Properties.Remove(key);
998  }
999 
1000  /// <summary>
1001  /// Removes a custom property.
1002  /// </summary>
1003  /// <param name="key">The property key</param>
1004  /// <param name="value">The removed property value</param>
1005  /// <returns>True if the property is successfully removed</returns>
1006  public bool Remove<T>(string key, out T value)
1007  {
1008  value = default;
1009  var result = Cache.Properties.Remove(key, out object objectValue);
1010  if (result)
1011  {
1012  value = CastDynamicPropertyValue<T>(objectValue);
1013  }
1014  return result;
1015  }
1016 
1017  /// <summary>
1018  /// Removes every custom property that had been set.
1019  /// </summary>
1020  public void Clear()
1021  {
1022  Cache.Properties.Clear();
1023  }
1024 
1025  /// <summary>
1026  /// Gets or sets the specified custom property through the indexer.
1027  /// This is a wrapper around the <see cref="Get{T}(string)"/> and <see cref="Add(string,object)"/> methods.
1028  /// </summary>
1029  /// <param name="key">The property key</param>
1030  public object this[string key]
1031  {
1032  get
1033  {
1034  return Get<object>(key);
1035  }
1036  set
1037  {
1038  Add(key, value);
1039  }
1040  }
1041 
1042  #endregion
1043 
1044  /// <summary>
1045  /// Returns a string that represents the current object.
1046  /// </summary>
1047  /// <returns>
1048  /// A string that represents the current object.
1049  /// </returns>
1050  /// <filterpriority>2</filterpriority>
1051  public override string ToString()
1052  {
1053  return Symbol.ToString();
1054  }
1055 
1056  /// <summary>
1057  /// Adds the specified data subscription to this security.
1058  /// </summary>
1059  /// <param name="subscription">The subscription configuration to add. The Symbol and ExchangeTimeZone properties must match the existing Security object</param>
1060  internal void AddData(SubscriptionDataConfig subscription)
1061  {
1062  lock (_subscriptionsBag)
1063  {
1064  if (subscription.Symbol != Symbol)
1065  {
1066  throw new ArgumentException(Messages.Security.UnmatchingSymbols, $"{nameof(subscription)}.{nameof(subscription.Symbol)}");
1067  }
1068  if (!subscription.ExchangeTimeZone.Equals(Exchange.TimeZone))
1069  {
1070  throw new ArgumentException(Messages.Security.UnmatchingExchangeTimeZones, $"{nameof(subscription)}.{nameof(subscription.ExchangeTimeZone)}");
1071  }
1072  _subscriptionsBag.Add(subscription);
1073  UpdateSubscriptionProperties();
1074  }
1075  }
1076 
1077  /// <summary>
1078  /// Adds the specified data subscriptions to this security.
1079  /// </summary>
1080  /// <param name="subscriptions">The subscription configuration to add. The Symbol and ExchangeTimeZone properties must match the existing Security object</param>
1081  internal void AddData(SubscriptionDataConfigList subscriptions)
1082  {
1083  lock (_subscriptionsBag)
1084  {
1085  foreach (var subscription in subscriptions)
1086  {
1087  if (subscription.Symbol != Symbol)
1088  {
1089  throw new ArgumentException(Messages.Security.UnmatchingSymbols, $"{nameof(subscription)}.{nameof(subscription.Symbol)}");
1090  }
1091  if (!subscription.ExchangeTimeZone.Equals(Exchange.TimeZone))
1092  {
1093  throw new ArgumentException(Messages.Security.UnmatchingExchangeTimeZones, $"{nameof(subscription)}.{nameof(subscription.ExchangeTimeZone)}");
1094  }
1095  _subscriptionsBag.Add(subscription);
1096  }
1097  UpdateSubscriptionProperties();
1098  }
1099  }
1100 
1101  /// <summary>
1102  /// Update market price of this Security
1103  /// </summary>
1104  /// <param name="data">Data to pull price from</param>
1105  protected virtual void UpdateConsumersMarketPrice(BaseData data)
1106  {
1107  if (data is OpenInterest || data.Price == 0m) return;
1109  VolatilityModel.Update(this, data);
1110  }
1111 
1112  /// <summary>
1113  /// Caller should hold the lock on '_subscriptionsBag'
1114  /// </summary>
1115  private void UpdateSubscriptionProperties()
1116  {
1117  Resolution = _subscriptionsBag.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Daily).Min();
1118  IsFillDataForward = _subscriptionsBag.Any(x => x.FillDataForward);
1119  IsExtendedMarketHours = _subscriptionsBag.Any(x => x.ExtendedMarketHours);
1121  }
1122 
1123  /// <summary>
1124  /// Updates consumers market price. It will do nothing if the passed data type is auxiliary.
1125  /// </summary>
1126  private void UpdateMarketPrice(BaseData data)
1127  {
1128  if (data.DataType != MarketDataType.Auxiliary)
1129  {
1131  }
1132  }
1133 
1134  /// <summary>
1135  /// Casts a dynamic property value to the specified type.
1136  /// Useful for cases where the property value is a PyObject and we want to cast it to the underlying type.
1137  /// </summary>
1138  private static T CastDynamicPropertyValue<T>(object obj)
1139  {
1140  T value;
1141  var pyObj = obj as PyObject;
1142  if (pyObj != null)
1143  {
1144  using (Py.GIL())
1145  {
1146  value = pyObj.As<T>();
1147  }
1148  }
1149  else
1150  {
1151  value = (T)obj;
1152  }
1153 
1154  return value;
1155  }
1156 
1157  /// <summary>
1158  /// Applies the split to the security
1159  /// </summary>
1160  internal void ApplySplit(Split split)
1161  {
1162  Cache.ApplySplit(split);
1163  UpdateMarketPrice(Cache.GetData());
1164  }
1165 
1166  /// <summary>
1167  /// Updates the symbol properties of this security
1168  /// </summary>
1169  internal virtual void UpdateSymbolProperties(SymbolProperties symbolProperties)
1170  {
1171  if (symbolProperties != null)
1172  {
1173  SymbolProperties = symbolProperties;
1174  }
1175  }
1176  }
1177 }