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

Finite differences between two caller-supplied option greek snapshots.

`diff/2` accepts the same `:chain` and `:as_of` fields used by
`ZenQuant.Options.Snapshot.to_report/2`. A `DateTime` is unambiguous; an
integer `:as_of` is interpreted as Unix seconds. The later snapshot must
follow the earlier snapshot.

Contracts are matched by exact Deribit-format symbol. Delta and gamma use
the canonical `ZenQuant.Options.Chain` units: delta is option-price change
per one-unit underlying-price change, and gamma is delta change per one-unit
underlying-price change. Results contain both the raw change and a change
normalized by elapsed calendar days. No position, open-interest, or notional
weighting is applied.

The output is deliberately labelled `:finite_difference_proxy`, not charm or
color. Exact-symbol matching holds underlying, strike, expiry, and option
type constant, but spot, implied volatility, rates, market quotes, and time
to expiry may all change between observed snapshots. Missing contracts and
expiry rolls are reported separately and never treated as zero-valued greeks.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `diff` | 2 | Compute per-contract delta/gamma finite-difference proxies between snapshots. | `earlier: exchange_data`, `later: exchange_data` |

# `available_difference`

```elixir
@type available_difference() :: %{
  status: :ok,
  from: float(),
  to: float(),
  change: float(),
  normalized_change: float()
}
```

Available finite difference for one greek.

# `contract_change`

```elixir
@type contract_change() :: %{
  symbol: String.t(),
  expiry: Date.t(),
  delta: available_difference() | unavailable_difference(),
  gamma: available_difference() | unavailable_difference()
}
```

One exact-symbol contract comparison.

# `contract_ref`

```elixir
@type contract_ref() :: %{symbol: String.t(), expiry: Date.t()}
```

Contract identity reported for an unmatched snapshot entry.

# `error_reason`

```elixir
@type error_reason() ::
  {:invalid_snapshot, :earlier | :later}
  | {:missing_field, :earlier | :later, :chain | :as_of}
  | {:invalid_field, :earlier | :later, :chain | :as_of}
  | {:invalid_contract, :earlier | :later, term()}
  | :zero_interval
  | :non_chronological_interval
```

Validation failure returned by `diff/2`.

# `result`

```elixir
@type result() :: %{
  label: :finite_difference_proxy,
  horizon: %{
    from: DateTime.t() | integer(),
    to: DateTime.t() | integer(),
    duration: float(),
    units: :seconds
  },
  units: %{source_and_change: map(), normalized_change: map()},
  normalization: %{
    basis: :elapsed_calendar_days,
    elapsed_calendar_days: float(),
    scope: :per_contract
  },
  assumptions: map(),
  contracts: [contract_change()],
  missing_contracts: %{removed: [contract_ref()], added: [contract_ref()]},
  expiry_roll: %{rolled_out: [Date.t()], rolled_in: [Date.t()]}
}
```

Explicit finite-difference proxy result.

# `snapshot`

```elixir
@type snapshot() :: %{
  chain: %{optional(String.t()) =&gt; map()},
  as_of: DateTime.t() | integer()
}
```

Greek snapshot using the Options.Snapshot chain/as_of contract.

# `unavailable_difference`

```elixir
@type unavailable_difference() :: %{
  status: :unavailable,
  from: float() | nil,
  to: float() | nil,
  change: nil,
  normalized_change: nil
}
```

Finite difference unavailable because one or both snapshots lack the greek.

# `diff`

```elixir
@spec diff(snapshot() | term(), snapshot() | term()) ::
  {:ok, result()} | {:error, error_reason()}
```

Returns explicitly labelled per-contract delta/gamma snapshot-change proxies.

---

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