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
104 changes: 94 additions & 10 deletions datamaxi/datamaxi/cex_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,129 @@ def __init__(self, api_key=None, **kwargs: Any):
super().__init__(api_key, **kwargs)

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

`GET /api/v1/cex/symbol/metadata`

Args:
exchange (str): Comma-separated exchange names (empty = all).
base (str): Base asset filter.
market (str): ``spot`` or ``futures`` (empty = both).
quote (str): Quote asset filter.
status (str): ``trading_status`` filter (comma-separated).
"""
return self.request_endpoint(
"cex_symbol_metadata", exchange=exchange, base=base
"cex_symbol_metadata",
exchange=exchange,
base=base,
market=market,
quote=quote,
status=status,
)

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

`GET /api/v1/cex/symbol/tags`

Args:
exchange (str): Exchange filter (comma-separated).
base (str): Base asset filter.
tag (str): Tag filter (comma-separated).
market (str): ``spot`` or ``futures``.
source (str): Tag source filter
(``rest_native``, ``announcement``, ``cmc``, ``manual``).
min_confidence (int): Minimum confidence (0-100).
"""
return self.request_endpoint("cex_symbol_tags", exchange=exchange, base=base)
return self.request_endpoint(
"cex_symbol_tags",
exchange=exchange,
base=base,
tag=tag,
market=market,
source=source,
min_confidence=min_confidence,
)

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

`GET /api/v1/cex/symbol/cautions`

Args:
exchange (str): Exchange filter (comma-separated, empty = all).
market (str): ``spot`` or ``futures``.
min_level (str): Minimum severity
(``caution``, ``warning``, ``danger``).
active_only (bool): Exclude rows whose ``end_at`` is in the past.
"""
return self.request_endpoint("cex_symbol_cautions", exchange=exchange)
return self.request_endpoint(
"cex_symbol_cautions",
exchange=exchange,
market=market,
min_level=min_level,
active_only=active_only,
)

def delistings(self, exchange: Optional[str] = None) -> Dict[str, Any]:
def delistings(
self,
exchange: Optional[str] = None,
market: Optional[str] = None,
from_ms: Optional[int] = None,
to_ms: Optional[int] = None,
include_past: Optional[bool] = None,
) -> Dict[str, Any]:
"""Scheduled delistings with timestamps.

`GET /api/v1/cex/symbol/delistings`

Args:
exchange (str): Exchange filter (comma-separated).
market (str): ``spot`` or ``futures``.
from_ms (int): Lower bound for ``delisting_at`` (ms epoch).
to_ms (int): Upper bound for ``delisting_at`` (ms epoch).
include_past (bool): Include already-delisted rows.
"""
return self.request_endpoint("cex_symbol_delistings", exchange=exchange)
return self.request_endpoint(
"cex_symbol_delistings",
exchange=exchange,
market=market,
from_ms=from_ms,
to_ms=to_ms,
include_past=include_past,
)

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

`GET /api/v1/cex/symbol/volume`

Args:
base (str): Base asset (e.g. ``BTC``).
market (str): Filter to ``spot`` or ``futures``.
"""
return self.request_endpoint("cex_symbol_volume", base=base)
return self.request_endpoint("cex_symbol_volume", base=base, market=market)

