From cd814a265cf7fa6a7a9087d3907fcd45635f838d Mon Sep 17 00:00:00 2001 From: Martin Kersner Date: Mon, 29 Jun 2026 19:31:36 +0900 Subject: [PATCH 1/2] liquidation/open_interest: send snake_case top_n/min_volume_usd wire keys Align public query params with backend snake_case canonical (dual-read). Wire keys top_n + min_volume_usd; public topN kwarg kept (non-breaking). --- datamaxi/_endpoints.py | 8 ++++---- datamaxi/datamaxi/liquidation.py | 2 +- datamaxi/datamaxi/open_interest.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/datamaxi/_endpoints.py b/datamaxi/_endpoints.py index 5d0ac7e..db4f771 100644 --- a/datamaxi/_endpoints.py +++ b/datamaxi/_endpoints.py @@ -789,7 +789,7 @@ "type": "str", "description": "Base asset filter (case-insensitive)", }, - "minVolumeUsd": { + "min_volume_usd": { "required": False, "type": "float", "description": "Minimum VolumeUsd filter", @@ -817,7 +817,7 @@ "enum": ["1h", "4h", "24h"], "description": "Rolling window", }, - "topN": { + "top_n": { "required": False, "type": "int", "default": 10, @@ -872,7 +872,7 @@ "type": "str", "description": "Exchange filter", }, - "minVolumeUsd": { + "min_volume_usd": { "required": False, "type": "float", "description": "Minimum VolumeUsd filter", @@ -1089,7 +1089,7 @@ "requires_auth": True, "group": "open_interest", "params": { - "topN": { + "top_n": { "required": False, "type": "int", "default": 10, diff --git a/datamaxi/datamaxi/liquidation.py b/datamaxi/datamaxi/liquidation.py index a6d7f8c..f38d4a9 100644 --- a/datamaxi/datamaxi/liquidation.py +++ b/datamaxi/datamaxi/liquidation.py @@ -65,7 +65,7 @@ def heatmap( 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} + "/api/v1/liquidation/heatmap", {"window": window, "top_n": topN} ) def map( diff --git a/datamaxi/datamaxi/open_interest.py b/datamaxi/datamaxi/open_interest.py index 0a299f5..73e2a62 100644 --- a/datamaxi/datamaxi/open_interest.py +++ b/datamaxi/datamaxi/open_interest.py @@ -80,7 +80,7 @@ def summary(self, topN: int = 10) -> Dict[str, Any]: """ if topN < 1 or topN > 30: raise ValueError("topN must be between 1 and 30") - return self.query("/api/v1/open-interest/summary", {"topN": topN}) + return self.query("/api/v1/open-interest/summary", {"top_n": topN}) def history_aggregated( self, From 5e9e62cdd887e8ad6e2dcbf9dac4cbb80deabcfa Mon Sep 17 00:00:00 2001 From: Martin Kersner Date: Mon, 29 Jun 2026 19:31:36 +0900 Subject: [PATCH 2/2] tests: assert top_n wire key for heatmap/summary --- tests/test_query_params.py | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_query_params.py diff --git a/tests/test_query_params.py b/tests/test_query_params.py new file mode 100644 index 0000000..73c2762 --- /dev/null +++ b/tests/test_query_params.py @@ -0,0 +1,51 @@ +""" +Offline unit tests asserting the wire query-string keys for snake_case +public params (top_n / min_volume_usd). + +Backend migrated these public params to snake_case canonical (dual-read). +These tests pin that the SDK sends the snake_case keys on the wire, and +that the user-facing ``topN`` kwarg stays accepted (non-breaking). +""" + +import re +import responses +from urllib.parse import urlparse, parse_qs + +from datamaxi.datamaxi.liquidation import Liquidation +from datamaxi.datamaxi.open_interest import OpenInterest + +BASE_URL = "https://api.datamaxiplus.com" + + +def _query_of(call): + return parse_qs(urlparse(call.request.url).query) + + +@responses.activate +def test_liquidation_heatmap_sends_top_n(): + responses.add( + responses.GET, + re.compile(".*/api/v1/liquidation/heatmap.*"), + json={}, + status=200, + ) + Liquidation(api_key="k", base_url=BASE_URL).heatmap(window="4h", topN=5) + + qs = _query_of(responses.calls[0]) + assert qs["top_n"] == ["5"] + assert "topN" not in qs + + +@responses.activate +def test_open_interest_summary_sends_top_n(): + responses.add( + responses.GET, + re.compile(".*/api/v1/open-interest/summary.*"), + json={}, + status=200, + ) + OpenInterest(api_key="k", base_url=BASE_URL).summary(topN=7) + + qs = _query_of(responses.calls[0]) + assert qs["top_n"] == ["7"] + assert "topN" not in qs