diff --git a/README.md b/README.md index edbcb9b..1f42fc7 100644 --- a/README.md +++ b/README.md @@ -6,35 +6,37 @@ [![Code Style](https://img.shields.io/badge/code_style-black-black)](https://black.readthedocs.io/en/stable/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -Official Python SDK for the [DataMaxi+ API](https://docs.datamaxiplus.com/). -Fetch both historical and latest market data across centralized exchanges (OHLCV -candles, tickers, trading fees, wallet status, announcements), perpetual funding -rates, cross-exchange price premiums for arbitrage, forex rates, Telegram channel -data, and Naver search trends. +Official Python SDK for the [DataMaxi+ API](https://docs.datamaxiplus.com/) — +one library for both **historical** and **real-time** crypto market data. -This package is compatible with Python v3.10+. +- **REST** — OHLCV candles, tickers, trading fees, wallet status, announcements + and token updates across centralized exchanges; perpetual funding rates, + liquidations, open interest, margin-borrow rates; cross-exchange price + premiums for arbitrage; index prices and forex rates; Telegram channel data + and Naver search trends. +- **WebSocket** — stream tickers, forex, premiums, funding rates, open interest, + liquidations and listing announcements as they happen. +- **Sync or async** — a synchronous client, a coroutine-based async twin, and an + async WebSocket client, all sharing the same resource tree and arguments. + +Compatible with Python v3.10+. ## Table of Contents - [Installation](#installation) -- [Configuration](#configuration) +- [Authentication](#authentication) - [Quickstart](#quickstart) -- [Async Client](#async-client) -- [API Reference](#api-reference) - - [CEX Candle Data](#cex-candle-data) - - [CEX Ticker Data](#cex-ticker-data) - - [CEX Trading Fees](#cex-trading-fees) - - [CEX Wallet Status](#cex-wallet-status) - - [CEX Announcements](#cex-announcements) - - [CEX Token Updates](#cex-token-updates) - - [Funding Rate](#funding-rate) - - [Premium](#premium) - - [Forex](#forex) - - [Telegram](#telegram) - - [Naver Trend](#naver-trend) +- [Clients](#clients) +- [REST API Reference](#rest-api-reference) + - [CEX](#cex) — [Candle](#cex-candle-data), [Ticker](#cex-ticker-data), [Fees](#cex-trading-fees), [Wallet Status](#cex-wallet-status), [Announcements](#cex-announcements), [Token Updates](#cex-token-updates), [Symbol](#cex-symbol) + - [Derivatives and Leverage](#derivatives-and-leverage) — [Funding Rate](#funding-rate), [Liquidation](#liquidation), [Open Interest](#open-interest), [Margin Borrow](#margin-borrow) + - [Pricing and Cross-Exchange](#pricing-and-cross-exchange) — [Premium](#premium), [Index Price](#index-price), [Forex](#forex) + - [Alternative Data](#alternative-data) — [Telegram](#telegram), [Naver Trend](#naver-trend) +- [WebSockets](#websockets) - [Response Types](#response-types) - [Pagination](#pagination) - [Error Handling](#error-handling) +- [Async Client](#async-client) - [Local Development](#local-development) - [Tests](#tests) - [Links](#links) @@ -47,44 +49,53 @@ This package is compatible with Python v3.10+. pip install datamaxi ``` -## Configuration +The SDK is lightweight by default (`requests` + `pandas`). Two features live +behind optional extras so you only install what you use: + +| Extra | Installs | Enables | +| ------------------------------- | ------------ | ------------------------------------------------ | +| `pip install "datamaxi[async]"` | `httpx` | The async client, [`AsyncDatamaxi`](#async-client). | +| `pip install "datamaxi[ws]"` | `websockets` | The async WebSocket client, [`AsyncDatamaxiWS`](#websockets). | + +Combine them in one shot: `pip install "datamaxi[async,ws]"`. + +## Authentication + +DataMaxi+ endpoints are protected by an API key. Get one by registering at +https://datamaxiplus.com/auth. + +Set it once via the `DATAMAXI_API_KEY` environment variable (recommended, so the +key stays out of source code) and every client picks it up automatically: + +```shell +export DATAMAXI_API_KEY="your_api_key" +``` -Private API endpoints are protected by an API key. -You can get the API key upon registering at https://datamaxiplus.com/auth. +Or pass it explicitly to any client: `Datamaxi(api_key="your_api_key")`. + +Every client accepts the following options: | Option | Explanation | |--------------------|---------------------------------------------------------------------------------------| -| `api_key` | Your API key | -| `base_url` | If `base_url` is not provided, it defaults to `https://api.datamaxiplus.com`. | -| `timeout` | Number of seconds to wait for a server response. By default requests do not time out. | -| `proxies` | Proxy through which the request is queried | -| `show_limit_usage` | Return response as dictionary including `"limit_usage"` and `"data"` keys | -| `show_header` | Return response as dictionary including `"header"` and `"data"` keys | +| `api_key` | Your API key. Falls back to `DATAMAXI_API_KEY` when omitted. | +| `base_url` | API base URL. Defaults to `https://api.datamaxiplus.com`. | +| `timeout` | Seconds to wait for a server response. By default requests do not time out. | +| `proxies` | Proxy through which the request is routed. | +| `show_limit_usage` | *(Deprecated)* Return a dict with `"limit_usage"` and `"data"` keys. See [Response Types](#response-types). | +| `show_header` | *(Deprecated)* Return a dict with `"header"` and `"data"` keys. See [Response Types](#response-types). | ### Environment Variables -You may use environment variables to configure the SDK to avoid any inline boilerplate. - | Env | Description | | ------------------ | -------------------------------------------- | | `DATAMAXI_API_KEY` | Used instead of `api_key` if none is passed. | ## Quickstart -DataMaxi+ Python package includes the following clients: - -- `Datamaxi` - Main (synchronous) client for crypto trading data (CEX, funding rates, premium, forex) -- `Telegram` - Client for Telegram channel data -- `Naver` - Client for Naver trend data - -An asynchronous variant, `AsyncDatamaxi`, is covered in [Async Client](#async-client). - -Set your API key via the `DATAMAXI_API_KEY` environment variable (recommended, so -the key stays out of source code): - -```shell -export DATAMAXI_API_KEY="your_api_key" -``` +Set `DATAMAXI_API_KEY` (see [Authentication](#authentication)), then pick the +style that fits your app — synchronous, `asyncio`, or streaming over WebSocket. +All three share the same resource tree, so an endpoint you learn in one works in +the others.
Sync @@ -102,16 +113,12 @@ df = maxi.cex.candle( exchange="binance", symbol="BTC-USDT", interval="1d", - market="spot" + market="spot", ) print(df.head()) # Fetch ticker data -ticker = maxi.cex.ticker.get( - exchange="binance", - symbol="BTC-USDT", - market="spot" -) +ticker = maxi.cex.ticker.get(exchange="binance", symbol="BTC-USDT", market="spot") print(ticker) # Fetch premium data @@ -132,24 +139,16 @@ async def main(): # Reads DATAMAXI_API_KEY from the environment automatically. # Alternatively, pass api_key="your_api_key" explicitly. async with AsyncDatamaxi() as client: - # Fetch CEX candle data (returns pandas DataFrame) df = await client.cex.candle( - exchange="binance", - symbol="BTC-USDT", - interval="1d", - market="spot", + exchange="binance", symbol="BTC-USDT", interval="1d", market="spot" ) print(df.head()) - # Fetch ticker data ticker = await client.cex.ticker.get( - exchange="binance", - symbol="BTC-USDT", - market="spot", + exchange="binance", symbol="BTC-USDT", market="spot" ) print(ticker) - # Fetch premium data premium = await client.premium(asset="BTC") print(premium.head()) @@ -159,52 +158,62 @@ asyncio.run(main())
-## Async Client +
WebSocket -`AsyncDatamaxi` is the asynchronous counterpart to `Datamaxi` (built on -[httpx](https://www.python-httpx.org/)). It mirrors the same resource tree and -arguments, with one rule: every method is a coroutine and must be `await`ed. -Install the async extra: - -```shell -pip install "datamaxi[async]" -``` +Requires the `ws` extra (`pip install "datamaxi[ws]"`). Streaming is +async-only — see [WebSockets](#websockets) for the full channel list. ```python import asyncio -from datamaxi.aio import AsyncDatamaxi +from datamaxi.aio.ws import AsyncDatamaxiWS async def main(): - # Reads DATAMAXI_API_KEY from the environment, or pass api_key=... explicitly. - async with AsyncDatamaxi() as client: - df = await client.cex.candle( - exchange="binance", symbol="BTC-USDT", interval="1d", market="spot" - ) - print(df.head()) + # Reads DATAMAXI_API_KEY from the environment automatically. + async with AsyncDatamaxiWS() as ws: + # subscribe() returns an async iterator over live messages + async for msg in await ws.ticker.subscribe("BTC-USDT@binance", market="spot"): + print(msg["s"], msg.get("p")) # symbol, price asyncio.run(main()) ``` -Use `AsyncDatamaxi` as an async context manager (shown above) or call -`await client.aclose()` yourself. Paginated endpoints return an async -`next_request` — `await` it too -(`data, next_request = await client.cex.announcement(...)`). Telegram and Naver -have standalone `AsyncTelegram` / `AsyncNaver` clients. +
+ +## Clients + +The package ships these clients, all configured the same way (see +[Authentication](#authentication)): -Every endpoint in the [API Reference](#api-reference) works the same under the -async client — see the [docs](https://datamaxi.readthedocs.io/) where each -example has a Sync/Async tab. +| Client | Import | Purpose | +| ----------------- | --------------------------------------------- | ---------------------------------------------------------- | +| `Datamaxi` | `from datamaxi import Datamaxi` | Synchronous client for all crypto REST data. | +| `Telegram` | `from datamaxi import Telegram` | Telegram channel messages and metadata. | +| `Naver` | `from datamaxi import Naver` | Naver search-trend data (South Korea). | +| `AsyncDatamaxi` | `from datamaxi.aio import AsyncDatamaxi` | Async twin of `Datamaxi` (needs the `[async]` extra). | +| `AsyncDatamaxiWS` | `from datamaxi.aio.ws import AsyncDatamaxiWS` | Async WebSocket streaming (needs the `[ws]` extra). | -## API Reference +`AsyncTelegram` and `AsyncNaver` are the async twins of `Telegram` / `Naver`, +also imported from `datamaxi.aio`. + +## REST API Reference > **Discovery helpers.** Most endpoints expose helpers to list valid argument > values before you fetch — commonly `.exchanges()`, `.symbols(exchange=...)`, and > (for candles) `.intervals()`. Use them to discover supported exchanges, trading > pairs, and intervals. The examples below show them per endpoint. -### CEX Candle Data +All examples use the sync `Datamaxi` client. Every endpoint works identically on +the [async client](#async-client) — just `await` the call. Each endpoint also has +a dedicated page with a Sync/Async tab in the +[docs](https://datamaxi.readthedocs.io/). + +### CEX + +Data from centralized exchanges: prices, fees, wallet status, and listings. + +#### CEX Candle Data Fetch historical candlestick (OHLCV) data from centralized exchanges. @@ -231,7 +240,7 @@ df = maxi.cex.candle( ) ``` -### CEX Ticker Data +#### CEX Ticker Data Fetch real-time ticker data from centralized exchanges. @@ -253,7 +262,7 @@ ticker = maxi.cex.ticker.get( ) ``` -### CEX Trading Fees +#### CEX Trading Fees Fetch trading fee information from centralized exchanges. @@ -271,7 +280,7 @@ fees = maxi.cex.fee( ) ``` -### CEX Wallet Status +#### CEX Wallet Status Fetch deposit/withdrawal status for assets on centralized exchanges. @@ -290,7 +299,7 @@ status = maxi.cex.wallet_status( ) ``` -### CEX Announcements +#### CEX Announcements Fetch exchange announcements (listings, delistings, etc.). @@ -309,7 +318,7 @@ data, next_request = maxi.cex.announcement( data2, next_request2 = next_request() ``` -### CEX Token Updates +#### CEX Token Updates Fetch token listing/delisting updates. @@ -322,7 +331,27 @@ data, next_request = maxi.cex.token.updates( ) ``` -### Funding Rate +#### CEX Symbol + +Per-base / per-symbol CEX metadata and aggregates: trading status, tags, +cautions, delistings, volume, open interest, and liquidation. + +```python +metadata = maxi.cex.symbol.metadata(exchange="binance", base="BTC") +tags = maxi.cex.symbol.tags(exchange="binance", base="BTC") +cautions = maxi.cex.symbol.cautions(exchange="binance") +delistings = maxi.cex.symbol.delistings(exchange="binance") +volume = maxi.cex.symbol.volume(base="BTC") +oi = maxi.cex.symbol.oi(base="BTC", exchange="binance") +oi_stats = maxi.cex.symbol.oi_stats(base="BTC", exchange="binance", currency="USD") +liquidation = maxi.cex.symbol.liquidation(base="BTC", window="24h") +``` + +### Derivatives and Leverage + +Perpetual funding, liquidations, open interest, and margin-borrow rates. + +#### Funding Rate Fetch funding rate data for perpetual futures. @@ -355,7 +384,48 @@ df = maxi.funding_rate.latest( ) ``` -### Premium +#### Liquidation + +CEX futures liquidation data: recent events, a firehose feed, heatmaps, maps, +and bucketed history. + +```python +events = maxi.liquidation(exchange="binance", symbol="BTC-USDT", limit=100) +feed = maxi.liquidation.feed(limit=100) # most recent across all symbols +heatmap = maxi.liquidation.heatmap(window="1h", topN=10) # window: 1h/4h/24h, topN 1-30 +stats = maxi.liquidation.stats(window="1h") +liq_map = maxi.liquidation.map(base="BTC", exchange="binance", quote="USDT") +history = maxi.liquidation.symbol_history( + symbol="BTC", quote="USDT", exchange="binance", interval="5m", window="24h" +) +``` + +#### Open Interest + +CEX futures open interest: latest snapshots, reporting pairs, the token × +exchange matrix, top-line aggregates, and aggregated history. + +```python +snapshot = maxi.open_interest(exchange="binance", symbol="BTC-USDT") +pairs = maxi.open_interest.list(exchange="binance") +overview = maxi.open_interest.overview(page=1, limit=20, key="binance", sort="desc") +summary = maxi.open_interest.summary(topN=10) +history = maxi.open_interest.history_aggregated(token_id="bitcoin", interval="1h") +``` + +#### Margin Borrow + +Margin-borrow data for a single asset. + +```python +data = maxi.margin_borrow(asset="BTC") +``` + +### Pricing and Cross-Exchange + +Cross-exchange premiums, index prices, and forex rates. + +#### Premium Fetch cross-exchange price premium data for arbitrage analysis. @@ -388,7 +458,20 @@ volume bounds (`min/max_sv`, `min/max_tv`), funding-rate bounds, `only_transfera `network`, and more. See the [premium endpoint docs](https://docs.datamaxiplus.com/) for the full list. -### Forex +#### Index Price + +Historical index-price time series for a single asset. + +```python +data = maxi.index_price( + asset="BTC", + from_="now - 1 month", # from_ has a trailing underscore (from is a keyword); + to="now", # the wire-level query param is still "from" + interval="5m", +) +``` + +#### Forex Fetch forex exchange rate data. @@ -403,7 +486,11 @@ df = maxi.forex( ) ``` -### Telegram +### Alternative Data + +Off-exchange signals: Telegram channels and Naver search trends. + +#### Telegram Fetch Telegram channel messages and metadata. @@ -432,7 +519,7 @@ data, next_request = telegram.messages( ) ``` -### Naver Trend +#### Naver Trend Fetch Naver search trend data (South Korea). @@ -451,6 +538,131 @@ data = naver.trend( ) ``` +## WebSockets + +Stream real-time market data over the DataMaxi+ WebSocket API. The WebSocket +client is **async-only** and lives behind the `ws` extra: + +```shell +pip install "datamaxi[ws]" +``` + +```python +import asyncio +from datamaxi.aio.ws import AsyncDatamaxiWS + + +async def main(): + # Reads DATAMAXI_API_KEY from the environment, or pass api_key=... explicitly. + async with AsyncDatamaxiWS() as ws: + stream = await ws.ticker.subscribe("BTC-USDT@binance", market="spot") + async for msg in stream: + print(msg["s"], msg.get("p")) # symbol, price + + +asyncio.run(main()) +``` + +### Channels + +Each accessor on `AsyncDatamaxiWS` maps to one channel. `subscribe(*params)` is a +coroutine returning an **async iterator** over live messages; `stream()` (for the +param-less firehose feeds) does the same. Pass the raw param strings shown below — +you can also read the expected shape at runtime via `ws..param_format`. + +| Accessor | Call | Param format | Plan | +| --------------------------- | ------------------------------------------- | --------------------------------------------------- | ----- | +| `ws.ticker` | `subscribe(*p, market="spot"\|"futures")` | `SYMBOL@exchange[@currency@conversionBase]` | Basic | +| `ws.forex` | `subscribe(*p)` | `SYMBOL` | Basic | +| `ws.premium` | `subscribe(*p)` | `src:tgt:tokenId:srcQuote:tgtQuote:srcMkt:tgtMkt` | Basic | +| `ws.funding_rate` | `subscribe(*p)` | `SYMBOL@exchange` | Basic | +| `ws.open_interest` | `subscribe(*p)` | `SYMBOL@exchange` | Basic | +| `ws.liquidation` | `subscribe(*p)` | `SYMBOL@exchange` | Basic | +| `ws.liquidation_feed` | `stream()` | — (firehose, no params) | Basic | +| `ws.announcement` | `subscribe()` | — (no params) | Pro+ | +| `ws.announcement_internal` | `subscribe()` | — (no params) | Pro+ | + +### Multiplexing and filtering + +One connection is opened per channel and multiplexes every param you subscribe +to. Because the protocol tags messages by payload fields (not by a subscription +id), `subscribe()` yields **every** message on the channel — filter client-side +by symbol (`msg["s"]`) when you subscribe to more than one: + +```python +stream = await ws.ticker.subscribe( + "BTC-USDT@binance", "ETH-USDT@binance", market="spot" +) +async for msg in stream: + if msg["s"] == "BTC-USDT": + handle_btc(msg) +``` + +Add or drop params on the fly: + +```python +await ws.ticker.subscribe("SOL-USDT@binance", market="spot") # add +await ws.ticker.unsubscribe("SOL-USDT@binance", market="spot") # remove +``` + +> Not every channel supports removing an individual param server-side — +> `liquidation` and `open_interest` are subscribe-only. Closing the client (see +> [Lifecycle](#lifecycle)) always stops all streams. + +### Firehose feeds + +`ws.liquidation_feed` needs no subscription — call `stream()` and consume: + +```python +async for evt in await ws.liquidation_feed.stream(): + print(evt["s"], evt.get("sd"), evt.get("p")) # symbol, side, price +``` + +### Reconnect and keepalive + +The client is resilient by default: + +- **Auto-reconnect** — if the connection drops it reconnects and replays your + active subscriptions, so your `async for` loop resumes without extra code. + Disable with `AsyncDatamaxiWS(reconnect=False)`. +- **Keepalive** — an app-level `PING` is sent every 30s to stay under the + server's idle timeout. Tune with the `keepalive=` argument (`0` + disables it). + +### Lifecycle + +Use `AsyncDatamaxiWS` as an async context manager (shown above) so all open +connections close cleanly, or manage it yourself: + +```python +ws = AsyncDatamaxiWS() +try: + async for msg in await ws.forex.subscribe("USD-KRW"): + ... +finally: + await ws.aclose() +``` + +Constructor options: `api_key`, `base_url` (derives the `wss://` URL) or an +explicit `ws_url`, `keepalive`, `reconnect`, and `connect_kwargs` (passed through +to the underlying `websockets.connect`). + +### Message shapes + +Each message is a plain `dict`. Compact channels use short wire keys (`s` = +symbol, plus per-channel fields like `p`, `e`, `r`, `oi`, …), while others +(`premium`, announcements) use descriptive keys. The exact typed shape of every +channel is generated into +[`datamaxi._ws_models`](https://github.com/bisonai/datamaxi-python/blob/main/datamaxi/_ws_models.py) +as `TypedDict`s, and field meanings are documented in the +[API docs](https://docs.datamaxiplus.com/): + +```python +from datamaxi._ws_models import TickerMessage, PremiumMessage +``` + +> Orderbook streaming is intentionally not exposed. + ## Response Types Most methods return pandas DataFrames by default. Set `pandas=False` to get raw dict/list responses. @@ -465,6 +677,11 @@ data = maxi.cex.candle(exchange="binance", symbol="BTC-USDT", interval="1d", mar print(type(data)) # ``` +Response metadata (rate-limit headers, etc.) is available on the client after a +call via `maxi..last_response`. The older `show_limit_usage` / +`show_header` options that folded metadata into the return value are deprecated +and will be removed in a future major release. + ## Pagination Many endpoints support pagination and return a `next_request` function: @@ -480,6 +697,9 @@ data2, next_request2 = next_request() data3, next_request3 = next_request2() ``` +On the [async client](#async-client), `next_request` is itself a coroutine — +`await` it: `data2, _ = await next_request()`. + ## Error Handling All SDK exceptions subclass `datamaxi.error.Error`: @@ -505,6 +725,45 @@ except ServerError as e: print(f"Server error {e.status_code}: {e.message}") ``` +## Async Client + +`AsyncDatamaxi` is the asynchronous counterpart to `Datamaxi` (built on +[httpx](https://www.python-httpx.org/)). It mirrors the same resource tree and +arguments, with one rule: every method is a coroutine and must be `await`ed. +Install the async extra: + +```shell +pip install "datamaxi[async]" +``` + +```python +import asyncio +from datamaxi.aio import AsyncDatamaxi + + +async def main(): + # Reads DATAMAXI_API_KEY from the environment, or pass api_key=... explicitly. + async with AsyncDatamaxi() as client: + df = await client.cex.candle( + exchange="binance", symbol="BTC-USDT", interval="1d", market="spot" + ) + print(df.head()) + + +asyncio.run(main()) +``` + +Use `AsyncDatamaxi` as an async context manager (shown above) or call +`await client.aclose()` yourself. Paginated endpoints return an async +`next_request` — `await` it too +(`data, next_request = await client.cex.announcement(...)`). Telegram and Naver +have standalone `AsyncTelegram` / `AsyncNaver` clients. + +Every endpoint in the [REST API Reference](#rest-api-reference) works the same +under the async client — see the [docs](https://datamaxi.readthedocs.io/) where +each example has a Sync/Async tab. For real-time streaming, see +[WebSockets](#websockets). + ## Local Development This project uses [uv](https://docs.astral.sh/uv/) for fast dev setup. Install @@ -532,6 +791,9 @@ Dependency files under `requirements/`: | `requirements-test.txt` | `common.txt` + test/lint tooling (pytest, responses, black, flake8). | | `requirements-dev.txt` | `requirements-test.txt` + docs tooling (mkdocs). | +The `[async]` and `[ws]` extras add `httpx` and `websockets` respectively — the +test stack installs them so the async and WebSocket suites can run. + ## Tests ```shell @@ -571,3 +833,4 @@ If you discover a bug in this project, please feel free to open an issue to disc ## License [MIT License](LICENSE) + diff --git a/docs/websocket.md b/docs/websocket.md new file mode 100644 index 0000000..cad477c --- /dev/null +++ b/docs/websocket.md @@ -0,0 +1,146 @@ +# WebSocket + +Stream real-time market data over the DataMaxi+ WebSocket API. Unlike the REST +resources, the WebSocket client is **async-only** — there is no synchronous +variant. + +## Installation + +The WebSocket client requires the `ws` extra, which pulls in +[`websockets`](https://websockets.readthedocs.io/): + +```shell +pip install "datamaxi[ws]" +``` + +## Quickstart + +`AsyncDatamaxiWS` reads the same `DATAMAXI_API_KEY` environment variable as the +REST clients (or pass `api_key=...`). Use it as an async context manager so open +connections close cleanly. + +```python +import asyncio +from datamaxi.aio.ws import AsyncDatamaxiWS + + +async def main(): + async with AsyncDatamaxiWS() as ws: + stream = await ws.ticker.subscribe("BTC-USDT@binance", market="spot") + async for msg in stream: + print(msg["s"], msg.get("p")) # symbol, price + + +asyncio.run(main()) +``` + +`subscribe(*params)` is a coroutine that returns an **async iterator** over live +messages. Iterate it with `async for`. + +## Channels + +Each accessor on `AsyncDatamaxiWS` maps to one channel. Pass the raw param +strings shown below; you can also inspect the expected format at runtime via +`ws..param_format`. + +| Accessor | Call | Param format | Plan | +| --------------------------- | ------------------------------------------- | --------------------------------------------------- | ----- | +| `ws.ticker` | `subscribe(*p, market="spot"\|"futures")` | `SYMBOL@exchange[@currency@conversionBase]` | Basic | +| `ws.forex` | `subscribe(*p)` | `SYMBOL` | Basic | +| `ws.premium` | `subscribe(*p)` | `src:tgt:tokenId:srcQuote:tgtQuote:srcMkt:tgtMkt` | Basic | +| `ws.funding_rate` | `subscribe(*p)` | `SYMBOL@exchange` | Basic | +| `ws.open_interest` | `subscribe(*p)` | `SYMBOL@exchange` | Basic | +| `ws.liquidation` | `subscribe(*p)` | `SYMBOL@exchange` | Basic | +| `ws.liquidation_feed` | `stream()` | — (firehose, no params) | Basic | +| `ws.announcement` | `subscribe()` | — (no params) | Pro+ | +| `ws.announcement_internal` | `subscribe()` | — (no params) | Pro+ | + +`ticker` is market-keyed — pass `market="spot"` (default) or `market="futures"`. +The announcement channels require a **Pro+** plan. + +## Multiplexing and filtering + +One connection is opened per channel and multiplexes every param you subscribe +to. Because the protocol tags messages by payload fields (not by a subscription +id), `subscribe()` yields **every** message on the channel — filter client-side +by symbol (`msg["s"]`) when subscribing to more than one: + +```python +stream = await ws.ticker.subscribe( + "BTC-USDT@binance", "ETH-USDT@binance", market="spot" +) +async for msg in stream: + if msg["s"] == "BTC-USDT": + handle_btc(msg) +``` + +Add or drop params on the fly: + +```python +await ws.ticker.subscribe("SOL-USDT@binance", market="spot") # add +await ws.ticker.unsubscribe("SOL-USDT@binance", market="spot") # remove +``` + +> Not every channel supports removing an individual param server-side — +> `liquidation` and `open_interest` are subscribe-only. Closing the client +> (`await ws.aclose()`, or exiting the `async with` block) always stops all +> streams. + +## Firehose feeds + +`ws.liquidation_feed` needs no subscription — call `stream()` and consume: + +```python +async for evt in await ws.liquidation_feed.stream(): + print(evt["s"], evt.get("sd"), evt.get("p")) # symbol, side, price +``` + +## Reconnect and keepalive + +The client is resilient by default: + +- **Auto-reconnect** — if the connection drops it reconnects and replays your + active subscriptions, so your `async for` loop resumes without extra code. + Disable with `AsyncDatamaxiWS(reconnect=False)`. +- **Keepalive** — an app-level `PING` is sent every 30 seconds to stay under the + server's idle timeout. Tune it with the `keepalive=` argument (`0` + disables it). + +## Lifecycle + +Use `AsyncDatamaxiWS` as an async context manager, or manage it yourself: + +```python +ws = AsyncDatamaxiWS() +try: + async for msg in await ws.forex.subscribe("USD-KRW"): + ... +finally: + await ws.aclose() +``` + +Constructor options: `api_key`, `base_url` (derives the `wss://` URL) or an +explicit `ws_url`, `keepalive`, `reconnect`, and `connect_kwargs` (passed through +to the underlying `websockets.connect`). + +## Message shapes + +Each message is a plain `dict`. Compact channels use short wire keys (`s` = +symbol, plus per-channel fields like `p`, `e`, `r`, `oi`, …), while others +(`premium`, announcements) use descriptive keys. The typed shape of every channel +is generated into `datamaxi._ws_models` as `TypedDict`s, and field meanings are +documented in the [API docs](https://docs.datamaxiplus.com/): + +```python +from datamaxi._ws_models import TickerMessage, PremiumMessage +``` + +Orderbook streaming is intentionally not exposed. + +## Reference + +::: datamaxi.aio.ws.AsyncDatamaxiWS + options: + show_submodules: false + show_source: false + diff --git a/mkdocs.yml b/mkdocs.yml index aaab41a..9dfc248 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,7 @@ nav: - Main: index.md - API: api.md - Async Client: async.md + - WebSocket: websocket.md - CEX: - Candle: cex-candle.md - Ticker: cex-ticker.md