def oi(self, base: str, exchange: Optional[str] = None) -> Dict[str, Any]:
"""Per-exchange Open Interest for a single base asset.
Expand Down
4 changes: 4 additions & 0 deletions datamaxi/datamaxi/cex_ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def get(
market: str,
currency: str = None,
conversion_base: str = None,
include_source: bool = False,
pandas: bool = True,
) -> Union[Dict, pd.DataFrame]:
"""Fetch ticker data
Expand All @@ -38,6 +39,8 @@ def get(
market (str): Market type (spot/futures)
currency (str): Price currency
conversion_base (str): Conversion base currency
include_source (bool): Include the frame's transport source
(``ws``|``rest``) in the response
pandas (bool): Return data as pandas DataFrame

Returns:
Expand All @@ -62,6 +65,7 @@ def get(
market=market,
currency=currency,
conversion_base=conversion_base,
include_source=include_source,
)

if pandas:
Expand Down
19 changes: 17 additions & 2 deletions datamaxi/datamaxi/liquidation.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,32 @@ def __call__(
"liquidation", exchange=exchange, symbol=symbol, limit=limit
)

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

`GET /api/v1/liquidation/feed`

Args:
limit (int): Max events to return.
exchange (str): Optional exchange filter.
base (str): Optional base asset filter (case-insensitive).
min_volume_usd (float): Minimum ``VolumeUsd`` filter.
"""
if limit < 1:
raise ValueError("limit must be greater than 0")
return self.request_endpoint("liquidation_feed", limit=limit)
return self.request_endpoint(
"liquidation_feed",
limit=limit,
exchange=exchange,
base=base,
min_volume_usd=min_volume_usd,
)

def heatmap(
self,
Expand Down
5 changes: 5 additions & 0 deletions datamaxi/datamaxi/premium.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __call__( # noqa: C901
premium_type: str = None,
token_include: str = None,
token_exclude: str = None,
query: str = None,
pandas: bool = True,
) -> Union[List, pd.DataFrame]:
"""Fetch premium data
Expand Down Expand Up @@ -68,6 +69,7 @@ def __call__( # noqa: C901
premium_type (str): Return based on matching premium_type
token_include (str): Return results containing only specified token
token_exclude (str): Return results not containing specified token
query (str): Search query for filtering assets

pandas (bool): Return data as pandas DataFrame

Expand Down Expand Up @@ -97,6 +99,9 @@ def __call__( # noqa: C901
if key is not None:
params["key"] = key

if query is not None:
params["query"] = query

if page is not None:
params["page"] = page

Expand Down
3 changes: 3 additions & 0 deletions datamaxi/telegram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def messages(
key: Optional[str] = None,
sort: str = "desc",
category: Optional[str] = None,
search_query: Optional[str] = None,
) -> Tuple[Dict[str, Any], Callable]:
"""Get Telegram posts for given channel username

Expand All @@ -93,6 +94,7 @@ def messages(
key (str): Specifies key to sort by
sort (str): Sort order
category (str): Specifies category
search_query (str): Specifies search query

Returns:
Tuple of message response and next request function
Expand All @@ -114,6 +116,7 @@ def messages(
key=key,
sort=sort,
category=category,
search_query=search_query,
)
if res["data"] is None:
raise ValueError("no data found")
Expand Down
74 changes: 74 additions & 0 deletions tests/test_cex_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,80 @@ def test_volume_sends_base_param():
assert res == {"BTC": {"binance": "100"}}


@responses.activate
def test_metadata_forwards_new_params():
responses.add(
responses.GET,
re.compile(".*/api/v1/cex/symbol/metadata.*"),
json={},
status=200,
)
_client().metadata(
exchange="binance", base="BTC", market="spot", quote="USDT", status="trading"
)
qs = _qs(responses.calls[0])
assert qs["market"] == ["spot"]
assert qs["quote"] == ["USDT"]
assert qs["status"] == ["trading"]


@responses.activate
def test_tags_forwards_new_params():
responses.add(
responses.GET,
re.compile(".*/api/v1/cex/symbol/tags.*"),
json={},
status=200,
)
_client().tags(base="BTC", tag="seed", source="manual", min_confidence=90)
qs = _qs(responses.calls[0])
assert qs["tag"] == ["seed"]
assert qs["source"] == ["manual"]
assert qs["min_confidence"] == ["90"]


@responses.activate
def test_cautions_forwards_new_params():
responses.add(
responses.GET,
re.compile(".*/api/v1/cex/symbol/cautions.*"),
json={},
status=200,
)
_client().cautions(exchange="binance", min_level="warning", active_only=True)
qs = _qs(responses.calls[0])
assert qs["min_level"] == ["warning"]
assert qs["active_only"] == ["True"]


@responses.activate
def test_delistings_forwards_new_params():
responses.add(
responses.GET,
re.compile(".*/api/v1/cex/symbol/delistings.*"),
json={},
status=200,
)
_client().delistings(exchange="binance", from_ms=1, to_ms=2, include_past=True)
qs = _qs(responses.calls[0])
assert qs["from_ms"] == ["1"]
assert qs["to_ms"] == ["2"]
assert qs["include_past"] == ["True"]


@responses.activate
def test_volume_forwards_market_param():
responses.add(
responses.GET,
re.compile(".*/api/v1/cex/symbol/volume.*"),
json={},
status=200,
)
_client().volume(base="BTC", market="futures")
qs = _qs(responses.calls[0])
assert qs["market"] == ["futures"]


@mock_http_response(responses.GET, "/api/v1/cex/symbol/oi", {"BTC": {"binance": "1"}})
def test_oi_returns_dict():
assert _client().oi(base="BTC") == {"BTC": {"binance": "1"}}
Expand Down
18 changes: 18 additions & 0 deletions tests/test_cex_ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ def test_ticker_get_sends_query_params():
assert qs["conversion_base"] == ["USDT"]


@responses.activate
def test_ticker_get_forwards_include_source():
responses.add(
responses.GET,
re.compile(".*/api/v1/ticker.*"),
json=_TICKER,
status=200,
)
_client().get(
exchange="binance",
market="spot",
symbol="BTC-USDT",
include_source=True,
)
qs = _qs(responses.calls[0])
assert qs["include_source"] == ["True"]


def test_ticker_invalid_market_raises_value_error():
with pytest.raises(ValueError):
_client().get(exchange="binance", market="bogus", symbol="BTC-USDT")
Expand Down
Loading
Loading