# `ZenQuant.Options.Surface`
[🔗](https://github.com/ZenHive/zen_quant/blob/v0.8.1/lib/zen_quant/options/surface.ex#L1)

Builds sparse implied-volatility surfaces from pre-fetched option chains.

The input is either the `%{symbol => option_map}` found under `:chain` in the
result of `ZenQuant.Options.Deribit.chain/3`, or a list of
`{symbol, option_map}` tuples. The list form can retain duplicate contract
observations. Each option map supplies IV through `:mark_iv`, or through
`:implied_volatility` when `:mark_iv` is absent. Values are preserved in the
caller/provider's units.

The result contains sorted `:expiries` and `:strikes` axes. `:cells` is a
row-major matrix: rows follow `:expiries`, columns follow `:strikes`, and
unobserved coordinates contain `nil`. No interpolation, smoothing, or repair
is applied.

Every observation at an expiry/strike coordinate has equal weight. This
includes duplicate contracts and call/put pairs; their positive IV values
are combined with an arithmetic mean. Invalid or non-positive observations
fail the whole build instead of being omitted.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `build` | 1 | Build a sparse implied-volatility surface from a pre-fetched option chain. | `chain: exchange_data` |

# `chain`

```elixir
@type chain() :: %{optional(String.t()) =&gt; map()} | [{String.t(), map()}]
```

A pre-fetched chain map or tuple list; lists may contain duplicate symbols.

# `error_reason`

```elixir
@type error_reason() ::
  {:invalid_input, :chain}
  | {:invalid_observation, term()}
  | {:invalid_symbol, term()}
  | {:invalid_implied_volatility, String.t(), term()}
```

Surface input validation failure.

# `surface`

```elixir
@type surface() :: %{
  expiries: [Date.t()],
  strikes: [float()],
  cells: [[float() | nil]]
}
```

A sparse surface with sorted axes and explicit nil cells.

# `build`

```elixir
@spec build(chain() | term()) :: {:ok, surface()} | {:error, error_reason()}
```

Builds a surface without inventing values for unobserved coordinates.

---

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