Cash-and-carry basis calculations for spot/perpetual arbitrage.
Pure functions for calculating basis between spot and derivative prices, annualized yields, and futures curve analysis.
Terminology
- Basis - Price difference between derivative and spot
- Contango - Futures trading above spot (positive basis)
- Backwardation - Futures trading below spot (negative basis)
Example
ZenQuant.Basis.spot_perp(50_000, 50_100)
# => %{absolute: 100.0, percent: 0.2, direction: :contango}
ZenQuant.Basis.annualized(50_000, 50_100, 30)
# => 0.0243 (2.43% annualized yield)API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
arbitrage_opportunity? | 3 | Check if basis is at arbitrage-worthy levels. | spot_price: value, derivative_price: value, threshold_pct: value |
compare | 1 | Compare basis across multiple exchanges, sorted by basis descending. | exchanges: exchange_data |
implied_funding | 3 | Calculate implied funding rate from spot-perp basis. | spot_price: value, perp_price: value, funding_interval_hours: value |
futures_curve | 2 | Build futures curve from multiple contracts with basis and annualized yield. | spot_price: value, futures: exchange_data |
annualized | 3 | Calculate annualized basis yield for cash-and-carry trade. | spot_price: value, futures_price: value, days_to_expiry: value |
spot_perp | 2 | Calculate basis between spot and perpetual/futures price. | spot_price: value, derivative_price: value |
Summary
Types
Basis calculation result
Market structure direction relative to spot price
Exchange basis comparison result
Enriched futures contract with basis metrics
Functions
Calculate annualized basis yield for cash-and-carry trade.
Check if basis is at arbitrage-worthy levels.
Compare basis across multiple exchanges, sorted by basis descending.
Build futures curve from multiple contracts with basis and annualized yield.
Calculate implied funding rate from spot-perp basis.
Calculate basis between spot and perpetual/futures price.
Types
Basis calculation result
@type direction() :: :contango | :backwardation | :flat
Market structure direction relative to spot price
@type exchange_basis() :: %{ exchange: atom() | String.t(), basis: number(), basis_pct: float(), implied_apr: float() }
Exchange basis comparison result
@type futures_point() :: %{ expiry: Date.t(), price: number(), days_to_expiry: integer(), basis: number(), basis_pct: float(), annualized: float() }
Enriched futures contract with basis metrics
Functions
@spec annualized(number(), number(), pos_integer()) :: float()
Calculate annualized basis yield for cash-and-carry trade.
Parameters
spot_price- Current spot price (value)futures_price- Futures price (value)days_to_expiry- Days until futures expiration (value)
Returns
Annualized yield as decimal (e.g., 0.05 = 5% APY) (float)
Example
0.1095# descripex:contract
%{
params: %{
days_to_expiry: %{
description: "Days until futures expiration",
kind: :value
},
spot_price: %{description: "Current spot price", kind: :value},
futures_price: %{description: "Futures price", kind: :value}
},
returns: %{
type: :float,
description: "Annualized yield as decimal (e.g., 0.05 = 5% APY)"
},
returns_example: 0.1095
}
Check if basis is at arbitrage-worthy levels.
Parameters
spot_price- Current spot price (value)derivative_price- Perpetual or futures price (value)threshold_pct- Minimum basis percentage to consider (default:0.1, value)
Returns
true if absolute basis >= threshold (boolean)
Example
true# descripex:contract
%{
params: %{
spot_price: %{description: "Current spot price", kind: :value},
derivative_price: %{description: "Perpetual or futures price", kind: :value},
threshold_pct: %{
default: 0.1,
description: "Minimum basis percentage to consider",
kind: :value
}
},
returns: %{type: :boolean, description: "true if absolute basis >= threshold"},
returns_example: true
}
@spec compare([map()]) :: [exchange_basis()]
Compare basis across multiple exchanges, sorted by basis descending.
Parameters
exchanges- List of maps with :exchange, :spot, and :perp fields (exchange_data)
Returns
List of maps with :exchange, :basis, :basis_pct, :implied_apr (list)
Example
[%{exchange: "binance", basis: 120.0, basis_pct: 0.14, implied_apr: 51.1}]# descripex:contract
%{
params: %{
exchanges: %{
description: "List of maps with :exchange, :spot, and :perp fields",
source: "multiple fetch_ticker calls",
kind: :exchange_data
}
},
returns: %{
type: :list,
description: "List of maps with :exchange, :basis, :basis_pct, :implied_apr"
},
returns_example: [
%{exchange: "binance", basis: 120.0, basis_pct: 0.14, implied_apr: 51.1}
]
}
@spec futures_curve(number(), [map()]) :: [futures_point()]
Build futures curve from multiple contracts with basis and annualized yield.
Parameters
spot_price- Current spot price (value)futures- List of maps with :expiry (Date) and :price fields (exchange_data)
Returns
List of maps sorted by expiry with :basis, :basis_pct, :annualized added (list)
Example
[
%{
price: 84500.0,
expiry: ~D[2026-03-28],
days_to_expiry: 30,
basis: 1000.0,
basis_pct: 1.2,
annualized: 0.146
}
]# descripex:contract
%{
params: %{
futures: %{
description: "List of maps with :expiry (Date) and :price fields",
source: "fetch_markets() filtered to futures",
kind: :exchange_data
},
spot_price: %{description: "Current spot price", kind: :value}
},
returns: %{
type: :list,
description: "List of maps sorted by expiry with :basis, :basis_pct, :annualized added"
},
returns_example: [
%{
price: 84500.0,
expiry: ~D[2026-03-28],
days_to_expiry: 30,
basis: 1000.0,
basis_pct: 1.2,
annualized: 0.146
}
]
}
@spec implied_funding(number(), number(), pos_integer()) :: float()
Calculate implied funding rate from spot-perp basis.
Parameters
spot_price- Current spot price (value)perp_price- Perpetual price (value)funding_interval_hours- Hours between funding (default:8, value)
Returns
Implied funding rate for one period (float)
Example
0.1095# descripex:contract
%{
params: %{
spot_price: %{description: "Current spot price", kind: :value},
perp_price: %{description: "Perpetual price", kind: :value},
funding_interval_hours: %{
default: 8,
description: "Hours between funding",
kind: :value
}
},
returns: %{type: :float, description: "Implied funding rate for one period"},
returns_example: 0.1095
}
@spec spot_perp(number(), number()) :: basis_result()
Calculate basis between spot and perpetual/futures price.
Parameters
spot_price- Current spot price (value)derivative_price- Perpetual or futures price (value)
Returns
Map with :absolute, :percent, :direction (:contango | :backwardation | :flat) (map)
Example
%{absolute: 100.0, direction: :contango, percent: 0.2}# descripex:contract
%{
params: %{
spot_price: %{description: "Current spot price", kind: :value},
derivative_price: %{description: "Perpetual or futures price", kind: :value}
},
returns: %{
type: :map,
description: "Map with :absolute, :percent, :direction (:contango | :backwardation | :flat)"
},
returns_example: %{absolute: 100.0, direction: :contango, percent: 0.2}
}