Example #1
0
        /// <summary>
        /// Ensures that we have a data feed to convert this currency into the base currency.
        /// This will add a <see cref="SubscriptionDataConfig"/> and create a <see cref="Security"/> 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="marketMap">The market map that decides which market the new security should be in</param>
        /// <param name="changes">Will be used to consume <see cref="SecurityChanges.AddedSecurities"/></param>
        /// <param name="securityService">Will be used to create required new <see cref="Security"/></param>
        /// <param name="accountCurrency">The account currency</param>
        /// <param name="defaultResolution">The default resolution to use for the internal subscriptions</param>
        /// <returns>Returns the added <see cref="SubscriptionDataConfig"/>, otherwise null</returns>
        public SubscriptionDataConfig EnsureCurrencyDataFeed(SecurityManager securities,
                                                             SubscriptionManager subscriptions,
                                                             IReadOnlyDictionary <SecurityType, string> marketMap,
                                                             SecurityChanges changes,
                                                             ISecurityService securityService,
                                                             string accountCurrency,
                                                             Resolution defaultResolution = Resolution.Minute
                                                             )
        {
            // 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 == accountCurrency)
            {
                ConversionRateSecurity = null;
                _isBaseCurrency        = true;
                ConversionRate         = 1.0m;
                return(null);
            }

            // we require a security that converts this into the base currency
            string normal             = Symbol + accountCurrency;
            string invert             = 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.Select(symbol => symbol.ID.Market).ToHashSet());

            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 forexEntries  = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Forex, marketMap, markets);
            var cfdEntries    = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Cfd, marketMap, markets);
            var cryptoEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Crypto, marketMap, markets);

            var potentialEntries = forexEntries
                                   .Concat(cfdEntries)
                                   .Concat(cryptoEntries)
                                   .ToList();

            if (!potentialEntries.Any(x =>
                                      Symbol == x.Key.Symbol.Substring(0, x.Key.Symbol.Length - x.Value.QuoteCurrency.Length) ||
                                      Symbol == x.Value.QuoteCurrency))
            {
                // currency not found in any tradeable pair
                Log.Error($"No tradeable pair was found for currency {Symbol}, conversion rate to account currency ({accountCurrency}) will be set to zero.");
                ConversionRateSecurity = null;
                ConversionRate         = 0m;
                return(null);
            }

            var potentials = potentialEntries
                             .Select(x => QuantConnect.Symbol.Create(x.Key.Symbol, x.Key.SecurityType, x.Key.Market));

            var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(defaultResolution).Min();

            foreach (var symbol in potentials)
            {
                if (symbol.Value == normal || symbol.Value == invert)
                {
                    _invertRealTimePrice = symbol.Value == invert;
                    var securityType = symbol.ID.SecurityType;

                    // use the first subscription defined in the subscription manager
                    var type       = subscriptions.LookupSubscriptionConfigDataTypes(securityType, minimumResolution, false).First();
                    var objectType = type.Item1;
                    var tickType   = type.Item2;

                    // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events
                    var config = subscriptions.SubscriptionDataConfigService.Add(symbol,
                                                                                 minimumResolution,
                                                                                 fillForward: true,
                                                                                 extendedMarketHours: false,
                                                                                 isInternalFeed: true,
                                                                                 subscriptionDataTypes: new List <Tuple <Type, TickType> > {
                        new Tuple <Type, TickType>(objectType, tickType)
                    }).First();

                    var security = securityService.CreateSecurity(symbol,
                                                                  config,
                                                                  addToSymbolCache: false);

                    ConversionRateSecurity = security;
                    securities.Add(config.Symbol, security);
                    Log.Trace($"Cash.EnsureCurrencyDataFeed(): Adding {symbol.Value} for cash {Symbol} currency feed");
                    return(config);
                }
            }

            // Special case for crypto markets without direct pairs (They wont be found by the above)
            // This allows us to add cash for "StableCoins" that are 1-1 with our account currency without needing a conversion security.
            // Check out the StableCoinsWithoutPairs static var for those that are missing their 1-1 conversion pairs
            if (Currencies.StableCoinsWithoutPairs.Contains(QuantConnect.Symbol.Create(normal, SecurityType.Crypto, marketMap[SecurityType.Crypto])))
            {
                ConversionRateSecurity = null;
                ConversionRate         = 1.0m;
                return(null);
            }

            // if this still hasn't been set then it's an error condition
            throw new ArgumentException($"In order to maintain cash in {Symbol} you are required to add a " +
                                        $"subscription for Forex pair {Symbol}{accountCurrency} or {accountCurrency}{Symbol}"
                                        );
        }
