XT Exchange Provider
Back your platform's spot markets with XT liquidity — how the provider layer works, what it powers, the one-active-provider rule, and the XT-specific quirks that cost operators money.
The XT Exchange Provider is one of three interchangeable back-ends for Bicrypto's
spot trading stack. With it installed and enabled, everything a customer sees on
/market and /trade — the tickers, the order book, the candles, the fills —
comes from XT, and every order they place is a real order on your XT account.
This is a provider, not a market. You are not running an exchange. You are running an interface in front of one, and XT holds the coins.
A SPOT wallet balance in Bicrypto is a database row that mirrors what your XT account holds. Deposits go to an XT address, orders are placed on XT's book, and withdrawals are XT withdrawals initiated over the API. If XT freezes the account the API key belongs to, your customers' spot balances are frozen with it and nothing in this platform can release them.
What it requires
| Requirement | Why | If it is missing |
|---|---|---|
| Bicrypto core | The provider is a licensed entry in the core's exchange table and uses the core's spot wallets, orders and cron worker |
There is nothing to enable it into |
| An XT account with API access | Every price, order and withdrawal is an authenticated call against it | The provider enables but reports "API credentials are missing" |
| A server XT will accept connections from | XT geo-blocks a long list of countries and does not whitelist IPv6 | Every call fails with HTTP 451, and it looks like a credential fault |
| Redis | Already mandatory for the core; the provider uses it for the exchange ban switch | Covered by the core requirement |
Nothing else. It does not need Ecosystem, ScyllaDB, or any blockchain addon.
It is also not a substitute for them. Ecosystem is the opposite architecture — your own matching engine, your own on-chain custody, your own keys. An install can run both at once: XT-backed SPOT markets and self-custodied ECO markets sit in separate wallet types and separate market lists, and the balances never mix.
Only one provider is ever active
Binance, KuCoin and XT are three rows in the same exchange table, and the
platform reads exactly one of them:
const provider = await models.exchange.findOne({ where: { status: true } });Enabling XT is therefore a switch, not an addition. The status endpoint sets
status = false on every other row inside the same transaction, and activating
an XT licence does the same thing again. There is no configuration in which both
Binance and XT serve spot traffic.
Currencies, markets, precisions and maker/taker fees in your database were imported from the previous provider. Symbols, network names and withdrawal minimums differ between exchanges. After a switch you must re-import both currencies and markets, and any SPOT wallet balance recorded while a different exchange held the coins is now a claim against an account that does not have them. Do not switch a provider on a live install with customer balances without a settlement plan.
What the provider actually powers
Market data. Tickers stream over /api/exchange/ticker and per-symbol
ticker, candle, trade and order-book streams over /api/exchange/market, all
via ccxt Pro's WebSocket transport (watchTickers, watchOHLCV, watchTrades,
watchOrderBook — XT supports all four). Historical candles come from
fetchOHLCV, cached by the chart builder.
Order placement. A spot order validates against your stored market metadata
(precision, min/max amount, min/max cost), debits the SPOT wallet, then calls
exchange.createOrder. Fees are taken from your stored maker/taker rate, not
XT's — the taker rate applies to buys and the maker rate to sells.
Reconciliation. The processPendingSpotOrders cron runs every 60 seconds and
settles open orders against XT: full fills, partial fills, cancellations and
rejections all land back in the wallet through it.
Prices. processCurrenciesPrices refreshes every stored currency price every
120 seconds from XT's tickers. This is what values a customer's portfolio.
Deposits and withdrawals. Deposit addresses are XT's addresses, fetched per
currency and network. Withdrawals call exchange.withdraw on your account. Both
paths carry XT-specific network-name translation — see
Deposits and withdrawals, because this is
where the platform most often looks healthy while a customer's money is nowhere.
XT-specific behaviour worth knowing on day zero
These are not bugs to report. They are shapes in XT's API that the platform already compensates for, and knowing them explains most surprising output.
XT needs a key and a secret, and nothing else. Unlike KuCoin there is no
passphrase. APP_XT_API_PASSPHRASE is read by the shared loader but XT declares
password: false in its required credentials, so setting it changes nothing.
Connections are forced onto IPv4. The shared HTTPS agent sets family: 4
specifically because XT's API-key IP whitelist does not accept IPv6 addresses. If
you route XT traffic through a proxy instead, that override no longer applies —
whitelist the proxy's address, and make sure it is an IPv4 one.
XT publishes no active flag. Every currency and every network comes back
with active: undefined. The platform treats only an explicit false as
disabled and reads deposit/withdraw as the real signal. Do not read a blank
active column as "XT has disabled this asset".
XT reports no memo information. Every imported network row is stored with
withdrawMemo: false, because XT's currency payload carries nothing to derive it
from. Memo-requiring assets need operator judgement rather than the imported
flag.
Filled buy quantities arrive in the quote asset. For a BUY, XT's
info.executedQty is the amount spent, not the amount bought, so the platform
divides it by info.avgPrice before crediting. This is why an XT-backed install
has a provider branch in the order adjuster that the other providers do not.
Ticker volume arrives as a string in info.q. The ticker WebSocket coerces
it to a number for XT specifically; a raw string reaching the browser used to
crash the trade page.
Where to start
Licence, credentials, enabling the provider, and the two imports that must run before anyone can trade.
Creating the key on XT, the permissions to grant, IP whitelisting, geo-blocking and the proxy path.
The two importers, what they delete, and how fees and precision are decided.
Three different network-name tables, the verification loop, and the withdrawal status mapping.
Every variable, endpoint, permission and cron the provider touches.
Symptom-first diagnosis, including the two failure modes that report green.
Three facts that catch operators out
A missing licence file blocks enabling, silently. The status endpoint checks
for lic/54510301.lic on disk before it will set status = true, and answers
403 with licenseRequired if it is absent. Activating the licence in the admin
panel writes that file.
Credentials are read from .env at connection time, and the connection is
cached for the process lifetime. Editing APP_XT_API_KEY does nothing until
the backend restarts. There is no reload button.
A rate-limit ban silences the entire spot stack while everything reports
green. When XT rate-limits you, the platform writes an unblock time to the
Redis key exchange:ban_status, and while that key is set every spot path —
prices, order reconciliation, deposits, withdrawals, routes — returns without
doing anything. It self-clears on a TTL, capped at 24 hours. Nothing in the admin
UI shows it; the backend log line is Exchange is banned for … more.