From 012ec851ccba2af9bef113127505bf3ebe6ec75d Mon Sep 17 00:00:00 2001 From: Martin Kersner Date: Fri, 3 Jul 2026 12:22:14 +0900 Subject: [PATCH] refactor: share one Session across the client tree via composition Resources subclassed API, so each opened its own requests.Session; a Datamaxi client spun up ~15 pools and every grouping class (Cex, ...) was itself an API it never used as one. Add an API-composing Resource base (has-a, not is-a) that holds a shared API and forwards request_endpoint/query. Datamaxi now builds ONE API and threads it through the whole tree via api=, so all sub-clients reuse a single Session / connection pool. Resource method bodies are unchanged (api rides through **kwargs); standalone instantiation still builds its own transport. No public surface change: client.cex.candle(...) etc. work as before. --- datamaxi/api.py | 23 +++++++++++++++++++++++ datamaxi/datamaxi/__init__.py | 22 ++++++++++++++-------- datamaxi/datamaxi/cex.py | 4 ++-- datamaxi/datamaxi/cex_announcement.py | 4 ++-- datamaxi/datamaxi/cex_candle.py | 4 ++-- datamaxi/datamaxi/cex_fee.py | 4 ++-- datamaxi/datamaxi/cex_symbol.py | 4 ++-- datamaxi/datamaxi/cex_ticker.py | 4 ++-- datamaxi/datamaxi/cex_token.py | 4 ++-- datamaxi/datamaxi/cex_wallet_status.py | 4 ++-- datamaxi/datamaxi/forex.py | 4 ++-- datamaxi/datamaxi/funding_rate.py | 4 ++-- datamaxi/datamaxi/index_price.py | 4 ++-- datamaxi/datamaxi/liquidation.py | 4 ++-- datamaxi/datamaxi/margin_borrow.py | 4 ++-- datamaxi/datamaxi/open_interest.py | 4 ++-- datamaxi/datamaxi/premium.py | 4 ++-- datamaxi/naver/__init__.py | 4 ++-- datamaxi/telegram/__init__.py | 4 ++-- 19 files changed, 71 insertions(+), 42 deletions(-) diff --git a/datamaxi/api.py b/datamaxi/api.py index 08e012e..30120cc 100644 --- a/datamaxi/api.py +++ b/datamaxi/api.py @@ -179,3 +179,26 @@ def _handle_exception(self, response): error_data, ) raise ServerError(status_code, response.text) + + +class Resource(object): + """Base for endpoint/resource clients — *composes* an `API` transport + rather than subclassing it. + + Every resource holds a shared `API` (one `requests.Session` / + connection pool) instead of each opening its own. A resource either + receives an already-built ``api`` (the normal path — `Datamaxi` + constructs one and threads it through the whole tree via ``**kwargs``) + or builds its own from ``api_key``/``**kwargs`` for direct, standalone + instantiation. The thin `request_endpoint`/`query` forwarders keep the + call sites in the resource methods unchanged (`self.request_endpoint(...)`). + """ + + def __init__(self, api_key=None, api=None, **kwargs): + self._api = api if api is not None else API(api_key, **kwargs) + + def request_endpoint(self, op_id, **params): + return self._api.request_endpoint(op_id, **params) + + def query(self, url_path, payload=None): + return self._api.query(url_path, payload=payload) diff --git a/datamaxi/datamaxi/__init__.py b/datamaxi/datamaxi/__init__.py index 9212c63..d202c39 100644 --- a/datamaxi/datamaxi/__init__.py +++ b/datamaxi/datamaxi/__init__.py @@ -1,4 +1,5 @@ from typing import Any +from datamaxi.api import API from datamaxi.lib.constants import BASE_URL from datamaxi.datamaxi.cex import Cex from datamaxi.datamaxi.funding_rate import FundingRate @@ -42,17 +43,22 @@ def __init__(self, api_key=None, **kwargs: Any): if "base_url" not in kwargs: kwargs["base_url"] = BASE_URL - self.cex = Cex(api_key, **kwargs) - self.funding_rate = FundingRate(api_key, **kwargs) - self.forex = Forex(api_key, **kwargs) - self.premium = Premium(api_key, **kwargs) + # One shared transport — a single `requests.Session` / connection + # pool threaded through every sub-client instead of each opening + # its own. Sub-clients receive it via `api=` and forward it down. + api = API(api_key, **kwargs) + + self.cex = Cex(api=api) + self.funding_rate = FundingRate(api=api) + self.forex = Forex(api=api) + self.premium = Premium(api=api) # 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) - self.margin_borrow = MarginBorrow(api_key, **kwargs) - self.index_price = IndexPrice(api_key, **kwargs) + self.liquidation = Liquidation(api=api) + self.open_interest = OpenInterest(api=api) + self.margin_borrow = MarginBorrow(api=api) + self.index_price = IndexPrice(api=api) diff --git a/datamaxi/datamaxi/cex.py b/datamaxi/datamaxi/cex.py index c3416c4..05a7890 100644 --- a/datamaxi/datamaxi/cex.py +++ b/datamaxi/datamaxi/cex.py @@ -1,5 +1,5 @@ from typing import Any -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.datamaxi.cex_candle import CexCandle from datamaxi.datamaxi.cex_ticker import CexTicker from datamaxi.datamaxi.cex_fee import CexFee @@ -9,7 +9,7 @@ from datamaxi.datamaxi.cex_symbol import CexSymbol -class Cex(API): +class Cex(Resource): """Client to fetch CEX data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_announcement.py b/datamaxi/datamaxi/cex_announcement.py index 010b49a..78420e2 100644 --- a/datamaxi/datamaxi/cex_announcement.py +++ b/datamaxi/datamaxi/cex_announcement.py @@ -1,9 +1,9 @@ from typing import Any, Dict, Optional, Tuple, Callable -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.constants import ASC, DESC, SortOrder -class CexAnnouncement(API): +class CexAnnouncement(Resource): """Client to fetch announcement data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_candle.py b/datamaxi/datamaxi/cex_candle.py index 1f06d97..f3863d3 100644 --- a/datamaxi/datamaxi/cex_candle.py +++ b/datamaxi/datamaxi/cex_candle.py @@ -1,13 +1,13 @@ from typing import Any, List, Dict, Union, Optional import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter from datamaxi.lib.utils import check_required_parameters from datamaxi.datamaxi.utils import convert_data_to_data_frame from datamaxi.lib.constants import SPOT, FUTURES, INTERVAL_1D, USD, Market, Interval -class CexCandle(API): +class CexCandle(Resource): """Client to fetch CEX candle data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_fee.py b/datamaxi/datamaxi/cex_fee.py index 3c1f590..cad3c26 100644 --- a/datamaxi/datamaxi/cex_fee.py +++ b/datamaxi/datamaxi/cex_fee.py @@ -1,9 +1,9 @@ from typing import Any, List, Dict -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter -class CexFee(API): +class CexFee(Resource): """Client to fetch CEX trading fee data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_symbol.py b/datamaxi/datamaxi/cex_symbol.py index 988c67c..6b644d0 100644 --- a/datamaxi/datamaxi/cex_symbol.py +++ b/datamaxi/datamaxi/cex_symbol.py @@ -1,8 +1,8 @@ from typing import Any, Dict, Optional -from datamaxi.api import API +from datamaxi.api import Resource -class CexSymbol(API): +class CexSymbol(Resource): """Client to fetch per-base / per-symbol CEX metadata + aggregates.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_ticker.py b/datamaxi/datamaxi/cex_ticker.py index 24abf79..d939124 100644 --- a/datamaxi/datamaxi/cex_ticker.py +++ b/datamaxi/datamaxi/cex_ticker.py @@ -1,11 +1,11 @@ from typing import Any, List, Dict, Union import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameters from datamaxi.lib.constants import SPOT, FUTURES, Market -class CexTicker(API): +class CexTicker(Resource): """Client to fetch ticker data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_token.py b/datamaxi/datamaxi/cex_token.py index b77a49e..5a266c1 100644 --- a/datamaxi/datamaxi/cex_token.py +++ b/datamaxi/datamaxi/cex_token.py @@ -1,8 +1,8 @@ from typing import Any, Dict, Optional, Tuple, Callable -from datamaxi.api import API +from datamaxi.api import Resource -class CexToken(API): +class CexToken(Resource): """Client to fetch token update data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/cex_wallet_status.py b/datamaxi/datamaxi/cex_wallet_status.py index b42786a..0be070e 100644 --- a/datamaxi/datamaxi/cex_wallet_status.py +++ b/datamaxi/datamaxi/cex_wallet_status.py @@ -1,11 +1,11 @@ from typing import Any, List, Dict, Union import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameters from datamaxi.lib.utils import check_required_parameter -class CexWalletStatus(API): +class CexWalletStatus(Resource): """Client to fetch transfer status data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/forex.py b/datamaxi/datamaxi/forex.py index e0165de..51a360c 100644 --- a/datamaxi/datamaxi/forex.py +++ b/datamaxi/datamaxi/forex.py @@ -1,10 +1,10 @@ from typing import Any, List, Dict, Union import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter -class Forex(API): +class Forex(Resource): """Client to fetch forex data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/funding_rate.py b/datamaxi/datamaxi/funding_rate.py index 946dc7d..bdc1ac4 100644 --- a/datamaxi/datamaxi/funding_rate.py +++ b/datamaxi/datamaxi/funding_rate.py @@ -1,13 +1,13 @@ from typing import Any, Callable, Tuple, List, Dict, Union import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter from datamaxi.lib.utils import check_required_parameters from datamaxi.datamaxi.utils import convert_data_to_data_frame from datamaxi.lib.constants import ASC, DESC, SortOrder -class FundingRate(API): +class FundingRate(Resource): """Client to fetch funding rate data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/index_price.py b/datamaxi/datamaxi/index_price.py index 4268ea9..a08e332 100644 --- a/datamaxi/datamaxi/index_price.py +++ b/datamaxi/datamaxi/index_price.py @@ -1,10 +1,10 @@ from typing import Any, Dict, Optional -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter from datamaxi.lib.constants import Interval -class IndexPrice(API): +class IndexPrice(Resource): """Client to fetch historical index price data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/liquidation.py b/datamaxi/datamaxi/liquidation.py index 6496173..8841dc3 100644 --- a/datamaxi/datamaxi/liquidation.py +++ b/datamaxi/datamaxi/liquidation.py @@ -1,9 +1,9 @@ from typing import Any, Dict, Optional -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.constants import Interval -class Liquidation(API): +class Liquidation(Resource): """Client to fetch CEX futures liquidation data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/margin_borrow.py b/datamaxi/datamaxi/margin_borrow.py index e1e8c78..37a9e3b 100644 --- a/datamaxi/datamaxi/margin_borrow.py +++ b/datamaxi/datamaxi/margin_borrow.py @@ -1,9 +1,9 @@ from typing import Any, Dict -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter -class MarginBorrow(API): +class MarginBorrow(Resource): """Client to fetch margin borrow data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/open_interest.py b/datamaxi/datamaxi/open_interest.py index 01f6c3c..28468aa 100644 --- a/datamaxi/datamaxi/open_interest.py +++ b/datamaxi/datamaxi/open_interest.py @@ -1,9 +1,9 @@ from typing import Any, Dict, Optional -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.constants import Interval, SortOrder -class OpenInterest(API): +class OpenInterest(Resource): """Client to fetch CEX futures Open Interest data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/datamaxi/premium.py b/datamaxi/datamaxi/premium.py index 040667a..959b23c 100644 --- a/datamaxi/datamaxi/premium.py +++ b/datamaxi/datamaxi/premium.py @@ -1,10 +1,10 @@ from typing import Any, List, Union, Optional import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.constants import Market, SortOrder -class Premium(API): +class Premium(Resource): """Client to fetch premium data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/naver/__init__.py b/datamaxi/naver/__init__.py index e4342c3..c5996ad 100644 --- a/datamaxi/naver/__init__.py +++ b/datamaxi/naver/__init__.py @@ -1,11 +1,11 @@ from typing import Any, List, Union import pandas as pd -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.utils import check_required_parameter from datamaxi.lib.constants import BASE_URL -class Naver(API): +class Naver(Resource): """Client to fetch Naver trend data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any): diff --git a/datamaxi/telegram/__init__.py b/datamaxi/telegram/__init__.py index 242650e..adca269 100644 --- a/datamaxi/telegram/__init__.py +++ b/datamaxi/telegram/__init__.py @@ -1,9 +1,9 @@ from typing import Any, Dict, Optional, Tuple, Callable -from datamaxi.api import API +from datamaxi.api import Resource from datamaxi.lib.constants import BASE_URL, SortOrder -class Telegram(API): +class Telegram(Resource): """Client to fetch Telegram data from DataMaxi+ API.""" def __init__(self, api_key=None, **kwargs: Any):