> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polaris.supply/llms.txt
> Use this file to discover all available pages before exploring further.

# Options

> Discover option markets, choose the right Polaris client method, and build a current option-chain snapshot.

Use this guide when you want to inspect an option chain, compare contracts, or
monitor one venue-native instrument. Polaris stores option tickers under a
normalized underlying such as `BTC`, while each event retains its exact contract
identifier.

## Representative coverage

This snapshot comes from the public Catalog at
`2026-08-29T15:40:40.625Z`. Coverage changes as datasets are added or updated,
so use [Catalog](/reference/catalog) before building a request.

| Source ID | Underlying markets | Example markets             | Access  |
| --------- | -----------------: | --------------------------- | ------- |
| `aevo`    |                  4 | `BTC`, `ETH`, `HYPE`, `SOL` | Preview |
| `deribit` |                  2 | `BTC`, `ETH`                | Preview |
| `derive`  |                  9 | `ADA`, `BTC`, `CC`, `ETH`   | Preview |
| `paradex` |                  3 | `BTC`, `ETH`, `HYPE`        | Preview |

Preview access can change by date and account. Read the selected catalog row's
`start`, `end`, and `access` fields before choosing a time range.

## Understand option identity

Options use three identifiers:

| Field        | Meaning                     | Example                                   |
| ------------ | --------------------------- | ----------------------------------------- |
| `source`     | Venue or recorder           | `aevo`                                    |
| `market`     | Normalized underlying       | `BTC`                                     |
| `instrument` | Exact venue-native contract | A contract ID returned by an option event |

Pass the underlying as `market`. Omit `instrument` to read the whole chain, or
pass an exact returned value to isolate one contract. Do not derive a contract
ID from another venue's naming convention.

## Choose a client method

| Task                            | Python                | TypeScript           | Rust                                |
| ------------------------------- | --------------------- | -------------------- | ----------------------------------- |
| Discover underlyings and bounds | `catalog(...)`        | `catalog(...)`       | `catalog(CatalogQuery)`             |
| Read a chain or one contract    | `option_tickers(...)` | `optionTickers(...)` | `option_tickers(OptionTickerQuery)` |
| Subscribe to current updates    | `stream(...)`         | `stream(...)`        | `stream(StreamQuery)`               |
| Read mixed standardized events  | `events(...)`         | `events(...)`        | `events(HistoricalQuery)`           |
| Replay stored events            | `replay(...)`         | `replay(...)`        | `replay(ReplayQuery)`               |
| Inspect venue-native payloads   | `raw(...)`            | Not exposed          | `raw(RawQuery)`                     |

Use `option_tickers` when you need typed ticker fields such as prices, implied
volatility, open interest, or Greeks. Use `events` or `replay` when ticker events
must remain interleaved with other event types. Use `raw` only when you need
venue-specific fields that are absent from the standardized event.

See [Python SDK](/sdks/python), [TypeScript SDK](/sdks/typescript), and
[Rust SDK](/sdks/rust) for complete signatures and return behavior.

## Build a current chain snapshot

Option ticker events are partial updates. A missing field means the venue did
not publish it in that event; it does not mean zero. Build a snapshot by sorting
updates, carrying the last published value forward per instrument, and then
retaining the latest row for each contract.

Install the DataFrame extra first:

```bash theme={null}
pip install "polaris-data[dataframe]"
```

The example derives a bounded range from Catalog, so it stays inside the
selected market's current coverage.

```python theme={null}
from datetime import timedelta

import pandas as pd
from polaris_data import PolarisClient

source = "aevo"
market = "BTC"

with PolarisClient() as client:
    catalog = client.catalog(source=source, market=market)
    coverage = catalog["markets"][0]
    coverage_start = pd.Timestamp(coverage["start"])
    coverage_end = pd.Timestamp(coverage["end"])
    public_cutoff = coverage["access"].get("public_cutoff_date")
    if public_cutoff:
        public_day = pd.Timestamp(public_cutoff, tz="UTC")
        start = max(coverage_start, public_day)
        end = min(coverage_end, start + timedelta(minutes=2))
    else:
        end = coverage_end
        start = max(coverage_start, end - timedelta(minutes=2))

    events = list(
        client.option_tickers(
            source=source,
            market=market,
            from_=start.to_pydatetime(),
            to=end.to_pydatetime(),
            allow_gaps=True,
        )
    )

records = []
for event in events:
    records.append(
        {
            "timestamp": event.get("collector_timestamp", event.get("timestamp")),
            "sequence": event.get("collector_sequence", 0),
            "instrument": event["instrument"],
            **event.get("data", {}),
        }
    )

if not records:
    print("No option ticker updates in the selected interval.")
else:
    updates = pd.DataFrame.from_records(records).sort_values(
        ["instrument", "timestamp", "sequence"]
    )
    ticker_fields = [
        "bid_price",
        "ask_price",
        "mark_price",
        "mark_iv",
        "open_interest",
    ]
    for field in ticker_fields:
        if field not in updates:
            updates[field] = pd.NA

    updates[ticker_fields] = updates.groupby("instrument")[ticker_fields].ffill()
    chain = updates.groupby("instrument", as_index=False).tail(1).copy()

    for field in ticker_fields:
        chain[field] = pd.to_numeric(chain[field], errors="coerce")

    midpoint = (chain["bid_price"] + chain["ask_price"]) / 2
    chain["spread_bps"] = (
        (chain["ask_price"] - chain["bid_price"]) / midpoint * 10_000
    )

    ranked = chain.sort_values(
        ["open_interest", "mark_iv"],
        ascending=[False, False],
        na_position="last",
    )
    print(
        ranked[
            [
                "instrument",
                "bid_price",
                "ask_price",
                "spread_bps",
                "mark_iv",
                "open_interest",
            ]
        ].head(20)
    )
```

The ranking compares the latest reconstructed state observed in the selected
window. It is not an atomic exchange-wide snapshot because contracts can update
at different times.

## Monitor a contract

After you discover an exact `instrument`, use the same identity for historical
and realtime filtering:

```python theme={null}
instrument = ranked.iloc[0]["instrument"]

with PolarisClient() as client:
    with client.stream(
        source=source,
        markets=[market],
        instrument=instrument,
    ) as stream:
        for event in stream:
            print(event)
```

## Related documentation

* [Option tickers](/schemas/option-tickers) for parameters, event fields, units, and multi-language examples
* [Event envelope](/concepts/event-envelope) for timestamps and stored ordering
* [Catalog](/reference/catalog) for current source, market, and access metadata
* [Events](/schemas/events) for mixed standardized event streams
