diff --git a/src/ExchangeSharp/API/Exchanges/Bithumb/ExchangeBithumbAPI.cs b/src/ExchangeSharp/API/Exchanges/Bithumb/ExchangeBithumbAPI.cs index 4932973c..392f7521 100644 --- a/src/ExchangeSharp/API/Exchanges/Bithumb/ExchangeBithumbAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Bithumb/ExchangeBithumbAPI.cs @@ -1,4 +1,4 @@ -/* +/* MIT LICENSE Copyright 2017 Digital Ruby, LLC - http://www.digitalruby.com @@ -40,12 +40,14 @@ public override string NormalizeMarketSymbol(string marketSymbol) public override Task ExchangeMarketSymbolToGlobalMarketSymbolAsync(string marketSymbol) { - return Task.FromResult("KRW" + GlobalMarketSymbolSeparator + marketSymbol); + return Task.FromResult(marketSymbol + GlobalMarketSymbolSeparator + "KRW"); //e.g. 1 btc worth 9.7m KRW } public override Task GlobalMarketSymbolToExchangeMarketSymbolAsync(string marketSymbol) { - return Task.FromResult(marketSymbol.Substring(marketSymbol.IndexOf(GlobalMarketSymbolSeparator) + 1)); + var values = marketSymbol.Split(GlobalMarketSymbolSeparator); //for Bitthumb, e.g. "BTC-KRW", 1 btc worth about 9.7m won. Market symbol is BTC. + + return Task.FromResult(values[0]); } private string StatusToError(string status) diff --git a/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs b/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs index 3ea56914..64f0dc45 100644 --- a/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs @@ -131,11 +131,6 @@ public override async Task ExchangeMarketSymbolToGlobalMarketSymbolAsync { quoteCurrencyNormalized = quoteCurrency; } - if (quoteCurrencyNormalized == "BTC") - { - // prefer BTC in front - return quoteCurrencyNormalized + GlobalMarketSymbolSeparatorString + baseCurrencyNormalized; - } return baseCurrencyNormalized + GlobalMarketSymbolSeparatorString + quoteCurrencyNormalized; } diff --git a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs index 16e66df7..14133bd5 100644 --- a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs @@ -155,36 +155,46 @@ protected async Task ClampOrderQuantity(string marketSymbol, decimal ou return market == null ? outputQuantity : CryptoUtility.ClampDecimal(market.MinTradeSize, market.MaxTradeSize, market.QuantityStepSize, outputQuantity); } - /// - /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. - /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). - /// Global symbols list the base currency first (i.e. BTC) and conversion currency - /// second (i.e. ETH). Example BTC-ETH, read as x BTC is worth y ETH. - /// BTC is always first, then ETH, etc. Fiat pair is always first in global symbol too. - /// - /// Exchange market symbol - /// Separator - /// Global symbol - protected async Task ExchangeMarketSymbolToGlobalMarketSymbolWithSeparatorAsync(string marketSymbol, char separator = GlobalMarketSymbolSeparator) - { - if (string.IsNullOrEmpty(marketSymbol)) - { - throw new ArgumentException("Symbol must be non null and non empty"); - } - string[] pieces = marketSymbol.Split(separator); - if (MarketSymbolIsReversed) - { - return (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[0])).ToUpperInvariant() + GlobalMarketSymbolSeparator + (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[1])).ToUpperInvariant(); - } - return (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[1])).ToUpperInvariant() + GlobalMarketSymbolSeparator + (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[0])).ToUpperInvariant(); - } + /// + /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. + /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). + /// Global symbols list the base currency first (i.e. BTC) and quote/conversion currency + /// second (i.e. USD). Global symbols are of the form BASE-QUOTE. BASE-QUOTE is read as + /// 1 BASE is worth y QUOTE. + /// + /// Examples: + /// On 1/25/2020, + /// - BTC-USD: $8,371; 1 BTC (base) is worth $8,371 USD (quote) + /// - ETH-BTC: 0.01934; 1 ETH is worth 0.01934 BTC + /// - EUR-USD: 1.2; 1 EUR worth 1.2 USD + /// + /// A value greater than 1 means one unit of base currency is more valuable than one unit of + /// quote currency. + /// + /// + /// Exchange market symbol + /// Separator + /// Global symbol + protected async Task ExchangeMarketSymbolToGlobalMarketSymbolWithSeparatorAsync(string marketSymbol, char separator = GlobalMarketSymbolSeparator) + { + if (string.IsNullOrEmpty(marketSymbol)) + { + throw new ArgumentException("Symbol must be non null and non empty"); + } + string[] pieces = marketSymbol.Split(separator); + if (MarketSymbolIsReversed == false) //if reversed then put quote currency first + { + return (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[0])).ToUpperInvariant() + GlobalMarketSymbolSeparator + (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[1])).ToUpperInvariant(); + } + return (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[1])).ToUpperInvariant() + GlobalMarketSymbolSeparator + (await ExchangeCurrencyToGlobalCurrencyAsync(pieces[0])).ToUpperInvariant(); + } - /// - /// Split a market symbol into currencies. For weird exchanges like Bitthumb, they can override and hard-code the other pair - /// - /// Market symbol - /// Base and quote currency - protected virtual (string baseCurrency, string quoteCurrency) OnSplitMarketSymbolToCurrencies(string marketSymbol) + /// + /// Split a market symbol into currencies. For weird exchanges like Bitthumb, they can override and hard-code the other pair + /// + /// Market symbol + /// Base and quote currency + protected virtual (string baseCurrency, string quoteCurrency) OnSplitMarketSymbolToCurrencies(string marketSymbol) { var pieces = marketSymbol.Split(MarketSymbolSeparator[0]); if (pieces.Length < 2) @@ -348,32 +358,32 @@ public static IExchangeAPI[] GetExchangeAPIs() } } - /// - /// Convert an exchange currency to a global currency. For example, on Binance, - /// BCH (Bitcoin Cash) is BCC but in most other exchanges it is BCH, hence - /// the global symbol is BCH. - /// - /// Exchange currency - /// Global currency - public Task ExchangeCurrencyToGlobalCurrencyAsync(string currency) - { - currency = (currency ?? string.Empty); - foreach (KeyValuePair kv in ExchangeGlobalCurrencyReplacements[GetType()]) - { - currency = currency.Replace(kv.Key, kv.Value); - } - return Task.FromResult(currency.ToUpperInvariant()); - } + /// + /// Convert an exchange currency to a global currency. For example, on Binance, + /// BCH (Bitcoin Cash) is BCC but in most other exchanges it is BCH, hence + /// the global symbol is BCH. + /// + /// Exchange currency + /// Global currency + public Task ExchangeCurrencyToGlobalCurrencyAsync(string currency) + { + currency = (currency ?? string.Empty); + foreach (KeyValuePair kv in ExchangeGlobalCurrencyReplacements[GetType()]) + { + currency = currency.Replace(kv.Key, kv.Value); + } + return Task.FromResult(currency.ToUpperInvariant()); + } - /// - /// Convert a global currency to exchange currency. For example, on Binance, - /// BCH (Bitcoin Cash) is BCC but in most other exchanges it is BCH, hence - /// the global symbol BCH would convert to BCC for Binance, but stay BCH - /// for most other exchanges. - /// - /// Global currency - /// Exchange currency - public string GlobalCurrencyToExchangeCurrency(string currency) + /// + /// Convert a global currency to exchange currency. For example, on Binance, + /// BCH (Bitcoin Cash) is BCC but in most other exchanges it is BCH, hence + /// the global symbol BCH would convert to BCC for Binance, but stay BCH + /// for most other exchanges. + /// + /// Global currency + /// Exchange currency + public string GlobalCurrencyToExchangeCurrency(string currency) { currency = (currency ?? string.Empty); foreach (KeyValuePair kv in ExchangeGlobalCurrencyReplacements[GetType()]) @@ -404,16 +414,25 @@ public virtual string NormalizeMarketSymbol(string? marketSymbol) return marketSymbol.ToLowerInvariant(); } - /// - /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. - /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). - /// Global symbols list the base currency first (i.e. BTC) and conversion currency - /// second (i.e. ETH). Example BTC-ETH, read as x BTC is worth y ETH. - /// BTC is always first, then ETH, etc. Fiat pair is always first in global symbol too. - /// - /// Exchange symbol - /// Global symbol - public virtual async Task ExchangeMarketSymbolToGlobalMarketSymbolAsync(string marketSymbol) + /// + /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. + /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). + /// Global symbols list the base currency first (i.e. BTC) and quote/conversion currency + /// second (i.e. USD). Global symbols are of the form BASE-QUOTE. BASE-QUOTE is read as + /// 1 BASE is worth y QUOTE. + /// + /// Examples: + /// On 1/25/2020, + /// - BTC-USD: $8,371; 1 BTC (base) is worth $8,371 USD (quote) + /// - ETH-BTC: 0.01934; 1 ETH is worth 0.01934 BTC + /// - EUR-USD: 1.2; 1 EUR worth 1.2 USD + /// + /// A value greater than 1 means one unit of base currency is more valuable than one unit of + /// quote currency. + /// + /// Exchange symbol + /// Global symbol + public virtual async Task ExchangeMarketSymbolToGlobalMarketSymbolAsync(string marketSymbol) { string modifiedMarketSymbol = marketSymbol; char separator; @@ -490,7 +509,7 @@ public virtual Task GlobalMarketSymbolToExchangeMarketSymbolAsync(string { throw new ArgumentException($"Market symbol {marketSymbol} is missing the global symbol separator '{GlobalMarketSymbolSeparator}'"); } - if (MarketSymbolIsReversed) + if (MarketSymbolIsReversed == false) { marketSymbol = GlobalCurrencyToExchangeCurrency(marketSymbol.Substring(0, pos)) + MarketSymbolSeparator + GlobalCurrencyToExchangeCurrency(marketSymbol.Substring(pos + 1)); } diff --git a/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs b/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs index 8614a5a2..ea76c904 100644 --- a/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs @@ -25,21 +25,32 @@ public interface IExchangeAPI : IDisposable, IBaseAPI, IOrderBookProvider #region Utility Methods /// - /// Normalize a symbol for use on this exchange + /// Normalize a symbol for use on this exchange. /// /// Symbol /// Normalized symbol string NormalizeMarketSymbol(string marketSymbol); - /// - /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. - /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). - /// Global symbols list the base currency first (i.e. BTC) and conversion currency - /// second (i.e. USD). Example BTC-USD, read as x BTC is worth y USD. - /// - /// Exchange symbol - /// Global symbol - Task ExchangeMarketSymbolToGlobalMarketSymbolAsync(string marketSymbol); + /// + /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. + /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). + /// Global symbols list the base currency first (i.e. BTC) and quote/conversion currency + /// second (i.e. USD). Global symbols are of the form BASE-QUOTE. BASE-QUOTE is read as + /// 1 BASE is worth y QUOTE. + /// + /// Examples: + /// On 1/25/2020, + /// - BTC-USD: $8,371; 1 BTC (base) is worth $8,371 USD (quote) + /// - ETH-BTC: 0.01934; 1 ETH is worth 0.01934 BTC + /// - EUR-USD: 1.2; 1 EUR worth 1.2 USD + /// + /// A value greater than 1 means one unit of base currency is more valuable than one unit of + /// quote currency. + /// + /// + /// Exchange symbol + /// Global symbol + Task ExchangeMarketSymbolToGlobalMarketSymbolAsync(string marketSymbol); /// /// Convert a global symbol into an exchange symbol, which will potentially be different from other exchanges. diff --git a/tests/ExchangeSharpTests/ExchangeTests.cs b/tests/ExchangeSharpTests/ExchangeTests.cs index 2dfa6412..3d56aa06 100644 --- a/tests/ExchangeSharpTests/ExchangeTests.cs +++ b/tests/ExchangeSharpTests/ExchangeTests.cs @@ -68,8 +68,8 @@ public async Task GlobalSymbolTest() // if tests fail, uncomment this and it will save a new test file // string allSymbolsJson = GetAllSymbolsJsonAsync().Sync(); System.IO.File.WriteAllText("TestData/AllSymbols.json", allSymbolsJson); - string globalMarketSymbol = "BTC-ETH"; - string globalMarketSymbolAlt = "KRW-BTC"; // WTF Bitthumb... + string globalMarketSymbol = "ETH-BTC"; //1 ETH is worth 0.0192 BTC... + string globalMarketSymbolAlt = "BTC-KRW"; // WTF Bitthumb... //1 BTC worth 9,783,000 won Dictionary allSymbols = JsonConvert.DeserializeObject>(System.IO.File.ReadAllText("TestData/AllSymbols.json")); // sanity test that all exchanges return the same global symbol when converted back and forth @@ -88,9 +88,10 @@ api is ExchangeOKCoinAPI || api is ExchangeDigifinexAPI || api is ExchangeNDAXAP bool isBithumb = (api.Name == ExchangeName.Bithumb); string exchangeMarketSymbol = await api.GlobalMarketSymbolToExchangeMarketSymbolAsync(isBithumb ? globalMarketSymbolAlt : globalMarketSymbol); string globalMarketSymbol2 = await api.ExchangeMarketSymbolToGlobalMarketSymbolAsync(exchangeMarketSymbol); - if ((!isBithumb && globalMarketSymbol2.EndsWith("-BTC")) || - globalMarketSymbol2.EndsWith("-USD") || - globalMarketSymbol2.EndsWith("-USDT")) + + if ((!isBithumb && globalMarketSymbol2.StartsWith("BTC-")) || + globalMarketSymbol2.StartsWith("USD-") || + globalMarketSymbol2.StartsWith("USDT-")) { Assert.Fail($"Exchange {api.Name} has wrong SymbolIsReversed parameter"); }