Example #2
0
        /// <summary>
        /// Ensures that we have a data feed to convert this currency into the base currency.
        /// This will add a <see cref="SubscriptionDataConfig"/> and create a <see cref="Security"/> 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="marketMap">The market map that decides which market the new security should be in</param>
        /// <param name="changes">Will be used to consume <see cref="SecurityChanges.AddedSecurities"/></param>
        /// <param name="securityService">Will be used to create required new <see cref="Security"/></param>
        /// <param name="accountCurrency">The account currency</param>
        /// <param name="defaultResolution">The default resolution to use for the internal subscriptions</param>
        /// <returns>Returns the added <see cref="SubscriptionDataConfig"/>, otherwise null</returns>
        public List <SubscriptionDataConfig> EnsureCurrencyDataFeed(SecurityManager securities,
                                                                    SubscriptionManager subscriptions,
                                                                    IReadOnlyDictionary <SecurityType, string> marketMap,
                                                                    SecurityChanges changes,
                                                                    ISecurityService securityService,
                                                                    string accountCurrency,
                                                                    Resolution defaultResolution = Resolution.Minute
                                                                    )
        {
            // 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 (CurrencyConversion != null)
            {
                return(null);
            }

            if (Symbol == accountCurrency)
            {
                _isBaseCurrency    = true;
                CurrencyConversion = null;
                ConversionRate     = 1.0m;
                return(null);
            }

            // existing securities
            var securitiesToSearch = securities.Select(kvp => kvp.Value)
                                     .Concat(changes.AddedSecurities)
                                     .Where(s => s.Type == SecurityType.Forex || s.Type == SecurityType.Cfd || s.Type == SecurityType.Crypto);

            // 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.Select(symbol => symbol.ID.Market).ToHashSet());

            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 forexEntries  = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Forex, marketMap, markets);
            var cfdEntries    = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Cfd, marketMap, markets);
            var cryptoEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Crypto, marketMap, markets);

            var potentialEntries = forexEntries
                                   .Concat(cfdEntries)
                                   .Concat(cryptoEntries)
                                   .ToList();

            if (!potentialEntries.Any(x =>
                                      Symbol == x.Key.Symbol.Substring(0, x.Key.Symbol.Length - x.Value.QuoteCurrency.Length) ||
                                      Symbol == x.Value.QuoteCurrency))
            {
                // currency not found in any tradeable pair
                Log.Error($"No tradeable pair was found for currency {Symbol}, conversion rate to account currency ({accountCurrency}) will be set to zero.");
                CurrencyConversion = null;
                ConversionRate     = 0m;
                return(null);
            }

            // Special case for crypto markets without direct pairs (They wont be found by the above)
            // This allows us to add cash for "StableCoins" that are 1-1 with our account currency without needing a conversion security.
            // Check out the StableCoinsWithoutPairs static var for those that are missing their 1-1 conversion pairs
            if (marketMap.TryGetValue(SecurityType.Crypto, out var market)
                &&
                (Currencies.IsStableCoinWithoutPair(Symbol + accountCurrency, market) ||
                 Currencies.IsStableCoinWithoutPair(accountCurrency + Symbol, market)))
            {
                CurrencyConversion = null;
                ConversionRate     = 1.0m;
                return(null);
            }

            var requiredSecurities = new List <SubscriptionDataConfig>();

            var potentials = potentialEntries
                             .Select(x => QuantConnect.Symbol.Create(x.Key.Symbol, x.Key.SecurityType, x.Key.Market));

            var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(defaultResolution).Min();

            var makeNewSecurity = new Func <Symbol, Security>(symbol =>
            {
                var securityType = symbol.ID.SecurityType;

                // use the first subscription defined in the subscription manager
                var type       = subscriptions.LookupSubscriptionConfigDataTypes(securityType, minimumResolution, false).First();
                var objectType = type.Item1;
                var tickType   = type.Item2;

                // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events
                var config = subscriptions.SubscriptionDataConfigService.Add(symbol,
                                                                             minimumResolution,
                                                                             fillForward: true,
                                                                             extendedMarketHours: false,
                                                                             isInternalFeed: true,
                                                                             subscriptionDataTypes: new List <Tuple <Type, TickType> >
                {
                    new Tuple <Type, TickType>(objectType, tickType)
                }).First();

                var newSecurity = securityService.CreateSecurity(symbol,
                                                                 config,
                                                                 addToSymbolCache: false);

                Log.Trace($"Cash.EnsureCurrencyDataFeed(): Adding {symbol.Value} for cash {Symbol} currency feed");

                securities.Add(symbol, newSecurity);
                requiredSecurities.Add(config);

                return(newSecurity);
            });

            CurrencyConversion = SecurityCurrencyConversion.LinearSearch(Symbol,
                                                                         accountCurrency,
                                                                         securitiesToSearch.ToList(),
                                                                         potentials,
                                                                         makeNewSecurity);

            return(requiredSecurities);
        }
Example #3
0
        /// <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, this, 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));
        }
