Futures Calendar Spread
Pair dated futures on one venue — every adjacent near/far pair, plus every pair of monthly and quarterly contracts at least 7 days apart — and rank them by the post-fee APR of locking in the spread between the two expiries.
List calendar spread rows
The board covers Binance (USDT-M and COIN-M), OKX, Bybit (linear and inverse) and Deribit; both legs share one venue and one margin type. While a venue's quotes in the 5-minute dated-quotes snapshot (dataset arbitrage_dated_quotes, written by the arbitrage-boards cron with top-20 book walks and VWAP fills at the default $10,000 notional) are at most 10 minutes old, the board prices that venue from it, so rows can be executable at any minute of the hour; the answer is then dated by the snapshot (dataset_id: "arbitrage_dated_quotes", stale after 10 minutes) and cached up to s-maxage=100, stale-while-revalidate=200. A venue the snapshot lacks, or holds only older quotes for, is served from the hourly term-structure snapshot (written at :03 UTC, stale after 2 hours), its pairs indicative. When the stored snapshot is past its 2-hour SLA, empty, or holds only reference quotes, the scanner reads the venues live (meta.source: "live") and merges per venue: a venue that answered replaces its stored rows, a venue that failed keeps its stored rows. A failed data-store read returns 503 (service_unavailable, Retry-After: 30).
Order: pairs meeting both tenor floors (near leg at least 3 days out, legs at least 7 days apart) first, then belowTenorFloor pairs; within each group executable pairs with a positive netRollApyPct, then positive pairs executable on a live read (indicativeReason: snapshot_quote), then every other pair, each by netRollApyPct, highest first. An executable pair at a post-fee loss never ranks above a positive spread.
Query parameters
- Name
coin- Type
- string
- Description
- Optional base coin. Supported launch coins: BTC, ETH, SOL, XRP, DOGE, MNT, XAUT.
- Name
exchanges- Type
- string
- Description
- Comma-separated exchange filter: Binance, OKX, Bybit, Deribit.
- Name
minApr- Type
- number
- Description
- Minimum
netRollApyPctin percentage points.
- Name
minOiUsd- Type
- number
- Description
- Minimum of near/far open interest in USD.
- Name
minVolumeUsd- Type
- number
- Description
- Minimum of near/far 24h futures volume in USD.
- Name
minDepthUsd- Type
- number
- Description
- Minimum of near/far depth in USD when depth is available.
- Name
marginType- Type
- string
- Description
- Futures margin filter:
linear,inverse, orboth.
- Name
notional- Type
- number
- Description
- Position notional in USD. Defaults to 10000. A pair is
executableonly when the depth behind both legs, the smaller leg's reported open interest and the thinner leg's 24h volume are at least this notional; a smaller notional widens the executable set.minDepthUsdfilters rows out instead.
- Name
limit- Type
- integer
- Description
- Rows per page. Defaults to 100.
- Name
cursor- Type
- string
- Description
- Opaque cursor from the previous page.
Request
curl -G https://www.sharpe.ai/api/v1/arbitrage/futures-calendar-spread \
-H "Authorization: Bearer sk_live_your_key_here" \
-d exchanges=OKX \
-d coin=BTC
Response
{
"data": {
"rows": [
{
"rank": 1,
"coin": "BTC",
"exchange": "OKX",
"nearContract": "BTC-USDT-261225",
"nearExpiry": "2026-12-25T08:00:00.000Z",
"farContract": "BTC-USDT-270326",
"farExpiry": "2027-03-26T08:00:00.000Z",
"nearBasisPct": 1.470868,
"farBasisPct": 2.89074,
"forwardYieldPct": 5.531205,
"curve": "Contango",
"direction": "Buy Near / Sell Far",
"netRollApyPct": 3.926809,
"minOpenInterestUsd": 300000000,
"minDepthUsd": 400000,
"executionStatus": "indicative",
"indicativeReason": "snapshot_quote",
"nearDays": 88.979167,
"gapDays": 91,
"belowTenorFloor": false,
"referenceForwardYieldPct": 5.612539,
"priceReference": "mark",
"feesPct": 0.4,
"updatedAt": "2026-09-27T08:03:00.000Z",
"isStale": false
}
],
"scannerMeta": {
"kind": "futures-calendar-spread",
"status": "ok",
"source": "supabase",
"freshnessSlaSeconds": 7200,
"notionalUsd": 10000
},
"pagination": { "cursor": null, "has_more": false, "total": 1 }
},
"meta": {
"request_id": "req_abc123def456ghij",
"timestamp": "2026-09-27T08:30:00.000Z",
"elapsed_ms": 17
}
}
Formula
referenceForwardYieldPct = (F_far / F_near − 1) × 100 × 365 / gapDays, on each contract's mark price, else its book mid (priceReference); never the last trade, which goes stale on an illiquid contract. It sets curve and direction: a far contract above the near one buys near and sells far.
forwardYieldPct = (sellBid / buyAsk − 1) × 100 × 365 / gapDays: the executable sides the direction trades (contango buys the near ask and sells the far bid; backwardation buys the far ask and sells the near bid).
feesPct = (4 × futures taker fee + 2 × spot taker fee) × 100: the lock-in's fills at the venue's standard rates (0.40 on OKX). Not annualized.
netRollApyPct = (sellBid / buyAsk − 1 − feesPct / 100) × 100 × 365 / gapDays. A losing post-fee spread stays negative rather than being clamped to zero.
gapDays is the fractional gap between the expiries, nearDays the fractional days from the request to the near expiry. nearBasisPct and farBasisPct are each contract's reference price against the venue's spot reference. minDepthUsd is the smaller depth behind the two legs (the top 20 levels where level 1 is thinner than $10,000); market impact within those levels is not modelled.
A pair is executable only when all of these hold; otherwise it is indicative and indicativeReason names the first check that failed:
indicativeReason | The check that failed |
|---|---|
stale_snapshot | Both legs' snapshots are inside the 2-hour term-structure SLA. |
no_depth | The depth behind both legs is known. |
depth_below_notional | That depth is at least notional. |
oi_below_notional | The smaller leg's open interest, when reported, is at least notional. |
no_volume | The thinner leg's 24h volume is known and at least notional. |
snapshot_quote | Every other check passes, but the quotes are older than 10 minutes: executable on a live read. |
Tenor floors: a pair whose near leg is under 3 days from expiry, or whose legs expire under 7 days apart, is served with belowTenorFloor: true and ranked after every pair meeting both floors (a 1-day Deribit daily spread annualizes a few basis points into −70% or worse).