Skip to content

exchanges: Update UpdateTickers method so that available pairs are used instead of enabled pairs.#2159

Open
shazbert wants to merge 3 commits intothrasher-corp:masterfrom
shazbert:wrapper_gettickers_rm_filter
Open

exchanges: Update UpdateTickers method so that available pairs are used instead of enabled pairs.#2159
shazbert wants to merge 3 commits intothrasher-corp:masterfrom
shazbert:wrapper_gettickers_rm_filter

Conversation

@shazbert
Copy link
Collaborator

@shazbert shazbert commented Feb 1, 2026

PR Description

Externally run strategies will have enabled pairs set but dynamically changing pairs create potential holes when pricing balances when using get tickers functionality.

  • Load everything the function returns, provided its maintained in the available pairs list.
  • Removes GetTickers functionality for assets that does consecutive REST calls. This may break certain engine batching calls but this can be mapped out per asset functionality if required.

note: It's imperative we remove wrapper impediments for REST calls e.g. if an asset is not enabled but supported. A client should be able to call these functions outside of enabled pairs/assets trading scope for hedging and pricing. This also includes returned websocket stream data.

gloriousEdit:

From my perspective wrapper/exchange layer's job is to translate exchange API responses into internal types. Policy decisions about which pairs to surface belong in the application layer (engine, gRPC handlers, strategies). Embedding an "enabled" filter inside a REST-response-processing loop conflates data acquisition with business logic

Fixes # (issue)

Type of change

Please delete options that are not relevant and add an x in [] as item is complete.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How has this been tested

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration and
also consider improving test coverage whilst working on a certain feature or package.

  • go test ./... -race
  • golangci-lint run
  • Test X

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation and regenerated documentation via the documentation tool
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally and on Github Actions with my changes
  • Any dependent changes have been merged and published in downstream modules

Copilot AI review requested due to automatic review settings February 1, 2026 21:02
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the UpdateTickers method across multiple exchanges to use available pairs instead of enabled pairs, which is a good improvement for efficiency and functionality. The changes are mostly consistent and correct. However, I've identified a recurring critical bug in the error handling for currency.ErrPairNotFound in bitmex_wrapper.go, exmo_wrapper.go, hitbtc_wrapper.go, and kucoin_wrapper.go. This bug could halt all ticker updates for these exchanges. I've included specific comments and code suggestions to address these issues. The other changes, such as removing functionality that involves consecutive REST calls, align well with the stated goals of the PR.

Comment on lines 340 to 342
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return err
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

When e.MatchSymbolCheckEnabled returns a currency.ErrPairNotFound error, the loop should continue to the next ticker. The current logic allows execution to proceed with a zero-value pair, which will cause ticker.ProcessTicker to fail and halt all ticker updates for the exchange.

		if err != nil {
			if errors.Is(err, currency.ErrPairNotFound) {
				continue
			}
			return err
		}

