# Python SDK

`fathom-charts` handles authentication, the initial snapshot, resuming after a disconnect, duplicates, history pagination, exports and rate-limit retries for you, on asyncio. This page describes every option and method; the behaviour the four SDKs share is in [SDKs](https://fathomcharts.com/docs/sdk.md).

## Install

```bash title="terminal"
pip install fathom-charts
```

Or `uv add fathom-charts`. **Python 3.11 or later**, asyncio only: in a notebook (Jupyter), `await` works at the top level of a cell. Its dependencies are `websockets`, `httpx` and `zstandard`. The package is typed (`py.typed`): `mypy --strict` checks your code against it.

### Indicator types

```bash title="terminal"
fathom-charts-types https://api.fathomcharts.com/v1/indicators fathom_charts_types.py
```

The command, installed with the package, takes the catalog’s URL or a file saved from `GET /v1/indicators`. Each indicator gets `<Name>Params` and `<Name>Data` (`TypedDict`), named after its id (`big-trades` → `BigTradesParams`, `BigTradesData`), with the catalog’s descriptions as docstrings; `IndicatorParams`, `IndicatorData` and `IndicatorId` cover them all. A tuple whose last elements are optional is the union of its allowed lengths (`tuple[int, int, int] | tuple[int, int, int, int]` for a footprint level); JSON arrays still arrive as lists. Generate the file again when the catalog changes.

## Real time: FathomChartsStream

```python title="big_trades.py"
import asyncio
import os

from fathom_charts import FathomChartsStream, Subscription, Upsert
from fathom_charts_types import BigTradesData


async def main() -> None:
    async with FathomChartsStream(url="wss://stream.fathomcharts.com/v1", api_key=os.environ["FATHOM_CHARTS_API_KEY"]) as stream:
        big_trades: Subscription[BigTradesData]
        async with stream.subscribe(instrument="NQ.front", indicator="big-trades", params={"minimum": 30}, mode="confirmed") as big_trades:
            async for event in big_trades:
                if isinstance(event, Upsert):
                    print(event.cursor, event.data["side"], event.data["volume"])


asyncio.run(main())
```

### Options

Durations are in seconds.

| Option              | Purpose                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`               | Required. The stream address: `wss://stream.fathomcharts.com/v1`.                                                                                                                                                                                                                                                                                                                                           |
| `api_key`           | API key, sent in the `Authorization: Bearer` header.                                                                                                                                                                                                                                                                                                                                                        |
| `stream_token`      | Instead of `api_key`, in an application distributed to end users, which must not hold the key: an async function returning a fresh single-use token that your backend gets from `POST /v1/stream-tokens` (`FathomChartsRest.stream_token()`). It is called on every connection, reconnections included. Give exactly one of `api_key` and `stream_token`, otherwise the constructor raises `CONFIGURATION`. |
| `web_socket`        | Optional: a function that opens the connection, to use another socket; a socket it returns with `pausable` false takes the `max_buffered` path below.                                                                                                                                                                                                                                                       |
| `reconnect`         | Optional: `Reconnect(initial_delay=0.25, max_delay=30.0, quota_retry=120.0, quota_delay=5.0)`: first reconnection delay, backoff ceiling, how long a `4029` close code is retried, minimum delay between two attempts after a `4029`.                                                                                                                                                                       |
| `heartbeat_timeout` | Silence after which the connection is considered lost, then reopened. 15 by default: the server sends an `hb` every 5 seconds.                                                                                                                                                                                                                                                                              |
| `high_water_mark`   | Number of events waiting to be read, all subscriptions together, above which the SDK stops reading the socket and keeps only the latest version of each object in progress. While it waits, it sends `ping` messages so that the server keeps the connection. 10,000 by default.                                                                                                                            |
| `max_buffered`      | For a socket that cannot be paused: number of waiting events above which the SDK closes the connection (close code `4000`, reason `client buffer full`), then reopens it from the last cursor received once half of `high_water_mark` has been read. Nothing waiting is lost. 4 × `high_water_mark` by default.                                                                                             |
| `random`            | Optional, for tests: the source of the reconnection jitter, a function returning a number in \[0, 1). `random.random` by default.                                                                                                                                                                                                                                                                           |
| `on_state_change`   | Called on every change of `connection_state`.                                                                                                                                                                                                                                                                                                                                                               |
| `on_notice`         | Called with each `notice` from the server (`kind`, `id`, `effective_at`, `ends_at`, `message`): a restart, a maintenance window announced or cancelled. See [Updates and maintenance](https://fathomcharts.com/docs/maintenance.md).                                                                                                                                                                        |

`async with FathomChartsStream(…) as stream` closes the stream at the end of the block; `await stream.close()` does the same. `stream.connection_state` is `idle`, `connecting`, `open`, `reconnecting` or `closed`; `stream.maintenance` lists the maintenance windows announced on the connection and not over, by start.

### subscribe()

`stream.subscribe(instrument=…, indicator=…, params=None, mode="live", from_="live")` opens a subscription and returns a `Subscription`, to read with `async for`. Its arguments are those of the [`subscribe`](https://fathomcharts.com/docs/websocket.md#subscribing) message; `from_` (`from` is a Python keyword) is `"live"`, `{"cursor": …, "engine": …}` or `{"time": …}`. The SDK picks the `sub` name itself. Annotate the result with the indicator’s data type: `big_trades: Subscription[BigTradesData]`.

The events are frozen dataclasses: `Subscribed`, `Snapshot` (its `items` are `Item`), `Upsert`, `Remove`, `Status`, `Reset` and `Notice`, each with its `type` (`"upsert"`…), to test with `isinstance` or `match`. Their fields are those of [Events](https://fathomcharts.com/docs/sdk.md#events), in snake case (`tick_size_nanos`, `lag_ms`). An `error` on the subscription ends the loop: `async for` raises a `FathomChartsError` carrying the received `code`.

### Subscription

| Member    | Purpose                                                                                                                                                                         |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cursor`  | The cursor of the last event handed to you: your resume point. `None` until a cursor has arrived. It also moves forward with `hb` messages when no event is waiting to be read. |
| `topic`   | The `topic` of the latest `subscribed`.                                                                                                                                         |
| `engine`  | The `engine` of the latest `subscribed`: the computation your state comes from. Save it with `cursor` to resume later.                                                          |
| `id`      | The `sub` name the SDK chose for this subscription (`s1`, `s2`…).                                                                                                               |
| `request` | The arguments of `subscribe()`, completed with the default values.                                                                                                              |
| `close()` | Async: stops the subscription; the SDK sends `unsubscribe`, and a pending loop ends.                                                                                            |

Leaving the `async with` block of a subscription closes it. Leaving an `async for` loop alone does not, in Python: use `async with`, or `await sub.close()`. Reconnection, resuming and close codes: see [Connection lifecycle](https://fathomcharts.com/docs/sdk.md#connection-lifecycle); to resume after your process restarts, see [Resume after a restart](https://fathomcharts.com/docs/sdk.md#resume-after-a-restart).

## History: FathomChartsRest

```python
from fathom_charts import FathomChartsRest

async with FathomChartsRest(base_url="https://api.fathomcharts.com", api_key=os.environ["FATHOM_CHARTS_API_KEY"]) as rest:
    usage = await rest.usage()
```

| Option               | Purpose                                                                                                                                                                                        |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `base_url`           | Required. The API address: `https://api.fathomcharts.com`.                                                                                                                                     |
| `api_key`            | Required. API key, sent in the `Authorization: Bearer` header.                                                                                                                                 |
| `rate_limit_retries` | Number of retries of a request the server did not process, or of a read that failed (see [Rate limit and retries](https://fathomcharts.com/docs/sdk.md#rate-limit-and-retries)). 3 by default. |
| `http_client`        | Optional: your own `httpx.AsyncClient` (proxy, timeouts…).                                                                                                                                     |

### Methods

Queries are dictionaries with the fields of the API, as on the [History](https://fathomcharts.com/docs/history.md#make-a-query) page (`instrument`, `indicator`, `params`, `from`, `to`, `mode`, and depending on the method `limit`, `cursor`, `untilCursor`, `snapshot`): `PageQuery` and `RangeQuery` type them. Responses are `TypedDict`s with the fields of the API.

| Method                                                  | Returns                                                                                                                                                                                                                          |
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `await page(query)`                                     | One page: `instrument`, `tickSizeNanos`, `snapshot`, `items`, `next`, `computeUnits`.                                                                                                                                            |
| `pages(query)`                                          | The pages one at a time, following `next`, as you read them (`async for`).                                                                                                                                                       |
| `history(query)`                                        | The results one at a time, across pages. With `"snapshot": True`, the first element is the state at `from`, `{"t": "snapshot", "cursor", "items"}`, then come the updates (`t` is `upsert` or `remove`, each with its `cursor`). |
| `await estimate(query)`                                 | The cost [estimate](https://fathomcharts.com/docs/history.md#estimate-the-cost), charging nothing.                                                                                                                               |
| `await export(query, resume=None)`                      | One [export](https://fathomcharts.com/docs/exports.md) response: `body` (the zstd-compressed file as is), `content_type`, `filename`, `token` (resumes the export with `resume`).                                                |
| `export_ndjson(query, max_resumes=5, resume_delay=1.0)` | The decompressed export, in chunks (`bytes`) of whole lines, with automatic resume. See [Exports](https://fathomcharts.com/docs/exports.md#with-the-sdk).                                                                        |
| `await stream_token()`                                  | A stream token: `token`, `expiresAt`.                                                                                                                                                                                            |
| `await usage()`                                         | Your [usage](https://fathomcharts.com/docs/limits.md#track-your-usage) this month and your limits.                                                                                                                               |
| `await instruments()`                                   | `instruments`: every [instrument](https://fathomcharts.com/docs.md#instruments) served.                                                                                                                                          |
| `await status()`                                        | The state of the computation, of each instrument, and the maintenance windows announced.                                                                                                                                         |
| `await offers()`                                        | The plans and the limits of each combination.                                                                                                                                                                                    |
| `await indicators()`                                    | The indicator catalog.                                                                                                                                                                                                           |

A call is stopped by cancelling its task (`asyncio.timeout()`, `Task.cancel()`), a wait for a retry included: the `asyncio.CancelledError` goes through untouched, as asyncio requires. Close an async generator you leave early (`contextlib.aclosing`), as with any async generator.

## Errors

Every error the SDK raises, or ends a loop with, is a `FathomChartsError`. Its codes are listed in [Errors](https://fathomcharts.com/docs/sdk.md#errors) (`ERROR_CODES`; the WebSocket close codes, `CloseCode`).

| Field         | Content                                                                                                              |
| ------------- | -------------------------------------------------------------------------------------------------------------------- |
| `code`        | The stable error code.                                                                                               |
| `message`     | A human-readable explanation.                                                                                        |
| `status`      | The HTTP status, over REST.                                                                                          |
| `close_code`  | The WebSocket close code, when the connection was closed.                                                            |
| `retry_after` | The value of the `Retry-After` header, in seconds, when the response has one.                                        |
| `problem`     | The full `problem+json` document of the response, with `errors` when the API lists invalid fields; `None` otherwise. |
| `__cause__`   | The original error, when there is one (the error of httpx…).                                                         |

## Cursors

```python
from fathom_charts import compare_cursors

compare_cursors("20720.111416.0", "20720.111417.0")  # < 0
```
