ZenQuant.Basis (zen_quant v0.2.0)

Copy Markdown View Source

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

FunctionArityDescriptionParam Kinds
arbitrage_opportunity?3Check if basis is at arbitrage-worthy levels.spot_price: value, derivative_price: value, threshold_pct: value
compare1Compare basis across multiple exchanges, sorted by basis descending.exchanges: exchange_data
implied_funding3Calculate implied funding rate from spot-perp basis.spot_price: value, perp_price: value, funding_interval_hours: value
futures_curve2Build futures curve from multiple contracts with basis and annualized yield.spot_price: value, futures: exchange_data
annualized3Calculate annualized basis yield for cash-and-carry trade.spot_price: value, futures_price: value, days_to_expiry: value
spot_perp2Calculate 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.

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_result()

@type basis_result() :: %{absolute: float(), percent: float(), direction: direction()}

Basis calculation result

direction()

@type direction() :: :contango | :backwardation | :flat

Market structure direction relative to spot price

exchange_basis()

@type exchange_basis() :: %{
  exchange: atom() | String.t(),
  basis: number(),
  basis_pct: float(),
  implied_apr: float()
}

Exchange basis comparison result

futures_point()

@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

annualized(spot_price, futures_price, days_to_expiry)

@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
}

arbitrage_opportunity?(spot_price, derivative_price, threshold_pct \\ 0.1)

@spec arbitrage_opportunity?(number(), number(), float()) :: boolean()

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
}

compare(exchanges)

@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}
  ]
}

futures_curve(spot_price, futures)

@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
    }
  ]
}

implied_funding(spot_price, perp_price, funding_interval_hours \\ 8)

@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
}

spot_perp(spot_price, derivative_price)

@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}
}