Comment on lines 167 to 171
if err != nil {
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

When e.MatchSymbolCheckEnabled returns a currency.ErrPairNotFound error, the loop should continue to the next ticker. Currently, it proceeds with a zero-value pair, which will cause ticker.ProcessTicker to fail and halt all ticker updates for the exchange.

		if err != nil {
			if !errors.Is(err, currency.ErrPairNotFound) {
				return err
			}
			continue
		}

Comment on lines 223 to 227
if err != nil {
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

When e.MatchSymbolCheckEnabled returns a currency.ErrPairNotFound error, the loop should continue to the next ticker. Currently, it proceeds with a zero-value pair, which will cause ticker.ProcessTicker to fail and halt all ticker updates for the exchange.

Suggested change
if err != nil {
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
}
if err != nil {
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
continue
}

Comment on lines 323 to 325
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return err
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

When e.MatchSymbolCheckEnabled returns a currency.ErrPairNotFound error, the loop should continue to the next ticker. The current logic allows execution to proceed with a zero-value pair, which will cause ticker.ProcessTicker to fail and halt all ticker updates for the exchange.

			if err != nil {
				if !errors.Is(err, currency.ErrPairNotFound) {
					return err
				}
				continue
			}

@thrasher-
Copy link
Collaborator

@codex please review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c5bcb19f1f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +164 to +168
Last: tickerInfo[j].Ticker.Latest,
High: tickerInfo[j].Ticker.High,
Low: tickerInfo[j].Ticker.Low,
Volume: tickerInfo[j].Ticker.Volume,
Pair: tickerInfo[j].Symbol,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter tickers against available pairs before storing

This loop now processes every ticker returned by LBank without checking that the symbol is in the exchange’s available pairs list. If the API includes delisted/unsupported markets (a common case when exchanges keep historical tickers), ticker.ProcessTicker will store prices for pairs that the exchange config explicitly doesn’t support, which can leak into balance pricing and strategy selection. Consider matching tickerInfo[j].Symbol via MatchSymbolWithAvailablePairs (or verifying against GetAvailablePairs) before calling ProcessTicker so only configured markets are stored.

Useful? React with 👍 / 👎.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts several exchange wrappers’ UpdateTickers implementations to stop filtering by enabled pairs and instead use available/supported pairs (or process all returned tickers), so clients can fetch tickers for supported markets even when not enabled for trading.

Changes:

  • Switch ticker updates from GetEnabledPairs-based filtering to using available pairs and/or symbol-to-available-pair matching.
  • Simplify ticker update loops by iterating over returned ticker payloads and matching symbols back to available pairs.
  • Mark some previously “batch ticker” paths as unsupported (e.g., OKX Spread, Gateio Options) to avoid consecutive REST calls.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
exchanges/yobit/yobit_wrapper.go Use available pairs for batching, then filter returned symbols via available-pair matching.
exchanges/poloniex/poloniex_wrapper.go Iterate tickers returned by API and match each symbol to available pairs.
exchanges/okx/okx_wrapper.go Remove per-enabled-pair filtering; Spread UpdateTickers now returns unsupported.
exchanges/lbank/lbank_wrapper.go Remove enabled-pair filtering and process all returned tickers.
exchanges/kucoin/kucoin_wrapper.go Remove enabled-pair filtering for futures and ignore “enabled” flag on symbol matching.
exchanges/hitbtc/hitbtc_wrapper.go Remove enabled-pair filtering by ignoring the “enabled” return from symbol matching.
exchanges/gateio/gateio_wrapper.go Options UpdateTickers now returns unsupported instead of per-underlying REST loops.
exchanges/exmo/exmo_wrapper.go Remove enabled-pair filtering by ignoring the “enabled” return from symbol matching.
exchanges/bybit/bybit_wrapper.go Remove enabled-pair membership checks; rely on available-pair symbol matching.
exchanges/btcmarkets/btcmarkets_wrapper.go Use available pairs for ticker fetching; remove strict length mismatch check.
exchanges/bitmex/bitmex_wrapper.go Remove enabled-pair filtering by ignoring the “enabled” return from symbol matching.
exchanges/bitfinex/bitfinex_wrapper.go Remove enabled-pair filtering by ignoring the “enabled” return from symbol matching.
exchanges/binance/binance_wrapper.go Replace nested enabled-pairs scan with symbol-to-available-pair matching per ticker.
Comments suppressed due to low confidence (1)

exchanges/okx/okx_wrapper.go:430

  • For asset.Margin, instrumentType is forced to instTypeSpot and the code processes all returned spot tickers without checking whether each instrument exists in the available pair set for the requested assetType. This can populate margin tickers for instruments that aren't actually available for margin. Filter each ticker through MatchSymbolWithAvailablePairs (or AvailablePairs.Contains) for assetType before calling ticker.ProcessTicker.
		instrumentType := GetInstrumentTypeFromAssetItem(assetType)
		if assetType == asset.Margin {
			instrumentType = instTypeSpot
		}
		ticks, err := e.GetTickers(ctx, instrumentType, "", "")

}
}
}
return common.ErrFunctionNotSupported
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdateTickers now returns common.ErrFunctionNotSupported for asset.Options, but Gateio's wrapper tests currently assert UpdateTickers does not error for all assets returned by GetAssetTypes(false) (see exchanges/gateio/gateio_test.go:1768-1773). To keep CI green, either maintain Options support here, or update the exchange/test expectations to treat Options UpdateTickers as unsupported.

Suggested change
return common.ErrFunctionNotSupported
// Options tickers are currently treated as a supported no-op.
// This ensures UpdateTickers does not return an error for all
// assets returned by GetAssetTypes(false), including Options.
return nil

Copilot uses AI. Check for mistakes.
Comment on lines 168 to 170
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If MatchSymbolCheckEnabled returns currency.ErrPairNotFound, this code continues and processes a ticker with an empty pair, which causes ticker.ProcessTicker to return an error and stops UpdateTickers. Add a continue when ErrPairNotFound is encountered (and only return non-ErrPairNotFound errors).

Suggested change
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if errors.Is(err, currency.ErrPairNotFound) {
continue
}
return err

