Funding rate analysis functions for perpetual futures.
Pure functions for analyzing funding rates, calculating annualized returns, detecting spikes, comparing rates across exchanges, trend detection, mean reversion signals, cross-exchange ranking, and delta-neutral funding-carry candidate ranking.
Works with plain maps from exchange APIs (e.g., maps with :funding_rate and :symbol keys).
Example
rates = [
%{symbol: "BTC/USDT:USDT", funding_rate: 0.0001},
%{symbol: "BTC/USDT:USDT", funding_rate: 0.00015}
]
ZenQuant.Funding.average(rates)
# => 0.000125
ZenQuant.Funding.annualize(0.0001)
# => 0.1095 (10.95% APR)API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
carry_candidates | 2 | Rank cross-venue delta-neutral funding-carry candidates on a common horizon. | venues: exchange_data |
favorable? | 2 | Check if current funding is favorable for a position direction. | rate: exchange_data, direction: value |
rank | 2 | Rank funding rates across exchanges, sorted by rate. | rates_by_exchange: exchange_data |
mean_reversion_signal | 2 | Generate mean reversion signal from funding rate history. | rates: exchange_data |
trend | 2 | Detect funding rate trend direction using linear regression. | rates: exchange_data |
volatility | 1 | Calculate funding rate volatility (standard deviation). | rates: exchange_data |
cumulative | 1 | Calculate cumulative funding over a period. | rates: exchange_data |
compare | 1 | Compare funding rates across exchanges, sorted by rate descending. | rates: exchange_data |
detect_spikes | 2 | Detect funding rate spikes (abnormally high or low rates). | rates: exchange_data |
average | 1 | Calculate average funding rate from a list of rates. | rates: exchange_data |
annualize | 2 | Annualize a per-period funding rate to APR. | rate: value, period_hours: value |
Summary
Types
Caller-supplied assumption required for each candidate leg
Delta-neutral funding-carry candidate between two venues
Estimated costs over the comparison horizon, as fractions of notional
Completeness label for a carry candidate's cost inputs
Functions
Annualize a per-period funding rate to APR.
Calculate average funding rate from a list of rates.
Rank cross-venue delta-neutral funding-carry candidates.
Compare funding rates across exchanges, sorted by rate descending.
Calculate cumulative funding over a period.
Detect funding rate spikes (abnormally high or low rates).
Check if current funding is favorable for a position direction.
Generate mean reversion signal from funding rate history.
Rank funding rates across exchanges, sorted by rate.
Detect funding rate trend direction using linear regression.
Calculate funding rate volatility (standard deviation).
Types
@type carry_assumption() ::
:fees | :borrow_cost | :basis | :transfer_cost | :capital_requirement
Caller-supplied assumption required for each candidate leg
@type carry_candidate() :: %{ long_venue: String.t(), short_venue: String.t(), long_symbol: String.t(), short_symbol: String.t(), horizon_hours: pos_integer(), long_funding_interval_hours: number(), short_funding_interval_hours: number(), long_rate_horizon_hours: number(), short_rate_horizon_hours: number(), long_rate: float(), short_rate: float(), long_rate_normalized: float(), short_rate_normalized: float(), gross_carry: float(), costs: carry_costs(), costs_by_leg: %{long: carry_costs(), short: carry_costs()}, capital_requirement: float() | nil, capital_requirement_by_leg: %{long: float() | nil, short: float() | nil}, net_carry: float() | nil, status: carry_status(), missing_inputs: [{:long | :short, carry_assumption()}], required_assumptions: [carry_assumption()] }
Delta-neutral funding-carry candidate between two venues
@type carry_costs() :: %{ fees: float() | nil, borrow_cost: float() | nil, basis: float() | nil, transfer_cost: float() | nil }
Estimated costs over the comparison horizon, as fractions of notional
@type carry_status() :: :complete | :incomplete
Completeness label for a carry candidate's cost inputs
Functions
@spec annualize(number(), pos_integer()) :: float()
Annualize a per-period funding rate to APR.
Parameters
rate- Per-period funding rate as decimal (e.g., 0.0001) (value)period_hours- Hours per funding period (default:8, value)
Returns
Annualized percentage rate (APR) (float)
Example
0.1095# descripex:contract
%{
params: %{
rate: %{
description: "Per-period funding rate as decimal (e.g., 0.0001)",
kind: :value
},
period_hours: %{
default: 8,
description: "Hours per funding period",
kind: :value
}
},
returns: %{type: :float, description: "Annualized percentage rate (APR)"},
returns_example: 0.1095
}
Calculate average funding rate from a list of rates.
Parameters
rates- List of maps with :funding_rate key (exchange_data)
Returns
Average funding rate, or nil if empty (float)
Example
0.1095Composes With
annualize
# descripex:contract
%{
params: %{
rates: %{
description: "List of maps with :funding_rate key",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{type: :float, description: "Average funding rate, or nil if empty"},
returns_example: 0.1095,
composes_with: [:annualize]
}
@spec carry_candidates( [map()], keyword() ) :: [carry_candidate()]
Rank cross-venue delta-neutral funding-carry candidates.
Each venue must identify the same :symbol and supply both its funding cadence
(:funding_interval_hours) and the horizon represented by its quoted rate
(:rate_horizon_hours). Rates are normalized from their quoted horizon to the
shared :horizon_hours; settlement cadence is exposed separately.
Each venue also supplies its own :fees, :borrow_cost, :basis,
:transfer_cost, and :capital_requirement assumptions. For each compatible
cross-venue pair, the candidate is long the lower normalized rate and short
the higher (stable venue-name order on ties). Gross carry, per-leg assumptions,
aggregate estimated costs, and capital are exposed separately. Incomplete
inputs yield status: :incomplete and net_carry: nil — never a
profitable/arbitrage label from spread alone.
Cost assumptions are fractions of notional over the comparison horizon.
Positive :basis is treated as a cost and negative :basis as a credit.
Compare funding rates across exchanges, sorted by rate descending.
Each rate map must include a positive :funding_interval_hours (caller-supplied
settlement cadence). APR is computed via annualize/2 with that cadence.
Missing cadence rule: rates without a funding rate or without a positive
whole-hour :funding_interval_hours are omitted from the result. There is no
silent 8-hour default.
Calculate cumulative funding over a period.
Parameters
rates- List of maps with :funding_rate key (exchange_data)
Returns
Sum of all funding rates (float)
Example
0.1095# descripex:contract
%{
params: %{
rates: %{
description: "List of maps with :funding_rate key",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{type: :float, description: "Sum of all funding rates"},
returns_example: 0.1095
}
Detect funding rate spikes (abnormally high or low rates).
Parameters
rates- List of maps with :funding_rate key (exchange_data)
Options
threshold- Std devs for spike detection (default:2.0)
Returns
List of {:high | :low, rate_map} tuples (list)
Example
[high: %{symbol: "BTC/USDT:USDT", funding_rate: 3.5e-4}]# descripex:contract
%{
opts: %{
threshold: %{
default: 2.0,
type: :float,
description: "Std devs for spike detection"
}
},
params: %{
rates: %{
description: "List of maps with :funding_rate key",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{
type: :list,
description: "List of {:high | :low, rate_map} tuples"
},
returns_example: [high: %{symbol: "BTC/USDT:USDT", funding_rate: 3.5e-4}]
}
Check if current funding is favorable for a position direction.
Parameters
rate- Map with :funding_rate key (exchange_data)direction- Position direction (:long or :short) (value)
Returns
true if you receive funding, false if you pay (boolean)
Example
true# descripex:contract
%{
params: %{
direction: %{
description: "Position direction (:long or :short)",
kind: :value
},
rate: %{
description: "Map with :funding_rate key",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{
type: :boolean,
description: "true if you receive funding, false if you pay"
},
returns_example: true
}
Generate mean reversion signal from funding rate history.
Parameters
rates- Time-ordered list of maps with :funding_rate key (last = current) (exchange_data)
Options
threshold- Z-score threshold for signal (default:2.0)lookback- Baseline point count (nil = all)
Returns
:overbought | :oversold | :neutral | nil (atom)
Example
:neutral# descripex:contract
%{
opts: %{
threshold: %{
default: 2.0,
type: :float,
description: "Z-score threshold for signal"
},
lookback: %{
default: nil,
type: :integer,
description: "Baseline point count (nil = all)"
}
},
params: %{
rates: %{
description: "Time-ordered list of maps with :funding_rate key (last = current)",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{
type: :atom,
description: ":overbought | :oversold | :neutral | nil"
},
returns_example: :neutral
}
Rank funding rates across exchanges, sorted by rate.
Parameters
rates_by_exchange- Map of %{exchange_name => [rate_maps]}. Each rate map needs :symbol, :funding_rate, and :funding_interval_hours (settlement cadence in hours; required). Rates missing a positive interval are omitted — no default 8h assumption. (exchange_data)
Options
direction- Sort direction (:desc or :asc) (default::desc)limit- Max results (alias: :top)
Returns
List of maps with :exchange, :symbol, :rate, :apr (list)
Example
[%{exchange: :binance, symbol: "BTC/USDT:USDT", rate: 0.0001, apr: 0.1095}]# descripex:contract
%{
opts: %{
limit: %{
default: nil,
type: :integer,
description: "Max results (alias: :top)"
},
direction: %{
default: :desc,
type: :atom,
description: "Sort direction (:desc or :asc)"
}
},
params: %{
rates_by_exchange: %{
description: "Map of %{exchange_name => [rate_maps]}. Each rate map needs :symbol, :funding_rate, and :funding_interval_hours (settlement cadence in hours; required). Rates missing a positive interval are omitted — no default 8h assumption.",
source: "multiple fetch_funding_rates calls",
kind: :exchange_data
}
},
returns: %{
type: :list,
description: "List of maps with :exchange, :symbol, :rate, :apr"
},
returns_example: [
%{exchange: :binance, symbol: "BTC/USDT:USDT", rate: 0.0001, apr: 0.1095}
]
}
Detect funding rate trend direction using linear regression.
Parameters
rates- Time-ordered list of maps with :funding_rate key (exchange_data)
Options
threshold- Min absolute slope for trending (default:0.0)
Returns
:rising | :falling | :stable | nil (atom)
Example
:rising# descripex:contract
%{
opts: %{
threshold: %{
default: 0.0,
type: :float,
description: "Min absolute slope for trending"
}
},
params: %{
rates: %{
description: "Time-ordered list of maps with :funding_rate key",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{type: :atom, description: ":rising | :falling | :stable | nil"},
returns_example: :rising
}
Calculate funding rate volatility (standard deviation).
Parameters
rates- List of maps with :funding_rate key (exchange_data)
Returns
Population std dev of rates, or nil if < 2 points (float)
Example
0.1095# descripex:contract
%{
params: %{
rates: %{
description: "List of maps with :funding_rate key",
source: "fetch_funding_rates(symbol)",
kind: :exchange_data
}
},
returns: %{
type: :float,
description: "Population std dev of rates, or nil if < 2 points"
},
returns_example: 0.1095
}