Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions datamaxi/datamaxi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from datamaxi.datamaxi.funding_rate import FundingRate
from datamaxi.datamaxi.forex import Forex
from datamaxi.datamaxi.premium import Premium
from datamaxi.datamaxi.liquidation import Liquidation
from datamaxi.datamaxi.open_interest import OpenInterest
from datamaxi.datamaxi.cex_candle import CexCandle # used in documentation # noqa:F401
from datamaxi.datamaxi.cex_ticker import ( # used in documentation # noqa:F401
CexTicker,
Expand All @@ -21,6 +23,9 @@
from datamaxi.datamaxi.cex_token import ( # used in documentation # noqa:F401
CexToken,
)
from datamaxi.datamaxi.cex_symbol import ( # used in documentation # noqa:F401
CexSymbol,
)


class Datamaxi:
Expand All @@ -41,3 +46,11 @@ def __init__(self, api_key=None, **kwargs: Any):
self.funding_rate = FundingRate(api_key, **kwargs)
self.forex = Forex(api_key, **kwargs)
self.premium = Premium(api_key, **kwargs)
# Futures-only surfaces. Top-level on the client so callers
# reach them via `client.liquidation.heatmap(...)` /
# `client.open_interest.summary(...)` — matches the
# `/api/v1/{liquidation,open-interest}/*` REST grouping and
# mirrors the equivalent typed wrappers in the Rust SDK
# (`datamaxi::generated::{Liquidation, OpenInterest}`).
self.liquidation = Liquidation(api_key, **kwargs)
self.open_interest = OpenInterest(api_key, **kwargs)
7 changes: 7 additions & 0 deletions datamaxi/datamaxi/cex.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datamaxi.datamaxi.cex_wallet_status import CexWalletStatus
from datamaxi.datamaxi.cex_announcement import CexAnnouncement
from datamaxi.datamaxi.cex_token import CexToken
from datamaxi.datamaxi.cex_symbol import CexSymbol


class Cex(API):
Expand All @@ -26,3 +27,9 @@ def __init__(self, api_key=None, **kwargs: Any):
self.wallet_status = CexWalletStatus(api_key, **kwargs)
self.announcement = CexAnnouncement(api_key, **kwargs)
self.token = CexToken(api_key, **kwargs)
# Per-base / per-symbol surfaces (metadata, tags, cautions,
# delistings, volume, OI / OI-stats / liquidation aggregates).
# Grouped under `cex.symbol` to mirror the REST path layout
# (`/api/v1/cex/symbol/*`) and to keep the top-level `Cex`
# surface flat.
self.symbol = CexSymbol(api_key, **kwargs)
126 changes: 126 additions & 0 deletions datamaxi/datamaxi/cex_symbol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from typing import Any, Dict, Optional
from datamaxi.api import API


class CexSymbol(API):
"""Client to fetch per-base / per-symbol CEX metadata + aggregates."""

def __init__(self, api_key=None, **kwargs: Any):
"""Initialize the object.

Args:
api_key (str): The DataMaxi+ API key
**kwargs: Keyword arguments used by `datamaxi.api.API`.
"""
super().__init__(api_key, **kwargs)

def metadata(
self, exchange: Optional[str] = None, base: Optional[str] = None
) -> Dict[str, Any]:
"""Trading status + caution + tags + delisting metadata.

`GET /api/v1/cex/symbol/metadata`
"""
params: Dict[str, Any] = {}
if exchange is not None:
params["exchange"] = exchange
if base is not None:
params["base"] = base
return self.query("/api/v1/cex/symbol/metadata", params)

def tags(
self, exchange: Optional[str] = None, base: Optional[str] = None
) -> Dict[str, Any]:
"""Exchange-assigned tags (e.g. seed, alpha) per symbol.

`GET /api/v1/cex/symbol/tags`
"""
params: Dict[str, Any] = {}
if exchange is not None:
params["exchange"] = exchange
if base is not None:
params["base"] = base
return self.query("/api/v1/cex/symbol/tags", params)

def cautions(
self, exchange: Optional[str] = None, base: Optional[str] = None
) -> Dict[str, Any]:
"""Active caution / investment-warning flags per symbol.

`GET /api/v1/cex/symbol/cautions`
"""
params: Dict[str, Any] = {}
if exchange is not None:
params["exchange"] = exchange
if base is not None:
params["base"] = base
return self.query("/api/v1/cex/symbol/cautions", params)

def delistings(
self, exchange: Optional[str] = None, base: Optional[str] = None
) -> Dict[str, Any]:
"""Scheduled delistings with timestamps.

`GET /api/v1/cex/symbol/delistings`
"""
params: Dict[str, Any] = {}
if exchange is not None:
params["exchange"] = exchange
if base is not None:
params["base"] = base
return self.query("/api/v1/cex/symbol/delistings", params)

def volume(self, base: str, exchange: Optional[str] = None) -> Dict[str, Any]:
"""Per-exchange 24h volume for a single base asset.

`GET /api/v1/cex/symbol/volume`
"""
params: Dict[str, Any] = {"base": base}
if exchange is not None:
params["exchange"] = exchange
return self.query("/api/v1/cex/symbol/volume", params)

def oi(self, base: str, exchange: Optional[str] = None) -> Dict[str, Any]:
"""Per-exchange Open Interest for a single base asset.

`GET /api/v1/cex/symbol/oi`
"""
params: Dict[str, Any] = {"base": base}
if exchange is not None:
params["exchange"] = exchange
return self.query("/api/v1/cex/symbol/oi", params)

def oi_stats(
self,
base: str,
exchange: Optional[str] = None,
currency: str = "USD",
) -> Dict[str, Any]:
"""Per-exchange OI snapshot with 1h / 4h / 24h deltas.

`GET /api/v1/cex/symbol/oi-stats`

Args:
base (str): Base asset (e.g. ``BTC``).
exchange (str): Optional single-exchange filter.
currency (str): ``USD`` or ``KRW``.
"""
if currency not in ("USD", "KRW"):
raise ValueError("currency must be either USD or KRW")
params: Dict[str, Any] = {"base": base, "currency": currency}
if exchange is not None:
params["exchange"] = exchange
return self.query("/api/v1/cex/symbol/oi-stats", params)

def liquidation(self, base: str, window: str = "24h") -> Dict[str, Any]:
"""Per-exchange long / short liquidation aggregates over a window.

`GET /api/v1/cex/symbol/liquidation`

Args:
base (str): Base asset (e.g. ``BTC``).
window (str): Time window (``1h``, ``24h``, ``7d`` — server caps at 30d).
"""
return self.query(
"/api/v1/cex/symbol/liquidation", {"base": base, "window": window}
)
117 changes: 117 additions & 0 deletions datamaxi/datamaxi/liquidation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from typing import Any, Dict, Optional
from datamaxi.api import API


class Liquidation(API):
"""Client to fetch CEX futures liquidation data from DataMaxi+ API."""

def __init__(self, api_key=None, **kwargs: Any):
"""Initialize the object.

Args:
api_key (str): The DataMaxi+ API key
**kwargs: Keyword arguments used by `datamaxi.api.API`.
"""
super().__init__(api_key, **kwargs)

def __call__(
self,
exchange: str,
symbol: str,
limit: int = 100,
) -> Dict[str, Any]:
"""Recent liquidation events for a specific futures symbol.

`GET /api/v1/liquidation`

Args:
exchange (str): Exchange name (e.g. ``binance``).
symbol (str): Exchange-native API symbol (e.g. ``BTC-USDT``).
limit (int): Max events to return (server caps).

Returns:
Liquidation events response.
"""
if limit < 1:
raise ValueError("limit must be greater than 0")
params = {"exchange": exchange, "symbol": symbol, "limit": limit}
return self.query("/api/v1/liquidation", params)

def feed(self, limit: int = 100) -> Dict[str, Any]:
"""Firehose: most recent liquidation events across every symbol.

`GET /api/v1/liquidation/feed`

Args:
limit (int): Max events to return.
"""
if limit < 1:
raise ValueError("limit must be greater than 0")
return self.query("/api/v1/liquidation/feed", {"limit": limit})

def heatmap(
self,
window: str = "1h",
topN: int = 10,
) -> Dict[str, Any]:
"""Token × exchange liquidation heatmap over a rolling window.

`GET /api/v1/liquidation/heatmap`

Args:
window (str): Rolling window (``1h``, ``4h``, or ``24h``).
topN (int): Top N tokens by total (1-30).
"""
if topN < 1 or topN > 30:
raise ValueError("topN must be between 1 and 30")
return self.query(
"/api/v1/liquidation/heatmap", {"window": window, "topN": topN}
)

def map(
self,
base: str,
exchange: str = "binance",
quote: str = "USDT",
) -> Dict[str, Any]:
"""Coinglass-style liquidation map (price × leverage tier).

`GET /api/v1/liquidation/map`

Args:
base (str): Base asset (e.g. ``BTC``).
exchange (str): Exchange (default ``binance``).
quote (str): Quote asset (default ``USDT``).
"""
params = {"base": base, "exchange": exchange, "quote": quote}
return self.query("/api/v1/liquidation/map", params)

def symbol_history(
self,
symbol: str,
quote: str = "USDT",
exchange: Optional[str] = None,
interval: str = "5m",
window: str = "24h",
) -> Dict[str, Any]:
"""Bucketed long / short liquidation USD time series + price line.

`GET /api/v1/liquidation/symbol-history`

Args:
symbol (str): Base asset (e.g. ``BTC``).
quote (str): Quote asset (default ``USDT``).
exchange (str): Optional exchange filter for the liquidation
aggregation. Price line stays on Binance unless set.
interval (str): Bucket interval (``5m``, ``15m``, or ``1h``).
window (str): Lookback window (``24h``, ``72h``, or ``7d``).
"""
params = {
"symbol": symbol,
"quote": quote,
"interval": interval,
"window": window,
}
if exchange is not None:
params["exchange"] = exchange
return self.query("/api/v1/liquidation/symbol-history", params)
Loading
Loading