Skip to content

Spot Markets

A spot market on Rubin is an exchange of one on-chain asset for another at an agreed price: a buyer gives the quote asset and receives the base asset, a seller does the opposite. Unlike perpetuals, a spot trade has no position, no leverage, no funding and no liquidation. What you buy is credited to your balance; what you sell leaves it.

Spot markets reuse the same central limit order book, matching engine and indexer as the perpetual markets, so the order flow, data feeds and client libraries are shared. The differences are listed on this page.

Market definition

A spot market is a CLOB pair whose metadata is SpotClobMetadata instead of PerpetualClobMetadata:

FieldMeaning
base_asset_idThe asset being bought or sold (e.g. MARTI, BTC). Any asset registered in x/assets.
quote_asset_idThe asset prices are quoted in. In spot v1 the quote must be USDC (asset 0) or RUB1 (asset 2).

The ticker is built from the two asset symbols: BASE-QUOTE, for example MARTI-USDC or BTC-RUB1. The same base asset may be listed against several quote assets, each as its own CLOB pair.

Price and size granularity use the same three parameters as perpetual CLOB pairs — step_base_quantums, subticks_per_tick and quantum_conversion_exponent — see Quantums and Subticks. The only change is that the quote leg is denominated in the quote asset rather than always in USDC:

price = subticks × 10^(quantum_conversion_exponent + quote_atomic_resolution − base_atomic_resolution)
size  = base_quantums × 10^(base_atomic_resolution)

For MARTI-USDC with quantum_conversion_exponent = -6, both atomic resolutions -6, subticks_per_tick = 10000 and step_base_quantums = 1000, the tick size is 0.01 USDC and the step size is 0.001 MARTI. The indexer exposes these as tickSize, stepSize, baseAtomicResolution and quoteAtomicResolution in the SpotMarket object, so clients do not have to compute them.

A spot market has the same status values as a perpetual one (ACTIVE, PAUSED, CANCEL_ONLY, POST_ONLY, INITIALIZING, FINAL_SETTLEMENT). FINAL_SETTLEMENT is the trading-halt state of a spot pair: there is nothing to settle, open orders are cancelled and balances stay where they are.

Spot subaccounts

Spot trading happens in a dedicated family of subaccounts. Subaccount numbers are grouped into 128 parent buckets (number % 128); bucket 1 — subaccounts 1, 129, 257, … — is reserved for spot:

  • Spot orders must be placed from a subaccount in bucket 1. The client libraries and the web app use subaccount 1.
  • Perpetual orders are rejected from bucket 1, with one exception: a short-term, immediate-or-cancel, reduce-only order, so that a perpetual position that ended up there can still be closed.
  • A spot subaccount holds asset balances only — no perpetual positions, no margin. Every asset balance must stay non-negative after an order, including its fee. There is no borrowing and no short selling: to sell MARTI you must hold MARTI, to buy it you must hold the quote asset.

You fund a spot subaccount the same way as any other subaccount: deposit from the main account (MsgDepositToSubaccount with the asset id) or transfer from another subaccount (MsgCreateTransfer). Withdrawals go back to the main account as bank coins. The indexer groups bucket 1 as parent subaccount 1, so GET /v4/addresses/{address}/parentSubaccountNumber/1 shows the whole spot balance.

Orders

Spot v1 accepts limit orders only. The order message is the same MsgPlaceOrder; a non-zero price (subticks) is required.

SupportedNot supported in spot v1
Short-term orders (good-til-block)Market orders (subticks = 0)
Long-term orders (good-til-block-time)Conditional orders: stop-loss, take-profit
Time in force: GTT, IOC, FOKTWAP orders
Post-onlyReduce-only
Builder codesLeverage, shorting

A market-style trade is emulated with an IOC limit order priced through the book: take the best opposite price, add a protection margin (the web app estimates the average fill price from the order book and allows 5 % slippage), and send it as IOC. Whatever is not filled immediately is cancelled, and the limit price caps the worst fill.

Collateral checks are balance checks. When an order is placed, the full size plus its fee must be covered by the asset the order sells. A long-term order that may execute as a taker reserves the higher of the maker and taker rate; a post-only order reserves the maker rate. Equity tier limits on the number of open stateful orders apply to spot subaccounts as well.

Fees

Spot fees use the same fee tier schedule as perpetuals — your tier is determined by 30-day volume, and spot volume counts toward it. Two things differ:

  • Fees are charged in the asset you sell (feeAssetPolicy: SOLD_ASSET). A buyer pays the fee in the quote asset, a seller in the base asset. The indexer reports buyFeeAssetId and sellFeeAssetId on the market and feeAssetId on every fill.
  • Maker rebates are not paid on spot. A negative maker rate from the fee tier table is clamped to zero.

Volume for fee tiers and statistics is normalised to USD from the quote leg: USDC counts 1:1, RUB1 is converted at its oracle price. If the quote asset has no usable oracle price, the market is reported as unavailable and its orders are rejected until the price is back.

Builder-code fees and affiliate revenue share work on spot fills exactly as on perpetual fills, in the fee asset of the fill.

Settlement

A match is settled atomically in one block as a SPOT_MATCH subaccount update: the base asset moves from seller to buyer, the quote asset from buyer to seller, and each side's fee moves to the fee collector. There is no insurance fund involvement and no oracle price in the settlement — the trade price is the limit price of the maker order.

Each fill updates the last traded price of the pair, which the indexer uses for priceChange24H, candles and sparklines.

Data

Spot markets are served by the same indexer endpoints as perpetual markets, under a spotMarket(s) path segment:

DataEndpoint
MarketsGET /v4/spotMarkets
Order bookGET /v4/orderbooks/spotMarket/{ticker}
TradesGET /v4/trades/spotMarket/{ticker}
CandlesGET /v4/candles/spotMarkets/{ticker}
Orders and fillsGET /v4/orders, GET /v4/fills with subaccountNumber=1 and marketType=SPOT
BalancesGET /v4/addresses/{address}/subaccountNumber/1 → assetPositions

Because a spot pair and a perpetual may share a ticker (for example BTC-USDC and a future BTC-USD), WebSocket channel ids and market filters use a product-qualified ticker: SPOT:MARTI-USDC. A bare ticker means a perpetual market. See the WebSockets reference.

Listing a spot market

Spot pairs, the assets they trade and the ERC-20 tokens behind them are created by governance. The full flow — deploying a token, registering it, creating the asset and the CLOB pair, converting tokens between the EVM and the bank — is described in Listing a Spot Market.

See also