Skip to content

Perp vs Dated Carry

Compare the funding a perpetual is expected to pay until a dated contract expires against the dated contract's basis to that perp. Short the perp and buy the dated contract when funding beats the basis (Perp Carry); buy the perp and short the dated contract when the basis beats funding (Dated Cash-and-Carry).


GET/v1/arbitrage/perp-dated-carry

List perp-dated carry rows

Each row pairs a perp (Binance, OKX, Bybit, Bitget, Hyperliquid, Aster, Kraken, Deribit and other funding venues) with a dated contract of the same coin and margin type. The legs are usually on two venues (crossVenue: true): two accounts, each collateralized separately, and the board does not price moving collateral between them.

The dated leg comes from the 5-minute dated-quotes snapshot (dataset arbitrage_dated_quotes, written by the arbitrage-boards cron with top-20 book walks) for every venue whose quotes there are at most 10 minutes old, else from the hourly term-structure snapshot (stale after 2 hours); the perp leg from the funding book and its 7-day sums, and the perp's book from the venue's bulk ticker. The answer's as_of is its oldest input; its SLA is the dated-quotes snapshot's 600 seconds (cached up to s-maxage=100, stale-while-revalidate=200) when that snapshot priced it, else the term structure's 7,200 seconds. When the stored snapshot is past its one-hour pairing window, empty, or reference-only, the scanner reads the venues live (meta.source: "live") and merges per venue. A failed data-store read returns 503 (service_unavailable, Retry-After: 30).

Order: rows at least 3 days from expiry first, then nearExpiry rows; within each group executable rows, then rows executable on a live read (indicativeReason: snapshot_quote), then every other indicative row, each by netCarryAprPct, highest first.

Query parameters

  • Name
    coin
    Type
    string
    Description
    Optional base coin.
  • Name
    exchanges
    Type
    string
    Description
    Comma-separated exchange filter applied to funding and dated-futures venues.
  • Name
    minApr
    Type
    number
    Description
    Minimum netCarryAprPct (the fee-adjusted annualized net carry, not the edge to expiry) in percentage points.
  • Name
    minOiUsd
    Type
    number
    Description
    Minimum open interest in USD when available.
  • Name
    minVolumeUsd
    Type
    number
    Description
    Minimum 24h futures volume in USD when available.
  • Name
    minDepthUsd
    Type
    number
    Description
    Minimum executable depth in USD when depth is available.
  • Name
    marginType
    Type
    string
    Description
    Futures margin filter: linear, inverse, or both.
  • Name
    notional
    Type
    number
    Description
    Position notional in USD. Defaults to 10000. A row is executable only when the depth on both sides the trade hits, any reported open interest and the dated contract's 24h volume are at least this notional. minDepthUsd filters 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

GET
/v1/arbitrage/perp-dated-carry
curl -G https://www.sharpe.ai/api/v1/arbitrage/perp-dated-carry \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -d coin=ETH \
  -d exchanges=Bybit

Response

{
  "data": {
    "rows": [
      {
        "rank": 1,
        "coin": "ETH",
        "perpVenue": "Bybit",
        "datedVenue": "Bybit",
        "datedContract": "ETHUSDT-27MAR27",
        "expiry": "2027-03-27T08:00:00.000Z",
        "days": 180.979167,
        "perpFundingAprPct": 10.95,
        "expectedFundingToExpiryPct": 4.912857,
        "datedBasisPct": 2.963969,
        "carryEdgePct": 1.948888,
        "netCarryEdgePct": 1.728888,
        "grossCarryAprPct": 3.930531,
        "netCarryAprPct": 3.486834,
        "bestLeg": "Perp Carry",
        "nextFunding": "2026-09-27T10:30:00.000Z",
        "funding7dStabilityPct": 0.19,
        "executableDepthUsd": 120000,
        "minOpenInterestUsd": 60000000,
        "oiDepthUsd": 120000,
        "executionStatus": "indicative",
        "indicativeReason": "snapshot_quote",
        "direction": "Short perp / Long dated",
        "crossVenue": false,
        "nearExpiry": false,
        "currentFundingAprPct": 10.95,
        "funding7dAprPct": 9.907143,
        "funding7dCumulativePct": 0.19,
        "fundingProjection": "7d_average",
        "fundingIntervalHours": 8,
        "fundingSettlementsToExpiry": 543,
        "datedLegPrice": 4120.412,
        "perpLegPrice": 4001.7999,
        "feesPct": 0.22,
        "updatedAt": "2026-09-27T08:03:00.000Z",
        "isStale": false
      }
    ],
    "scannerMeta": {
      "kind": "perp-dated-carry",
      "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": 16
  }
}

Formula

The basis is measured against the perp, not spot, at the sides the chosen trade hits:

  • Perp Carry (short perp / long dated): datedBasisPct = (F_ask / P_bid − 1) × 100
  • Dated Cash-and-Carry (long perp / short dated): datedBasisPct = (F_bid / P_ask − 1) × 100

Without a book for the perp's own contract, mark or last prices stand in and the row is indicative no_perp_book.

currentFundingAprPct = fundingRate × 8760 / fundingIntervalHours × 100 (the perp's own interval; perpFundingAprPct is the same number). funding7dAprPct = acc_7d × 365 / 7 × 100, the 7-day realised funding annualized.

expectedFundingToExpiryPct = projected rate per settlement × fundingSettlementsToExpiry, the settlements on the perp's own grid from its next settlement to the expiry instant. The projected rate is the 7-day realised average (fundingProjection: 7d_average); under 3 days to expiry, or without a current 7-day sum, it is the current print (current_print).

carryEdgePct = expectedFundingToExpiryPct − datedBasisPct

feesPct = (2 × perp taker fee + 2 × dated taker fee) × 100, each at its venue's standard rate. Not annualized.

netCarryEdgePct = carryEdgePct − feesPct for Perp Carry, −carryEdgePct − feesPct for Dated Cash-and-Carry. bestLeg and direction name the trade with the larger edge.

netCarryAprPct = netCarryEdgePct × 365 / days; grossCarryAprPct is the edge before fees, annualized. Both may be negative. minApr and ranking use netCarryAprPct.

executableDepthUsd is the smaller of the depth on the dated side and the perp side the trade hits; null when the perp has no book. minOpenInterestUsd is the smaller of the two legs' open interest. oiDepthUsd remains only as a backward-compatible mixed fallback. funding7dStabilityPct is a deprecated alias of funding7dCumulativePct (the 7-day cumulative funding, never a stability figure).

Before 2026-09-30, datedBasisPct was measured against spot and expectedFundingToExpiryPct extrapolated the current funding print (perpFundingAprPct × days / 365).

A row is executable only when all of these hold; otherwise it is indicative and indicativeReason names the first check that failed:

indicativeReasonThe check that failed
stale_snapshotBoth legs are inside the 2-hour freshness SLA.
no_perp_bookThe perp has a bid and ask for its own contract.
no_depthThe depth on both sides the trade hits is known.
depth_below_notionalThat depth is at least notional.
oi_below_notionalThe smaller leg's open interest, when reported, is at least notional.
no_volumeThe dated contract's 24h volume is known and at least notional.
snapshot_quoteEvery other check passes, but a quote is older than 10 minutes: executable on a live read.

Rows under 3 days to expiry are served with nearExpiry: true and ranked after every other row. Borrow cost, the cost of moving collateral between two venues, and market impact beyond the displayed depth are not modelled.

Was this page helpful?