/// <summary> /// Creates a security and matching configuration. This applies the default leverage if /// leverage is less than or equal to zero. /// This method also add the new symbol mapping to the <see cref="SymbolCache"/> /// </summary> public static Security CreateSecurity(SecurityPortfolioManager securityPortfolioManager, SubscriptionManager subscriptionManager, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase, ISecurityInitializer securityInitializer, Symbol symbol, Resolution resolution, bool fillDataForward, decimal leverage, bool extendedMarketHours, bool isInternalFeed, bool isCustomData, bool isLiveMode, bool addToSymbolCache = true ) { var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; var defaultQuoteCurrency = CashBook.AccountCurrency; if (symbol.ID.SecurityType == SecurityType.Forex || symbol.ID.SecurityType == SecurityType.Crypto) { defaultQuoteCurrency = symbol.Value.Substring(3); } var symbolProperties = symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol, symbol.ID.SecurityType, defaultQuoteCurrency); var types = subscriptionManager.LookupSubscriptionConfigDataTypes(symbol.SecurityType, resolution, symbol.IsCanonical()); return(CreateSecurity(types, securityPortfolioManager, subscriptionManager, exchangeHours, marketHoursDbEntry.DataTimeZone, symbolProperties, securityInitializer, symbol, resolution, fillDataForward, leverage, extendedMarketHours, isInternalFeed, isCustomData, isLiveMode, addToSymbolCache)); }
/// <summary> /// Creates a security and matching configuration. This applies the default leverage if /// leverage is less than or equal to zero. /// This method also add the new symbol mapping to the <see cref="SymbolCache"/> /// </summary> public static Security CreateSecurity(SecurityPortfolioManager securityPortfolioManager, SubscriptionManager subscriptionManager, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase, ISecurityInitializer securityInitializer, Symbol symbol, Resolution resolution, bool fillDataForward, decimal leverage, bool extendedMarketHours, bool isInternalFeed, bool isCustomData, bool addToSymbolCache = true) { var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; var symbolProperties = symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType); var tradeBarType = typeof(TradeBar); var type = resolution == Resolution.Tick ? typeof(Tick) : tradeBarType; return(CreateSecurity(type, securityPortfolioManager, subscriptionManager, exchangeHours, marketHoursDbEntry.DataTimeZone, symbolProperties, securityInitializer, symbol, resolution, fillDataForward, leverage, extendedMarketHours, isInternalFeed, isCustomData, addToSymbolCache)); }
/// <summary> /// Creates a security and matching configuration. This applies the default leverage if /// leverage is less than or equal to zero. /// This method also add the new symbol mapping to the <see cref="SymbolCache"/> /// </summary> public static Security CreateSecurity(SecurityPortfolioManager securityPortfolioManager, SubscriptionManager subscriptionManager, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase, ISecurityInitializer securityInitializer, Symbol symbol, Resolution resolution, bool fillDataForward, decimal leverage, bool extendedMarketHours, bool isInternalFeed, bool isCustomData, bool addToSymbolCache = true) { var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; var defaultQuoteCurrency = CashBook.AccountCurrency; if (symbol.ID.SecurityType == SecurityType.Forex) { defaultQuoteCurrency = symbol.Value.Substring(3); } var symbolProperties = symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType, defaultQuoteCurrency); var type = resolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar); if (symbol.ID.SecurityType == SecurityType.Option && resolution != Resolution.Tick) { type = typeof(QuoteBar); } return(CreateSecurity(type, securityPortfolioManager, subscriptionManager, exchangeHours, marketHoursDbEntry.DataTimeZone, symbolProperties, securityInitializer, symbol, resolution, fillDataForward, leverage, extendedMarketHours, isInternalFeed, isCustomData, addToSymbolCache)); }
/// <summary> /// Ensures that we have a data feed to convert this currency into the base currency. /// This will add a subscription at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketHoursDatabase">A security exchange hours provider instance used to resolve exchange hours for new subscriptions</param> /// <param name="symbolPropertiesDatabase">A symbol properties database instance</param> /// <param name="marketMap">The market map that decides which market the new security should be in</param> /// <param name="cashBook">The cash book - used for resolving quote currencies for created conversion securities</param> /// <returns>Returns the added currency security if needed, otherwise null</returns> public Security EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase, IReadOnlyDictionary <SecurityType, string> marketMap, CashBook cashBook) { if (Symbol == CashBook.AccountCurrency) { SecuritySymbol = QuantConnect.Symbol.Empty; _isBaseCurrency = true; ConversionRate = 1.0m; return(null); } if (subscriptions.Count == 0) { throw new InvalidOperationException("Unable to add cash when no subscriptions are present. Please add subscriptions in the Initialize() method."); } // we require a subscription that converts this into the base currency string normal = Symbol + CashBook.AccountCurrency; string invert = CashBook.AccountCurrency + Symbol; foreach (var config in subscriptions.Subscriptions.Where(config => config.SecurityType == SecurityType.Forex || config.SecurityType == SecurityType.Cfd)) { if (config.Symbol.Value == normal) { SecuritySymbol = config.Symbol; return(null); } if (config.Symbol.Value == invert) { SecuritySymbol = config.Symbol; _invertRealTimePrice = true; return(null); } } // if we've made it here we didn't find a subscription, so we'll need to add one var currencyPairs = Currencies.CurrencyPairs.Select(x => { // allow XAU or XAG to be used as quote currencies, but pairs including them are CFDs var securityType = Symbol.StartsWith("X") ? SecurityType.Cfd : SecurityType.Forex; var market = marketMap[securityType]; return(QuantConnect.Symbol.Create(x, securityType, market)); }); var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Minute).Min(); var objectType = minimumResolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar); foreach (var symbol in currencyPairs) { if (symbol.Value == normal || symbol.Value == invert) { _invertRealTimePrice = symbol.Value == invert; var securityType = symbol.ID.SecurityType; var symbolProperties = symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol.Value, securityType, Symbol); Cash quoteCash; if (!cashBook.TryGetValue(symbolProperties.QuoteCurrency, out quoteCash)) { throw new Exception("Unable to resolve quote cash: " + symbolProperties.QuoteCurrency + ". This is required to add conversion feed: " + symbol.ToString()); } var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.Add(objectType, symbol, minimumResolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, false, true, false, true); SecuritySymbol = config.Symbol; Security security; if (securityType == SecurityType.Cfd) { security = new Cfd.Cfd(exchangeHours, quoteCash, config, symbolProperties); } else { security = new Forex.Forex(exchangeHours, quoteCash, config, symbolProperties); } securities.Add(config.Symbol, security); Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol.Value + " for cash " + Symbol + " currency feed"); return(security); } } // if this still hasn't been set then it's an error condition throw new ArgumentException(string.Format("In order to maintain cash in {0} you are required to add a subscription for Forex pair {0}{1} or {1}{0}", Symbol, CashBook.AccountCurrency)); }
/// <summary> /// Ensures that we have a data feed to conver this currency into the base currency. /// This will add a subscription at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketHoursDatabase">A security exchange hours provider instance used to resolve exchange hours for new subscriptions</param> public void EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, MarketHoursDatabase marketHoursDatabase) { if (Symbol == CashBook.AccountCurrency) { SecuritySymbol = QuantConnect.Symbol.Empty; _isBaseCurrency = true; ConversionRate = 1.0m; return; } if (subscriptions.Count == 0) { throw new InvalidOperationException("Unable to add cash when no subscriptions are present. Please add subscriptions in the Initialize() method."); } // we require a subscription that converts this into the base currency string normal = Symbol + CashBook.AccountCurrency; string invert = CashBook.AccountCurrency + Symbol; foreach (var config in subscriptions.Subscriptions.Where(config => config.SecurityType == SecurityType.Forex)) { if (config.Symbol.Value == normal) { SecuritySymbol = config.Symbol; return; } if (config.Symbol.Value == invert) { SecuritySymbol = config.Symbol; _invertRealTimePrice = true; return; } } // get the market from the first Forex subscription string market = (from config in subscriptions.Subscriptions where config.SecurityType == SecurityType.Forex select config.Market).FirstOrDefault() ?? Market.FXCM; // if we've made it here we didn't find a subscription, so we'll need to add one var currencyPairs = Forex.Forex.CurrencyPairs.Select(x => new Symbol(SecurityIdentifier.GenerateForex(x, market), x)); var minimumResolution = subscriptions.Subscriptions.Min(x => x.Resolution); var objectType = minimumResolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar); foreach (var symbol in currencyPairs) { if (symbol.Value == normal || symbol.Value == invert) { _invertRealTimePrice = symbol.Value == invert; var marketHoursDbEntry = marketHoursDatabase.GetEntry(market, symbol, SecurityType.Forex); var exchangeHours = marketHoursDbEntry.ExchangeHours; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.Add(objectType, symbol, minimumResolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, false, true, false, true); var security = new Forex.Forex(exchangeHours, this, config, 1m); SecuritySymbol = config.Symbol; securities.Add(config.Symbol, security); Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol.ToString() + " for cash " + Symbol + " currency feed"); return; } } // if this still hasn't been set then it's an error condition throw new ArgumentException(string.Format("In order to maintain cash in {0} you are required to add a subscription for Forex pair {0}{1} or {1}{0}", Symbol, CashBook.AccountCurrency)); }
/// <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: security = new Equity.Equity(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook, _registeredTypes, cache); 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.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.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> /// Ensures that we have a data feed to convert this currency into the base currency. /// This will add a subscription at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketHoursDatabase">A security exchange hours provider instance used to resolve exchange hours for new subscriptions</param> /// <returns>Returns the added currency security if needed, otherwise null</returns> public Security EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, MarketHoursDatabase marketHoursDatabase) { if (Symbol == CashBook.AccountCurrency) { SecuritySymbol = QuantConnect.Symbol.Empty; _isBaseCurrency = true; ConversionRate = 1.0m; return null; } if (subscriptions.Count == 0) { throw new InvalidOperationException("Unable to add cash when no subscriptions are present. Please add subscriptions in the Initialize() method."); } // we require a subscription that converts this into the base currency string normal = Symbol + CashBook.AccountCurrency; string invert = CashBook.AccountCurrency + Symbol; foreach (var config in subscriptions.Subscriptions.Where(config => config.SecurityType == SecurityType.Forex)) { if (config.Symbol.Value == normal) { SecuritySymbol = config.Symbol; return null; } if (config.Symbol.Value == invert) { SecuritySymbol = config.Symbol; _invertRealTimePrice = true; return null; } } // get the market from the first Forex subscription string market = (from config in subscriptions.Subscriptions where config.SecurityType == SecurityType.Forex select config.Market).FirstOrDefault() ?? Market.FXCM; // if we've made it here we didn't find a subscription, so we'll need to add one var currencyPairs = Forex.Forex.CurrencyPairs.Select(x => QuantConnect.Symbol.Create(x, SecurityType.Forex, market)); var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Minute).Min(); var objectType = minimumResolution == Resolution.Tick ? typeof (Tick) : typeof (TradeBar); foreach (var symbol in currencyPairs) { if (symbol.Value == normal || symbol.Value == invert) { _invertRealTimePrice = symbol.Value == invert; var marketHoursDbEntry = marketHoursDatabase.GetEntry(market, symbol.Value, SecurityType.Forex); var exchangeHours = marketHoursDbEntry.ExchangeHours; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.Add(objectType, symbol, minimumResolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, false, true, false, true); var security = new Forex.Forex(exchangeHours, this, config); SecuritySymbol = config.Symbol; securities.Add(config.Symbol, security); Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol.Value + " for cash " + Symbol + " currency feed"); return security; } } // if this still hasn't been set then it's an error condition throw new ArgumentException(string.Format("In order to maintain cash in {0} you are required to add a subscription for Forex pair {0}{1} or {1}{0}", Symbol, CashBook.AccountCurrency)); }
/// <summary> /// Ensures that we have a data feed to convert this currency into the base currency. /// This will add a subscription at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketHoursDatabase">A security exchange hours provider instance used to resolve exchange hours for new subscriptions</param> /// <param name="symbolPropertiesDatabase">A symbol properties database instance</param> /// <param name="marketMap">The market map that decides which market the new security should be in</param> /// <param name="cashBook">The cash book - used for resolving quote currencies for created conversion securities</param> /// <returns>Returns the added currency security if needed, otherwise null</returns> public Security EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase, IReadOnlyDictionary<SecurityType, string> marketMap, CashBook cashBook) { if (Symbol == CashBook.AccountCurrency) { SecuritySymbol = QuantConnect.Symbol.Empty; _isBaseCurrency = true; ConversionRate = 1.0m; return null; } if (subscriptions.Count == 0) { throw new InvalidOperationException("Unable to add cash when no subscriptions are present. Please add subscriptions in the Initialize() method."); } // we require a subscription that converts this into the base currency string normal = Symbol + CashBook.AccountCurrency; string invert = CashBook.AccountCurrency + Symbol; foreach (var config in subscriptions.Subscriptions.Where(config => config.SecurityType == SecurityType.Forex || config.SecurityType == SecurityType.Cfd)) { if (config.Symbol.Value == normal) { SecuritySymbol = config.Symbol; return null; } if (config.Symbol.Value == invert) { SecuritySymbol = config.Symbol; _invertRealTimePrice = true; return null; } } // if we've made it here we didn't find a subscription, so we'll need to add one var currencyPairs = Currencies.CurrencyPairs.Select(x => { // allow XAU or XAG to be used as quote currencies, but pairs including them are CFDs var securityType = Symbol.StartsWith("X") ? SecurityType.Cfd : SecurityType.Forex; var market = marketMap[securityType]; return QuantConnect.Symbol.Create(x, securityType, market); }); var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Minute).Min(); var objectType = minimumResolution == Resolution.Tick ? typeof (Tick) : typeof (TradeBar); foreach (var symbol in currencyPairs) { if (symbol.Value == normal || symbol.Value == invert) { _invertRealTimePrice = symbol.Value == invert; var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.Add(objectType, symbol, minimumResolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, false, true, false, true); SecuritySymbol = config.Symbol; var securityType = symbol.ID.SecurityType; Security security; if (securityType == SecurityType.Cfd) { var symbolProperties = symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol.Value, securityType); Cash quoteCash; if (!cashBook.TryGetValue(symbolProperties.QuoteCurrency, out quoteCash)) { throw new Exception("Unable to resolve quote cash: " + symbolProperties.QuoteCurrency + ". This is required to add conversion feed: " + symbol.ToString()); } security = new Cfd.Cfd(exchangeHours, quoteCash, config, symbolProperties); } else { security = new Forex.Forex(exchangeHours, this, config); } securities.Add(config.Symbol, security); Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol.Value + " for cash " + Symbol + " currency feed"); return security; } } // if this still hasn't been set then it's an error condition throw new ArgumentException(string.Format("In order to maintain cash in {0} you are required to add a subscription for Forex pair {0}{1} or {1}{0}", Symbol, CashBook.AccountCurrency)); }
/// <summary> /// Ensures that we have a data feed to convert this currency into the base currency. /// This will add a subscription at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketHoursDatabase">A security exchange hours provider instance used to resolve exchange hours for new subscriptions</param> /// <param name="symbolPropertiesDatabase">A symbol properties database instance</param> /// <param name="marketMap">The market map that decides which market the new security should be in</param> /// <param name="cashBook">The cash book - used for resolving quote currencies for created conversion securities</param> /// <param name="changes"></param> /// <returns>Returns the added currency security if needed, otherwise null</returns> public Security EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase, IReadOnlyDictionary <SecurityType, string> marketMap, CashBook cashBook, SecurityChanges changes ) { // this gets called every time we add securities using universe selection, // so must of the time we've already resolved the value and don't need to again if (ConversionRateSecurity != null) { return(null); } if (Symbol == CashBook.AccountCurrency) { ConversionRateSecurity = null; _isBaseCurrency = true; ConversionRate = 1.0m; return(null); } // we require a security that converts this into the base currency string normal = Symbol + CashBook.AccountCurrency; string invert = CashBook.AccountCurrency + Symbol; var securitiesToSearch = securities.Select(kvp => kvp.Value) .Concat(changes.AddedSecurities) .Where(s => s.Type == SecurityType.Forex || s.Type == SecurityType.Cfd || s.Type == SecurityType.Crypto); foreach (var security in securitiesToSearch) { if (security.Symbol.Value == normal) { ConversionRateSecurity = security; return(null); } if (security.Symbol.Value == invert) { ConversionRateSecurity = security; _invertRealTimePrice = true; return(null); } } // if we've made it here we didn't find a security, so we'll need to add one // Create a SecurityType to Market mapping with the markets from SecurityManager members var markets = securities.Select(x => x.Key).GroupBy(x => x.SecurityType).ToDictionary(x => x.Key, y => y.First().ID.Market); if (markets.ContainsKey(SecurityType.Cfd) && !markets.ContainsKey(SecurityType.Forex)) { markets.Add(SecurityType.Forex, markets[SecurityType.Cfd]); } if (markets.ContainsKey(SecurityType.Forex) && !markets.ContainsKey(SecurityType.Cfd)) { markets.Add(SecurityType.Cfd, markets[SecurityType.Forex]); } var potentials = Currencies.CurrencyPairs.Select(fx => CreateSymbol(marketMap, fx, markets, SecurityType.Forex)) .Concat(Currencies.CfdCurrencyPairs.Select(cfd => CreateSymbol(marketMap, cfd, markets, SecurityType.Cfd))) .Concat(Currencies.CryptoCurrencyPairs.Select(crypto => CreateSymbol(marketMap, crypto, markets, SecurityType.Crypto))); var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Minute).Min(); var objectType = minimumResolution == Resolution.Tick ? typeof(Tick) : typeof(QuoteBar); foreach (var symbol in potentials) { if (symbol.Value == normal || symbol.Value == invert) { _invertRealTimePrice = symbol.Value == invert; var securityType = symbol.ID.SecurityType; var symbolProperties = symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol.Value, securityType, Symbol); Cash quoteCash; if (!cashBook.TryGetValue(symbolProperties.QuoteCurrency, out quoteCash)) { throw new Exception("Unable to resolve quote cash: " + symbolProperties.QuoteCurrency + ". This is required to add conversion feed: " + symbol.Value); } var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.Add(objectType, TickType.Quote, symbol, minimumResolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, false, true, false, true); Security security; if (securityType == SecurityType.Cfd) { security = new Cfd.Cfd(exchangeHours, quoteCash, config, symbolProperties); } else if (securityType == SecurityType.Crypto) { security = new Crypto.Crypto(exchangeHours, quoteCash, config, symbolProperties); } else { security = new Forex.Forex(exchangeHours, quoteCash, config, symbolProperties); } ConversionRateSecurity = security; securities.Add(config.Symbol, security); Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol.Value + " for cash " + Symbol + " currency feed"); return(security); } } // if this still hasn't been set then it's an error condition throw new ArgumentException(string.Format("In order to maintain cash in {0} you are required to add a subscription for Forex pair {0}{1} or {1}{0}", Symbol, CashBook.AccountCurrency)); }
/// <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 || symbol.ID.SecurityType == SecurityType.Crypto) { defaultQuoteCurrency = symbol.Value.Substring(3); } var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol, symbol.ID.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; Forex.Forex.DecomposeCurrencyPair(symbol.Value, 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]; Security security; switch (symbol.ID.SecurityType) { case SecurityType.Equity: security = new Equity.Equity(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook); break; case SecurityType.Option: if (addToSymbolCache) { SymbolCache.Set(symbol.Underlying.Value, symbol.Underlying); } security = new Option.Option(symbol, exchangeHours, _cashBook[_cashBook.AccountCurrency], new Option.OptionSymbolProperties(symbolProperties), _cashBook); break; case SecurityType.Future: security = new Future.Future(symbol, exchangeHours, _cashBook[_cashBook.AccountCurrency], symbolProperties, _cashBook); break; case SecurityType.Forex: security = new Forex.Forex(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook); break; case SecurityType.Cfd: security = new Cfd.Cfd(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook); break; case SecurityType.Crypto: security = new Crypto.Crypto(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook); break; default: case SecurityType.Base: security = new Security(symbol, exchangeHours, quoteCash, symbolProperties, _cashBook); 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 > 0) { security.SetLeverage(leverage); } // In live mode, equity assumes specific price variation model if (_isLiveMode && security.Type == SecurityType.Equity) { security.PriceVariationModel = new EquityPriceVariationModel(); } return(security); }
/// <summary> /// Creates a security and matching configuration. This applies the default leverage if /// leverage is less than or equal to zero. /// This method also add the new symbol mapping to the <see cref="SymbolCache"/> /// </summary> public static Security CreateSecurity(SecurityPortfolioManager securityPortfolioManager, SubscriptionManager subscriptionManager, MarketHoursDatabase marketHoursDatabase, Symbol symbol, Resolution resolution, bool fillDataForward, decimal leverage, bool extendedMarketHours, bool isInternalFeed, bool isCustomData) { var sid = symbol.ID; //If it hasn't been set, use some defaults based on the security type if (leverage <= 0) { if (sid.SecurityType == SecurityType.Equity) { leverage = 2; } else if (sid.SecurityType == SecurityType.Forex) { leverage = 50; } // default to 1 for everything else else { leverage = 1m; } } // add the symbol to our cache SymbolCache.Set(symbol.Value, symbol); var market = sid.Market; var securityType = sid.SecurityType; //Add the symbol to Data Manager -- generate unified data streams for algorithm events var marketHoursDbEntry = marketHoursDatabase.GetEntry(market, symbol, securityType); var exchangeHours = marketHoursDbEntry.ExchangeHours; var tradeBarType = typeof(TradeBar); var type = resolution == Resolution.Tick ? typeof(Tick) : tradeBarType; var config = subscriptionManager.Add(type, symbol, resolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, isCustomData, fillDataForward: fillDataForward, extendedMarketHours: extendedMarketHours, isInternalFeed: isInternalFeed); Security security; switch (config.SecurityType) { case SecurityType.Equity: security = new Equity.Equity(exchangeHours, config, leverage); break; case SecurityType.Forex: // decompose the symbol into each currency pair string baseCurrency, quoteCurrency; Forex.Forex.DecomposeCurrencyPair(symbol.Value, out baseCurrency, out quoteCurrency); if (!securityPortfolioManager.CashBook.ContainsKey(baseCurrency)) { // since we have none it's safe to say the conversion is zero securityPortfolioManager.CashBook.Add(baseCurrency, 0, 0); } if (!securityPortfolioManager.CashBook.ContainsKey(quoteCurrency)) { // since we have none it's safe to say the conversion is zero securityPortfolioManager.CashBook.Add(quoteCurrency, 0, 0); } security = new Forex.Forex(exchangeHours, securityPortfolioManager.CashBook[quoteCurrency], config, leverage); break; default: case SecurityType.Base: security = new Security(exchangeHours, config, leverage); break; } return(security); }