diff --git a/currency/manager_types.go b/currency/manager_types.go index ad2540ca..7bc0e759 100644 --- a/currency/manager_types.go +++ b/currency/manager_types.go @@ -8,12 +8,13 @@ import ( // PairsManager manages asset pairs type PairsManager struct { - RequestFormat *PairFormat `json:"requestFormat,omitempty"` - ConfigFormat *PairFormat `json:"configFormat,omitempty"` - UseGlobalFormat bool `json:"useGlobalFormat,omitempty"` - LastUpdated int64 `json:"lastUpdated,omitempty"` - Pairs map[asset.Item]*PairStore `json:"pairs"` - m sync.RWMutex + BypassConfigFormatUpgrades bool `json:"bypassConfigFormatUpgrades"` + RequestFormat *PairFormat `json:"requestFormat,omitempty"` + ConfigFormat *PairFormat `json:"configFormat,omitempty"` + UseGlobalFormat bool `json:"useGlobalFormat,omitempty"` + LastUpdated int64 `json:"lastUpdated,omitempty"` + Pairs map[asset.Item]*PairStore `json:"pairs"` + m sync.RWMutex } // PairStore stores a currency pair store diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 2b28ff01..d2832255 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -172,7 +172,7 @@ func TestUExchangeInfo(t *testing.T) { func TestUFuturesOrderbook(t *testing.T) { t.Parallel() - _, err := b.UFuturesOrderbook(context.Background(), currency.Pair{Delimiter: "_", Base: currency.BTC, Quote: currency.USDT}, 1000) + _, err := b.UFuturesOrderbook(context.Background(), currency.NewPair(currency.BTC, currency.USDT), 1000) if err != nil { t.Error(err) } @@ -2647,3 +2647,139 @@ func TestWsOutboundAccountPosition(t *testing.T) { t.Fatal(err) } } + +func TestFormatExchangeCurrency(t *testing.T) { + t.Parallel() + type testos struct { + name string + pair currency.Pair + asset asset.Item + expectedDelimiter string + } + testerinos := []testos{ + { + name: "spot-btcusdt", + pair: currency.NewPairWithDelimiter("BTC", "USDT", currency.UnderscoreDelimiter), + asset: asset.Spot, + expectedDelimiter: "", + }, + { + name: "coinmarginedfutures-btcusd_perp", + pair: currency.NewPairWithDelimiter("BTCUSD", "PERP", currency.DashDelimiter), + asset: asset.CoinMarginedFutures, + expectedDelimiter: currency.UnderscoreDelimiter, + }, + { + name: "coinmarginedfutures-btcusd_211231", + pair: currency.NewPairWithDelimiter("BTCUSD", "211231", currency.DashDelimiter), + asset: asset.CoinMarginedFutures, + expectedDelimiter: currency.UnderscoreDelimiter, + }, + { + name: "margin-ltousdt", + pair: currency.NewPairWithDelimiter("LTO", "USDT", currency.UnderscoreDelimiter), + asset: asset.Margin, + expectedDelimiter: "", + }, + { + name: "usdtmarginedfutures-btcusdt", + pair: currency.NewPairWithDelimiter("btc", "usdt", currency.DashDelimiter), + asset: asset.USDTMarginedFutures, + expectedDelimiter: "", + }, + { + name: "usdtmarginedfutures-btcusdt_211231", + pair: currency.NewPairWithDelimiter("btcusdt", "211231", currency.UnderscoreDelimiter), + asset: asset.USDTMarginedFutures, + expectedDelimiter: currency.UnderscoreDelimiter, + }, + } + for i := range testerinos { + tt := testerinos[i] + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + result, err := b.FormatExchangeCurrency(tt.pair, tt.asset) + if err != nil { + t.Error(err) + } + if result.Delimiter != tt.expectedDelimiter { + t.Errorf("received '%v' expected '%v'", result.Delimiter, tt.expectedDelimiter) + } + }) + } +} + +func TestFormatSymbol(t *testing.T) { + t.Parallel() + type testos struct { + name string + pair currency.Pair + asset asset.Item + expectedString string + } + testerinos := []testos{ + { + name: "spot-BTCUSDT", + pair: currency.NewPairWithDelimiter("BTC", "USDT", currency.UnderscoreDelimiter), + asset: asset.Spot, + expectedString: "BTCUSDT", + }, + { + name: "coinmarginedfutures-btcusdperp", + pair: currency.NewPairWithDelimiter("BTCUSD", "PERP", currency.DashDelimiter), + asset: asset.CoinMarginedFutures, + expectedString: "BTCUSD_PERP", + }, + { + name: "coinmarginedfutures-BTCUSD_211231", + pair: currency.NewPairWithDelimiter("BTCUSD", "211231", currency.DashDelimiter), + asset: asset.CoinMarginedFutures, + expectedString: "BTCUSD_211231", + }, + { + name: "margin-LTOUSDT", + pair: currency.NewPairWithDelimiter("LTO", "USDT", currency.UnderscoreDelimiter), + asset: asset.Margin, + expectedString: "LTOUSDT", + }, + { + name: "usdtmarginedfutures-BTCUSDT", + pair: currency.NewPairWithDelimiter("btc", "usdt", currency.DashDelimiter), + asset: asset.USDTMarginedFutures, + expectedString: "BTCUSDT", + }, + { + name: "usdtmarginedfutures-BTCUSDT_211231", + pair: currency.NewPairWithDelimiter("btcusdt", "211231", currency.UnderscoreDelimiter), + asset: asset.USDTMarginedFutures, + expectedString: "BTCUSDT_211231", + }, + } + for i := range testerinos { + tt := testerinos[i] + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + result, err := b.FormatSymbol(tt.pair, tt.asset) + if err != nil { + t.Error(err) + } + if result != tt.expectedString { + t.Errorf("received '%v' expected '%v'", result, tt.expectedString) + } + }) + } +} + +func TestFormatUSDTMarginedFuturesPair(t *testing.T) { + t.Parallel() + pairFormat := currency.PairFormat{Uppercase: true} + resp := b.formatUSDTMarginedFuturesPair(currency.NewPair(currency.DOGE, currency.USDT), pairFormat) + if resp.String() != "DOGEUSDT" { + t.Errorf("received '%v' expected '%v'", resp.String(), "DOGEUSDT") + } + + resp = b.formatUSDTMarginedFuturesPair(currency.NewPair(currency.DOGE, currency.NewCode("1234567890")), pairFormat) + if resp.String() != "DOGE_1234567890" { + t.Errorf("received '%v' expected '%v'", resp.String(), "DOGE_1234567890") + } +} diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 1522faec..2ba72721 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -84,6 +84,7 @@ func (b *Binance) SetDefaults() { }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, + Delimiter: currency.UnderscoreDelimiter, }, } err := b.StoreAssetPairFormat(asset.Spot, fmt1) @@ -264,6 +265,7 @@ func (b *Binance) Run() { b.PrintEnabledPairs() } + forceUpdate := false a := b.GetAssetTypes(true) for x := range a { if err := b.UpdateOrderExecutionLimits(context.TODO(), a[x]); err != nil { @@ -272,13 +274,60 @@ func (b *Binance) Run() { b.Name, err) } + if a[x] == asset.USDTMarginedFutures && !b.BypassConfigFormatUpgrades { + format, err := b.GetPairFormat(asset.USDTMarginedFutures, false) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", + b.Name, + err) + return + } + var enabled, avail currency.Pairs + enabled, err = b.CurrencyPairs.GetPairs(asset.USDTMarginedFutures, true) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", + b.Name, + err) + return + } + + avail, err = b.CurrencyPairs.GetPairs(asset.USDTMarginedFutures, false) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get available currencies. Err %s\n", + b.Name, + err) + return + } + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var enabledPairs currency.Pairs + enabledPairs, err = currency.NewPairsFromStrings([]string{ + currency.BTC.String() + format.Delimiter + currency.USDT.String(), + }) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err %s\n", + b.Name, + err) + } else { + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, b.Name, a[x], enabledPairs) + forceUpdate = true + err = b.UpdatePairs(enabledPairs, a[x], true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + } + } + } + } } - if !b.GetEnabledFeatures().AutoPairUpdates { + if !b.GetEnabledFeatures().AutoPairUpdates && !forceUpdate { return } - if err := b.UpdateTradablePairs(context.TODO(), false); err != nil { + if err := b.UpdateTradablePairs(context.TODO(), forceUpdate); err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", b.Name, @@ -295,53 +344,70 @@ func (b *Binance) FetchTradablePairs(ctx context.Context, a asset.Item) ([]strin if err != nil { return nil, err } + tradingStatus := "TRADING" var pairs []string switch a { case asset.Spot, asset.Margin: - info, err := b.GetExchangeInfo(ctx) + var info ExchangeInfo + info, err = b.GetExchangeInfo(ctx) if err != nil { return nil, err } for x := range info.Symbols { - if info.Symbols[x].Status == "TRADING" { - pair := info.Symbols[x].BaseAsset + - format.Delimiter + - info.Symbols[x].QuoteAsset - if a == asset.Spot && info.Symbols[x].IsSpotTradingAllowed { - pairs = append(pairs, pair) - } - if a == asset.Margin && info.Symbols[x].IsMarginTradingAllowed { - pairs = append(pairs, pair) - } + if info.Symbols[x].Status != tradingStatus { + continue + } + pair := info.Symbols[x].BaseAsset + + format.Delimiter + + info.Symbols[x].QuoteAsset + if a == asset.Spot && info.Symbols[x].IsSpotTradingAllowed { + pairs = append(pairs, pair) + } + if a == asset.Margin && info.Symbols[x].IsMarginTradingAllowed { + pairs = append(pairs, pair) } } case asset.CoinMarginedFutures: - cInfo, err := b.FuturesExchangeInfo(ctx) + var cInfo CExchangeInfo + cInfo, err = b.FuturesExchangeInfo(ctx) if err != nil { return pairs, err } for z := range cInfo.Symbols { - if cInfo.Symbols[z].ContractStatus == "TRADING" { - curr, err := currency.NewPairFromString(cInfo.Symbols[z].Symbol) - if err != nil { - return nil, err - } - pairs = append(pairs, format.Format(curr)) + if cInfo.Symbols[z].ContractStatus != tradingStatus { + continue } + var curr currency.Pair + curr, err = currency.NewPairFromString(cInfo.Symbols[z].Symbol) + if err != nil { + return nil, err + } + pairs = append(pairs, format.Format(curr)) } case asset.USDTMarginedFutures: - uInfo, err := b.UExchangeInfo(ctx) + var uInfo UFuturesExchangeInfo + uInfo, err = b.UExchangeInfo(ctx) if err != nil { return pairs, err } for u := range uInfo.Symbols { - if uInfo.Symbols[u].Status == "TRADING" { - curr, err := currency.NewPairFromString(uInfo.Symbols[u].Symbol) + if uInfo.Symbols[u].Status != tradingStatus { + continue + } + var curr currency.Pair + if uInfo.Symbols[u].ContractType == "PERPETUAL" { + curr, err = currency.NewPairFromStrings(uInfo.Symbols[u].BaseAsset, uInfo.Symbols[u].QuoteAsset) + if err != nil { + return nil, err + } + } else { + curr, err = currency.NewPairFromString(uInfo.Symbols[u].Symbol) if err != nil { return nil, err } - pairs = append(pairs, format.Format(curr)) } + + pairs = append(pairs, format.Format(curr)) } } return pairs, nil @@ -1742,3 +1808,44 @@ func (b *Binance) GetAvailableTransferChains(ctx context.Context, cryptocurrency } return availableChains, nil } + +// FormatExchangeCurrency is a method that formats and returns a currency pair +// based on the user currency display preferences +// overrides default implementation to use optional delimiter +func (b *Binance) FormatExchangeCurrency(p currency.Pair, a asset.Item) (currency.Pair, error) { + pairFmt, err := b.GetPairFormat(a, true) + if err != nil { + return currency.Pair{}, err + } + if a == asset.USDTMarginedFutures { + return b.formatUSDTMarginedFuturesPair(p, pairFmt), nil + } + return p.Format(pairFmt.Delimiter, pairFmt.Uppercase), nil +} + +// FormatSymbol formats the given pair to a string suitable for exchange API requests +// overrides default implementation to use optional delimiter +func (b *Binance) FormatSymbol(p currency.Pair, a asset.Item) (string, error) { + pairFmt, err := b.GetPairFormat(a, true) + if err != nil { + return p.String(), err + } + if a == asset.USDTMarginedFutures { + p = b.formatUSDTMarginedFuturesPair(p, pairFmt) + return p.String(), nil + } + return pairFmt.Format(p), nil +} + +// formatUSDTMarginedFuturesPair Binance USDTMarginedFutures pairs have a delimiter +// only if the contract has an expiry date +func (b *Binance) formatUSDTMarginedFuturesPair(p currency.Pair, pairFmt currency.PairFormat) currency.Pair { + quote := p.Quote.String() + for _, c := range quote { + if c < '0' || c > '9' { + // character rune is alphabetic, cannot be expiring contract + return p.Format(pairFmt.Delimiter, pairFmt.Uppercase) + } + } + return p.Format(currency.UnderscoreDelimiter, pairFmt.Uppercase) +} diff --git a/exchanges/binance/cfutures_types.go b/exchanges/binance/cfutures_types.go index d54b04b8..ad2a9e0b 100644 --- a/exchanges/binance/cfutures_types.go +++ b/exchanges/binance/cfutures_types.go @@ -554,36 +554,51 @@ type UFuturesExchangeInfo struct { Limit int64 `json:"limit"` RateLimitType string `json:"rateLimitType"` } `json:"rateLimits"` - ServerTime int64 `json:"serverTime"` - Symbols []struct { - Symbol string `json:"symbol"` - Status string `json:"status"` - MaintenanceMarginPercent float64 `json:"maintMarginPercent,string"` - RequiredMarginPercent float64 `json:"requiredMarginPercent,string"` - BaseAsset string `json:"baseAsset"` - QuoteAsset string `json:"quoteAsset"` - PricePrecision int64 `json:"pricePrecision"` - QuantityPrecision int64 `json:"quantityPrecision"` - BaseAssetPrecision int64 `json:"baseAssetPrecision"` - QuotePrecision int64 `json:"quotePrecision"` - Filters []struct { - MinPrice float64 `json:"minPrice,string"` - MaxPrice float64 `json:"maxPrice,string"` - FilterType string `json:"filterType"` - TickSize float64 `json:"tickSize,string"` - StepSize float64 `json:"stepSize,string"` - MaxQty float64 `json:"maxQty,string"` - MinQty float64 `json:"minQty,string"` - Limit int64 `json:"limit"` - MultiplierDown float64 `json:"multiplierDown,string"` - MultiplierUp float64 `json:"multiplierUp,string"` - MultiplierDecimal float64 `json:"multiplierDecimal,string"` - Notional float64 `json:"notional,string"` - } `json:"filters"` - OrderTypes []string `json:"orderTypes"` - TimeInForce []string `json:"timeInForce"` - } `json:"symbols"` - Timezone string `json:"timezone"` + ServerTime int64 `json:"serverTime"` + Symbols []UFuturesSymbolInfo `json:"symbols"` + Timezone string `json:"timezone"` +} + +// UFuturesSymbolInfo contains details of a currency symbol +// for a usdt margined future contract +type UFuturesSymbolInfo struct { + Symbol string `json:"symbol"` + Pair string `json:"pair"` + ContractType string `json:"contractType"` + DeliveryDate time.Time `json:"deliveryDate"` + OnboardDate time.Time `json:"onboardDate"` + Status string `json:"status"` + MaintenanceMarginPercent float64 `json:"maintMarginPercent,string"` + RequiredMarginPercent float64 `json:"requiredMarginPercent,string"` + BaseAsset string `json:"baseAsset"` + QuoteAsset string `json:"quoteAsset"` + MarginAsset string `json:"marginAsset"` + PricePrecision int64 `json:"pricePrecision"` + QuantityPrecision int64 `json:"quantityPrecision"` + BaseAssetPrecision int64 `json:"baseAssetPrecision"` + QuotePrecision int64 `json:"quotePrecision"` + UnderlyingType string `json:"underlyingType"` + UnderlyingSubType []string `json:"underlyingSubType"` + SettlePlan float64 `json:"settlePlan"` + TriggerProtect float64 `json:"triggerProtect,string"` + Filters []struct { + FilterType string `json:"filterType"` + MinPrice float64 `json:"minPrice,string"` + MaxPrice float64 `json:"maxPrice,string"` + TickSize float64 `json:"tickSize,string"` + StepSize float64 `json:"stepSize,string"` + MaxQty float64 `json:"maxQty,string"` + MinQty float64 `json:"minQty,string"` + Limit int64 `json:"limit"` + MultiplierDown float64 `json:"multiplierDown,string"` + MultiplierUp float64 `json:"multiplierUp,string"` + MultiplierDecimal float64 `json:"multiplierDecimal,string"` + Notional float64 `json:"notional,string"` + } `json:"filters"` + OrderTypes []string `json:"OrderType"` + TimeInForce []string `json:"timeInForce"` + LiquidationFee float64 `json:"liquidationFee,string"` + MarketTakeBound float64 `json:"marketTakeBound,string"` } // CExchangeInfo stores exchange info for cfutures diff --git a/exchanges/binance/type_convert.go b/exchanges/binance/type_convert.go index 98163866..2a8565db 100644 --- a/exchanges/binance/type_convert.go +++ b/exchanges/binance/type_convert.go @@ -423,3 +423,21 @@ func (a *wsListStatus) UnmarshalJSON(data []byte) error { a.Data.TransactionTime = aux.Data.TransactionTime.Time() return nil } + +// UnmarshalJSON deserialises the JSON info, including the timestamp +func (u *UFuturesSymbolInfo) UnmarshalJSON(data []byte) error { + type Alias UFuturesSymbolInfo + aux := &struct { + DeliveryDate binanceTime `json:"deliveryDate"` + OnboardDate binanceTime `json:"onboardDate"` + *Alias + }{ + Alias: (*Alias)(u), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + u.DeliveryDate = aux.DeliveryDate.Time() + u.OnboardDate = aux.OnboardDate.Time() + return nil +} diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index a429b6b2..11f1f24e 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -203,51 +203,53 @@ func (b *Bitstamp) Run() { } forceUpdate := false - format, err := b.GetPairFormat(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to get pair format. Err %s\n", - b.Name, - err) - return - } - - enabled, err := b.CurrencyPairs.GetPairs(asset.Spot, true) - if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", - b.Name, - err) - return - } - - avail, err := b.CurrencyPairs.GetPairs(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to get available currencies. Err %s\n", - b.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - var enabledPairs currency.Pairs - enabledPairs, err = currency.NewPairsFromStrings([]string{ - currency.BTC.String() + format.Delimiter + currency.USD.String(), - }) + if !b.BypassConfigFormatUpgrades { + format, err := b.GetPairFormat(asset.Spot, false) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err %s\n", + log.Errorf(log.ExchangeSys, "%s failed to get pair format. Err %s\n", b.Name, err) - } else { - log.Warn(log.ExchangeSys, - "Bitstamp: Enabled and available pairs reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + return + } - err = b.UpdatePairs(enabledPairs, asset.Spot, true, true) + enabled, err := b.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", + b.Name, + err) + return + } + + avail, err := b.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get available currencies. Err %s\n", + b.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var enabledPairs currency.Pairs + enabledPairs, err = currency.NewPairsFromStrings([]string{ + currency.BTC.String() + format.Delimiter + currency.USD.String(), + }) if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", + log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err %s\n", b.Name, err) + } else { + log.Warnf(log.ExchangeSys, + exchange.ResetConfigPairsWarningMessage, b.Name, asset.Spot, enabledPairs) + forceUpdate = true + + err = b.UpdatePairs(enabledPairs, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + } } } } @@ -256,7 +258,7 @@ func (b *Bitstamp) Run() { return } - err = b.UpdateTradablePairs(context.TODO(), forceUpdate) + err := b.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 1bf7b36f..13101586 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -220,7 +220,6 @@ func (b *BTCMarkets) Run() { if !common.StringDataContains(pairs.Strings(), format.Delimiter) || !common.StringDataContains(avail.Strings(), format.Delimiter) { - log.Warnln(log.ExchangeSys, "Available pairs for BTC Markets reset due to config upgrade, please enable the pairs you would like again.") forceUpdate = true } if forceUpdate { @@ -230,6 +229,7 @@ func (b *BTCMarkets) Run() { Delimiter: format.Delimiter, }, } + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, b.Name, asset.Spot, enabledPairs) err = b.UpdatePairs(enabledPairs, asset.Spot, true, true) if err != nil { log.Errorf(log.ExchangeSys, diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 3b26c83f..5c568af9 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -207,54 +207,55 @@ func (c *CoinbasePro) Run() { } forceUpdate := false - format, err := c.GetPairFormat(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - c.Name, - err) - return - } - enabled, err := c.CurrencyPairs.GetPairs(asset.Spot, true) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - c.Name, - err) - return - } - - avail, err := c.CurrencyPairs.GetPairs(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - c.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - var p currency.Pairs - p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + - format.Delimiter + - currency.USD.String()}) + if !c.BypassConfigFormatUpgrades { + format, err := c.GetPairFormat(asset.Spot, false) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", c.Name, err) - } else { - log.Warn(log.ExchangeSys, - "Enabled pairs for CoinbasePro reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + return + } + enabled, err := c.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } - err = c.UpdatePairs(p, asset.Spot, true, true) + avail, err := c.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + + format.Delimiter + + currency.USD.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", c.Name, err) + } else { + forceUpdate = true + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, c.Name, asset.Spot, p) + + err = c.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + } } } } @@ -263,7 +264,7 @@ func (c *CoinbasePro) Run() { return } - err = c.UpdateTradablePairs(context.TODO(), forceUpdate) + err := c.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", c.Name, err) } diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index f47d5787..67056003 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -187,54 +187,55 @@ func (c *COINUT) Run() { } forceUpdate := false - format, err := c.GetPairFormat(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - c.Name, - err) - return - } - - enabled, err := c.CurrencyPairs.GetPairs(asset.Spot, true) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - c.Name, - err) - return - } - avail, err := c.CurrencyPairs.GetPairs(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - c.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - var p currency.Pairs - p, err = currency.NewPairsFromStrings([]string{currency.LTC.String() + - format.Delimiter + - currency.USDT.String()}) + if !c.BypassConfigFormatUpgrades { + format, err := c.GetPairFormat(asset.Spot, false) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", c.Name, err) - } else { - log.Warn(log.ExchangeSys, - "Enabled pairs for Coinut reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + return + } - err = c.UpdatePairs(p, asset.Spot, true, true) + enabled, err := c.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + avail, err := c.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.LTC.String() + + format.Delimiter + + currency.USDT.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", c.Name, err) + } else { + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, c.Name, asset.Spot, p) + forceUpdate = true + + err = c.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + } } } } @@ -243,7 +244,7 @@ func (c *COINUT) Run() { return } - err = c.UpdateTradablePairs(context.TODO(), forceUpdate) + err := c.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", c.Name, err) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 83093aa0..e0e88136 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -37,6 +37,8 @@ const ( DefaultWebsocketResponseMaxLimit = time.Second * 7 // DefaultWebsocketOrderbookBufferLimit is the maximum number of orderbook updates that get stored before being applied DefaultWebsocketOrderbookBufferLimit = 5 + // ResetConfigPairsWarningMessage is displayed when a currency pair format in the config needs to be updated + ResetConfigPairsWarningMessage = "%s Enabled and available pairs for %s reset due to config upgrade, please enable the ones you would like to use again. Defaulting to %v" ) var ( @@ -595,6 +597,7 @@ func (b *Base) SetupDefaults(exch *config.Exchange) error { } b.HTTPDebugging = exch.HTTPDebugging + b.BypassConfigFormatUpgrades = exch.CurrencyPairs.BypassConfigFormatUpgrades b.SetHTTPClientUserAgent(exch.HTTPUserAgent) b.SetCurrencyPairFormat() diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 83f5d868..4f35119e 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -222,6 +222,7 @@ type Base struct { HTTPUserAgent string HTTPRecording bool HTTPDebugging bool + BypassConfigFormatUpgrades bool WebsocketResponseCheckTimeout time.Duration WebsocketResponseMaxLimit time.Duration WebsocketOrderbookBufferLimit int64 diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 469c43c6..cdd9ce6d 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -201,50 +201,51 @@ func (g *Gemini) Run() { } forceUpdate := false - format, err := g.GetPairFormat(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", - g.Name, - err) - return - } - - enabled, err := g.CurrencyPairs.GetPairs(asset.Spot, true) - if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", - g.Name, - err) - return - } - - avail, err := g.CurrencyPairs.GetPairs(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to get available currencies. Err %s\n", - g.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - var enabledPairs currency.Pairs - enabledPairs, err = currency.NewPairsFromStrings([]string{ - currency.BTC.String() + format.Delimiter + currency.USD.String()}) + if !g.BypassConfigFormatUpgrades { + format, err := g.GetPairFormat(asset.Spot, false) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err %s\n", + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", g.Name, err) - } else { - log.Warn(log.ExchangeSys, - "Available pairs for Gemini reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + return + } - err = g.UpdatePairs(enabledPairs, asset.Spot, true, true) + enabled, err := g.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", + g.Name, + err) + return + } + + avail, err := g.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get available currencies. Err %s\n", + g.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var enabledPairs currency.Pairs + enabledPairs, err = currency.NewPairsFromStrings([]string{ + currency.BTC.String() + format.Delimiter + currency.USD.String()}) if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", + log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err %s\n", g.Name, err) + } else { + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, g.Name, asset.Spot, enabledPairs) + forceUpdate = true + + err = g.UpdatePairs(enabledPairs, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + g.Name, + err) + } } } } @@ -252,7 +253,7 @@ func (g *Gemini) Run() { if !g.GetEnabledFeatures().AutoPairUpdates && !forceUpdate { return } - err = g.UpdateTradablePairs(context.TODO(), forceUpdate) + err := g.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index 4d10a435..c4fabbe7 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -203,40 +203,8 @@ func (h *HitBTC) Run() { } forceUpdate := false - format, err := h.GetPairFormat(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - h.Name, - err) - return - } - enabled, err := h.GetEnabledPairs(asset.Spot) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - h.Name, - err) - return - } - - avail, err := h.GetAvailablePairs(asset.Spot) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - h.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - enabledPairs := []string{currency.BTC.String() + format.Delimiter + currency.USD.String()} - log.Warn(log.ExchangeSys, - "Available pairs for HitBTC reset due to config upgrade, please enable the ones you would like again.") - forceUpdate = true - var p currency.Pairs - p, err = currency.NewPairsFromStrings(enabledPairs) + if !h.BypassConfigFormatUpgrades { + format, err := h.GetPairFormat(asset.Spot, false) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -244,11 +212,44 @@ func (h *HitBTC) Run() { err) return } - err = h.UpdatePairs(p, asset.Spot, true, true) + enabled, err := h.GetEnabledPairs(asset.Spot) if err != nil { log.Errorf(log.ExchangeSys, - "%s failed to update enabled currencies.\n", - h.Name) + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } + + avail, err := h.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + enabledPairs := []string{currency.BTC.String() + format.Delimiter + currency.USD.String()} + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, h.Name, asset.Spot, enabledPairs) + forceUpdate = true + var p currency.Pairs + p, err = currency.NewPairsFromStrings(enabledPairs) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } + err = h.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update enabled currencies.\n", + h.Name) + } } } @@ -256,7 +257,7 @@ func (h *HitBTC) Run() { return } - err = h.UpdateTradablePairs(context.TODO(), forceUpdate) + err := h.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 50da1d79..a0941a2f 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -300,9 +300,7 @@ func (h *HUOBI) Run() { Delimiter: format.Delimiter, }, } - log.Warn(log.ExchangeSys, - "Available and enabled pairs for Huobi reset due to config upgrade, please enable the ones you would like again") - + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, h.Name, asset.Spot, enabledPairs) err = h.UpdatePairs(enabledPairs, asset.Spot, true, true) if err != nil { log.Errorf(log.ExchangeSys, diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 0a838876..7d13d423 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -260,54 +260,56 @@ func (k *Kraken) Run() { } forceUpdate := false - format, err := k.GetPairFormat(asset.UseDefault(), false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - k.Name, - err) - return - } - enabled, err := k.GetEnabledPairs(asset.UseDefault()) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - k.Name, - err) - return - } - - avail, err := k.GetAvailablePairs(asset.UseDefault()) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - k.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) || - common.StringDataContains(avail.Strings(), "ZUSD") { - var p currency.Pairs - p, err = currency.NewPairsFromStrings([]string{currency.XBT.String() + - format.Delimiter + - currency.USD.String()}) + if !k.BypassConfigFormatUpgrades { + format, err := k.GetPairFormat(asset.UseDefault(), false) if err != nil { log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", + "%s failed to update tradable pairs. Err: %s", k.Name, err) - } else { - log.Warn(log.ExchangeSys, "Available pairs for Kraken reset due to config upgrade, please enable the ones you would like again") - forceUpdate = true + return + } + enabled, err := k.GetEnabledPairs(asset.UseDefault()) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + k.Name, + err) + return + } - err = k.UpdatePairs(p, asset.UseDefault(), true, true) + avail, err := k.GetAvailablePairs(asset.UseDefault()) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + k.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) || + common.StringDataContains(avail.Strings(), "ZUSD") { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.XBT.String() + + format.Delimiter + + currency.USD.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", k.Name, err) + } else { + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, k.Name, asset.UseDefault(), p) + forceUpdate = true + + err = k.UpdatePairs(p, asset.UseDefault(), true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + k.Name, + err) + } } } } @@ -316,7 +318,7 @@ func (k *Kraken) Run() { return } - err = k.UpdateTradablePairs(context.TODO(), forceUpdate) + err := k.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index 57f2f2e8..7234264c 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -173,55 +173,55 @@ func (o *OKCoin) Run() { } forceUpdate := false - format, err := o.GetPairFormat(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - o.Name, - err) - return - } - enabled, err := o.CurrencyPairs.GetPairs(asset.Spot, true) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - o.Name, - err) - return - } - - avail, err := o.CurrencyPairs.GetPairs(asset.Spot, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - o.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - var p currency.Pairs - p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + - format.Delimiter + - currency.USD.String()}) + if !o.BypassConfigFormatUpgrades { + format, err := o.GetPairFormat(asset.Spot, false) if err != nil { log.Errorf(log.ExchangeSys, - "%s failed to update currencies.\n", - o.Name) - } else { - log.Warnf(log.ExchangeSys, - "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.\n", - o.Name) - forceUpdate = true + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } + enabled, err := o.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } - err = o.UpdatePairs(p, asset.Spot, true, true) + avail, err := o.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + + format.Delimiter + + currency.USD.String()}) if err != nil { log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", - o.Name, - err) - return + "%s failed to update currencies.\n", + o.Name) + } else { + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, o.Name, asset.Spot, p) + forceUpdate = true + + err = o.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } } } } @@ -230,7 +230,7 @@ func (o *OKCoin) Run() { return } - err = o.UpdateTradablePairs(context.TODO(), forceUpdate) + err := o.UpdateTradablePairs(context.TODO(), forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index a2ff24a6..cc8a7bcb 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -244,46 +244,47 @@ func (o *OKEX) Run() { } forceUpdate := false - enabled, err := o.GetEnabledPairs(asset.Spot) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - o.Name, - err) - return - } - - avail, err := o.GetAvailablePairs(asset.Spot) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - o.Name, - err) - return - } - - if !common.StringDataContains(enabled.Strings(), format.Delimiter) || - !common.StringDataContains(avail.Strings(), format.Delimiter) { - forceUpdate = true - var p currency.Pairs - p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + - format.Delimiter + - currency.USDT.String()}) + if !o.BypassConfigFormatUpgrades { + var enabled, avail currency.Pairs + enabled, err = o.GetEnabledPairs(asset.Spot) if err != nil { log.Errorf(log.ExchangeSys, - "%s failed to update currencies.\n", - o.Name) - } else { - log.Warnf(log.ExchangeSys, - "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", - o.Name) + "%s failed to update tradable pairs. Err: %s", + o.Name, + err) + return + } - err = o.UpdatePairs(p, asset.Spot, true, forceUpdate) + avail, err = o.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + o.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + forceUpdate = true + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + + format.Delimiter + + currency.USDT.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.Name) - return + } else { + log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, o.Name, asset.Spot, p) + + err = o.UpdatePairs(p, asset.Spot, true, forceUpdate) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies.\n", + o.Name) + return + } } } }