Files
quantconnect--lean/Common/Securities/SecurityService.cs
T
Gerardo Salazar 4c085ff853 Adds Indexes and Index Options asset types (Backtesting/Live, IB only) (#5379)
* Add support for Index SecurityType  🚀 (#5364)

* Add Index SecurityType  🚀

* Extend SecurityIdentifier & Lean Data classes with Index support

* Add Index SecurityType  🚀

* Extend SecurityIdentifier & Lean Data classes with Index support

* Fixes

* Added index cross basic template demonstration

* WIP: Prototype index security type for LEAN as non tradable asset

* Re-adds Index entries to MHDB after rebase

* First steps to getting Index Options running

  * Looks at any instance where we pattern match for an option type
    and replaces it with a generic call to `.IsOption()` for easier
    extensibility in the future for additional option security types

  * Adds IndexOption security and misc. classes

  * Misc. changes, mainly related to any sort of special casing of
    equity options and made index options take the same path

* Enables index options data for backtesting

  * Adds new index options market hours to MHDB
  * Misc. bug fixes for index options
  * WIP: add live support for index options and indexes
  * Use OptionMarginModel for Index Options because they both use the
    same calculation for margin requirements

* Fixes contract not found errors on SPX index options and SPX index in IB

  * Turns out index options' last trading day is the day before expiry,
    which IB was expecting the last trading day.

* Add index option test cases (temp)

* LiveOptionChainProvider fix, use Symbol vs. ticker

  * Description updates to regression algorithms

* Fixes bug in live trading for indexes and index options

  * Adds overridable minimum price variation symbol property
  * Adds variable sized minimum price variation for index options
  * Adjusts symbol properties for index options
  * Misc. bug fixes

* Fixes option assignment simulation for European options

  * Updates index options regression algorithms (WIP)

* Fixes bug where index option exercise would trade index underlying

  * Fixes bugs where SecurityType.Index was getting flagged as tradable

* Regression algorithms updates and addresses review

  * Misc. style fixes and refactoring + a few bug fixes
  * Updates regression algorithms to run without runtime errors
  * Adds data for regression algos

* Sets DefaultOptionStyle on Canonical and support index options

* Update regression algos statistics

* Removes bad line in regression algorithm causing build to fail

* Minor tweaks

* Address review add comment about quoteBar parse scale

Co-authored-by: Balamurali Pandranki <balamurali@live.com>
Co-authored-by: Jared Broad <jaredbroad@gmail.com>
Co-authored-by: Martin-Molinero <martin@quantconnect.com>
2021-03-12 20:46:23 -03:00

239 lines
11 KiB
C#

/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using System;
namespace QuantConnect.Securities
{
/// <summary>
/// This class implements interface <see cref="ISecurityService"/> providing methods for creating new <see cref="Security"/>
/// </summary>
public class SecurityService : ISecurityService
{
private readonly CashBook _cashBook;
private readonly MarketHoursDatabase _marketHoursDatabase;
private readonly SymbolPropertiesDatabase _symbolPropertiesDatabase;
private readonly IRegisteredSecurityDataTypesProvider _registeredTypes;
private readonly ISecurityInitializerProvider _securityInitializerProvider;
private readonly SecurityCacheProvider _cacheProvider;
private readonly IPrimaryExchangeProvider _primaryExchangeProvider;
private bool _isLiveMode;
/// <summary>
/// Creates a new instance of the SecurityService class
/// </summary>
public SecurityService(CashBook cashBook,
MarketHoursDatabase marketHoursDatabase,
SymbolPropertiesDatabase symbolPropertiesDatabase,
ISecurityInitializerProvider securityInitializerProvider,
IRegisteredSecurityDataTypesProvider registeredTypes,
SecurityCacheProvider cacheProvider,
IPrimaryExchangeProvider primaryExchangeProvider=null)
{
_cashBook = cashBook;
_registeredTypes = registeredTypes;
_marketHoursDatabase = marketHoursDatabase;
_symbolPropertiesDatabase = symbolPropertiesDatabase;
_securityInitializerProvider = securityInitializerProvider;
_cacheProvider = cacheProvider;
_primaryExchangeProvider = primaryExchangeProvider;
}
/// <summary>
/// Creates a new security
/// </summary>
/// <remarks>Following the obsoletion of Security.Subscriptions,
/// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
public Security CreateSecurity(Symbol symbol,
List<SubscriptionDataConfig> subscriptionDataConfigList,
decimal leverage = 0,
bool addToSymbolCache = true)
{
var configList = new SubscriptionDataConfigList(symbol);
configList.AddRange(subscriptionDataConfigList);
var exchangeHours = _marketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType).ExchangeHours;
var defaultQuoteCurrency = _cashBook.AccountCurrency;
if (symbol.ID.SecurityType == SecurityType.Forex)
{
defaultQuoteCurrency = symbol.Value.Substring(3);
}
if (symbol.ID.SecurityType == SecurityType.Crypto && !_symbolPropertiesDatabase.ContainsKey(symbol.ID.Market, symbol, symbol.ID.SecurityType))
{
throw new ArgumentException($"Symbol can't be found in the Symbol Properties Database: {symbol.Value}");
}
// For Futures Options that don't have a SPDB entry, the futures entry will be used instead.
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(
symbol.ID.Market,
symbol,
symbol.SecurityType,
defaultQuoteCurrency);
// add the symbol to our cache
if (addToSymbolCache)
{
SymbolCache.Set(symbol.Value, symbol);
}
// verify the cash book is in a ready state
var quoteCurrency = symbolProperties.QuoteCurrency;
if (!_cashBook.ContainsKey(quoteCurrency))
{
// since we have none it's safe to say the conversion is zero
_cashBook.Add(quoteCurrency, 0, 0);
}
if (symbol.ID.SecurityType == SecurityType.Forex || symbol.ID.SecurityType == SecurityType.Crypto)
{
// decompose the symbol into each currency pair
string baseCurrency;
if (symbol.ID.SecurityType == SecurityType.Forex)
{
Forex.Forex.DecomposeCurrencyPair(symbol.Value, out baseCurrency, out quoteCurrency);
}
else
{
Crypto.Crypto.DecomposeCurrencyPair(symbol, symbolProperties, out baseCurrency, out quoteCurrency);
}
if (!_cashBook.ContainsKey(baseCurrency))
{
// since we have none it's safe to say the conversion is zero
_cashBook.Add(baseCurrency, 0, 0);
}
if (!_cashBook.ContainsKey(quoteCurrency))
{
// since we have none it's safe to say the conversion is zero
_cashBook.Add(quoteCurrency, 0, 0);
}
}
var quoteCash = _cashBook[symbolProperties.QuoteCurrency];
var cache = _cacheProvider.GetSecurityCache(symbol);
Security security;
switch (symbol.ID.SecurityType)
{
case SecurityType.Equity:
var primaryExchange =
_primaryExchangeProvider?.GetPrimaryExchange(symbol.ID) ??
PrimaryExchange.UNKNOWN;
security = new Equity.Equity(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache, primaryExchange);
break;
case SecurityType.Option:
if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
security = new Option.Option(symbol, exchangeHours, quoteCash, new Option.OptionSymbolProperties(symbolProperties), _cashBook, _registeredTypes, cache);
break;
case SecurityType.IndexOption:
if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
security = new IndexOption.IndexOption(symbol, exchangeHours, quoteCash, new IndexOption.IndexOptionSymbolProperties(symbolProperties), _cashBook, _registeredTypes, cache);
break;
case SecurityType.FutureOption:
if (addToSymbolCache) SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying);
var optionSymbolProperties = new Option.OptionSymbolProperties(symbolProperties);
// Future options exercised only gives us one contract back, rather than the
// 100x seen in equities.
optionSymbolProperties.SetContractUnitOfTrade(1);
security = new FutureOption.FutureOption(symbol, exchangeHours, quoteCash, optionSymbolProperties, _cashBook, _registeredTypes, cache);
break;
case SecurityType.Future:
security = new Future.Future(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
break;
case SecurityType.Forex:
security = new Forex.Forex(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
break;
case SecurityType.Cfd:
security = new Cfd.Cfd(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
break;
case SecurityType.Index:
security = new Index.Index(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
break;
case SecurityType.Crypto:
security = new Crypto.Crypto(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
break;
default:
case SecurityType.Base:
security = new Security(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache);
break;
}
// if we're just creating this security and it only has an internal
// feed, mark it as non-tradable since the user didn't request this data
if (!configList.IsInternalFeed)
{
security.IsTradable = true;
}
security.AddData(configList);
// invoke the security initializer
_securityInitializerProvider.SecurityInitializer.Initialize(security);
// if leverage was specified then apply to security after the initializer has run, parameters of this
// method take precedence over the intializer
if (leverage != Security.NullLeverage)
{
security.SetLeverage(leverage);
}
var isNotNormalized = configList.DataNormalizationMode() == DataNormalizationMode.Raw;
// In live mode and non normalized data, equity assumes specific price variation model
if ((_isLiveMode || isNotNormalized) && security.Type == SecurityType.Equity)
{
security.PriceVariationModel = new EquityPriceVariationModel();
}
return security;
}
/// <summary>
/// Creates a new security
/// </summary>
/// <remarks>Following the obsoletion of Security.Subscriptions,
/// both overloads will be merged removing <see cref="SubscriptionDataConfig"/> arguments</remarks>
public Security CreateSecurity(Symbol symbol, SubscriptionDataConfig subscriptionDataConfig, decimal leverage = 0, bool addToSymbolCache = true)
{
return CreateSecurity(symbol, new List<SubscriptionDataConfig> { subscriptionDataConfig }, leverage, addToSymbolCache);
}
/// <summary>
/// Set live mode state of the algorithm
/// </summary>
/// <param name="isLiveMode">True, live mode is enabled</param>
public void SetLiveMode(bool isLiveMode)
{
_isLiveMode = isLiveMode;
}
}
}