Copilot uses AI. Check for mistakes.
for t := range ticks.Tickers {
pair, enabled, err := e.MatchSymbolCheckEnabled(ticks.Tickers[t].Symbol, assetType, true)
pair, _, err := e.MatchSymbolCheckEnabled(ticks.Tickers[t].Symbol, assetType, true)
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ErrPairNotFound from MatchSymbolCheckEnabled is currently ignored, but the code then calls ticker.ProcessTicker with pair (which will be EMPTYPAIR on ErrPairNotFound). This results in a "pair not set" error and may cause UpdateTickers to return errors unexpectedly. Skip processing (continue) when the pair cannot be matched.

Suggested change
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
if err != nil {
if errors.Is(err, currency.ErrPairNotFound) {
continue
}

Copilot uses AI. Check for mistakes.
Comment on lines 423 to +424
case asset.Spread:
format, err := e.GetPairFormat(asset.Spread, true)
if err != nil {
return err
}
pairs, err := e.GetEnabledPairs(assetType)
if err != nil {
return err
}
for y := range pairs {
var spreadTickers []SpreadTicker
spreadTickers, err = e.GetPublicSpreadTickers(ctx, format.Format(pairs[y]))
if err != nil {
return err
}
for x := range spreadTickers {
pair, err := currency.NewPairDelimiter(spreadTickers[x].SpreadID, format.Delimiter)
if err != nil {
return err
}
err = ticker.ProcessTicker(&ticker.Price{
Last: spreadTickers[x].Last.Float64(),
Bid: spreadTickers[x].BidPrice.Float64(),
BidSize: spreadTickers[x].BidSize.Float64(),
Ask: spreadTickers[x].AskPrice.Float64(),
AskSize: spreadTickers[x].AskSize.Float64(),
Pair: pair,
ExchangeName: e.Name,
AssetType: assetType,
})
if err != nil {
return err
}
}
}
return common.ErrFunctionNotSupported
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdateTickers now returns common.ErrFunctionNotSupported for asset.Spread, but Okx's wrapper tests currently expect UpdateTickers to succeed for all assets returned by GetAssetTypes(false) (see exchanges/okx/okx_test.go:3355-3361). Either restore support for Spread tickers, adjust the exchange's advertised asset set for this operation, or update the tests/callers to allow ErrFunctionNotSupported for Spread.

Copilot uses AI. Check for mistakes.
Comment on lines 224 to 226
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When MatchSymbolCheckEnabled returns currency.ErrPairNotFound, the loop still falls through and calls ticker.ProcessTicker with an empty pair, which will error ("pair not set") and abort UpdateTickers. Treat ErrPairNotFound as a skip condition (continue) and only return unexpected errors.

Suggested change
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if errors.Is(err, currency.ErrPairNotFound) {
continue
}
return err

Copilot uses AI. Check for mistakes.
Comment on lines 340 to 344
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if !enabled {
continue
}

err = ticker.ProcessTicker(&ticker.Price{
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If MatchSymbolCheckEnabled returns currency.ErrPairNotFound, UpdateTickers still calls ticker.ProcessTicker with an empty pair, which will error and abort the whole ticker update. Handle ErrPairNotFound by continuing the loop (only returning for non-ErrPairNotFound errors).

Copilot uses AI. Check for mistakes.
for key, val := range t {
pair, enabled, err := e.MatchSymbolCheckEnabled(key[1:], a, true)
pair, _, err := e.MatchSymbolCheckEnabled(key[1:], a, true)
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currency.ErrPairNotFound is not treated as a skip: when MatchSymbolCheckEnabled returns ErrPairNotFound, the code still calls ticker.ProcessTicker with an empty pair and then appends that error. Add an explicit continue when ErrPairNotFound occurs to avoid generating avoidable errors for unmatched symbols.

Suggested change
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
if err != nil {
if errors.Is(err, currency.ErrPairNotFound) {
// Skip unmatched symbols to avoid processing tickers with an empty pair.
continue
}

Copilot uses AI. Check for mistakes.
@shazbert shazbert requested a review from Copilot February 1, 2026 22:53
shazbert pushed a commit to shazbert/gocryptotrader that referenced this pull request Feb 1, 2026
@shazbert shazbert added the review me This pull request is ready for review label Feb 4, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@thrasher- thrasher- requested review from gbjk and removed request for gbjk February 25, 2026 05:26
@shazbert shazbert added nomerge Add this when you know the PR should not be merged but will remain open and removed review me This pull request is ready for review labels Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

nomerge Add this when you know the PR should not be merged but will remain open

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants