DpExchange.Gemini.SymbolFormat (DpExchangeGemini v0.1.1)

Copy Markdown View Source

Gemini's symbol mapping — the family's first genuinely lossy one.

Gemini's native symbol is lowercase and separatorless: btcusd, aavegusd, jitosolsol. Canonical is BASE-QUOTE. So unlike Coinbase, where the conversion is effectively identity, here both directions do real work and one of them can be wrong.

Why the quote list is ordered longest-first, and why here it bites

With sep: "" there is no separator to split on, so CanonicalPair recovers the base by matching a known quote suffix. The list is consulted in order, so a shorter quote that is also the tail of a longer one will win if it is checked first — and Gemini has three overlapping pairs of exactly that shape:

NativeCorrect splitWhat USD-before-GUSD would give
aavegusdAAVE / GUSDAAVEG / USD
solrlusdSOL / RLUSDSOLRL / USD
aaveusdcAAVE / USDC— (USDC is longer, so safe either way)

AAVEG is not an asset. A symbol split that way matches no catalogue entry and collects nothing, silently — the same shape of failure as a wrong timeframe, one layer down.

Of the 346 symbols live on 2026-08-28, 157 end in a quote that is a suffix of another quote (80 GUSD + 77 RLUSD). This is not a precaution on this venue; it is 45% of the catalogue.

The quote list was derived, not assumed

Taken from the live /v1/symbols list on 2026-08-28 by tallying suffixes and then chasing what did not match. Two symbols were left over — jitosolsol and efilfil — which is how SOL and FIL got here. Guessing the list from the well-known quotes would have produced exactly those two silent mis-splits.

Perpetuals are not spot pairs and do not round-trip

Thirteen symbols carry a perp suffix (avaxgusdperp). They end in no quote, so they take the :nomatch path and are returned uppercased and unsplit. That is deliberate: this package declares supported_instrument_types: [:spot], and inventing a canonical spot pair for a perpetual would be a substitution. Rest.get_symbols/1 filters them out rather than emitting a symbol the rest of the family cannot interpret.

Downcase on the way out

CanonicalPair.to_exchange/2 returns uppercase (BTCUSD). Gemini's REST accepts either case, but /v1/symbols returns lowercase and the WebSocket stream names are lowercasebtcusd@bookTicker. So the exchange form is downcased here, once, rather than at each of the several call sites that would otherwise have to remember.

Summary

Functions

The mapping, exposed so the conformance suite can drive CanonicalPair with it.

Whether a native symbol names a perpetual rather than a spot pair.

The quote currencies this venue settles in, canonical and uppercase.

Functions

mapping()

The mapping, exposed so the conformance suite can drive CanonicalPair with it.

perpetual?(native)

@spec perpetual?(String.t()) :: boolean()

Whether a native symbol names a perpetual rather than a spot pair.

Measured, not documented: 13 of the 346 symbols live on 2026-08-28 carry the suffix.

quotes()

@spec quotes() :: [String.t()]

The quote currencies this venue settles in, canonical and uppercase.

Exposed because capabilities/0 declares the same list, and a declaration that can disagree with the mapping it describes is a declaration worth nothing.