Positions
Sequence unifies everything the user owns into a single primitive: a Position.
- Fiat cash (USD on Kalshi, stablecoin-pegged USD), Crypto (BTC, ETH, USDC across CEXes and on-chain wallets), Perps (Hyperliquid signed positions with margin locks), and Event contracts (Kalshi YES/NO, Polymarket outcome tokens) all live as rows in the same
positions_v2table. - One endpoint, one response shape. Old
/v1/balances,/v1/positions/perp, and/v1/portfolio/*have been removed.
Get Positions
/v1/positionsReturns active positions across every venue and every instrument kind.
Values come from three reconciled sources that all write through the same manager:
- Fill events (hot path) — every fill routed through
FillRouterdecomposes into typed deltas and updates the row - Venue balance APIs (cold sync) — startup balance recovery and periodic reconciliation pull authoritative balances from Kraken / Coinbase / DEX / Kalshi / Hyperliquid
- Venue portfolio APIs (cold sync) — Kalshi
/portfolio/positions, Polymarketdata-api/positions, Polymarket USDC via PolygonbalanceOf
Query Parameters
| Parameter | Default | Description |
|---|---|---|
venues | all | Comma-separated venue filter (e.g., kraken,coinbase) |
kinds | all | Comma-separated instrument-kind filter: fiat, crypto, perp, event_contract |
include_closed | false | Include terminal rows (closed, redeemed). Resolved rows are always in the default response so settled event contracts surface alongside open positions. |
Response
{
"positions": [
{
"id": 101,
"venue": "coinbase",
"instrument": { "kind": "crypto", "symbol": "USDC" },
"qty_1e8": 237848346884,
"locked_qty_1e8": 0,
"available_qty_1e8": 237848346884,
"entry_price_usd_1e9": 1000000000,
"current_price_usd_1e9": 1000000000,
"current_value_usd_1e9": 2378483468840,
"unrealized_pnl_usd_1e9": 0,
"realized_pnl_usd_1e9": 0,
"fees_usd_1e9": 0,
"lifecycle": { "state": "active" },
"transferable": true,
"settled": false,
"settled_mark_usd_1e9": null
},
{
"id": 102,
"venue": "kalshi",
"instrument": { "kind": "fiat", "code": "USD" },
"qty_1e8": 10000000000,
"entry_price_usd_1e9": 1000000000,
"current_value_usd_1e9": 100000000000,
"unrealized_pnl_usd_1e9": 0,
"lifecycle": { "state": "active" },
"transferable": true,
"settled": false,
"settled_mark_usd_1e9": null
},
{
"id": 103,
"venue": "hyperliquid",
"instrument": { "kind": "perp", "symbol": "BTC-PERP", "base": "BTC" },
"qty_1e8": 50000000,
"entry_price_usd_1e9": 90000000000000,
"current_price_usd_1e9": 91500000000000,
"current_value_usd_1e9": 45750000000000,
"unrealized_pnl_usd_1e9": 750000000000,
"lifecycle": { "state": "active" },
"transferable": false,
"settled": false,
"settled_mark_usd_1e9": null
},
{
"id": 104,
"venue": "polymarket",
"instrument": {
"kind": "event_contract",
"market_slug": "0x14f1b88cd673ffb56c010fc065cdebee82f0bdb6b9a38109109cc71c1ed7aee9",
"outcome": "DOWN",
"token_id": "16669419551497791855954864481644480637553894270103338484394743642601464846109",
"expiry_ts": null
},
"qty_1e8": 619800000,
"entry_price_usd_1e9": 320000000,
"current_price_usd_1e9": 0,
"current_value_usd_1e9": 0,
"unrealized_pnl_usd_1e9": -1983360000,
"lifecycle": {
"state": "resolved",
"outcome": "Up",
"economic_value_usd_1e9": 0,
"resolved_at": 1776971900987654321
},
"transferable": false,
"settled": true,
"settled_mark_usd_1e9": 0
},
{
"id": 105,
"venue": "kalshi",
"instrument": {
"kind": "event_contract",
"market_slug": "KXPGATOUR-ZUCONO26-THVI",
"outcome": "YES",
"token_id": null,
"expiry_ts": null
},
"qty_1e8": 500000000,
"entry_price_usd_1e9": 600000000,
"current_price_usd_1e9": 1000000000,
"current_value_usd_1e9": 5000000000,
"unrealized_pnl_usd_1e9": 2000000000,
"lifecycle": {
"state": "resolved",
"outcome": "YES",
"economic_value_usd_1e9": 5000000000,
"resolved_at": 1776971900987654321
},
"transferable": false,
"settled": true,
"settled_mark_usd_1e9": 1000000000
}
],
"totals": {
"nav_usd_1e9": 48233483468840,
"cash_usd_1e9": 2478483468840,
"unrealized_pnl_usd_1e9": 750016640000,
"realized_pnl_usd_1e9": 0,
"fees_usd_1e9": 0,
"num_positions": 5
},
"as_of_ns": 1776828335325953000
}Instrument discriminant
instrument.kind selects which additional fields are populated:
kind | Additional fields | Example identifier |
|---|---|---|
fiat | code | "USD" |
crypto | symbol | "BTC", "USDC", "ETH.B" |
perp | symbol, base | {symbol: "BTC-PERP", base: "BTC"} |
event_contract | market_slug, outcome, token_id?, expiry_ts? | Polymarket condition_id or Kalshi ticker + "YES"/"NO" |
Position fields
| Field | Description |
|---|---|
id | Database primary key |
venue | Venue identifier (lowercase) |
instrument | Tagged union; see instrument discriminant above |
qty_1e8 | Signed qty in native units × 10⁸. Positive for long, negative for short perp/event contracts |
locked_qty_1e8 | Portion of qty_1e8 reserved as perp margin |
available_qty_1e8 | qty - locked — spendable qty |
entry_price_usd_1e9 | Weighted-average USD entry price × 10⁹ (historical fact; not FX-sensitive) |
current_price_usd_1e9 | Current mark price × 10⁹. Sourced from the consolidated NBBO for crypto/perp rows, from the settled mark ($1 for winners, $0 for losers) for settled event contracts, otherwise falls back to entry_price. |
current_value_usd_1e9 | qty * current_price / 1e8 — mark-to-market value, authoritative post-settlement |
unrealized_pnl_usd_1e9 | current_value - entry_value |
realized_pnl_usd_1e9 | Accrued from closed portions of this row |
fees_usd_1e9 | Cumulative fees charged |
lifecycle | Tagged union. {"state":"active"} for open positions. For settled event contracts: {"state":"resolved", "outcome": string, "economic_value_usd_1e9": int, "resolved_at": ns} — written automatically the moment Sequence observes the Kalshi determined / Polymarket market_resolved telemetry, independent of when the venue cleans up its qty. {"state":"redeemed", "cash_received_usd_1e9": int, "redeemed_at": ns} once POST /v1/markets/:slug/redeem succeeds. {"state":"closed"} once qty goes to 0. |
transferable | true for fungible assets (Fiat, Crypto) — can deposit/withdraw between venues |
settled | true iff the position's market has resolved. Set the instant resolution telemetry lands — may briefly be true with lifecycle.state still active during the ~ms race between the hub broadcast and the DB commit (cache read-through covers this gap). Once settled, stays true through redeemed. |
settled_mark_usd_1e9 | Settled per-unit mark × 10⁹: 1000000000 ($1) for winners, 0 for losers, null pre-settlement. Redundant with lifecycle.economic_value_usd_1e9 / qty_1e8 * 1e8 but explicit — clients don't have to pattern-match the tagged union. |
totals block
Aggregates across the returned rows (respects filters):
| Field | Description |
|---|---|
nav_usd_1e9 | Sum of all current_value_usd_1e9 |
cash_usd_1e9 | Sum for fiat + stablecoin rows (USDC/USDT/DAI/BUSD/FDUSD/TUSD/USDP = $1 pegs) |
unrealized_pnl_usd_1e9 | Mark-to-market P&L across returned positions |
realized_pnl_usd_1e9 | Sum of realized P&L |
fees_usd_1e9 | Sum of cumulative fees |
num_positions | Count of rows returned |
Common queries
All cash across every venue:
curl -H "Authorization: Bearer seq_live_..." \
"https://api.sequencemkts.com/v1/positions?kinds=fiat,crypto"Only Hyperliquid perps:
curl -H "Authorization: Bearer seq_live_..." \
"https://api.sequencemkts.com/v1/positions?kinds=perp&venues=hyperliquid"Event contracts on Kalshi + Polymarket:
curl -H "Authorization: Bearer seq_live_..." \
"https://api.sequencemkts.com/v1/positions?kinds=event_contract&venues=kalshi,polymarket"Resolved markets surface in the default response with settled=true and the terminal settled mark — you do not need include_closed to see them. Add include_closed=true only if you also want the redeemed or closed terminal states.
Sync Cost Basis
/v1/positions/syncTriggers an immediate fetch of trade history from connected exchanges to refresh cost basis on existing crypto positions. Useful when entry_value_usd_1e9 shows 0 on an asset you deposited externally — the venue-balance poll knows the qty but not what you paid for it.
Response:
{ "synced": true, "message": "venue trade sync kicked off" }The refresh runs in the background; positions update within ~30 seconds for venues with trade-history APIs.
Fee Tiers
/v1/feesMaker/taker fees for one ticker across one or more venues. Returns the fee components and curve formula so latency-sensitive callers can compute the exact realized fee locally — no per-(qty, price) round-trip. Fetch once per ticker, cache, evaluate the formula client-side.
Query Parameters
| Parameter | Default | Description |
|---|---|---|
ticker | Required | Symbol, Kalshi ticker, Polymarket clob_token_id, or Polymarket slug |
venues | All connected | Comma-separated venue list (e.g. "kalshi,polymarket") |
The handler accepts:
- CEX symbols:
BTC-USD,BTCUSDT,ETH-USDC - Kalshi tickers:
KXMLBHR-26APR-FOO:YES - Polymarket
clob_token_id(78-digit numeric) - Polymarket slug (
nba-finals-okc-2026) — resolved via Gamma →conditionId→ CLOB fee schedule
When venues= is omitted, only venues that can plausibly list the ticker
shape are returned (Kalshi tickers route to Kalshi only; CEX symbols
route to CEX/DEX only). When venues= is explicit, the user's intent
is honored — useful for slug lookups that don't match a built-in shape.
Response
{
"ticker": "BTC-USD",
"fees": [
{
"venue": "binance",
"maker_bps": 0,
"taker_bps": 10,
"fee_model": "linear",
"components": {},
"formula": {
"taker_fee_usd": "qty * price * taker_bps / 10000",
"maker_fee_usd": "qty * price * maker_bps / 10000"
}
},
{
"venue": "hyperliquid",
"maker_bps": 0,
"taker_bps": 2,
"fee_model": "linear",
"components": {},
"formula": {
"taker_fee_usd": "qty * price * taker_bps / 10000",
"maker_fee_usd": "qty * price * maker_bps / 10000"
}
}
]
}For a Polymarket prediction market:
{
"venue": "polymarket",
"maker_bps": 0,
"taker_bps": 360,
"fee_model": "binary_quadratic",
"components": {
"rate_bps": 720,
"exponent": 1
},
"formula": {
"taker_fee_usd": "qty * price * pow(1 - price, exponent) * rate_bps / 10000",
"maker_fee_usd": "0"
}
}For a Kalshi market:
{
"venue": "kalshi",
"maker_bps": 0,
"taker_bps": 350,
"fee_model": "binary_quadratic",
"components": {
"rate_bps": 700,
"multiplier": 1.0,
"rounding": "ceil_to_cent"
},
"formula": {
"taker_fee_usd": "ceil((qty * price * (1 - price) * rate_bps * multiplier / 10000) * 100) / 100",
"maker_fee_usd": "0"
}
}Field Reference
| Field | Description |
|---|---|
ticker | The queried ticker (echoed back) |
venue | Venue identifier |
maker_bps | Maker fee in basis points. For binary_quadratic venues this is the at-mid rate (typically 0 for Polymarket and standard Kalshi markets). null = unknown |
taker_bps | Taker fee in basis points. At-mid value for binary_quadratic (rate at price=0.5); flat rate for linear. null = unknown — treat as ineligible for routing |
fee_model | Curve type: "linear" (CEX/DEX, fee is qty × price × bps) or "binary_quadratic" (Polymarket / Kalshi prediction, fee is qty × price × (1-price) × rate_bps) |
components | Curve parameters. Stable keys per fee_model. |
formula.taker_fee_usd | Plain-string formula the client can evaluate locally for the exact taker fee at any (qty, price) |
formula.maker_fee_usd | Same for maker fee |
components keys per fee_model:
| Model | Keys |
|---|---|
linear | {} (no extra params; taker_bps is the rate) |
binary_quadratic (Polymarket) | { rate_bps, exponent } |
binary_quadratic (Kalshi) | { rate_bps, multiplier, rounding } |
rate_bps is the curve coefficient r × 10_000 (i.e. 2 × taker_bps_at_mid). exponent defaults to 1 (reserved for future non-linear curves). multiplier is per-Kalshi-series (currently 1.0 on every active series we've sampled). rounding: "ceil_to_cent" documents Kalshi's per-fill ceil-to-cent behavior — clients should mirror it to avoid under-quoting.
Computing fees client-side (latency-sensitive path)
Fetch /v1/fees once per ticker (cache forever — fees don't change
minute-to-minute), then evaluate the formula locally per fill candidate:
row = fees_response["fees"][venue_index]
if row["fee_model"] == "binary_quadratic":
rate = row["components"]["rate_bps"]
exponent = row["components"].get("exponent", 1)
fee = qty * price * pow(1 - price, exponent) * rate / 10000
if row["components"].get("rounding") == "ceil_to_cent":
fee = math.ceil(fee * 100) / 100 # Kalshi
elif row["fee_model"] == "linear":
fee = qty * price * row["taker_bps"] / 10000For a server-authoritative answer that also includes size-aware
slippage + total landed cost (not just fees), use POST /v1/orders/preview.
That endpoint composes fee + slippage + venue-specific semantics for
your exact order shape and is the canonical "how much will this cost?"
surface. Use /v1/fees when you want the static fee parameters and
plan to compute fees yourself per fill.
The SOR uses taker fees when routing market orders and accounts for maker fees on limit orders. Fee differentials between venues are a significant factor in routing decisions — the cheapest quoted price may not be the cheapest after fees.
Removed endpoints
The following endpoints were consolidated into GET /v1/positions and now return 404:
| Old endpoint | Replacement |
|---|---|
GET /v1/balances | GET /v1/positions?kinds=fiat,crypto |
GET /v1/positions/perp | GET /v1/positions?kinds=perp |
GET /v1/portfolio/summary | GET /v1/positions — read totals block |
GET /v1/portfolio/snapshots | removed (not replaced) |
GET /v1/portfolio/unified | GET /v1/positions |
GET /v1/analytics/portfolio | GET /v1/positions |
POST /v1/positions/perp/funding | removed; perp funding ingestion is automatic |