Example #4
0
File: Cash.cs Project: wocclyl/Lean
        /// <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();

            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;

                    // use the first subscription defined in the subscription manager
                    var type       = subscriptions.LookupSubscriptionConfigDataTypes(securityType, minimumResolution, false).First();
                    var objectType = type.Item1;
                    var tickType   = type.Item2;

                    // 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, symbol, minimumResolution, marketHoursDbEntry.DataTimeZone, exchangeHours.TimeZone, false, true, false, true);

                    Security security;
                    if (securityType == SecurityType.Cfd)
                    {
                        security = new Cfd.Cfd(exchangeHours, quoteCash, config, symbolProperties, cashBook);
                    }
                    else if (securityType == SecurityType.Crypto)
                    {
                        security = new Crypto.Crypto(exchangeHours, quoteCash, config, symbolProperties, cashBook);
                    }
                    else
                    {
                        security = new Forex.Forex(exchangeHours, quoteCash, config, symbolProperties, cashBook);
                    }

                    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));
        }
Example #5
0
        /// <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="exchangeHoursProvider">A security exchange hours provider instance used to resolve exchange hours for new subscriptions</param>
        public void EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, SecurityExchangeHoursProvider exchangeHoursProvider)
        {
            if (Symbol == CashBook.AccountCurrency)
            {
                _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;

            for (int i = 0; i < subscriptions.Subscriptions.Count; i++)
            {
                var config = subscriptions.Subscriptions[i];
                if (config.SecurityType != SecurityType.Forex)
                {
                    continue;
                }
                if (config.Symbol == normal)
                {
                    _config = config;
                    return;
                }
                if (config.Symbol == invert)
                {
                    _config = config;
                    _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() ?? subscriptions.Subscriptions[0].Market;

            // if we've made it here we didn't find a subscription, so we'll need to add one
            var currencyPairs     = Forex.Forex.CurrencyPairs;
            var minimumResolution = subscriptions.Subscriptions.Min(x => x.Resolution);
            var objectType        = minimumResolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar);
            var isTradeBar        = objectType == typeof(TradeBar);

            foreach (var symbol in currencyPairs)
            {
                if (symbol == normal || symbol == invert)
                {
                    _invertRealTimePrice = symbol == invert;
                    var exchangeHours = exchangeHoursProvider.GetExchangeHours(market, symbol, SecurityType.Forex);
                    // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events
                    _config = subscriptions.Add(objectType, SecurityType.Forex, symbol, minimumResolution, market, exchangeHours.TimeZone, true, false, isTradeBar, isTradeBar, true);
                    var security = new Forex.Forex(this, _config, 1m, false);
                    securities.Add(symbol, security);
                    Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol + " for cash " + this.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));
        }
Example #6
0
        /// <summary>
        /// Ensures that we have a data feed to convert this currency into the base currency.
        /// This will add a <see cref="SubscriptionDataConfig"/> and create a <see cref="Security"/> 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="marketMap">The market map that decides which market the new security should be in</param>
        /// <param name="changes">Will be used to consume <see cref="SecurityChanges.AddedSecurities"/></param>
        /// <param name="securityService">Will be used to create required new <see cref="Security"/></param>
        /// <param name="accountCurrency">The account currency</param>
        /// <returns>Returns the added <see cref="SubscriptionDataConfig"/>, otherwise null</returns>
        public SubscriptionDataConfig EnsureCurrencyDataFeed(SecurityManager securities,
                                                             SubscriptionManager subscriptions,
                                                             IReadOnlyDictionary <SecurityType, string> marketMap,
                                                             SecurityChanges changes,
                                                             ISecurityService securityService,
                                                             string accountCurrency
                                                             )
        {
            // 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);
            }

            AccountCurrency = accountCurrency;

            if (Symbol == AccountCurrency)
            {
                ConversionRateSecurity = null;
                _isBaseCurrency        = true;
                ConversionRate         = 1.0m;
                return(null);
            }

            // we require a security that converts this into the base currency
            string normal             = Symbol + AccountCurrency;
            string invert             = 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();

            foreach (var symbol in potentials)
            {
                if (symbol.Value == normal || symbol.Value == invert)
                {
                    _invertRealTimePrice = symbol.Value == invert;
                    var securityType = symbol.ID.SecurityType;

                    // use the first subscription defined in the subscription manager
                    var type       = subscriptions.LookupSubscriptionConfigDataTypes(securityType, minimumResolution, false).First();
                    var objectType = type.Item1;
                    var tickType   = type.Item2;

                    // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events
                    var config = subscriptions.SubscriptionDataConfigService.Add(symbol,
                                                                                 minimumResolution,
                                                                                 fillForward: true,
                                                                                 extendedMarketHours: false,
                                                                                 isInternalFeed: true,
                                                                                 subscriptionDataTypes: new List <Tuple <Type, TickType> > {
                        new Tuple <Type, TickType>(objectType, tickType)
                    }).First();

                    var security = securityService.CreateSecurity(symbol,
                                                                  config,
                                                                  addToSymbolCache: false);

                    ConversionRateSecurity = security;
                    securities.Add(config.Symbol, security);
                    Log.Trace("Cash.EnsureCurrencyDataFeed(): Adding " + symbol.Value + " for cash " + Symbol + " currency feed");
                    return(config);
                }
            }

            // 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, AccountCurrency));
        }