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

Summarize pre-fetched option block-trade records.

Pure aggregation only — no network calls and no interpretation of size as
informed or "smart-money" intent. Aggressor side is preserved when supplied
and left unknown when absent; it is never inferred from trade size.

## Trade contract

Each trade is a plain map. Identity fields may come from a Deribit-style
`:symbol`, or from explicit `:expiry`, `:strike`, and `:option_side`.

| Field | Required | Notes |
| --- | --- | --- |
| `:symbol` | no* | Deribit option symbol; used when expiry/strike/side omitted |
| `:expiry` | no* | `Date.t()` |
| `:strike` | no* | Numeric strike |
| `:option_side` | no* | `:call` or `:put` (aliases: `:type`, `"C"`/`"P"`) |
| `:aggressor_side` | no | `:buy`, `:sell`, or `:unknown` / missing |
| `:premium` | no | Premium in caller units |
| `:notional` | no | Notional in caller units |
| `:amount` | no | Size in contracts (or caller units); aliases `:size`, `:quantity` |

\* At least one of symbol or the explicit expiry/strike/option_side triple
must resolve for grouping dimensions.

Threshold, currency, and units are caller-supplied and echoed in output
metadata. Trades at or above the threshold enter the summary; below-threshold
trades are counted but not grouped. Premium and notional concentration are
measured across the included trades.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `summarize` | 2 | Summarize pre-fetched option block trades by structure and premium/notional. | `trades: exchange_data` |

# `concentration_metrics`

```elixir
@type concentration_metrics() :: %{max: float(), hhi: float(), top3: float()} | nil
```

Concentration metrics over included-trade premium or notional weights

# `error_reason`

```elixir
@type error_reason() ::
  :invalid_trades
  | :invalid_opts
  | :missing_threshold
  | :invalid_threshold
  | :invalid_threshold_field
  | {:invalid_trade, non_neg_integer(), term()}
```

Validation failure

# `group_row`

```elixir
@type group_row() :: %{
  key: term(),
  trade_count: non_neg_integer(),
  premium: float(),
  notional: float(),
  amount: float(),
  share_premium: float() | nil,
  share_notional: float() | nil
}
```

One grouping bucket with counts and summed premium/notional/amount

# `metadata`

```elixir
@type metadata() :: %{
  threshold: float(),
  threshold_field: :notional | :premium | :amount,
  currency: atom() | String.t() | nil,
  units: atom() | String.t() | nil,
  trade_count: non_neg_integer(),
  included_count: non_neg_integer(),
  excluded_count: non_neg_integer(),
  total_premium: float(),
  total_notional: float(),
  total_amount: float(),
  concentration_basis: :included_trades
}
```

Caller-supplied threshold, currency, and unit metadata

# `summary`

```elixir
@type summary() :: %{
  metadata: metadata(),
  by_expiry: [group_row()],
  by_strike: [group_row()],
  by_option_side: [group_row()],
  by_aggressor_side: [group_row()],
  concentration: %{
    premium: concentration_metrics(),
    notional: concentration_metrics()
  }
}
```

Block-trade summary

# `trade`

```elixir
@type trade() :: %{
  optional(:symbol) =&gt; String.t(),
  optional(:expiry) =&gt; Date.t() | nil,
  optional(:strike) =&gt; float() | nil,
  optional(:option_side) =&gt; :call | :put,
  optional(:aggressor_side) =&gt; :buy | :sell | :unknown,
  optional(:premium) =&gt; float() | nil,
  optional(:notional) =&gt; float() | nil,
  optional(:amount) =&gt; float() | nil
}
```

Normalized pre-fetched block-trade row after validation

# `summarize`

```elixir
@spec summarize(
  [map()] | term(),
  keyword()
) :: {:ok, summary()} | {:error, error_reason()}
```

Aggregate pre-fetched block trades without network I/O or intent labeling.

---

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