Data Formats & Units
Before building on the Sequence API, you need to understand how quantities, prices, and timestamps are encoded. Getting this wrong is the most common source of bugs - a misplaced scale factor makes your orders 10x or 1000x wrong.
Why fixed-point?
Floating-point arithmetic introduces rounding errors. If you represent 0.1 BTC as a float64, you get 0.10000000000000001 - close, but not exact. When you're aggregating fills across venues or calculating P&L, those errors compound. Every financial system that cares about precision uses integers instead.
Sequence encodes all order-related values as integers with a known scale factor. The field name tells you the scale:
| Suffix | Scale factor | What it measures |
|---|---|---|
_1e8 | × 10⁸ (100,000,000) | Quantities (how much of something) |
_1e9 | × 10⁹ (1,000,000,000) | Prices (how much it costs) |
_bps | basis points (1/100 of 1%) | Fees, spreads, slippage |
_ns | nanoseconds since Unix epoch | Timestamps |
If a field doesn't have a suffix (like bid_price on the BBO endpoint), it's a human-readable float provided for convenience. These float fields should not be used for order submission or P&L calculation.
Quantities (_1e8)
Quantities are scaled by 10⁸. This gives 8 decimal places of precision - enough to represent 1 satoshi (0.00000001 BTC).
| You want to trade | qty_1e8 value | Conversion |
|---|---|---|
| 1 BTC | 100_000_000 | 1.0 × 10⁸ |
| 0.5 BTC | 50_000_000 | 0.5 × 10⁸ |
| 0.01 BTC | 1_000_000 | 0.01 × 10⁸ |
| 0.001 BTC | 100_000 | 0.001 × 10⁸ |
| 1 satoshi | 1 | 0.00000001 × 10⁸ |
To convert:
# Human-readable to API
api_qty = int(human_qty * 100_000_000) # 0.5 BTC → 50_000_000
# API to human-readable
human_qty = api_qty / 100_000_000 # 50_000_000 → 0.5 BTC// Human-readable to API
const apiQty = Math.floor(humanQty * 1e8); // 0.5 BTC → 50000000
// API to human-readable
const humanQty = apiQty / 1e8; // 50000000 → 0.5Prices (_1e9)
Prices are scaled by 10⁹. The extra decimal place (compared to quantities) accommodates sub-cent price levels common in crypto markets.
| Price | price_1e9 value | Conversion |
|---|---|---|
| $100,000.00 | 100_000_000_000_000 | 100,000.00 × 10⁹ |
| $50,000.50 | 50_000_500_000_000 | 50,000.50 × 10⁹ |
| $1.00 | 1_000_000_000 | 1.00 × 10⁹ |
| $0.001 | 1_000_000 | 0.001 × 10⁹ |
To convert:
# Human-readable to API
api_price = int(human_price * 1_000_000_000) # $50,000.50 → 50_000_500_000_000
# API to human-readable
human_price = api_price / 1_000_000_000 # 50_000_500_000_000 → $50,000.50A qty_1e8 value of 50_000_000_000_000 is not 0.5 BTC - that's 500,000 BTC. If your numbers look suspiciously large or small, check whether you're dividing by 10⁸ (quantities) or 10⁹ (prices). The suffixes are different for a reason.
Fees and spreads (_bps)
Fees, spreads, and slippage are expressed in basis points. One basis point is 0.01%, or 1/10,000 of the notional value.
| Human-readable | _bps value |
|---|---|
| 0.10% (10 bps) | 10 |
| 0.26% (26 bps) | 26 |
| 1.00% (100 bps) | 100 |
To convert basis points to a decimal multiplier: bps / 10_000. So 26 bps = 0.0026, meaning a $10,000 trade costs $26 in fees.
Timestamps (_ns)
All timestamps are nanoseconds since the Unix epoch (January 1, 1970 UTC). This provides nanosecond precision for measuring latency and ordering events.
# Python: nanoseconds to datetime
from datetime import datetime, timezone
dt = datetime.fromtimestamp(timestamp_ns / 1_000_000_000, tz=timezone.utc)// JavaScript: nanoseconds to Date
const date = new Date(Number(timestampNs / 1_000_000n));
// or if it's a number (not BigInt):
const date = new Date(timestampNs / 1_000_000);Many market data messages carry a triple timestamp chain: exch_ts_ns (when the exchange generated the event), edge_ts_ns (when our edge server received it), and cc_ts_ns (when the central coordinator processed it). This lets you measure propagation delay at each hop.
Symbols
All trading pairs use BASE-QUOTE format, hyphen-delimited, case-sensitive:
BTC-USD Bitcoin priced in US Dollars
ETH-USDT Ethereum priced in Tether
SOL-USDC Solana priced in USD Coin
Routing is exact-match
Orders route only to venues that list the exact symbol you requested. There is no automatic conversion between quote currencies:
| Symbol | Routes to |
|---|---|
BTC-USD | Kraken, Coinbase |
BTC-USDT | Binance, Bitget, Crypto.com, BitMart, OKX, Bybit |
BTC-USDC | Bitget, Crypto.com, OKX, Bybit, Hyperliquid |
If you submit a BTC-USD order but only have Binance credentials configured, it will be rejected - Binance doesn't support USD pairs. Use GET /v1/pairs/{symbol}/venues to check which venues support a given symbol.
Market data endpoints like NBBO merge USD and USDC venues so you see the full liquidity picture. But order routing is always exact-match on the symbol.
When to expect floats vs fixed-point
Not every endpoint uses fixed-point. Read-only convenience endpoints return human-readable floats:
| Endpoint | Format | Example fields |
|---|---|---|
| Order submission / response | Fixed-point | qty_1e8, price_1e9, fee_1e9 |
| Positions | Fixed-point | qty_1e8, avg_cost_1e9, unrealized_pnl_1e9 |
| gRPC market data streams | Fixed-point | bid_px_1e9, bid_sz_1e8, exch_ts_ns |
| NBBO (REST) | Float | best_bid_px, best_bid_sz, mid_px |
| BBO (REST) | Float | bid_price, bid_size, ask_price, ask_size |
| Balances | Float | available, total |
| Portfolio snapshots | Float | nav_usd, cash_usd |
Rule of thumb: if the field name has a suffix (_1e8, _1e9, _bps, _ns), it's a fixed-point integer. If it doesn't, it's a float.
Order statuses
| Status | Terminal? | Meaning |
|---|---|---|
ACCEPTED | No | Order received, passing through validation and risk checks |
RUNNING | No | Actively being executed - child orders have been dispatched to venues |
COMPLETED | Yes | Fully filled |
CANCELLED | Yes | Cancelled by you or by the system (e.g., kill switch) |
REJECTED | Yes | Failed validation, risk check, or insufficient balance |
FAILED | Yes | Execution error (venue timeout, exchange rejection) |
PARTIAL | Yes | Partially filled, then cancelled or failed |
"Terminal" means the order has reached a final state and won't change further. Non-terminal orders are still in progress.
Algo orders (TWAP, VWAP, Iceberg) also use ACTIVE and PAUSED statuses while the algo scheduler manages their slice execution. FILLED is accepted as an alias for COMPLETED.
Error format
All errors follow the same structure:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "qty_1e8: must be positive",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"details": [
{ "field": "qty_1e8", "message": "must be positive" }
]
}
}Use error.code for programmatic handling - it's stable. The message is for humans and may change. The details array only appears on VALIDATION_ERROR. See the API Overview for the full error code table.
Idempotency
Order submissions are idempotent via client_request_id. Submitting the same client_request_id twice returns the original parent_id without creating a duplicate order. This makes retries safe on network timeouts or 5xx errors.
If you retry with the same client_request_id but different parameters (e.g., changed quantity), you'll get a 409 Conflict.