Lean  $LEAN_TAG$
SecurityService.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 
17 using System;
18 using System.Linq;
19 using QuantConnect.Util;
20 using QuantConnect.Data;
22 using System.Collections.Generic;
23 
25 {
26  /// <summary>
27  /// This class implements interface <see cref="ISecurityService"/> providing methods for creating new <see cref="Security"/>
28  /// </summary>
30  {
31  private readonly CashBook _cashBook;
32  private readonly MarketHoursDatabase _marketHoursDatabase;
33  private readonly SymbolPropertiesDatabase _symbolPropertiesDatabase;
34  private readonly IRegisteredSecurityDataTypesProvider _registeredTypes;
35  private readonly ISecurityInitializerProvider _securityInitializerProvider;
36  private readonly SecurityCacheProvider _cacheProvider;
37  private readonly IPrimaryExchangeProvider _primaryExchangeProvider;
38  private readonly IAlgorithm _algorithm;
39  private bool _isLiveMode;
40  private bool _modelsMismatchWarningSent;
41 
42  /// <summary>
43  /// Creates a new instance of the SecurityService class
44  /// </summary>
45  public SecurityService(CashBook cashBook,
46  MarketHoursDatabase marketHoursDatabase,
47  SymbolPropertiesDatabase symbolPropertiesDatabase,
48  ISecurityInitializerProvider securityInitializerProvider,
50  SecurityCacheProvider cacheProvider,
51  IPrimaryExchangeProvider primaryExchangeProvider = null,
52  IAlgorithm algorithm = null)
53  {
54  _cashBook = cashBook;
55  _registeredTypes = registeredTypes;
56  _marketHoursDatabase = marketHoursDatabase;
57  _symbolPropertiesDatabase = symbolPropertiesDatabase;
58  _securityInitializerProvider = securityInitializerProvider;
59  _cacheProvider = cacheProvider;
60  _primaryExchangeProvider = primaryExchangeProvider;
61  _algorithm = algorithm;
62  }
63 
64  /// <summary>
65  /// Creates a new security
66  /// </summary>
67  /// <remarks>Following the obsoletion of Security.Subscriptions,
68  /// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
70  List<SubscriptionDataConfig> subscriptionDataConfigList,
71  decimal leverage = 0,
72  bool addToSymbolCache = true,
73  Security underlying = null)
74  {
75  var configList = new SubscriptionDataConfigList(symbol);
76  configList.AddRange(subscriptionDataConfigList);
77 
78  var dataTypes = Enumerable.Empty<Type>();
79  if(symbol.SecurityType == SecurityType.Base && SecurityIdentifier.TryGetCustomDataTypeInstance(symbol.ID.Symbol, out var type))
80  {
81  dataTypes = new[] { type };
82  }
83  var exchangeHours = _marketHoursDatabase.GetEntry(symbol, dataTypes).ExchangeHours;
84 
85  var defaultQuoteCurrency = _cashBook.AccountCurrency;
86  if (symbol.ID.SecurityType == SecurityType.Forex)
87  {
88  defaultQuoteCurrency = symbol.Value.Substring(3);
89  }
90 
91  if (symbol.ID.SecurityType == SecurityType.Crypto && !_symbolPropertiesDatabase.ContainsKey(symbol.ID.Market, symbol, symbol.ID.SecurityType))
92  {
93  throw new ArgumentException(Messages.SecurityService.SymbolNotFoundInSymbolPropertiesDatabase(symbol));
94  }
95 
96  // For Futures Options that don't have a SPDB entry, the futures entry will be used instead.
97  var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(
98  symbol.ID.Market,
99  symbol,
100  symbol.SecurityType,
101  defaultQuoteCurrency);
102 
103  // add the symbol to our cache
104  if (addToSymbolCache)
105  {
106  SymbolCache.Set(symbol.Value, symbol);
107  }
108 
109  // verify the cash book is in a ready state
110  var quoteCurrency = symbolProperties.QuoteCurrency;
111  if (!_cashBook.TryGetValue(quoteCurrency, out var quoteCash))
112  {
113  // since we have none it's safe to say the conversion is zero
114  quoteCash = _cashBook.Add(quoteCurrency, 0, 0);
115  }
116 
117  Cash baseCash = null;
118  // we skip cfd because we don't need to add the base cash
119  if (symbol.SecurityType != SecurityType.Cfd && CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrencySymbol, out _))
120  {
121  if (!_cashBook.TryGetValue(baseCurrencySymbol, out baseCash))
122  {
123  // since we have none it's safe to say the conversion is zero
124  baseCash = _cashBook.Add(baseCurrencySymbol, 0, 0);
125  }
126  }
127 
128  var cache = _cacheProvider.GetSecurityCache(symbol);
129 
130  Security security;
131  switch (symbol.ID.SecurityType)
132  {
133  case SecurityType.Equity:
134  var primaryExchange =
135  _primaryExchangeProvider?.GetPrimaryExchange(symbol.ID) ??
137  security = new Equity.Equity(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache, primaryExchange);
138  break;
139 
140  case SecurityType.Option:
141  if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
142  security = new Option.Option(symbol, exchangeHours, quoteCash, new Option.OptionSymbolProperties(symbolProperties), _cashBook, _registeredTypes, cache, underlying);
143  break;
144 
145  case SecurityType.IndexOption:
146  if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
147  security = new IndexOption.IndexOption(symbol, exchangeHours, quoteCash, new IndexOption.IndexOptionSymbolProperties(symbolProperties), _cashBook, _registeredTypes, cache, underlying);
148  break;
149 
150  case SecurityType.FutureOption:
151  if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
152  var optionSymbolProperties = new Option.OptionSymbolProperties(symbolProperties);
153 
154  // Future options exercised only gives us one contract back, rather than the
155  // 100x seen in equities.
156  optionSymbolProperties.SetContractUnitOfTrade(1);
157 
158  security = new FutureOption.FutureOption(symbol, exchangeHours, quoteCash, optionSymbolProperties, _cashBook, _registeredTypes, cache, underlying);
159  break;
160 
161  case SecurityType.Future:
162  security = new Future.Future(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache, underlying);
163  break;
164 
165  case SecurityType.Forex:
166  security = new Forex.Forex(symbol, exchangeHours, quoteCash, baseCash, symbolProperties, _cashBook, _registeredTypes, cache);
167  break;
168 
169  case SecurityType.Cfd:
170  security = new Cfd.Cfd(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
171  break;
172 
173  case SecurityType.Index:
174  security = new Index.Index(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
175  break;
176 
177  case SecurityType.Crypto:
178  security = new Crypto.Crypto(symbol, exchangeHours, quoteCash, baseCash, symbolProperties, _cashBook, _registeredTypes, cache);
179  break;
180 
181  case SecurityType.CryptoFuture:
182  security = new CryptoFuture.CryptoFuture(symbol, exchangeHours, quoteCash, baseCash, symbolProperties, _cashBook, _registeredTypes, cache);
183  break;
184 
185  default:
186  case SecurityType.Base:
187  security = new Security(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
188  break;
189  }
190 
191  // if we're just creating this security and it only has an internal
192  // feed, mark it as non-tradable since the user didn't request this data
193  if (security.IsTradable)
194  {
195  security.IsTradable = !configList.IsInternalFeed;
196  }
197 
198  security.AddData(configList);
199 
200  // invoke the security initializer
201  _securityInitializerProvider.SecurityInitializer.Initialize(security);
202 
203  CheckCanonicalSecurityModels(security);
204 
205  // if leverage was specified then apply to security after the initializer has run, parameters of this
206  // method take precedence over the intializer
207  if (leverage != Security.NullLeverage)
208  {
209  security.SetLeverage(leverage);
210  }
211 
212  var isNotNormalized = configList.DataNormalizationMode() == DataNormalizationMode.Raw;
213 
214  // In live mode and non normalized data, equity assumes specific price variation model
215  if ((_isLiveMode || isNotNormalized) && security.Type == SecurityType.Equity)
216  {
218  }
219 
220  return security;
221  }
222 
223  /// <summary>
224  /// Creates a new security
225  /// </summary>
226  /// <remarks>Following the obsoletion of Security.Subscriptions,
227  /// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
228  public Security CreateSecurity(Symbol symbol, SubscriptionDataConfig subscriptionDataConfig, decimal leverage = 0, bool addToSymbolCache = true, Security underlying = null)
229  {
230  return CreateSecurity(symbol, new List<SubscriptionDataConfig> { subscriptionDataConfig }, leverage, addToSymbolCache, underlying);
231  }
232 
233  /// <summary>
234  /// Set live mode state of the algorithm
235  /// </summary>
236  /// <param name="isLiveMode">True, live mode is enabled</param>
237  public void SetLiveMode(bool isLiveMode)
238  {
239  _isLiveMode = isLiveMode;
240  }
241 
242  /// <summary>
243  /// Checks whether the created security has the same models as its canonical security (in case it has one)
244  /// and sends a one-time warning if it doesn't.
245  /// </summary>
246  private void CheckCanonicalSecurityModels(Security security)
247  {
248  if (!_modelsMismatchWarningSent &&
249  _algorithm != null &&
250  security.Symbol.HasCanonical() &&
251  _algorithm.Securities.TryGetValue(security.Symbol.Canonical, out var canonicalSecurity))
252  {
253  if (security.FillModel.GetType() != canonicalSecurity.FillModel.GetType() ||
254  security.FeeModel.GetType() != canonicalSecurity.FeeModel.GetType() ||
255  security.BuyingPowerModel.GetType() != canonicalSecurity.BuyingPowerModel.GetType() ||
256  security.MarginInterestRateModel.GetType() != canonicalSecurity.MarginInterestRateModel.GetType() ||
257  security.SlippageModel.GetType() != canonicalSecurity.SlippageModel.GetType() ||
258  security.VolatilityModel.GetType() != canonicalSecurity.VolatilityModel.GetType() ||
259  security.SettlementModel.GetType() != canonicalSecurity.SettlementModel.GetType())
260  {
261  _modelsMismatchWarningSent = true;
262  _algorithm.Debug($"Warning: Security {security.Symbol} its canonical security {security.Symbol.Canonical} have at least one model of different types (fill, fee, buying power, margin interest rate, slippage, volatility, settlement). To avoid this, consider using a security initializer to set the right models to each security type according to your algorithm's requirements.");
263  }
264  }
265  }
266  }
267 }