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