# `ZenQuant.Basis`
[🔗](https://github.com/ZenHive/zen_quant/blob/v0.8.1/lib/zen_quant/basis.ex#L1)

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` | 3 | 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` |

# `basis_result`

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

Basis calculation result

# `direction`

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

Market structure direction relative to spot price

# `exchange_basis`

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

Exchange basis comparison result

# `futures_point`

```elixir
@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

# `annualized`

```elixir
@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

```elixir
0.1095
```

```elixir
# 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?`

```elixir
@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

```elixir
true
```

```elixir
# descripex:contract
%{
  params: %{
    threshold_pct: %{
      default: 0.1,
      description: "Minimum basis percentage to consider",
      kind: :value
    },
    spot_price: %{description: "Current spot price", kind: :value},
    derivative_price: %{description: "Perpetual or futures price", kind: :value}
  },
  returns: %{type: :boolean, description: "true if absolute basis >= threshold"},
  returns_example: true
}
```

# `compare`

```elixir
@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

```elixir
[%{exchange: "binance", basis: 120.0, basis_pct: 0.14, implied_apr: 51.1}]
```

```elixir
# 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`

```elixir
@spec futures_curve(number(), [map()], keyword()) :: [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)

## Options

  * `as_of` - Reference date for days_to_expiry; named :as_of because it is a Date rather than a timestamp. Defaults to the system clock; supply a fixed Date for reproducible output (default: `"Date.utc_today()"`)

## Returns

List of maps sorted by expiry with :basis, :basis_pct, :annualized added (`list`)

### Example

```elixir
[
  %{
    price: 84500.0,
    expiry: ~D[2026-03-28],
    basis: 1000.0,
    days_to_expiry: 30,
    basis_pct: 1.2,
    annualized: 0.146
  }
]
```

```elixir
# descripex:contract
%{
  opts: %{
    as_of: %{
      default: "Date.utc_today()",
      type: :date,
      description: "Reference date for days_to_expiry; named :as_of because it is a Date rather than a timestamp. Defaults to the system clock; supply a fixed Date for reproducible output"
    }
  },
  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],
      basis: 1000.0,
      days_to_expiry: 30,
      basis_pct: 1.2,
      annualized: 0.146
    }
  ]
}
```

# `implied_funding`

```elixir
@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

```elixir
0.1095
```

```elixir
# 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`

```elixir
@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

```elixir
%{absolute: 100.0, direction: :contango, percent: 0.2}
```

```elixir
# 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}
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
