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>
69  private Security CreateSecurity(Symbol symbol,
70  List<SubscriptionDataConfig> subscriptionDataConfigList,
71  decimal leverage,
72  bool addToSymbolCache,
73  Security underlying,
74  bool initializeSecurity,
75  bool reCreateSecurity)
76  {
77  var configList = new SubscriptionDataConfigList(symbol);
78  configList.AddRange(subscriptionDataConfigList);
79 
80  if (!reCreateSecurity && _algorithm != null && _algorithm.Securities.TryGetValue(symbol, out var existingSecurity))
81  {
82  existingSecurity.AddData(configList);
83 
84  // If non-internal, mark as tradable if it was not already since this is an existing security but might include new subscriptions
85  if (!configList.IsInternalFeed)
86  {
87  existingSecurity.MakeTradable();
88  }
89 
90  InitializeSecurity(initializeSecurity, existingSecurity);
91 
92  return existingSecurity;
93  }
94 
95  var dataTypes = Enumerable.Empty<Type>();
96  if(symbol.SecurityType == SecurityType.Base && SecurityIdentifier.TryGetCustomDataTypeInstance(symbol.ID.Symbol, out var type))
97  {
98  dataTypes = new[] { type };
99  }
100  var exchangeHours = _marketHoursDatabase.GetEntry(symbol, dataTypes).ExchangeHours;
101 
102  var defaultQuoteCurrency = _cashBook.AccountCurrency;
103  if (symbol.ID.SecurityType == SecurityType.Forex)
104  {
105  defaultQuoteCurrency = symbol.Value.Substring(3);
106  }
107 
108  if (symbol.ID.SecurityType == SecurityType.Crypto && !_symbolPropertiesDatabase.ContainsKey(symbol.ID.Market, symbol, symbol.ID.SecurityType))
109  {
110  throw new ArgumentException(Messages.SecurityService.SymbolNotFoundInSymbolPropertiesDatabase(symbol));
111  }
112 
113  // For Futures Options that don't have a SPDB entry, the futures entry will be used instead.
114  var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(
115  symbol.ID.Market,
116  symbol,
117  symbol.SecurityType,
118  defaultQuoteCurrency);
119 
120  // add the symbol to our cache
121  if (addToSymbolCache)
122  {
123  SymbolCache.Set(symbol.Value, symbol);
124  }
125 
126  // verify the cash book is in a ready state
127  var quoteCurrency = symbolProperties.QuoteCurrency;
128  if (!_cashBook.TryGetValue(quoteCurrency, out var quoteCash))
129  {
130  // since we have none it's safe to say the conversion is zero
131  quoteCash = _cashBook.Add(quoteCurrency, 0, 0);
132  }
133 
134  Cash baseCash = null;
135  // we skip cfd because we don't need to add the base cash
136  if (symbol.SecurityType != SecurityType.Cfd)
137  {
138  if (CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrencySymbol, out _))
139  {
140  if (!_cashBook.TryGetValue(baseCurrencySymbol, out baseCash))
141  {
142  // since we have none it's safe to say the conversion is zero
143  baseCash = _cashBook.Add(baseCurrencySymbol, 0, 0);
144  }
145  }
146  else if (CurrencyPairUtil.IsValidSecurityType(symbol.SecurityType, false))
147  {
148  throw new ArgumentException($"Failed to resolve base currency for '{symbol.ID.Symbol}', it might be missing from the Symbol database or market '{symbol.ID.Market}' could be wrong");
149  }
150  }
151 
152  var cache = _cacheProvider.GetSecurityCache(symbol);
153 
154  Security security;
155  switch (symbol.ID.SecurityType)
156  {
157  case SecurityType.Equity:
158  var primaryExchange =
159  _primaryExchangeProvider?.GetPrimaryExchange(symbol.ID) ??
160  Exchange.UNKNOWN;
161  security = new Equity.Equity(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache, primaryExchange);
162  break;
163 
164  case SecurityType.Option:
165  if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
166  security = new Option.Option(symbol, exchangeHours, quoteCash, new Option.OptionSymbolProperties(symbolProperties), _cashBook, _registeredTypes, cache, underlying);
167  break;
168 
169  case SecurityType.IndexOption:
170  if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
171  security = new IndexOption.IndexOption(symbol, exchangeHours, quoteCash, new IndexOption.IndexOptionSymbolProperties(symbolProperties), _cashBook, _registeredTypes, cache, underlying);
172  break;
173 
174  case SecurityType.FutureOption:
175  if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
176  var optionSymbolProperties = new Option.OptionSymbolProperties(symbolProperties);
177 
178  // Future options exercised only gives us one contract back, rather than the
179  // 100x seen in equities.
180  optionSymbolProperties.SetContractUnitOfTrade(1);
181 
182  security = new FutureOption.FutureOption(symbol, exchangeHours, quoteCash, optionSymbolProperties, _cashBook, _registeredTypes, cache, underlying);
183  break;
184 
185  case SecurityType.Future:
186  security = new Future.Future(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
187  break;
188 
189  case SecurityType.Forex:
190  security = new Forex.Forex(symbol, exchangeHours, quoteCash, baseCash, symbolProperties, _cashBook, _registeredTypes, cache);
191  break;
192 
193  case SecurityType.Cfd:
194  security = new Cfd.Cfd(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
195  break;
196 
197  case SecurityType.Index:
198  security = new Index.Index(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
199  break;
200 
201  case SecurityType.Crypto:
202  security = new Crypto.Crypto(symbol, exchangeHours, quoteCash, baseCash, symbolProperties, _cashBook, _registeredTypes, cache);
203  break;
204 
205  case SecurityType.CryptoFuture:
206  security = new CryptoFuture.CryptoFuture(symbol, exchangeHours, quoteCash, baseCash, symbolProperties, _cashBook, _registeredTypes, cache);
207  break;
208 
209  default:
210  case SecurityType.Base:
211  security = new Security(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
212  break;
213  }
214 
215  // if we're just creating this security and it only has an internal
216  // feed, mark it as non-tradable since the user didn't request this data
217  if (security.IsTradable)
218  {
219  security.IsTradable = !configList.IsInternalFeed;
220  }
221 
222  security.AddData(configList);
223 
224  // invoke the security initializer
225  InitializeSecurity(initializeSecurity, security);
226 
227  CheckCanonicalSecurityModels(security);
228 
229  // if leverage was specified then apply to security after the initializer has run, parameters of this
230  // method take precedence over the intializer
231  if (leverage != Security.NullLeverage)
232  {
233  security.SetLeverage(leverage);
234  }
235 
236  var isNotNormalized = configList.DataNormalizationMode() == DataNormalizationMode.Raw;
237 
238  // In live mode and non normalized data, equity assumes specific price variation model
239  if ((_isLiveMode || isNotNormalized) && security.Type == SecurityType.Equity)
240  {
241  security.PriceVariationModel = new EquityPriceVariationModel();
242  }
243 
244  return security;
245  }
246 
247  /// <summary>
248  /// Creates a new security
249  /// </summary>
250  /// <remarks>Following the obsoletion of Security.Subscriptions,
251  /// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
253  List<SubscriptionDataConfig> subscriptionDataConfigList,
254  decimal leverage = 0,
255  bool addToSymbolCache = true,
256  Security underlying = null)
257  {
258  return CreateSecurity(symbol, subscriptionDataConfigList, leverage, addToSymbolCache, underlying,
259  initializeSecurity: true, reCreateSecurity: false);
260  }
261 
262  /// <summary>
263  /// Creates a new security
264  /// </summary>
265  /// <remarks>Following the obsoletion of Security.Subscriptions,
266  /// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
267  public Security CreateSecurity(Symbol symbol, SubscriptionDataConfig subscriptionDataConfig, decimal leverage = 0, bool addToSymbolCache = true, Security underlying = null)
268  {
269  return CreateSecurity(symbol, new List<SubscriptionDataConfig> { subscriptionDataConfig }, leverage, addToSymbolCache, underlying);
270  }
271 
272  /// <summary>
273  /// Creates a new security
274  /// </summary>
275  /// <remarks>Following the obsoletion of Security.Subscriptions,
276  /// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
278  {
279  return CreateSecurity(symbol,
280  new List<SubscriptionDataConfig>(),
281  leverage: 1,
282  addToSymbolCache: false,
283  underlying: null,
284  initializeSecurity: false,
285  reCreateSecurity: true);
286  }
287 
288  /// <summary>
289  /// Set live mode state of the algorithm
290  /// </summary>
291  /// <param name="isLiveMode">True, live mode is enabled</param>
292  public void SetLiveMode(bool isLiveMode)
293  {
294  _isLiveMode = isLiveMode;
295  }
296 
297  /// <summary>
298  /// Checks whether the created security has the same models as its canonical security (in case it has one)
299  /// and sends a one-time warning if it doesn't.
300  /// </summary>
301  private void CheckCanonicalSecurityModels(Security security)
302  {
303  if (!_modelsMismatchWarningSent &&
304  _algorithm != null &&
305  security.Symbol.HasCanonical() &&
306  _algorithm.Securities.TryGetValue(security.Symbol.Canonical, out var canonicalSecurity))
307  {
308  if (security.FillModel.GetType() != canonicalSecurity.FillModel.GetType() ||
309  security.FeeModel.GetType() != canonicalSecurity.FeeModel.GetType() ||
310  security.BuyingPowerModel.GetType() != canonicalSecurity.BuyingPowerModel.GetType() ||
311  security.MarginInterestRateModel.GetType() != canonicalSecurity.MarginInterestRateModel.GetType() ||
312  security.SlippageModel.GetType() != canonicalSecurity.SlippageModel.GetType() ||
313  security.VolatilityModel.GetType() != canonicalSecurity.VolatilityModel.GetType() ||
314  security.SettlementModel.GetType() != canonicalSecurity.SettlementModel.GetType())
315  {
316  _modelsMismatchWarningSent = true;
317  _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.");
318  }
319  }
320  }
321 
322  private void InitializeSecurity(bool initializeSecurity, Security security)
323  {
324  if (initializeSecurity && !security.IsInitialized)
325  {
326  _securityInitializerProvider.SecurityInitializer.Initialize(security);
327  security.IsInitialized = true;
328  }
329  }
330  }
331 }