コード例 #1
0
        public void LinearSearchFindsTwoLegConversions()
        {
            var existingSecurities = new List <Security>(0);
            var potentialSymbols   = new List <Symbol> {
                Symbols.BTCUSD, Symbols.EURUSD
            };

            var subscriptions = new SubscriptionManager();
            var dataManager   = new DataManagerStub();

            subscriptions.SetDataManager(dataManager);

            var createdSecurities = new List <Security>();
            var makeNewSecurity   = new Func <Symbol, Security>(symbol =>
            {
                var security = CreateSecurity(symbol);
                createdSecurities.Add(security);
                return(security);
            });

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "BTC",
                "EUR",
                existingSecurities,
                potentialSymbols,
                makeNewSecurity);

            var securities = currencyConversion.ConversionRateSecurities.ToList();

            Assert.AreEqual(2, securities.Count);
            Assert.AreEqual(createdSecurities, securities);
            Assert.AreEqual(Symbols.BTCUSD, securities[0].Symbol);
            Assert.AreEqual(Symbols.EURUSD, securities[1].Symbol);
        }
コード例 #2
0
        public void UpdateCalculatesNewConversionRateTwoLeg(
            string sourceCurrency,
            string destinationCurrency,
            Symbol symbol1,
            Symbol symbol2,
            decimal expectedRate)
        {
            var existingSecurities = new List <Security>
            {
                CreateSecurity(symbol1),
                CreateSecurity(symbol2)
            };

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                sourceCurrency,
                destinationCurrency,
                existingSecurities,
                new List <Symbol>(0),
                CreateSecurity);

            existingSecurities[0].SetMarketPrice(new Tick {
                Value = 15m
            });
            existingSecurities[1].SetMarketPrice(new Tick {
                Value = 25m
            });

            Assert.AreEqual(expectedRate, currencyConversion.Update());
        }
コード例 #3
0
        public void ConversionRateReturnsLatestConversionRate()
        {
            var existingSecurities = new List <Security> {
                CreateSecurity(Symbols.BTCUSD)
            };

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "BTC",
                "USD",
                existingSecurities,
                new List <Symbol>(0),
                CreateSecurity);

            Assert.AreEqual(0m, currencyConversion.ConversionRate);

            existingSecurities[0].SetMarketPrice(new Tick {
                Value = 10m
            });
            currencyConversion.Update();
            Assert.AreEqual(10m, currencyConversion.ConversionRate);

            existingSecurities[0].SetMarketPrice(new Tick {
                Value = 20m
            });
            currencyConversion.Update();
            Assert.AreEqual(20m, currencyConversion.ConversionRate);
        }
コード例 #4
0
        public void LinearSearchPrefersExistingSecuritiesOverNewOnesOneLeg()
        {
            var existingSecurities = new List <Security> {
                CreateSecurity(Symbols.EURUSD)
            };
            var potentialSymbols = new List <Symbol> {
                Symbols.EURUSD
            };

            var subscriptions = new SubscriptionManager();
            var dataManager   = new DataManagerStub();

            subscriptions.SetDataManager(dataManager);

            var createdSecurities = new List <Security>();
            var makeNewSecurity   = new Func <Symbol, Security>(symbol =>
            {
                var security = CreateSecurity(symbol);
                createdSecurities.Add(security);
                return(security);
            });

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "EUR",
                "USD",
                existingSecurities,
                potentialSymbols,
                makeNewSecurity);

            var securities = currencyConversion.ConversionRateSecurities.ToList();

            Assert.AreEqual(1, securities.Count);
            Assert.AreEqual(0, createdSecurities.Count);
            Assert.AreEqual(existingSecurities, securities);
        }
コード例 #5
0
        public void LinearSearchThrowsWhenNoConversionPossible()
        {
            var existingSecurities = new List <Security>(0);
            var potentialSymbols   = new List <Symbol> {
                Symbols.EURGBP
            };

            Assert.Throws <ArgumentException>(() => SecurityCurrencyConversion.LinearSearch(
                                                  "EUR",
                                                  "USD",
                                                  existingSecurities,
                                                  potentialSymbols,
                                                  CreateSecurity));
        }
コード例 #6
0
        public void UpdateReturnsZeroWhenNoData()
        {
            var existingSecurities = new List <Security> {
                CreateSecurity(Symbols.BTCUSD)
            };

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "BTC",
                "USD",
                existingSecurities,
                new List <Symbol>(0),
                CreateSecurity);

            Assert.AreEqual(0m, currencyConversion.Update());
        }
コード例 #7
0
        public void DestinationCurrencyReturnsCorrectValue()
        {
            var existingSecurities = new List <Security>(0);
            var potentialSymbols   = new List <Symbol> {
                Symbols.EURUSD
            };

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "EUR",
                "USD",
                existingSecurities,
                potentialSymbols,
                CreateSecurity);

            Assert.AreEqual("USD", currencyConversion.DestinationCurrency);
        }
コード例 #8
0
        public void ConversionRateZeroAtStart()
        {
            var existingSecurities = new List <Security>(0);
            var potentialSymbols   = new List <Symbol> {
                Symbols.EURUSD
            };

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "EUR",
                "USD",
                existingSecurities,
                potentialSymbols,
                CreateSecurity);

            Assert.AreEqual(0, currencyConversion.ConversionRate);
        }
コード例 #9
0
        public void UpdateReturnsZeroWhenNoDataForOneOfTwoSymbols()
        {
            var existingSecurities = new List <Security>
            {
                CreateSecurity(Symbols.ETHBTC),
                CreateSecurity(Symbols.BTCUSD)
            };

            var currencyConversion = SecurityCurrencyConversion.LinearSearch(
                "ETH",
                "USD",
                existingSecurities,
                new List <Symbol>(0),
                CreateSecurity);

            existingSecurities[0].SetMarketPrice(new Tick {
                Value = 15m
            });

            Assert.AreEqual(0m, currencyConversion.Update());
        }
コード例 #10
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.ContainsKey(SecurityType.Crypto) &&
                Currencies.StableCoinsWithoutPairs.Contains(QuantConnect.Symbol.Create(Symbol + accountCurrency, SecurityType.Crypto, marketMap[SecurityType.Crypto])))
            {
                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);
        }