From 3a80e5e27288110ab53971a434f363d560df5710 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 16:38:46 +0330 Subject: [PATCH 01/12] =?UTF-8?q?=F0=9F=92=A5=20BREAKING=20CHANGE:=20clien?= =?UTF-8?q?ts=20updated=20to=20adhere=20to=20new=20Bitpin=20API=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 618 bytes .../__pycache__/async_client.cpython-312.pyc | Bin 0 -> 33582 bytes .../__pycache__/client.cpython-312.pyc | Bin 0 -> 29815 bytes .../clients/__pycache__/core.cpython-312.pyc | Bin 0 -> 21942 bytes src/bitpin/clients/async_client.py | 368 ++++++++++++----- src/bitpin/clients/client.py | 389 +++++++++++++----- src/bitpin/clients/core.py | 268 ++++++++---- 7 files changed, 737 insertions(+), 288 deletions(-) create mode 100644 src/bitpin/clients/__pycache__/__init__.cpython-312.pyc create mode 100644 src/bitpin/clients/__pycache__/async_client.cpython-312.pyc create mode 100644 src/bitpin/clients/__pycache__/client.cpython-312.pyc create mode 100644 src/bitpin/clients/__pycache__/core.cpython-312.pyc diff --git a/src/bitpin/clients/__pycache__/__init__.cpython-312.pyc b/src/bitpin/clients/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..950d0c3efb4b8bc49d39b66dbe6afc790179039d GIT binary patch literal 618 zcmaiyF-yZh6vyw9G?iEpoy6%xk!CQriwLzYE+W>cMUI%9<}kS{cUPpNpTfxx;Aim* zNWsa;ts*X+d`YYbBHr-$zkBa~@6SsfmzDwq=WKc9>K-HXQYQ1`e2Moqh&z;`K1y-w zHKznK9%IP=8e%Oq+02FMJ17U|kl5Q@zv{4Y$f?O&+FLh3i;J(K> z8Gt0G{E}-f6Mo*#tR0$NN>i1X=%NBeLesE%T`0|okN_%TE0hGrpoX8flWJN>dZUX% zrK6maL`fS9X^71@F=LrzT1hoBq;8FZL8rW(1L87a|6r=7Uz{%Z0tXQPoL?5d2fxCe zP>3gsKMX+X*H*L(Rqa7I4*f}!G1vuTlh#jzRnNq)-B!J2W1Q!VJ)n1Zie^VNQw66w zPBWet^iUO#D* literal 0 HcmV?d00001 diff --git a/src/bitpin/clients/__pycache__/async_client.cpython-312.pyc b/src/bitpin/clients/__pycache__/async_client.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ee2744016a7b69f553117af65f36b2c8fb21112 GIT binary patch literal 33582 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 D%oU{U literal 0 HcmV?d00001 diff --git a/src/bitpin/clients/__pycache__/core.cpython-312.pyc b/src/bitpin/clients/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dce3673a53f109abbb6541e008c01ff58c1cba6b GIT binary patch literal 21942 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 t.DictStrAny: # return {"status": "success", "id": response.request_info.url.parts[-2]} return await response.json() # type: ignore[no-any-return] except ValueError as exc: - raise RequestException(f"Invalid Response: {await response.text()}") from exc + msg = f"Invalid Response: {await response.text()}" + raise RequestException(msg) from exc async def _background_relogin_task(self) -> None: # type: ignore[override] """Background relogin task.""" @@ -342,10 +353,10 @@ async def _handle_login(self) -> None: # type: ignore[override] await self.login() if self._background_relogin: - self.loop.create_task(self._background_relogin_task()) # noqa + self.loop.create_task(self._background_relogin_task()) if self._background_refresh_token: - self.loop.create_task(self._background_refresh_token_task()) # noqa + self.loop.create_task(self._background_refresh_token_task()) async def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def, override] """ @@ -358,7 +369,7 @@ async def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-d Response (LoginResponse): Response. References: - [API Docs](https://docs.bitpin.ir/#02c24a5326) + [API Docs](https://docs.bitpin.ir/v1/docs/authentication/intro) """ kwargs["json"] = {"api_key": self.api_key, "secret_key": self.api_secret} @@ -383,7 +394,7 @@ async def refresh_access_token( # type: ignore[no-untyped-def, override] Response (RefreshTokenResponse): Response. References: - [API Docs](https://docs.bitpin.ir/#9b81094f74) + [API Docs](https://docs.bitpin.ir/v1/docs/authentication/refresh_token) """ kwargs["json"] = {"refresh": refresh_token or self.refresh_token} @@ -393,196 +404,252 @@ async def refresh_access_token( # type: ignore[no-untyped-def, override] return _ - async def get_user_info(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def, override] + # Deprecated Methods + async def get_user_info(self, **kwargs) -> None: # type: ignore[no-untyped-def, override] + """ + Get user info (DEPRECATED). """ - Get user info. - Args: - **kwargs: Kwargs. + warn( + "get_user_info is deprecated! if deprecated method's usage is still in need import client from bitpin.deprecated instead!", + DeprecationWarning, + 2, + ) + + # Working Methods + async def get_currencies_info( # type: ignore[no-untyped-def, override] + self, + ) -> t.CurrenciesInfo: + """ + Get currencies info. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#5b3c85d79e) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/currencies) + + Notes: + Rate limit: 10000/day or 200/minute if you are authenticated. """ - return await self._get(self.USER_INFO_URL, signed=True, **kwargs) + return await self._get(self.CURRENCIES_LIST_URL) - async def get_currencies_info( # type: ignore[no-untyped-def, override] - self, page: int = 1, **kwargs - ) -> t.DictStrAny: + async def get_markets_info(self) -> t.MarketsInfo: # type: ignore[no-untyped-def, override] """ - Get currencies info. - - Args: - page (int): Page. - **kwargs: Kwargs. + Get markets info. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#7e59da3d0d) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/markets) Notes: Rate limit: 10000/day or 200/minute if you are authenticated. """ - return await self._get(self.CURRENCIES_LIST_URL.format(page), **kwargs) + return await self._get(self.MARKETS_LIST_URL) - async def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def, override] + async def get_tickers_info(self) -> t.DictStrAny: """ - Get markets info. - - Args: - page (int): Page. - **kwargs: Kwargs. + Get tickets info. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#334792bb2b) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/tickers) Notes: - Rate limit: 10000/day or 200/minute if you are authenticated. + Rate limit: 80/minute . """ - return await self._get(self.MARKETS_LIST_URL.format(page), **kwargs) + return await self._get(self.TICKERS_LIST_URL) - async def get_wallets(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def, override] + async def get_wallets( # type: ignore[no-untyped-def, override] + self, + assets: t.OptionalStr = None, + service: t.OptionalStr = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, + **kwargs, + ) -> t.WalletInfo: """ Get wallets. Args: - **kwargs: Kwargs. + assets: asset name [BTC, IRT, USDT, ...] + service: name of service + offset: asset balance offset, i.e. assets below 10000 + limit: maximum received assets info Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#9b93495188) + [API Docs](https://docs.bitpin.ir/v1/docs/wallets) Notes: Rate limit: 10000/day. """ + kwargs["params"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } return await self._get(self.WALLETS_URL, signed=True, **kwargs) async def get_orderbook( # type: ignore[no-untyped-def, override] self, - market_id: int, - type: t.OrderTypes, - **kwargs, # pylint: disable=redefined-builtin + symbol: str, ) -> t.OrderbookResponse: """ Get orderbook. Args: - market_id (int): Market ID. - type (OrderTypes): Type. - **kwargs: Kwargs. + symbol (str): i.e. BTC_IRT, ETH_USDT Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#ec7180fc0e) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/orderbook) + + Notes: + Rate Limit: 60 Requests/minute """ + return await self._get( # type: ignore[return-value] - self.ORDERBOOK_URL.format(market_id, str(type)), version=self.PUBLIC_API_VERSION_2, **kwargs + self.ORDERBOOK_URL.format(symbol, str(type)), + version=self.PUBLIC_API_VERSION_1, ) async def get_recent_trades( # type: ignore[no-untyped-def, override] - self, market_id: int, **kwargs - ) -> t.TradeResponse: + self, symbol: str + ) -> t.RecentTradesInfo: """ Get recent trades. Args: - market_id (int): Market ID. - **kwargs: Kwargs. + symbol (str): i.e. BTC_IRT. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#1dd63530b5) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/matches) + + Notes: + Rate Limit: 60 requests/minute """ - return await self._get(self.RECENT_TRADES_URL.format(market_id), **kwargs) # type: ignore[return-value] + return await self._get(self.RECENT_TRADES_URL.format(symbol)) # type: ignore[return-value] async def get_user_orders( # type: ignore[no-untyped-def, override] self, - market_id: t.OptionalInt = None, - type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin - state: t.OptionalStr = None, - mode: t.OptionalStr = None, + symbol: t.OptionalStr = None, + side: t.OptionalOrderTypesList = None, # pylint: disable=redefined-builtin + state: t.OptionalOrderStateList = None, + type: t.OptionalOrderModesList = None, identifier: t.OptionalStr = None, - page: int = 1, + start: t.OptionalDate = None, + end: t.OptionalDate = None, + ids_in: t.OptionalStrList = None, + identifiers_in: t.OptionalStrList = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, **kwargs, ) -> t.OpenOrdersResponse: """ Get user orders. Args: - market_id (int): Market ID. - type (OrderTypes): Type. - state (str): State. - mode (str): Mode. - identifier (str): Identifier. - page (int): Page. + symbol (Optional[str]): symbol (e.g., BTC_IRT, ETH_USDT). Defaults to None. + side (Optional[List[str]]): The type of order, either 'buy' or 'sell'. Defaults to None. + state (Optional[List[str]]): The state of the order, can be 'initial', 'active', or 'closed'. Defaults to None. + type (Optional[List[str]]): The type of the order, can be 'limit', 'market', 'stop_limit', or 'oco'. Defaults to None. + identifier (Optional[str]): A unique identifier for the order, useful for tracking or preventing duplicate entries. Defaults to None. + start (Optional[date]): Show orders created after this date. Defaults to None. + end (Optional[date]): Show orders created before this date. Defaults to None. + ids_in (Optional[List[str]]): A list of order IDs to filter results. Defaults to None. + identifiers_in (Optional[List[str]]): A list of specific order identifiers to filter results. Defaults to None. + offset (Optional[int]): Show orders with IDs less than this value. Defaults to None. + limit (Optional[int]): The maximum number of orders to retrieve (maximum: 100). Defaults to None. **kwargs: Kwargs. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#8a7c2a2af5) + [API Docs](https://docs.bitpin.ir/v1/docs/order/get_order_list) + + Notes: + Rate Limit: 80 Requests/minute """ - kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} + kwargs["params"] = kwargs.get("params", {}) + kwargs["params"].update( + { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + ) return await self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] async def create_order( # type: ignore[no-untyped-def, override] self, - market: int, - amount1: float, - price: float, - mode: t.OrderModes, - type: t.OrderTypes, # pylint: disable=redefined-builtin + symbol: str, + type: t.OrderModes, + side: t.OrderTypes, # pylint: disable=redefined-builtin + base_amount: float, + quote_amount: t.OptionalFloat = None, + price: t.OptionalFloat = None, + stop_price: t.OptionalFloat = None, + oco_target_price: t.OptionalFloat = None, identifier: t.OptionalStr = None, - price_limit: t.OptionalFloat = None, - price_stop: t.OptionalFloat = None, - price_limit_oco: t.OptionalFloat = None, - amount2: t.OptionalFloat = None, **kwargs, ) -> t.CreateOrderResponse: """ Create order. Args: - market (int): Market. - amount1 (float): Amount1. - price (float): Price. - mode (OrderModes): Mode. - type (OrderTypes): Type. - identifier (str): Identifier. - price_limit (float): Price limit. - price_stop (float): Price stop. - price_limit_oco (float): Price limit oco. - amount2 (float): Amount2. + symbol (str): i.e. [USDT_IRT] + type: t.OrderModes + side: t.OrderTypes + price: float + base_amount: float + quote_amount: t.OptionalFloat = None + stop_price: t.OptionalFloat = None + oco_target_price: t.OptionalFloat = None + identifier: t.OptionalStr = None **kwargs: Kwargs. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#34b353d77b) - """ + [API Docs](https://docs.bitpin.ir/v1/docs/order/place_order) - kwargs["json"] = {k: v for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} + Notes: + Rate Limit: 5400 Requests/hour + """ + + kwargs["json"] = { + "symbol": symbol, + "type": type, + "side": side, + "price": price, + "base_amount": base_amount, + "quote_amount": quote_amount, + "stop_price": stop_price, + "oco_target_price": oco_target_price, + "identifier": identifier, + } + + kwargs["json"] = {k: v for k, v in kwargs["json"].items() if v is not None} return await self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] async def cancel_order( # type: ignore[no-untyped-def, override] @@ -599,36 +666,131 @@ async def cancel_order( # type: ignore[no-untyped-def, override] Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#3fe8d57657) + [API Docs](https://docs.bitpin.ir/v1/docs/order/cancel) + + Notes: + Rate Limit: 5400 Requests/hour + """ + + try: + await self._delete(self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs) # type: ignore[return-value] + except APIException as e: + raise e from e + else: + return {"status": "success", "id": order_id} + + async def create_order_bulk(self, orders: t.BulkOrderList, **kwargs): + """ + Create multiple orders in bulk. + + Args: + orders (BulkOrderList): A list of order objects to be created in bulk. + Each order object (dict) should contain: + - symbol (str): The market symbol for the order (e.g., USDT_IRT). + - base_amount (float): The amount of the base asset to be ordered. + - price (float): The price at which the order is placed (for limit orders). + - side (str): The side of the order, either 'buy' or 'sell'. + - type (str): The type of the order, such as 'limit', 'market', etc. + **kwargs: Additional parameters to be passed in the request. + + Returns: + Response (dict): Response. + + limitations: + - a maximum of 10 orders can be placed at a time + - all orders must be in the same market + - if one wrong order is in the list of order objects (dict), the entire request will raise an exception + + References: + [API Docs](https://docs.bitpin.ir/v1/docs/order/Bulk%20Orders/Place_Bulk_Orders) + + Notes: + Rate Limit: 1800 Requests/hour + """ + + max_orders = 10 + if len(orders) > max_orders: + msg = "A maximum of 10 orders can be placed at a time! not creating order!" + raise ValueError(msg) + + market = orders[0]["symbol"] if orders else None + if any(order["symbol"] != market for order in orders): + msg = "All orders must be in the same market! not creating order!" + raise ValueError(msg) + + kwargs["json"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + return await self._post(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] + + async def cancel_order_bulk( + self, + ids: t.OptionalStrList = None, + identifiers: t.OptionalStrList = None, + **kwargs, + ) -> t.CancelBulkOrderResponse: + """ + Cancel multiple orders in bulk using either order IDs or specific identifiers. + + Args: + ids (Optional[List[str]]): A list of order IDs to cancel. Defaults to None. + identifiers (Optional[List[str]]): A list of specific order identifiers to cancel. Defaults to None. + **kwargs: Additional parameters. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/v1/docs/order/Bulk%20Orders/Cancel_Bulk_Orders) """ - return await self._delete(self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs) # type: ignore[return-value] + kwargs["json"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + return await self._delete(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] async def get_user_trades( # type: ignore[no-untyped-def, override] self, - market_id: t.OptionalInt = None, - type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin - page: int = 1, + symbol: t.OptionalStr = None, + side: t.OptionalOrderTypesList = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, **kwargs, - ) -> t.DictStrAny: + ) -> t.TradeResponse: """ - Get user trades. + Retrieve user filled (executed) orders. Args: - market_id (int): Market ID. - type (OrderTypes): Type. - page (int): Page. - **kwargs: Kwargs. + symbol (Optional[str]): symbol (e.g., BTC_IRT, ETH_USDT). Defaults to None. + side (Optional[str]): The side of the trade, either 'buy' or 'sell'. Defaults to None. + offset (Optional[int]): Fetch trades where the order ID is less than this value. Useful for pagination. Defaults to None. + limit (Optional[int]): Maximum number of trades to retrieve, with an upper limit of 100. Defaults to None. + **kwargs: Additional parameters. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#3fe8d57657) + [API Docs](https://docs.bitpin.ir/v1/docs/order/get_fills_list) + + Notes: + Rate Limit: 80 Requests/minute """ - kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} - return await self._get(self.USER_TRADES_URL, signed=True, **kwargs) + kwargs["params"] = kwargs.get("params", {}) + kwargs["params"].update( + { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + ) + + return await self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] async def close_connection(self) -> None: # type: ignore[override] """Close connection.""" diff --git a/src/bitpin/clients/client.py b/src/bitpin/clients/client.py index 560c794..28c22e3 100644 --- a/src/bitpin/clients/client.py +++ b/src/bitpin/clients/client.py @@ -2,15 +2,17 @@ import time from threading import Thread +from warnings import warn + import requests -from .core import CoreClient -from .. import types as t from .. import enums +from .. import response_types as t from ..exceptions import ( APIException, RequestException, ) +from .core import CoreClient class Client(CoreClient): @@ -132,7 +134,9 @@ def _get( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api(enums.RequestMethod.GET, path, signed, version, **kwargs) + return self._request_api( + enums.RequestMethod.GET, path, signed, version, **kwargs + ) def _post( # type: ignore[no-untyped-def] self, @@ -154,7 +158,9 @@ def _post( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api(enums.RequestMethod.POST, path, signed, version, **kwargs) + return self._request_api( + enums.RequestMethod.POST, path, signed, version, **kwargs + ) def _delete( # type: ignore[no-untyped-def] self, @@ -176,7 +182,9 @@ def _delete( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api(enums.RequestMethod.DELETE, path, signed, version, **kwargs) + return self._request_api( + enums.RequestMethod.DELETE, path, signed, version, **kwargs + ) def _request_api( # type: ignore[no-untyped-def] self, @@ -243,12 +251,16 @@ def _handle_response(response: requests.Response) -> t.DictStrAny: # type: igno if not str(response.status_code).startswith("2"): if response.request.method.lower() == enums.RequestMethod.DELETE: # type: ignore[union-attr] - return {"status": "success", "id": response.request.path_url.split("/")[-2]} + return { + "status": "success", + "id": response.request.path_url.split("/")[-2], + } raise APIException(response, response.status_code, response.text) try: return response.json() # type: ignore[no-any-return] except ValueError as exc: - raise RequestException(f"Invalid Response: {response.text}") from exc + msg = f"Invalid Response: {response.text}" + raise RequestException(msg) from exc def _handle_login(self) -> None: """Handle login.""" @@ -282,7 +294,7 @@ def _background_refresh_token_task(self) -> None: except Exception: # pylint: disable=broad-except continue - def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def] + def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def, override] """ Login and set (refresh_token/access_token). @@ -293,7 +305,7 @@ def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def] Response (LoginResponse): Response. References: - [API Docs](https://docs.bitpin.ir/#02c24a5326) + [API Docs](https://docs.bitpin.ir/v1/docs/authentication/intro) """ kwargs["json"] = {"api_key": self.api_key, "secret_key": self.api_secret} @@ -304,7 +316,7 @@ def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def] return _ - def refresh_access_token( # type: ignore[no-untyped-def] + def refresh_access_token( # type: ignore[no-untyped-def, override] self, refresh_token: t.OptionalStr = None, **kwargs ) -> t.RefreshTokenResponse: """ @@ -318,7 +330,7 @@ def refresh_access_token( # type: ignore[no-untyped-def] Response (RefreshTokenResponse): Response. References: - [API Docs](https://docs.bitpin.ir/#9b81094f74) + [API Docs](https://docs.bitpin.ir/v1/docs/authentication/refresh_token) """ kwargs["json"] = {"refresh": refresh_token or self.refresh_token} @@ -328,194 +340,257 @@ def refresh_access_token( # type: ignore[no-untyped-def] return _ - def get_user_info(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + # Deprecated Methods + def get_user_info(self, **kwargs) -> None: # type: ignore[no-untyped-def, override] + """ + Get user info (DEPRECATED). """ - Get user info. - Args: - **kwargs: Kwargs. + warn( + "get_user_info is deprecated! if deprecated method's usage is still in need import client from bitpin.deprecated instead!", + DeprecationWarning, + 2, + ) + + # Working Methods + def get_currencies_info( # type: ignore[no-untyped-def, override] + self, + ) -> t.CurrenciesInfo: + """ + Get currencies info. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#5b3c85d79e) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/currencies) + + Notes: + Rate limit: 10000/day or 200/minute if you are authenticated. """ - return self._get(self.USER_INFO_URL, signed=True, **kwargs) + return self._get(self.CURRENCIES_LIST_URL) - def get_currencies_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_markets_info(self) -> t.MarketsInfo: # type: ignore[no-untyped-def, override] """ - Get currencies info. - - Args: - page (int): Page. - **kwargs: Kwargs. + Get markets info. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#7e59da3d0d) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/markets) Notes: Rate limit: 10000/day or 200/minute if you are authenticated. """ - return self._get(self.CURRENCIES_LIST_URL.format(page), **kwargs) + return self._get(self.MARKETS_LIST_URL) - def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_tickers_info(self) -> t.DictStrAny: """ - Get markets info. - - Args: - page (int): Page. - **kwargs: Kwargs. + Get tickets info. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#334792bb2b) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/tickers) Notes: - Rate limit: 10000/day or 200/minute if you are authenticated. + Rate limit: 80/minute . """ - return self._get(self.MARKETS_LIST_URL.format(page), **kwargs) + return self._get(self.TICKERS_LIST_URL) - def get_wallets(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_wallets( # type: ignore[no-untyped-def, override] + self, + assets: t.OptionalStr = None, + service: t.OptionalStr = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, + **kwargs, + ) -> t.WalletInfo: """ Get wallets. Args: - **kwargs: Kwargs. + assets: asset name [BTC, IRT, USDT, ...] + service: name of service + offset: asset balance offset, i.e. assets below 10000 + limit: maximum received assets info Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#9b93495188) + [API Docs](https://docs.bitpin.ir/v1/docs/wallets) Notes: Rate limit: 10000/day. """ + kwargs["params"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } return self._get(self.WALLETS_URL, signed=True, **kwargs) - def get_orderbook( # type: ignore[no-untyped-def] + def get_orderbook( # type: ignore[no-untyped-def, override] self, - market_id: int, - type: t.OrderTypes, - **kwargs, # pylint: disable=redefined-builtin + symbol: str, ) -> t.OrderbookResponse: """ Get orderbook. Args: - market_id (int): Market ID. - type (OrderTypes): Type. - **kwargs: Kwargs. + symbol (str): i.e. BTC_IRT, ETH_USDT Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#ec7180fc0e) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/orderbook) + + Notes: + Rate Limit: 60 Requests/minute """ - return self._get(self.ORDERBOOK_URL.format(market_id, str(type)), **kwargs) # type: ignore[return-value] + return self._get( # type: ignore[return-value] + self.ORDERBOOK_URL.format(symbol, str(type)), + version=self.PUBLIC_API_VERSION_1, + ) - def get_recent_trades(self, market_id: int, **kwargs) -> t.TradeResponse: # type: ignore[no-untyped-def] + def get_recent_trades( # type: ignore[no-untyped-def, override] + self, symbol: str + ) -> t.RecentTradesInfo: """ Get recent trades. Args: - market_id (int): Market ID. - **kwargs: Kwargs. + symbol (str): i.e. BTC_IRT. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#1dd63530b5) + [API Docs](https://docs.bitpin.ir/v1/docs/market-data/matches) + + Notes: + Rate Limit: 60 requests/minute """ - return self._get(self.RECENT_TRADES_URL.format(market_id), **kwargs) # type: ignore[return-value] + return self._get(self.RECENT_TRADES_URL.format(symbol)) # type: ignore[return-value] - def get_user_orders( # type: ignore[no-untyped-def] + def get_user_orders( # type: ignore[no-untyped-def, override] self, - market_id: t.OptionalInt = None, - type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin - state: t.OptionalStr = None, - mode: t.OptionalStr = None, + symbol: t.OptionalStr = None, + side: t.OptionalOrderTypesList = None, # pylint: disable=redefined-builtin + state: t.OptionalOrderStateList = None, + type: t.OptionalOrderModesList = None, identifier: t.OptionalStr = None, - page: int = 1, + start: t.OptionalDate = None, + end: t.OptionalDate = None, + ids_in: t.OptionalStrList = None, + identifiers_in: t.OptionalStrList = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, **kwargs, ) -> t.OpenOrdersResponse: """ - Get user Orders. + Get user orders. Args: - market_id (int): Market ID. - type (OrderTypes): Type. - state (str): State. - mode (str): Mode. - identifier (str): Identifier. - page (int): Page. + symbol (Optional[str]): symbol (e.g., BTC_IRT, ETH_USDT). Defaults to None. + side (Optional[List[str]]): The type of order, either 'buy' or 'sell'. Defaults to None. + state (Optional[List[str]]): The state of the order, can be 'initial', 'active', or 'closed'. Defaults to None. + type (Optional[List[str]]): The type of the order, can be 'limit', 'market', 'stop_limit', or 'oco'. Defaults to None. + identifier (Optional[str]): A unique identifier for the order, useful for tracking or preventing duplicate entries. Defaults to None. + start (Optional[date]): Show orders created after this date. Defaults to None. + end (Optional[date]): Show orders created before this date. Defaults to None. + ids_in (Optional[List[str]]): A list of order IDs to filter results. Defaults to None. + identifiers_in (Optional[List[str]]): A list of specific order identifiers to filter results. Defaults to None. + offset (Optional[int]): Show orders with IDs less than this value. Defaults to None. + limit (Optional[int]): The maximum number of orders to retrieve (maximum: 100). Defaults to None. **kwargs: Kwargs. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#8a7c2a2af5) + [API Docs](https://docs.bitpin.ir/v1/docs/order/get_order_list) + + Notes: + Rate Limit: 80 Requests/minute """ - kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} + kwargs["params"] = kwargs.get("params", {}) + kwargs["params"].update( + { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + ) return self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] - def create_order( # type: ignore[no-untyped-def] + def create_order( # type: ignore[no-untyped-def, override] self, - market: int, - amount1: float, - price: float, - mode: t.OrderModes, - type: t.OrderTypes, # pylint: disable=redefined-builtin + symbol: str, + type: t.OrderModes, + side: t.OrderTypes, # pylint: disable=redefined-builtin + base_amount: float, + quote_amount: t.OptionalFloat = None, + price: t.OptionalFloat = None, + stop_price: t.OptionalFloat = None, + oco_target_price: t.OptionalFloat = None, identifier: t.OptionalStr = None, - price_limit: t.OptionalFloat = None, - price_stop: t.OptionalFloat = None, - price_limit_oco: t.OptionalFloat = None, - amount2: t.OptionalFloat = None, **kwargs, ) -> t.CreateOrderResponse: """ Create order. Args: - market (int): Market. - amount1 (float): Amount1. - price (float): Price. - mode (OrderModes): Mode. - type (OrderTypes): Type. - identifier (str): Identifier. - price_limit (float): Price limit. - price_stop (float): Price stop. - price_limit_oco (float): Price limit oco. - amount2 (float): Amount2. + symbol (str): i.e. [USDT_IRT] + type: t.OrderModes + side: t.OrderTypes + price: float + base_amount: float + quote_amount: t.OptionalFloat = None + stop_price: t.OptionalFloat = None + oco_target_price: t.OptionalFloat = None + identifier: t.OptionalStr = None **kwargs: Kwargs. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#34b353d77b) - """ + [API Docs](https://docs.bitpin.ir/v1/docs/order/place_order) - kwargs["json"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} + Notes: + Rate Limit: 5400 Requests/hour + """ + + kwargs["json"] = { + "symbol": symbol, + "type": type, + "side": side, + "price": price, + "base_amount": base_amount, + "quote_amount": quote_amount, + "stop_price": stop_price, + "oco_target_price": oco_target_price, + "identifier": identifier, + } + + kwargs["json"] = {k: v for k, v in kwargs["json"].items() if v is not None} return self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] - def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # type: ignore[no-untyped-def] + def cancel_order( # type: ignore[no-untyped-def, override] + self, order_id: str, **kwargs + ) -> t.CancelOrderResponse: """ Cancel order. @@ -527,38 +602,138 @@ def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # typ Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#3fe8d57657) + [API Docs](https://docs.bitpin.ir/v1/docs/order/cancel) + + Notes: + Rate Limit: 5400 Requests/hour + """ + + try: + self._delete(self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs) # type: ignore[return-value] + except APIException as e: + raise e from e + else: + return {"status": "success", "id": order_id} + + def create_order_bulk(self, orders: t.BulkOrderList, **kwargs): + """ + Create multiple orders in bulk. + + Args: + orders (BulkOrderList): A list of order objects to be created in bulk. + Each order object (dict) should contain: + - symbol (str): The market symbol for the order (e.g., USDT_IRT). + - base_amount (float): The amount of the base asset to be ordered. + - price (float): The price at which the order is placed (for limit orders). + - side (str): The side of the order, either 'buy' or 'sell'. + - type (str): The type of the order, such as 'limit', 'market', etc. + **kwargs: Additional parameters to be passed in the request. + + Returns: + Response (dict): Response. + + limitations: + - a maximum of 10 orders can be placed at a time + - all orders must be in the same market + - if one wrong order is in the list of order objects (dict), the entire request will raise an exception + + References: + [API Docs](https://docs.bitpin.ir/v1/docs/order/Bulk%20Orders/Place_Bulk_Orders) + + Notes: + Rate Limit: 1800 Requests/hour """ - return self._delete(self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs) # type: ignore[return-value] + max_orders = 10 + if len(orders) > max_orders: + msg = "A maximum of 10 orders can be placed at a time! not creating order!" + raise ValueError(msg) + + market = orders[0]["symbol"] if orders else None + if any(order["symbol"] != market for order in orders): + msg = "All orders must be in the same market! not creating order!" + raise ValueError(msg) - def get_user_trades( # type: ignore[no-untyped-def] + kwargs["json"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + return self._post(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] + + def cancel_order_bulk( self, - market_id: t.OptionalInt = None, - type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin - page: int = 1, + ids: t.OptionalStrList = None, + identifiers: t.OptionalStrList = None, **kwargs, - ) -> t.DictStrAny: + ) -> t.CancelBulkOrderResponse: """ - Get user trades. + Cancel multiple orders in bulk using either order IDs or specific identifiers. Args: - market_id (int): Market ID. - type (OrderTypes): Type. - page (int): Page. - **kwargs: Kwargs. + ids (Optional[List[str]]): A list of order IDs to cancel. Defaults to None. + identifiers (Optional[List[str]]): A list of specific order identifiers to cancel. Defaults to None. + **kwargs: Additional parameters. Returns: Response (dict): Response. References: - [API Docs](https://docs.bitpin.ir/#3fe8d57657) + [API Docs](https://docs.bitpin.ir/v1/docs/order/Bulk%20Orders/Cancel_Bulk_Orders) + """ + + kwargs["json"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in {"self", "kwargs"} + } + return self._delete(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] + + def get_user_trades( # type: ignore[no-untyped-def, override] + self, + symbol: t.OptionalStr = None, + side: t.OptionalOrderTypesList = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, + **kwargs, + ) -> t.TradeResponse: """ + Retrieve user filled (executed) orders. + + Args: + symbol (Optional[str]): symbol (e.g., BTC_IRT, ETH_USDT). Defaults to None. + side (Optional[str]): The side of the trade, either 'buy' or 'sell'. Defaults to None. + offset (Optional[int]): Fetch trades where the order ID is less than this value. Useful for pagination. Defaults to None. + limit (Optional[int]): Maximum number of trades to retrieve, with an upper limit of 100. Defaults to None. + **kwargs: Additional parameters. - kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} - return self._get(self.USER_TRADES_URL, signed=True, **kwargs) + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/v1/docs/order/get_fills_list) + + Notes: + Rate Limit: 80 Requests/minute + """ + + kwargs["params"] = kwargs.get("params", {}) + kwargs["params"].update( + { + k: str(v) + for k, v in locals().items() + if v is not None + and k + not in { + "self", + "kwargs", + } + } + ) + + return self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] - def close_connection(self) -> None: + def close_connection(self) -> None: # type: ignore[override] """Close connection.""" - self.session.close() + self.session.close() # type: ignore[misc] diff --git a/src/bitpin/clients/core.py b/src/bitpin/clients/core.py index 0e3b558..cd2951e 100644 --- a/src/bitpin/clients/core.py +++ b/src/bitpin/clients/core.py @@ -5,29 +5,32 @@ ABC, abstractmethod, ) -from .. import types as t + +from .. import response_types as t class CoreClient(ABC): # pylint: disable=too-many-instance-attributes """Core Client.""" - API_URL = "https://api.bitpin.ir" + API_URL = "https://api.bitpin.ir/api" PUBLIC_API_VERSION_1 = "v1" PUBLIC_API_VERSION_2 = "v2" REQUEST_TIMEOUT: float = 10 - LOGIN_URL = "usr/api/login/" + LOGIN_URL = "usr/authenticate/" REFRESH_TOKEN_URL = "usr/refresh_token/" - USER_INFO_URL = "usr/info/" - CURRENCIES_LIST_URL = "mkt/currencies/?page={}" - MARKETS_LIST_URL = "mkt/markets/?page={}" + CURRENCIES_LIST_URL = "mkt/currencies/" + MARKETS_LIST_URL = "mkt/markets/" + TICKERS_LIST_URL = "mkt/tickers/" WALLETS_URL = "wlt/wallets/" - ORDERBOOK_URL = "mth/actives/{}/?type={}" + ORDERBOOK_URL = "mth/orderbook/{}/" RECENT_TRADES_URL = "mth/matches/{}/" ORDERS_URL = "odr/orders/" + FILLED_ORDERS_URL = "odr/fills/" USER_TRADES_URL = "odr/matches/?type={}" + BULK_ORDER_URL = "odr/orders/bulk/" def __init__( # type: ignore[no-untyped-def] self, @@ -75,8 +78,12 @@ def __init__( # type: ignore[no-untyped-def] self.api_key = api_key or os.environ.get("BITPIN_API_KEY") self.api_secret = api_secret or os.environ.get("BITPIN_API_SECRET") - self.access_token: t.OptionalStr = access_token or os.environ.get("BITPIN_ACCESS_TOKEN") - self.refresh_token: t.OptionalStr = refresh_token or os.environ.get("BITPIN_REFRESH_TOKEN") + self.access_token: t.OptionalStr = access_token or os.environ.get( + "BITPIN_ACCESS_TOKEN" + ) + self.refresh_token: t.OptionalStr = refresh_token or os.environ.get( + "BITPIN_REFRESH_TOKEN" + ) self._background_relogin = background_relogin self._background_relogin_interval = background_relogin_interval @@ -86,13 +93,15 @@ def __init__( # type: ignore[no-untyped-def] self._requests_params = requests_params self.session = self._init_session() - def _get_request_kwargs(self, method: t.RequestMethods, signed: bool, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def _get_request_kwargs( + self, method: t.RequestMethods, signed: bool, **kwargs + ) -> t.DictStrAny: # type: ignore[no-untyped-def] kwargs["timeout"] = self.REQUEST_TIMEOUT if self._requests_params: kwargs.update(self._requests_params) - data = kwargs.get("data", None) + data = kwargs.get("data") if data and isinstance(data, dict): kwargs["data"] = data @@ -106,13 +115,17 @@ def _get_request_kwargs(self, method: t.RequestMethods, signed: bool, **kwargs) kwargs["headers"] = headers if data and method == "get": - kwargs["params"] = "&".join(f"{data[0]}={data[1]}" for data in kwargs["data"]) + kwargs["params"] = "&".join( + f"{data[0]}={data[1]}" for data in kwargs["data"] + ) del kwargs["data"] return kwargs @staticmethod - def _pick(response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results") -> t.DictStrAny: + def _pick( + response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results" + ) -> t.DictStrAny: for _ in response.get(result_key, []): if _[key] == value: response[result_key] = _ @@ -134,7 +147,13 @@ def _init_session(self) -> t.HttpSession: raise NotImplementedError @abstractmethod - def _get(self, path: str, signed: bool = False, version: str = PUBLIC_API_VERSION_1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def _get( + self, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] """ Make a GET request. @@ -151,7 +170,13 @@ def _get(self, path: str, signed: bool = False, version: str = PUBLIC_API_VERSIO raise NotImplementedError @abstractmethod - def _post(self, path: str, signed: bool = False, version: str = PUBLIC_API_VERSION_1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def _post( + self, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] """ Make a POST request. @@ -168,7 +193,13 @@ def _post(self, path: str, signed: bool = False, version: str = PUBLIC_API_VERSI raise NotImplementedError @abstractmethod - def _delete(self, path: str, signed: bool = False, version: str = PUBLIC_API_VERSION_1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def _delete( + self, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] """ Make a DELETE request. @@ -210,7 +241,9 @@ def _request_api( # type: ignore[no-untyped-def] raise NotImplementedError @abstractmethod - def _request(self, method: t.RequestMethods, uri: str, signed: bool, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def _request( + self, method: t.RequestMethods, uri: str, signed: bool, **kwargs + ) -> t.DictStrAny: # type: ignore[no-untyped-def] """ Request. @@ -271,7 +304,9 @@ def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def] raise NotImplementedError @abstractmethod - def refresh_access_token(self, refresh_token: t.OptionalStr = None, **kwargs) -> t.RefreshTokenResponse: # type: ignore[no-untyped-def] + def refresh_access_token( + self, refresh_token: t.OptionalStr = None, **kwargs + ) -> t.RefreshTokenResponse: # type: ignore[no-untyped-def] """ Refresh token. @@ -285,24 +320,21 @@ def refresh_access_token(self, refresh_token: t.OptionalStr = None, **kwargs) -> raise NotImplementedError @abstractmethod - def get_user_info(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_user_info(self, **kwargs) -> None: # type: ignore[no-untyped-def] """ - Get user info. + Get user info. (Deprecated) Returns: - dict: Response. + None. """ raise NotImplementedError @abstractmethod - def get_currencies_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_currencies_info(self) -> t.CurrenciesInfo: # type: ignore[no-untyped-def] """ Get currencies info. - Args: - page (int): Page. - Returns: dict: Response. """ @@ -310,12 +342,20 @@ def get_currencies_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: raise NotImplementedError @abstractmethod - def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_markets_info(self) -> t.MarketsInfo: # type: ignore[no-untyped-def] """ Get markets info. - Args: - page (int): Page. + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_tickers_info(self) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get tickers info. Returns: dict: Response. @@ -324,41 +364,58 @@ def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ig raise NotImplementedError @abstractmethod - def get_wallets(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + def get_wallets( + self, + assets: t.OptionalStr, + service: t.OptionalStr, + offset: t.OptionalFloat, + limit: t.OptionalInt, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] """ Get wallets. + Args: + assets: asset name [BTC, IRT, USDT, ...] + service: name of service + offset: asset balance offset, i.e. assets below 10000 + limit: maximum received assets info + Returns: - dict: Response. + Response (dict): Response. + + Notes: + Rate limit: 10000/day. """ raise NotImplementedError @abstractmethod - def get_orderbook(self, market_id: int, type: t.OrderTypes, **kwargs) -> t.OrderbookResponse: # type: ignore[no-untyped-def] # pylint: disable=redefined-builtin + def get_orderbook( + self, + symbol: str, + ) -> t.OrderbookResponse: # type: ignore[no-untyped-def] # pylint: disable=redefined-builtin """ Get orderbook. Args: - market_id (int): Market ID. - type (str): Type. + symbol (str): i.e. BTC_IRT, ETH_USDT Returns: - dict: Response. + Response (dict): Response. """ raise NotImplementedError @abstractmethod - def get_recent_trades(self, market_id: int, **kwargs) -> t.TradeResponse: # type: ignore[no-untyped-def] + def get_recent_trades(self, symbol: str) -> t.RecentTradesInfo: """ Get recent trades. Args: - market_id (int): Market ID. + symbol (str): i.e. BTC_IRT. Returns: - dict: Response. + Response (dict): Response. """ raise NotImplementedError @@ -366,27 +423,38 @@ def get_recent_trades(self, market_id: int, **kwargs) -> t.TradeResponse: # typ @abstractmethod def get_user_orders( # type: ignore[no-untyped-def] self, - market_id: t.OptionalInt = None, - type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin - state: t.OptionalStr = None, - mode: t.OptionalStr = None, + symbol: t.OptionalStr = None, + side: t.OptionalOrderTypesList = None, # pylint: disable=redefined-builtin + state: t.OptionalOrderStateList = None, + type: t.OptionalOrderModesList = None, identifier: t.OptionalStr = None, - page: int = 1, + start: t.OptionalDate = None, + end: t.OptionalDate = None, + ids_in: t.OptionalStrList = None, + identifiers_in: t.OptionalStrList = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, **kwargs, ) -> t.OpenOrdersResponse: """ Get user orders. Args: - market_id (int): Market ID. - type (str): Type. - state (str): State. - mode (str): Mode. - identifier (str): Identifier. - page (int): Page. + symbol (Optional[str]): symbol (e.g., BTC_IRT, ETH_USDT). Defaults to None. + side (Optional[List[str]]): The type of order, either 'buy' or 'sell'. Defaults to None. + state (Optional[List[str]]): The state of the order, can be 'initial', 'active', or 'closed'. Defaults to None. + type (Optional[List[str]]): The type of the order, can be 'limit', 'market', 'stop_limit', or 'oco'. Defaults to None. + identifier (Optional[str]): A unique identifier for the order, useful for tracking or preventing duplicate entries. Defaults to None. + start (Optional[date]): Show orders created after this date. Defaults to None. + end (Optional[date]): Show orders created before this date. Defaults to None. + ids_in (Optional[List[str]]): A list of order IDs to filter results. Defaults to None. + identifiers_in (Optional[List[str]]): A list of specific order identifiers to filter results. Defaults to None. + offset (Optional[int]): Show orders with IDs less than this value. Defaults to None. + limit (Optional[int]): The maximum number of orders to retrieve (maximum: 100). Defaults to None. + **kwargs: Kwargs. Returns: - dict: Response. + Response (dict): Response. """ raise NotImplementedError @@ -394,35 +462,76 @@ def get_user_orders( # type: ignore[no-untyped-def] @abstractmethod def create_order( # type: ignore[no-untyped-def] self, - market: int, - amount1: float, - price: float, - mode: t.OrderModes, - type: t.OrderTypes, # pylint: disable=redefined-builtin + symbol: str, + type: t.OrderModes, + side: t.OrderTypes, # pylint: disable=redefined-builtin + base_amount: float, + quote_amount: t.OptionalFloat = None, + price: t.OptionalFloat = None, + stop_price: t.OptionalFloat = None, + oco_target_price: t.OptionalFloat = None, identifier: t.OptionalStr = None, - price_limit: t.OptionalFloat = None, - price_stop: t.OptionalFloat = None, - price_limit_oco: t.OptionalFloat = None, - amount2: t.OptionalFloat = None, **kwargs, ) -> t.CreateOrderResponse: """ Create order. Args: - market (int): Market. - amount1 (float): Amount1. - price (float): Price. - mode (str): Mode. - type (str): Type. - identifier (str): Identifier. - price_limit (float): Price limit. - price_stop (float): Price stop. - price_limit_oco (float): Price limit oco. - amount2 (float): Amount2. + symbol (str): i.e. [USDT_IRT] + type: t.OrderModes + side: t.OrderTypes + price: float + base_amount: float + quote_amount: t.OptionalFloat = None + stop_price: t.OptionalFloat = None + oco_target_price: t.OptionalFloat = None + identifier: t.OptionalStr = None + **kwargs: Kwargs. Returns: - dict: Response. + Response (dict): Response. + """ + + raise NotImplementedError + + @abstractmethod + async def create_order_bulk(self, orders: t.BulkOrderList, **kwargs): + """ + Create multiple orders in bulk. + + Args: + orders (BulkOrderList): A list of order objects to be created in bulk. + Each order object (dict) should contain: + - symbol (str): The market symbol for the order (e.g., USDT_IRT). + - base_amount (float): The amount of the base asset to be ordered. + - price (float): The price at which the order is placed (for limit orders). + - side (str): The side of the order, either 'buy' or 'sell'. + - type (str): The type of the order, such as 'limit', 'market', etc. + **kwargs: Additional parameters to be passed in the request. + + Returns: + Response (dict): Response. + """ + + raise NotImplementedError + + @abstractmethod + def cancel_order_bulk( + self, + ids: t.OptionalStrList = None, + identifiers: t.OptionalStrList = None, + **kwargs, + ) -> t.CancelBulkOrderResponse: + """ + Cancel multiple orders in bulk using either order IDs or specific identifiers. + + Args: + ids (Optional[List[str]]): A list of order IDs to cancel. Defaults to None. + identifiers (Optional[List[str]]): A list of specific order identifiers to cancel. Defaults to None. + **kwargs: Additional parameters. + + Returns: + t.CancelBulkOrderResponse """ raise NotImplementedError @@ -444,21 +553,24 @@ def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # typ @abstractmethod def get_user_trades( # type: ignore[no-untyped-def] self, - market_id: t.OptionalInt = None, - type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin - page: int = 1, + symbol: t.OptionalStr = None, + side: t.OptionalOrderTypesList = None, + offset: t.OptionalInt = None, + limit: t.OptionalInt = None, **kwargs, - ) -> t.DictStrAny: + ) -> t.TradeResponse: """ - Get user trades. + Retrieve user filled (executed) orders. Args: - market_id (int): Market ID. - type (str): Type. - page (int): Page. + symbol (Optional[str]): symbol (e.g., BTC_IRT, ETH_USDT). Defaults to None. + side (Optional[str]): The side of the trade, either 'buy' or 'sell'. Defaults to None. + offset (Optional[int]): Fetch trades where the order ID is less than this value. Useful for pagination. Defaults to None. + limit (Optional[int]): Maximum number of trades to retrieve, with an upper limit of 100. Defaults to None. + **kwargs: Additional parameters. Returns: - dict: Response. + Response (dict): Response. """ raise NotImplementedError From 609e406b93f2be9e3ed7db2d3fc3b74da062edc7 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 16:49:24 +0330 Subject: [PATCH 02/12] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20deprecate:=20depr?= =?UTF-8?q?ecated=20old=20API=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/deprecated/__init__.py | 9 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 373 bytes .../deprecated_enums.cpython-312.pyc | Bin 0 -> 7155 bytes .../deprecated_types.cpython-312.pyc | Bin 0 -> 7698 bytes src/bitpin/deprecated/clients/__init__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 569 bytes .../__pycache__/async_client.cpython-312.pyc | Bin 0 -> 24741 bytes .../__pycache__/client.cpython-312.pyc | Bin 0 -> 21087 bytes .../clients/__pycache__/core.cpython-312.pyc | Bin 0 -> 16793 bytes src/bitpin/deprecated/clients/async_client.py | 669 ++++++++++++++++++ src/bitpin/deprecated/clients/client.py | 594 ++++++++++++++++ src/bitpin/deprecated/clients/core.py | 505 +++++++++++++ src/bitpin/deprecated/deprecated_enums.py | 200 ++++++ src/bitpin/deprecated/deprecated_types.py | 204 ++++++ 14 files changed, 2198 insertions(+) create mode 100644 src/bitpin/deprecated/__init__.py create mode 100644 src/bitpin/deprecated/__pycache__/__init__.cpython-312.pyc create mode 100644 src/bitpin/deprecated/__pycache__/deprecated_enums.cpython-312.pyc create mode 100644 src/bitpin/deprecated/__pycache__/deprecated_types.cpython-312.pyc create mode 100644 src/bitpin/deprecated/clients/__init__.py create mode 100644 src/bitpin/deprecated/clients/__pycache__/__init__.cpython-312.pyc create mode 100644 src/bitpin/deprecated/clients/__pycache__/async_client.cpython-312.pyc create mode 100644 src/bitpin/deprecated/clients/__pycache__/client.cpython-312.pyc create mode 100644 src/bitpin/deprecated/clients/__pycache__/core.cpython-312.pyc create mode 100644 src/bitpin/deprecated/clients/async_client.py create mode 100644 src/bitpin/deprecated/clients/client.py create mode 100644 src/bitpin/deprecated/clients/core.py create mode 100644 src/bitpin/deprecated/deprecated_enums.py create mode 100644 src/bitpin/deprecated/deprecated_types.py diff --git a/src/bitpin/deprecated/__init__.py b/src/bitpin/deprecated/__init__.py new file mode 100644 index 0000000..10baab5 --- /dev/null +++ b/src/bitpin/deprecated/__init__.py @@ -0,0 +1,9 @@ +"""# Deprecated Bitpin Python Library.""" + +from .clients.async_client import AsyncClient +from .clients.client import Client + +__all__ = [ + "AsyncClient", + "Client", +] diff --git a/src/bitpin/deprecated/__pycache__/__init__.cpython-312.pyc b/src/bitpin/deprecated/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..663d94e280d11b570d4c8c9ea11a40ae1c0e8362 GIT binary patch literal 373 zcmX|-y-ve05XWuj3(=MVSdk*SkfHGlAS9q;g_yHgsckhDb`m+MP}q119swcoCcFSd zVnPgTk-C)yJ8ik){QLVpeCM~(Xdf7R{ykc}n)AzwouMtP&I~*O1`@y^LlP>KAZXc< zj&c*%u*2F9oCaPWIZX&-82w?>!q&=p6UKOXdh-_4!+aByf+e=$(3sWfxc>yaU!Fq|nPdJ(P^WsWVyhZlE3?@Y6q9!CRoAutgD%eu;3q3T= j?LO6sah(Gp`~cB6n0$iM&IjJ%yF2VWXMi39ZGZa@3A<`z literal 0 HcmV?d00001 diff --git a/src/bitpin/deprecated/__pycache__/deprecated_enums.cpython-312.pyc b/src/bitpin/deprecated/__pycache__/deprecated_enums.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38799d34bccc8667bc152640811a1982137ab170 GIT binary patch literal 7155 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 O(CW?v9}plI*#829h5I!C literal 0 HcmV?d00001 diff --git a/src/bitpin/deprecated/__pycache__/deprecated_types.cpython-312.pyc b/src/bitpin/deprecated/__pycache__/deprecated_types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7510d302f338ef42af040e8559203b04f2ad31a GIT binary patch literal 7698 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 O1qH$XV0S?Z!T$#g6d8X2 literal 0 HcmV?d00001 diff --git a/src/bitpin/deprecated/clients/__init__.py b/src/bitpin/deprecated/clients/__init__.py new file mode 100644 index 0000000..9e36ecb --- /dev/null +++ b/src/bitpin/deprecated/clients/__init__.py @@ -0,0 +1,17 @@ +""" +# Deprecated Bitpin Client. + +Client for Bitpin API. + +[Client](client) Submodule contains the synchronous client. +[AsyncClient](async_client) Submodule contains the asynchronous client. +[Core](core) Submodule contains the core client. +""" + +from .async_client import AsyncClient +from .client import Client + +__all__ = [ + "AsyncClient", + "Client", +] diff --git a/src/bitpin/deprecated/clients/__pycache__/__init__.cpython-312.pyc b/src/bitpin/deprecated/clients/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b22297c601f4cfc178df8fc147699427d7a42079 GIT binary patch literal 569 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 U(X}7gW*v0x!q_h;{3Q+kAAr+u`~Uy| literal 0 HcmV?d00001 diff --git a/src/bitpin/deprecated/clients/__pycache__/core.cpython-312.pyc b/src/bitpin/deprecated/clients/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c337605713a656aa9759b101969ec2f88473951c GIT binary patch literal 16793 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 "AsyncClient": + """ + Create AsyncClient. + + Args: + api_key (str): API key. + api_secret (str): API secret. + access_token (str): Access token. + refresh_token (str): Refresh token. + requests_params (dict): Requests params. + session_params (dict): Session params. + loop (asyncio.AbstractEventLoop): Event loop. + background_relogin (bool): Background refresh. + background_relogin_interval (int): Background refresh interval. + background_refresh_token (bool): Background refresh token. + background_refresh_token_interval (int): Background refresh token interval. + + Returns: + AsyncClient: AsyncClient. + """ + + self = cls( + api_key, + api_secret, + access_token, + refresh_token, + requests_params, + session_params, + loop, + background_relogin, + background_relogin_interval, + background_refresh_token, + background_refresh_token_interval, + ) + + await self._handle_login() + return self + + def _init_session(self) -> aiohttp.ClientSession: + """ + Initialize session. + + Returns: + session (aiohttp.ClientSession): Session. + + """ + + session = aiohttp.ClientSession( + loop=self.loop, + headers={ + "Content-Type": "application/json", + "Accept": "application/json", + }, + **self._session_params, + ) + return session + + async def _get( # type: ignore[no-untyped-def, override] + self, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Make a GET request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + return await self._request_api( + deprecated_enums.RequestMethod.GET, path, signed, version, **kwargs + ) + + async def _post( # type: ignore[no-untyped-def, override] + self, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Make a POST request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + return await self._request_api( + deprecated_enums.RequestMethod.POST, path, signed, version, **kwargs + ) + + async def _delete( # type: ignore[no-untyped-def, override] + self, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Make a DELETE request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + return await self._request_api( + deprecated_enums.RequestMethod.DELETE, path, signed, version, **kwargs + ) + + async def _request_api( # type: ignore[no-untyped-def, override] + self, + method: t.RequestMethods, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Request API. + + Args: + method (RequestMethod): Method. + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + uri = self._create_api_uri(path, version) + return await self._request(method, uri, signed, **kwargs) + + async def _request( # type: ignore[no-untyped-def, override] + self, method: t.RequestMethods, uri: str, signed: bool, **kwargs + ) -> t.DictStrAny: + """ + Request. + + Args: + method (RequestMethod): Method. + uri (str): URI. + signed (bool): Signed. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + kwargs = self._get_request_kwargs(method, signed, **kwargs) + + async with getattr(self.session, method)(uri, **kwargs) as response: + self.response = response # pylint: disable=attribute-defined-outside-init + return await self._handle_response(response) + + @staticmethod + async def _handle_response(response: aiohttp.ClientResponse) -> t.DictStrAny: # type: ignore[override] + """ + Handle response. + + Args: + response (aiohttp.ClientResponse): Response. + + Returns: + dict: Response. + + Raises: + APIException: API Exception. + RequestException: Request Exception. + """ + + if not str(response.status).startswith("2"): + raise APIException(response, response.status, await response.text()) + try: + if response.method.lower() == deprecated_enums.RequestMethod.DELETE: + return {"status": "success", "id": response.request_info.url.parts[-2]} + return await response.json() # type: ignore[no-any-return] + except ValueError as exc: + raise RequestException( + f"Invalid Response: {await response.text()}" + ) from exc + + async def _background_relogin_task(self) -> None: # type: ignore[override] + """Background relogin task.""" + + while True: + try: + await self.login() + await asyncio.sleep(self._background_relogin_interval) + except Exception: # pylint: disable=broad-except + continue + + async def _background_refresh_token_task(self) -> None: # type: ignore[override] + """Background refresh token task.""" + + while True: + try: + await self.refresh_access_token() + await asyncio.sleep(self._background_refresh_token_interval) + except Exception: # pylint: disable=broad-except + continue + + async def _handle_login(self) -> None: # type: ignore[override] + """Handle login.""" + + if self.api_key and self.api_secret: + await self.login() + + if self._background_relogin: + self.loop.create_task(self._background_relogin_task()) + + if self._background_refresh_token: + self.loop.create_task(self._background_refresh_token_task()) + + async def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def, override] + """ + Login and set (refresh_token/access_token). + + Args: + **kwargs: Kwargs. + + Returns: + Response (LoginResponse): Response. + + References: + [API Docs](https://docs.bitpin.ir/#02c24a5326) + """ + + kwargs["json"] = {"api_key": self.api_key, "secret_key": self.api_secret} + _: t.LoginResponse = await self._post(self.LOGIN_URL, **kwargs) # type: ignore[assignment] + + self.refresh_token = _["refresh"] + self.access_token = _["access"] + + return _ + + async def refresh_access_token( # type: ignore[no-untyped-def, override] + self, refresh_token: t.OptionalStr = None, **kwargs + ) -> t.RefreshTokenResponse: + """ + Refresh token. + + Args: + refresh_token (str): Refresh token. + **kwargs: Kwargs. + + Returns: + Response (RefreshTokenResponse): Response. + + References: + [API Docs](https://docs.bitpin.ir/#9b81094f74) + """ + + kwargs["json"] = {"refresh": refresh_token or self.refresh_token} + _: t.RefreshTokenResponse = await self._post(self.REFRESH_TOKEN_URL, **kwargs) # type: ignore[assignment] + + self.access_token = _["access"] + + return _ + + async def get_user_info(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def, override] + """ + Get user info. + + Args: + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#5b3c85d79e) + """ + + return await self._get(self.USER_INFO_URL, signed=True, **kwargs) + + async def get_currencies_info( # type: ignore[no-untyped-def, override] + self, page: int = 1, **kwargs + ) -> t.DictStrAny: + """ + Get currencies info. + + Args: + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#7e59da3d0d) + + Notes: + Rate limit: 10000/day or 200/minute if you are authenticated. + """ + + return await self._get(self.CURRENCIES_LIST_URL.format(page), **kwargs) + + async def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def, override] + """ + Get markets info. + + Args: + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#334792bb2b) + + Notes: + Rate limit: 10000/day or 200/minute if you are authenticated. + """ + + return await self._get(self.MARKETS_LIST_URL.format(page), **kwargs) + + async def get_wallets(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def, override] + """ + Get wallets. + + Args: + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#9b93495188) + + Notes: + Rate limit: 10000/day. + """ + + return await self._get(self.WALLETS_URL, signed=True, **kwargs) + + async def get_orderbook( # type: ignore[no-untyped-def, override] + self, + market_id: int, + type: t.OrderTypes, + **kwargs, # pylint: disable=redefined-builtin + ) -> t.OrderbookResponse: + """ + Get orderbook. + + Args: + market_id (int): Market ID. + type (OrderTypes): Type. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#ec7180fc0e) + """ + return await self._get( # type: ignore[return-value] + self.ORDERBOOK_URL.format(market_id, str(type)), + version=self.PUBLIC_API_VERSION_2, + **kwargs, + ) + + async def get_recent_trades( # type: ignore[no-untyped-def, override] + self, market_id: int, **kwargs + ) -> t.TradeResponse: + """ + Get recent trades. + + Args: + market_id (int): Market ID. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#1dd63530b5) + """ + + return await self._get(self.RECENT_TRADES_URL.format(market_id), **kwargs) # type: ignore[return-value] + + async def get_user_orders( # type: ignore[no-untyped-def, override] + self, + market_id: t.OptionalInt = None, + type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin + state: t.OptionalStr = None, + mode: t.OptionalStr = None, + identifier: t.OptionalStr = None, + page: int = 1, + **kwargs, + ) -> t.OpenOrdersResponse: + """ + Get user orders. + + Args: + market_id (int): Market ID. + type (OrderTypes): Type. + state (str): State. + mode (str): Mode. + identifier (str): Identifier. + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#8a7c2a2af5) + """ + + kwargs["params"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in ("self", "kwargs") + } + return await self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] + + async def create_order( # type: ignore[no-untyped-def, override] + self, + market: int, + amount1: float, + price: float, + mode: t.OrderModes, + type: t.OrderTypes, # pylint: disable=redefined-builtin + identifier: t.OptionalStr = None, + price_limit: t.OptionalFloat = None, + price_stop: t.OptionalFloat = None, + price_limit_oco: t.OptionalFloat = None, + amount2: t.OptionalFloat = None, + **kwargs, + ) -> t.CreateOrderResponse: + """ + Create order. + + Args: + market (int): Market. + amount1 (float): Amount1. + price (float): Price. + mode (OrderModes): Mode. + type (OrderTypes): Type. + identifier (str): Identifier. + price_limit (float): Price limit. + price_stop (float): Price stop. + price_limit_oco (float): Price limit oco. + amount2 (float): Amount2. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#34b353d77b) + """ + + kwargs["json"] = { + k: v + for k, v in locals().items() + if v is not None and k not in ("self", "kwargs") + } + return await self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] + + async def cancel_order( # type: ignore[no-untyped-def, override] + self, order_id: str, **kwargs + ) -> t.CancelOrderResponse: + """ + Cancel order. + + Args: + order_id (str): Order ID. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#3fe8d57657) + """ + + return await self._delete( + self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs + ) # type: ignore[return-value] + + async def get_user_trades( # type: ignore[no-untyped-def, override] + self, + market_id: t.OptionalInt = None, + type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin + page: int = 1, + **kwargs, + ) -> t.DictStrAny: + """ + Get user trades. + + Args: + market_id (int): Market ID. + type (OrderTypes): Type. + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#3fe8d57657) + """ + + kwargs["params"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in ("self", "kwargs") + } + return await self._get(self.USER_TRADES_URL, signed=True, **kwargs) + + async def close_connection(self) -> None: # type: ignore[override] + """Close connection.""" + + await self.session.close() # type: ignore[misc] diff --git a/src/bitpin/deprecated/clients/client.py b/src/bitpin/deprecated/clients/client.py new file mode 100644 index 0000000..5f2d927 --- /dev/null +++ b/src/bitpin/deprecated/clients/client.py @@ -0,0 +1,594 @@ +"""# Deprecated Bitpin Client.""" + +import time +from threading import Thread + +import requests +from deprecated import deprecated + +from src.bitpin.deprecated import deprecated_enums +from src.bitpin.deprecated import deprecated_types as t +from src.bitpin.exceptions import ( + APIException, + RequestException, +) + +from .core import CoreClient + + +@deprecated( + version="1.0.0", + reason="API end point has been updated and will be deprecated soon!" + "please import desired client from bitpin!" + "if you still need to use these methods import from bitpin.deprecated", +) +class Client(CoreClient): + """ + Client. + + Methods: + login: Login and set (refresh_token/access_token) + refresh_access_token: Refresh token. + get_user_info: Get user info. + get_currencies_info: Get currencies info. + get_markets_info: Get markets info. + get_wallets: Get wallets. + get_orderbook: Get orderbook. + get_recent_trades: Get recent trades. + get_user_orders: Get use orders. + create_order: Create order. + cancel_order: Cancel order. + get_user_trades: Get user trades. + close_connection: Close connection. + + Attributes: + session (aiohttp.ClientSession): Session. + api_key (str): API key. + api_secret (str): API secret. + refresh_token (str): Refresh token. + access_token (str): Access token. + """ + + def __init__( # type: ignore[no-untyped-def] + self, + api_key: t.OptionalStr = None, + api_secret: t.OptionalStr = None, + access_token: t.OptionalStr = None, + refresh_token: t.OptionalStr = None, + requests_params: t.OptionalDictStrAny = None, + background_relogin: bool = False, + background_relogin_interval: int = 60 * 60 * 24 * 6, + background_refresh_token: bool = False, + background_refresh_token_interval: int = 60 * 13, + ): + """ + Constructor. + + Args: + api_key (str): API key. + api_secret (str): API secret. + access_token (str): Access token. + refresh_token (str): Refresh token. + requests_params (dict): Requests params. + background_relogin (bool): Background refresh. + background_relogin_interval (int): Background refresh interval. + background_refresh_token (bool): Background refresh token. + background_refresh_token_interval (int): Background refresh token interval. + + Notes: + If `api_key` and `api_secret` are not provided, they will be read from the environment variables + `BITPIN_API_KEY` and `BITPIN_API_SECRET` respectively. + + If `access_token` and `refresh_token` are not provided, they will be read from the environment variables + `BITPIN_ACCESS_TOKEN` and `BITPIN_REFRESH_TOKEN` respectively. + + If `requests_params` are provided, they will be used as default for every request. + + If `requests_params` are provided in `kwargs`, they will override existing `requests_params`. + + If `background_relogin` is enabled, access token will be refreshed in background every + `background_relogin_interval` seconds. + + If `background_refresh_token` is enabled, refresh token will be refreshed in background every + `background_refresh_token_interval` seconds. + """ + + super().__init__( + api_key, + api_secret, + access_token, + refresh_token, + requests_params, + background_relogin, + background_relogin_interval, + background_refresh_token, + background_refresh_token_interval, + ) + + self._handle_login() + + def _init_session(self) -> requests.Session: + """ + Initialize session. + + Returns: + session (aiohttp.ClientSession): Session. + + """ + + session = requests.Session() + session.headers["Content-Type"] = "application/json" + session.headers["Accept"] = "application/json" + return session + + def _get( # type: ignore[no-untyped-def] + self, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Make a GET request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + return self._request_api( + deprecated_enums.RequestMethod.GET, path, signed, version, **kwargs + ) + + def _post( # type: ignore[no-untyped-def] + self, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Make a POST request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + return self._request_api( + deprecated_enums.RequestMethod.POST, path, signed, version, **kwargs + ) + + def _delete( # type: ignore[no-untyped-def] + self, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Make a DELETE request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + return self._request_api( + deprecated_enums.RequestMethod.DELETE, path, signed, version, **kwargs + ) + + def _request_api( # type: ignore[no-untyped-def] + self, + method: t.RequestMethods, + path: str, + signed: bool = False, + version: str = CoreClient.PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Request API. + + Args: + method (RequestMethod): Method. + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + uri = self._create_api_uri(path, version) + return self._request(method, uri, signed, **kwargs) + + def _request( # type: ignore[no-untyped-def] + self, method: t.RequestMethods, uri: str, signed: bool, **kwargs + ) -> t.DictStrAny: + """ + Request. + + Args: + method (RequestMethod): Method. + uri (str): URI. + signed (bool): Signed. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + kwargs = self._get_request_kwargs(method, signed, **kwargs) + + with getattr(self.session, method)(uri, **kwargs) as response: + self.response = response # pylint: disable=attribute-defined-outside-init + return self._handle_response(response) + + @staticmethod + def _handle_response(response: requests.Response) -> t.DictStrAny: # type: ignore[override] + """ + Handle response. + + Args: + response (aiohttp.ClientResponse): Response. + + Returns: + dict: Response. + + Raises: + APIException: API Exception. + RequestException: Request Exception. + """ + + if not str(response.status_code).startswith("2"): + if response.request.method.lower() == deprecated_enums.RequestMethod.DELETE: # type: ignore[union-attr] + return { + "status": "success", + "id": response.request.path_url.split("/")[-2], + } + raise APIException(response, response.status_code, response.text) + try: + return response.json() # type: ignore[no-any-return] + except ValueError as exc: + raise RequestException(f"Invalid Response: {response.text}") from exc + + def _handle_login(self) -> None: + """Handle login.""" + + if self.api_key and self.api_secret: + self.login() + + if self._background_relogin: + Thread(target=self._background_relogin_task, daemon=True).start() + + if self._background_refresh_token: + Thread(target=self._background_refresh_token_task, daemon=True).start() + + def _background_relogin_task(self) -> None: + """Background relogin task.""" + + while True: + try: + self.login() + time.sleep(self._background_relogin_interval) + except Exception: # pylint: disable=broad-except + continue + + def _background_refresh_token_task(self) -> None: + """Background refresh token task.""" + + while True: + try: + self.refresh_access_token() + time.sleep(self._background_refresh_token_interval) + except Exception: # pylint: disable=broad-except + continue + + def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def] + """ + Login and set (refresh_token/access_token). + + Args: + **kwargs: Kwargs. + + Returns: + Response (LoginResponse): Response. + + References: + [API Docs](https://docs.bitpin.ir/#02c24a5326) + """ + + kwargs["json"] = {"api_key": self.api_key, "secret_key": self.api_secret} + _: t.LoginResponse = self._post(self.LOGIN_URL, **kwargs) # type: ignore[assignment] + + self.refresh_token = _["refresh"] + self.access_token = _["access"] + + return _ + + def refresh_access_token( # type: ignore[no-untyped-def] + self, refresh_token: t.OptionalStr = None, **kwargs + ) -> t.RefreshTokenResponse: + """ + Refresh token. + + Args: + refresh_token (str): Refresh token. + **kwargs: Kwargs. + + Returns: + Response (RefreshTokenResponse): Response. + + References: + [API Docs](https://docs.bitpin.ir/#9b81094f74) + """ + + kwargs["json"] = {"refresh": refresh_token or self.refresh_token} + _: t.RefreshTokenResponse = self._post(self.REFRESH_TOKEN_URL, **kwargs) # type: ignore[assignment] + + self.access_token = _["access"] + + return _ + + def get_user_info(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get user info. + + Args: + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#5b3c85d79e) + """ + + return self._get(self.USER_INFO_URL, signed=True, **kwargs) + + def get_currencies_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get currencies info. + + Args: + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#7e59da3d0d) + + Notes: + Rate limit: 10000/day or 200/minute if you are authenticated. + """ + + return self._get(self.CURRENCIES_LIST_URL.format(page), **kwargs) + + def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get markets info. + + Args: + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#334792bb2b) + + Notes: + Rate limit: 10000/day or 200/minute if you are authenticated. + """ + + return self._get(self.MARKETS_LIST_URL.format(page), **kwargs) + + def get_wallets(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get wallets. + + Args: + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#9b93495188) + + Notes: + Rate limit: 10000/day. + """ + + return self._get(self.WALLETS_URL, signed=True, **kwargs) + + def get_orderbook( # type: ignore[no-untyped-def] + self, + market_id: int, + type: t.OrderTypes, + **kwargs, # pylint: disable=redefined-builtin + ) -> t.OrderbookResponse: + """ + Get orderbook. + + Args: + market_id (int): Market ID. + type (OrderTypes): Type. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#ec7180fc0e) + """ + + return self._get(self.ORDERBOOK_URL.format(market_id, str(type)), **kwargs) # type: ignore[return-value] + + def get_recent_trades(self, market_id: int, **kwargs) -> t.TradeResponse: # type: ignore[no-untyped-def] + """ + Get recent trades. + + Args: + market_id (int): Market ID. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#1dd63530b5) + """ + + return self._get(self.RECENT_TRADES_URL.format(market_id), **kwargs) # type: ignore[return-value] + + def get_user_orders( # type: ignore[no-untyped-def] + self, + market_id: t.OptionalInt = None, + type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin + state: t.OptionalStr = None, + mode: t.OptionalStr = None, + identifier: t.OptionalStr = None, + page: int = 1, + **kwargs, + ) -> t.OpenOrdersResponse: + """ + Get user Orders. + + Args: + market_id (int): Market ID. + type (OrderTypes): Type. + state (str): State. + mode (str): Mode. + identifier (str): Identifier. + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#8a7c2a2af5) + """ + + kwargs["params"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in ("self", "kwargs") + } + return self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] + + def create_order( # type: ignore[no-untyped-def] + self, + market: int, + amount1: float, + price: float, + mode: t.OrderModes, + type: t.OrderTypes, # pylint: disable=redefined-builtin + identifier: t.OptionalStr = None, + price_limit: t.OptionalFloat = None, + price_stop: t.OptionalFloat = None, + price_limit_oco: t.OptionalFloat = None, + amount2: t.OptionalFloat = None, + **kwargs, + ) -> t.CreateOrderResponse: + """ + Create order. + + Args: + market (int): Market. + amount1 (float): Amount1. + price (float): Price. + mode (OrderModes): Mode. + type (OrderTypes): Type. + identifier (str): Identifier. + price_limit (float): Price limit. + price_stop (float): Price stop. + price_limit_oco (float): Price limit oco. + amount2 (float): Amount2. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#34b353d77b) + """ + + kwargs["json"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in ("self", "kwargs") + } + return self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] + + def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # type: ignore[no-untyped-def] + """ + Cancel order. + + Args: + order_id (str): Order ID. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#3fe8d57657) + """ + + return self._delete(self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs) # type: ignore[return-value] + + def get_user_trades( # type: ignore[no-untyped-def] + self, + market_id: t.OptionalInt = None, + type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin + page: int = 1, + **kwargs, + ) -> t.DictStrAny: + """ + Get user trades. + + Args: + market_id (int): Market ID. + type (OrderTypes): Type. + page (int): Page. + **kwargs: Kwargs. + + Returns: + Response (dict): Response. + + References: + [API Docs](https://docs.bitpin.ir/#3fe8d57657) + """ + + kwargs["params"] = { + k: str(v) + for k, v in locals().items() + if v is not None and k not in ("self", "kwargs") + } + return self._get(self.USER_TRADES_URL, signed=True, **kwargs) + + def close_connection(self) -> None: + """Close connection.""" + + self.session.close() diff --git a/src/bitpin/deprecated/clients/core.py b/src/bitpin/deprecated/clients/core.py new file mode 100644 index 0000000..168ab8c --- /dev/null +++ b/src/bitpin/deprecated/clients/core.py @@ -0,0 +1,505 @@ +"""# Core Client.""" + +import os +from abc import ( + ABC, + abstractmethod, +) + +from src.bitpin.deprecated import deprecated_types as t + + +class CoreClient(ABC): # pylint: disable=too-many-instance-attributes + """Core Client.""" + + API_URL = "https://api.bitpin.ir" + + PUBLIC_API_VERSION_1 = "v1" + PUBLIC_API_VERSION_2 = "v2" + + REQUEST_TIMEOUT: float = 10 + + LOGIN_URL = "usr/api/login/" + REFRESH_TOKEN_URL = "usr/refresh_token/" + USER_INFO_URL = "usr/info/" + CURRENCIES_LIST_URL = "mkt/currencies/?page={}" + MARKETS_LIST_URL = "mkt/markets/?page={}" + WALLETS_URL = "wlt/wallets/" + ORDERBOOK_URL = "mth/actives/{}/?type={}" + RECENT_TRADES_URL = "mth/matches/{}/" + ORDERS_URL = "odr/orders/" + USER_TRADES_URL = "odr/matches/?type={}" + + def __init__( # type: ignore[no-untyped-def] + self, + api_key: t.OptionalStr = None, + api_secret: t.OptionalStr = None, + access_token: t.OptionalStr = None, + refresh_token: t.OptionalStr = None, + requests_params: t.OptionalDictStrAny = None, + background_relogin: bool = False, + background_relogin_interval: int = 60 * 60 * 24 * 6, + background_refresh_token: bool = False, + background_refresh_token_interval: int = 60 * 13, + ): + """ + Constructor. + + Args: + api_key (str): API key. + api_secret (str): API secret. + access_token (str): Access token. + refresh_token (str): Refresh token. + requests_params (dict): Requests params. + background_relogin (bool): Background refresh. + background_relogin_interval (int): Background refresh interval. + background_refresh_token (bool): Background refresh token. + background_refresh_token_interval (int): Background refresh token interval. + + Notes: + If `api_key` and `api_secret` are not provided, they will be read from the environment variables + `BITPIN_API_KEY` and `BITPIN_API_SECRET` respectively. + + If `access_token` and `refresh_token` are not provided, they will be read from the environment variables + `BITPIN_ACCESS_TOKEN` and `BITPIN_REFRESH_TOKEN` respectively. + + If `requests_params` are provided, they will be used as default for every request. + + If `requests_params` are provided in method's `kwargs`, they will override existing `requests_params`. + + If `background_relogin` is enabled, access token will be refreshed in background every + `background_relogin_interval` seconds. + + If `background_refresh_token` is enabled, refresh token will be refreshed in background every + `background_refresh_token_interval` seconds. + """ + + self.api_key = api_key or os.environ.get("BITPIN_API_KEY") + self.api_secret = api_secret or os.environ.get("BITPIN_API_SECRET") + self.access_token: t.OptionalStr = access_token or os.environ.get( + "BITPIN_ACCESS_TOKEN" + ) + self.refresh_token: t.OptionalStr = refresh_token or os.environ.get( + "BITPIN_REFRESH_TOKEN" + ) + + self._background_relogin = background_relogin + self._background_relogin_interval = background_relogin_interval + self._background_refresh_token = background_refresh_token + self._background_refresh_token_interval = background_refresh_token_interval + + self._requests_params = requests_params + self.session = self._init_session() + + def _get_request_kwargs( + self, method: t.RequestMethods, signed: bool, **kwargs + ) -> t.DictStrAny: # type: ignore[no-untyped-def] + kwargs["timeout"] = self.REQUEST_TIMEOUT + + if self._requests_params: + kwargs.update(self._requests_params) + + data = kwargs.get("data") + if data and isinstance(data, dict): + kwargs["data"] = data + + if "requests_params" in kwargs["data"]: + kwargs.update(kwargs["data"]["requests_params"]) + del kwargs["data"]["requests_params"] + + if signed is True: + headers: t.DictStrAny = kwargs.get("headers", {}) + headers.update({"Authorization": f"Bearer {self.access_token}"}) + kwargs["headers"] = headers + + if data and method == "get": + kwargs["params"] = "&".join( + f"{data[0]}={data[1]}" for data in kwargs["data"] + ) + del kwargs["data"] + + return kwargs + + @staticmethod + def _pick( + response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results" + ) -> t.DictStrAny: + for _ in response.get(result_key, []): + if _[key] == value: + response[result_key] = _ + return response + raise ValueError(f"{key} {value} not found in {response}") + + def _create_api_uri(self, path: str, version: str = PUBLIC_API_VERSION_1) -> str: + return self.API_URL + "/" + str(version) + "/" + path + + @abstractmethod + def _init_session(self) -> t.HttpSession: + """ + Initialize session. + + Returns: + session (t.Union[requests.Session, aiohttp.ClientSession]): Session. + """ + + raise NotImplementedError + + @abstractmethod + def _get( + self, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Make a GET request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def _post( + self, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Make a POST request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def _delete( + self, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Make a DELETE request. + + Args: + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def _request_api( # type: ignore[no-untyped-def] + self, + method: t.RequestMethods, + path: str, + signed: bool = False, + version: str = PUBLIC_API_VERSION_1, + **kwargs, + ) -> t.DictStrAny: + """ + Request API. + + Args: + method (str): Method (GET, POST, PUT, DELETE). + path (str): Path. + signed (bool): Signed. + version (str): Version. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def _request( + self, method: t.RequestMethods, uri: str, signed: bool, **kwargs + ) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Request. + + Args: + method (str): Method (GET, POST, PUT, DELETE). + uri (str): URI. + signed (bool): Signed. + **kwargs: Kwargs. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @staticmethod + @abstractmethod + def _handle_response(response: t.HttpResponses) -> t.DictStrAny: + """ + Handle response. + + Args: + response (t.Union[requests.Response, aiohttp.ClientResponse]): Response. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def _handle_login(self) -> None: + """Handle login.""" + + raise NotImplementedError + + @abstractmethod + def _background_relogin_task(self) -> None: + """Background relogin task.""" + + raise NotImplementedError + + @abstractmethod + def _background_refresh_token_task(self) -> None: + """Background refresh token task.""" + + raise NotImplementedError + + @abstractmethod + def login(self, **kwargs) -> t.LoginResponse: # type: ignore[no-untyped-def] + """ + Login. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def refresh_access_token( + self, refresh_token: t.OptionalStr = None, **kwargs + ) -> t.RefreshTokenResponse: # type: ignore[no-untyped-def] + """ + Refresh token. + + Args: + refresh_token (str): Refresh token. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_user_info(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get user info. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_currencies_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get currencies info. + + Args: + page (int): Page. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_markets_info(self, page: int = 1, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get markets info. + + Args: + page (int): Page. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_wallets(self, **kwargs) -> t.DictStrAny: # type: ignore[no-untyped-def] + """ + Get wallets. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_orderbook( + self, market_id: int, type: t.OrderTypes, **kwargs + ) -> t.OrderbookResponse: # type: ignore[no-untyped-def] # pylint: disable=redefined-builtin + """ + Get orderbook. + + Args: + market_id (int): Market ID. + type (str): Type. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_recent_trades(self, market_id: int, **kwargs) -> t.TradeResponse: # type: ignore[no-untyped-def] + """ + Get recent trades. + + Args: + market_id (int): Market ID. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_user_orders( # type: ignore[no-untyped-def] + self, + market_id: t.OptionalInt = None, + type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin + state: t.OptionalStr = None, + mode: t.OptionalStr = None, + identifier: t.OptionalStr = None, + page: int = 1, + **kwargs, + ) -> t.OpenOrdersResponse: + """ + Get user orders. + + Args: + market_id (int): Market ID. + type (str): Type. + state (str): State. + mode (str): Mode. + identifier (str): Identifier. + page (int): Page. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def create_order( # type: ignore[no-untyped-def] + self, + market: int, + amount1: float, + price: float, + mode: t.OrderModes, + type: t.OrderTypes, # pylint: disable=redefined-builtin + identifier: t.OptionalStr = None, + price_limit: t.OptionalFloat = None, + price_stop: t.OptionalFloat = None, + price_limit_oco: t.OptionalFloat = None, + amount2: t.OptionalFloat = None, + **kwargs, + ) -> t.CreateOrderResponse: + """ + Create order. + + Args: + market (int): Market. + amount1 (float): Amount1. + price (float): Price. + mode (str): Mode. + type (str): Type. + identifier (str): Identifier. + price_limit (float): Price limit. + price_stop (float): Price stop. + price_limit_oco (float): Price limit oco. + amount2 (float): Amount2. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # type: ignore[no-untyped-def] + """ + Cancel order. + + Args: + order_id (str): Order ID. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def get_user_trades( # type: ignore[no-untyped-def] + self, + market_id: t.OptionalInt = None, + type: t.OptionalOrderTypes = None, # pylint: disable=redefined-builtin + page: int = 1, + **kwargs, + ) -> t.DictStrAny: + """ + Get user trades. + + Args: + market_id (int): Market ID. + type (str): Type. + page (int): Page. + + Returns: + dict: Response. + """ + + raise NotImplementedError + + @abstractmethod + def close_connection(self) -> None: + """Close connection.""" + + raise NotImplementedError diff --git a/src/bitpin/deprecated/deprecated_enums.py b/src/bitpin/deprecated/deprecated_enums.py new file mode 100644 index 0000000..9506a63 --- /dev/null +++ b/src/bitpin/deprecated/deprecated_enums.py @@ -0,0 +1,200 @@ +""" +# Enums. + +Enum classes for the BitPin API. +""" + +from enum import ( + Enum as _Enum, +) +from enum import ( + EnumMeta as _EnumMeta, +) +from typing import Any + + +class EnumMeta(_EnumMeta): + """Enum Meta.""" + + def __call__(cls, value: Any, *args: Any, **kwargs: Any) -> "Enum": # type: ignore[override] + """Call.""" + if isinstance(value, str): # pragma: no cover + value = value.upper() + + return super().__call__(value, *args, **kwargs) # type: ignore[no-any-return] + + def __contains__(cls, value: Any) -> bool: + """Contains.""" + + if isinstance(value, str): + return value.upper() in cls._value2member_map_ # type: ignore[attr-defined] + + return super().__contains__(value) # type: ignore[call-arg] + + +class Enum(_Enum, metaclass=EnumMeta): + """Enum.""" + + def __repr__(self) -> str: + """ + Representation. + + Returns: + str: Representation. + """ + + return f"{self.__class__.__name__}.{self.name}" + + def __str__(self) -> str: + """ + String representation. + + Returns: + str: String representation. + """ + + return str(self.value) + + def __eq__(self, other: Any) -> bool: # pragma: no cover + """ + Equal. + + Args: + other (Any): Other. + + Returns: + bool: True if equal, else False. + """ + + if isinstance(other, Enum): + return self.value == other.value # type: ignore[no-any-return] + return self.value == other # type: ignore[no-any-return] + + def __hash__(self) -> int: # pragma: no cover + """ + Hash. + + Returns: + int: Hash. + """ + + return hash(self.value) + + @classmethod + def _missing_(cls, value: object) -> "Enum": + """ + Missing. + + Args: + value (object): Value. + + Returns: + Enum: Enum. + """ + + for member in cls: + if ( + isinstance(value, str) and member.value == value.upper() + ): # pragma: no cover + return member + + return super()._missing_(value) # type: ignore[attribute-error, no-any-return] + + @classmethod + def get_by_value(cls, value: Any) -> "Enum": + """ + Get Enum by value. + + Args: + value (Any): Value. + + Returns: + Enum: Enum. + """ + + for enum in cls: + if enum.value.lower() == value.lower(): + return enum + raise ValueError(f"Invalid value: {value}") + + @classmethod + def get_by_name(cls, name: Any) -> "Enum": + """ + Get Enum by name. + + Args: + name (Any): Name. + + Returns: + Enum: Enum. + """ + + for enum in cls: + if enum.name.lower() == name.lower(): + return enum + raise ValueError(f"Invalid name: {name}") + + @classmethod + def get_by_name_or_value(cls, name_or_value: Any) -> "Enum": + """ + Get Enum by name or value. + + Args: + name_or_value (Any): Name or value. + + Returns: + Enum: Enum. + """ + + try: + return cls.get_by_name(name_or_value) + except ValueError: + return cls.get_by_value(name_or_value) + + @classmethod + def get_all_values(cls) -> list[Any]: + """Get all values.""" + return [enum.value for enum in cls] + + @classmethod + def get_all_names(cls) -> list[str]: + """Get all names.""" + return [enum.name for enum in cls] + + @classmethod + def to_django_choices(cls) -> list[tuple]: + """Get all names.""" + return [(enum.name, enum.name) for enum in cls] + + +class OrderType(str, Enum): + """Order Type (BUY/SELL).""" + + BUY = "buy" + SELL = "sell" + + +class OrderMode(str, Enum): + """Order Mode (LIMIT/MARKET/OCO/STOP_LIMIT).""" + + LIMIT = "limit" + MARKET = "market" + OCO = "oco" + STOP_LIMIT = "stop_limit" + + +class OrderState(str, Enum): + """Order State (INITIAL/ACTIVE/CLOSED).""" + + INITIAL = "initial" + ACTIVE = "active" + CLOSED = "closed" + + +class RequestMethod(str, Enum): + """Request Methods (GET/POST/PUT/DELETE).""" + + GET = "get" + POST = "post" + PUT = "put" + DELETE = "delete" diff --git a/src/bitpin/deprecated/deprecated_types.py b/src/bitpin/deprecated/deprecated_types.py new file mode 100644 index 0000000..f7ccd94 --- /dev/null +++ b/src/bitpin/deprecated/deprecated_types.py @@ -0,0 +1,204 @@ +""" +# Deprecated Types. + +Types for Python Bitpin. + +## Description +This file contains all the types used in the project. +""" + +import asyncio +import typing as t + +import aiohttp +import requests + +from . import deprecated_enums + +# General Types: +OptionalStr = t.Optional[str] +OptionalInt = t.Optional[int] +OptionalFloat = t.Optional[float] + +DictStrAny = dict[str, t.Any] +OptionalDictStrAny = t.Optional[DictStrAny] + +EventLoop = asyncio.AbstractEventLoop +OptionalEventLoop = t.Optional[EventLoop] + +# Client Types: +OrderTypeBuy = t.Literal["buy"] +OrderTypeSell = t.Literal["sell"] +OrderTypes = t.Union[OrderTypeBuy, OrderTypeSell, deprecated_enums.OrderType] +OptionalOrderTypes = t.Optional[OrderTypes] + +OrderModeLimit = t.Literal["limit"] +OrderModeMarket = t.Literal["market"] +OrderModeOCO = t.Literal["oco"] +OrderModeStopLimit = t.Literal["stop_limit"] +OrderModes = t.Union[ + OrderModeLimit, + OrderModeMarket, + OrderModeOCO, + OrderModeStopLimit, + deprecated_enums.OrderMode, +] +OptionalOrderModes = t.Optional[OrderModes] + +# HTTP Types: +HttpSession = t.Union[requests.Session, aiohttp.ClientSession] +HttpResponses = t.Union[requests.Response, aiohttp.ClientResponse] + +# Request Types: +RequestMethodGet = t.Literal["get"] +RequestMethodPost = t.Literal["post"] +RequestMethodPut = t.Literal["put"] +RequestMethodDelete = t.Literal["delete"] +RequestMethods = t.Union[ + RequestMethodGet, + RequestMethodPost, + RequestMethodPut, + RequestMethodDelete, + deprecated_enums.RequestMethod, +] + + +# Response Types: +class ResultListResponse(t.TypedDict): + count: int | None + next: str | None + previous: str | None + results: list[DictStrAny] + + +class InnerOrderbookResponse(t.TypedDict): + amount: str + price: str + remain: str + value: str + + +class OrderbookResponse(t.TypedDict): + orders: list[InnerOrderbookResponse] + volume: str + + +class InnerTradeResponse(t.TypedDict): + time: float + price: str + value: str + match_amount: str + type: str + match_id: str + + +TradeResponse = list[InnerTradeResponse] + + +class LoginResponse(t.TypedDict): + refresh: str + access: str + + +class RefreshTokenResponse(t.TypedDict): + access: str + + +class CurrencyInfo(t.TypedDict): + id: int + title: str + title_fa: str + code: str + tradable: bool + for_test: bool + image: str + decimal: int + decimal_amount: int + decimal_irt: int + color: str + high_risk: bool + show_high_risk: bool + withdraw_commission: str + tags: list[DictStrAny] + + +class WalletInfo(t.TypedDict): + id: int + currency: CurrencyInfo + balance: str + frozen: str + total: str + value: str + value_frozen: str + value_total: str + usdt_value: str + usdt_value_frozen: str + usdt_value_total: str + address: str + inviter_commission: str + service: str + daily_withdraw: str + + +class MarketInfo(t.TypedDict): + id: int + currency1: CurrencyInfo + currency2: CurrencyInfo + code: str + title: str + title_fa: str + commissions: dict[str, float] + + +class CreateOrderResponse(t.TypedDict): + id: int + market: MarketInfo + amount1: str + amount2: str + price: str + price_limit: str + price_stop: OptionalStr + price_limit_oco: OptionalStr + type: str + active_limit: str + identifier: OptionalStr + mode: str + expected_gain: str + expected_resource: str + commission_percent: float + user_share_percent: float + expected_commission: str + expected_user_gain: str + expected_user_price: str + gain_currency: CurrencyInfo + resource_currency: CurrencyInfo + fulfilled: float + exchanged1: str + exchanged2: str + gain: str + resource: str + remain_amount: str + average_price: str + average_user_price: str + commission: str + user_commission: str + user_gain: str + created_at: str + activated_at: str + state: str + req_to_cancel: bool + info: dict[str, t.Any] + closed_at: OptionalStr + external_address: str + + +class OpenOrdersResponse(t.TypedDict): + count: int + next: OptionalStr + previous: OptionalStr + results: list[CreateOrderResponse] + + +class CancelOrderResponse(t.TypedDict): + status: str + id: str From 82c958915f107b8a6d4152a830ba8a04ec799982 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 16:56:50 +0330 Subject: [PATCH 03/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20added=20?= =?UTF-8?q?support=20for=20deprecated=20API=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitpin/exceptions.py b/src/bitpin/exceptions.py index b54d71f..47addd7 100644 --- a/src/bitpin/exceptions.py +++ b/src/bitpin/exceptions.py @@ -9,7 +9,7 @@ import json -from . import types as t +from . import response_types as t class APIException(Exception): From e0c638773afe1846cb0540c282832fe57242cbbe Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 16:57:13 +0330 Subject: [PATCH 04/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20added=20?= =?UTF-8?q?support=20for=20deprecated=20API=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bitpin/__init__.py b/src/bitpin/__init__.py index 49f9dce..751457f 100644 --- a/src/bitpin/__init__.py +++ b/src/bitpin/__init__.py @@ -1,12 +1,11 @@ """# Bitpin Python Library.""" +import deprecated + from .clients.async_client import AsyncClient from .clients.client import Client -__all__ = [ - "AsyncClient", - "Client", -] +__all__ = ["AsyncClient", "Client", "deprecated"] # Meta From 1a4e96d53625593a4019c408d24135cb5282e128 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:00:37 +0330 Subject: [PATCH 05/12] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20support=20for?= =?UTF-8?q?=20all=20endpoint=20responses=20as=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/response_types.py | 249 +++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 src/bitpin/response_types.py diff --git a/src/bitpin/response_types.py b/src/bitpin/response_types.py new file mode 100644 index 0000000..98127b9 --- /dev/null +++ b/src/bitpin/response_types.py @@ -0,0 +1,249 @@ +""" +# Types. + +Types for Python Bitpin. + +## Description +This file contains all the types used in the project. +""" + +import asyncio +import typing as t +from datetime import datetime + +import aiohttp +import requests + +from . import enums + +# General Types: +OptionalStr = t.Optional[str] +OptionalInt = t.Optional[int] +OptionalFloat = t.Optional[float] +OptionalStrList = t.Optional[list[str]] +OptionalDate = t.Optional[datetime] + +DictStrAny = dict[str, t.Any] +OptionalDictStrAny = t.Optional[DictStrAny] + +EventLoop = asyncio.AbstractEventLoop +OptionalEventLoop = t.Optional[EventLoop] + +# Client Types: +OrderTypeBuy = t.Literal["buy"] +OrderTypeSell = t.Literal["sell"] +OrderTypes = t.Union[OrderTypeBuy, OrderTypeSell, enums.OrderType] + +OptionalOrderTypes = t.Optional[OrderTypes] +OptionalOrderTypesList = t.Optional[list[OrderTypes]] + +OrderStateInitial = t.Literal["initial"] +OrderStateActive = t.Literal["active"] +OrderStateClosed = t.Literal["closed"] +OrderState = t.Union[ + OrderStateInitial, OrderStateActive, OrderStateClosed, enums.OrderState +] +OptionalOrderState = t.Optional[OrderState] +OptionalOrderStateList = t.Optional[list[OrderState]] + +QuoteAssetIRT = t.Literal["IRT"] +QuoteAssetUSDT = t.Literal["USDT"] +OrderbookQuoteAsset = t.Union[QuoteAssetIRT, QuoteAssetUSDT, enums.OrderBookQuoteAsset] +OptionalQuoteAsset = t.Optional[OrderbookQuoteAsset] + +OrderModeLimit = t.Literal["limit"] +OrderModeMarket = t.Literal["market"] +OrderModeOCO = t.Literal["oco"] +OrderModeStopLimit = t.Literal["stop_limit"] +OrderModes = t.Union[ + OrderModeLimit, OrderModeMarket, OrderModeOCO, OrderModeStopLimit, enums.OrderMode +] +OptionalOrderModes = t.Optional[OrderModes] +OptionalOrderModesList = t.Optional[list[OrderModes]] + +# HTTP Types: +HttpSession = t.Union[requests.Session, aiohttp.ClientSession] +HttpResponses = t.Union[requests.Response, aiohttp.ClientResponse] + +# Request Types: +RequestMethodGet = t.Literal["get"] +RequestMethodPost = t.Literal["post"] +RequestMethodPut = t.Literal["put"] +RequestMethodDelete = t.Literal["delete"] +RequestMethods = t.Union[ + RequestMethodGet, + RequestMethodPost, + RequestMethodPut, + RequestMethodDelete, + enums.RequestMethod, +] + + +# Request Types: +class OrderDict(t.TypedDict): + symbol: str + base_amount: float + price: float + side: OrderTypes + type: OrderModes + + +BulkOrderList = list[OrderDict] + +# Response Types: +CurrenciesInfo = list[ + t.TypedDict( + "CurrenciesInfo", + {"currency": str, "name": str, "tradable": bool, "precision": str}, + ) +] + +MarketsInfo = list[ + t.TypedDict( + "MarketsInfo", + { + "symbol": str, + "name": str, + "base": str, + "quote": str, + "tradable": bool, + "price_precision": str, + "base_amount_precision": str, + "quote_amount_precision": str, + }, + ) +] + +TickersInfo = list[ + t.TypedDict( + "TickersInfo", + { + "symbol": str, + "price": str, + "daily_change_price": float, + "low": str, + "high": str, + "timestamp": float, + }, + ) +] + +RecentTradesInfo = list[ + t.TypedDict( + "RecentTradesInfo", + { + "id": str, + "price": str, + "base_amount": str, + "quote_amount": str, + "side": OrderTypes, + }, + ) +] + + +class InnerOrderbookResponse(t.TypedDict): + price: str + quantity: str + + +class OrderbookResponse(t.TypedDict): + asks: list[InnerOrderbookResponse] + bids: list[InnerOrderbookResponse] + + +class LoginResponse(t.TypedDict): + refresh: str + access: str + + +class RefreshTokenResponse(t.TypedDict): + access: str + + +WalletInfo = list[ + t.TypedDict( + "WalletInfo", + {"id": int, "asset": str, "balance": str, "frozen": str, "service": str}, + ) +] + + +class CreateOrderResponse(t.TypedDict): + id: int + symbol: str + type: OrderModes + side: OrderTypes + price: str + stop_price: OptionalStr + oco_target_price: OptionalStr + base_amount: str + quote_amount: str + identifier: OptionalStr + state: str + closed_at: OptionalStr + created_at: str + dealed_base_amount: str + dealed_quote_amount: str + req_to_cancel: bool + commission: str + + +OpenOrdersResponse = list[ + t.TypedDict( + "OpenOrdersResponse", + { + "id": int, + "symbol": str, + "type": OrderModes, + "side": OrderTypes, + "base_amount": str, + "quote_amount": str, + "price": str, + "stop_price": OptionalStr, + "oco_target_price": OptionalStr, + "identifier": OptionalStr, + "state": str, + "created_at": str, + "closed_at": OptionalStr, + "dealed_base_amount": str, + "dealed_quote_amount": str, + "req_to_cancel": bool, + "commission": str, + }, + ) +] + +TradeResponse = list[ + t.TypedDict( + "TradeResponse", + { + "id": int, + "symbol": str, + "base_amount": str, + "quote_amount": str, + "price": str, + "created_at": str, + "commission": str, + "side": OrderTypes, + "order_id": int, + "identifier": OptionalStr, + }, + ) +] + + +class CancelOrderResponse(t.TypedDict): + status: str + id: str + + +CancelBulkOrderResponse = list[ + t.TypedDict( + "CancelBulkOrderResponse", + { + "canceled_orders": list[OptionalStr], + "not_canceled_orders": list[OptionalStr], + }, + ) +] From c4aa004d984437956e8c731ad5120f9b9d66c099 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:03:27 +0330 Subject: [PATCH 06/12] =?UTF-8?q?=E2=9C=A8=20feat:=20aded=20quoet=20assets?= =?UTF-8?q?=20to=20enums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/enums.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/bitpin/enums.py b/src/bitpin/enums.py index cc23231..3637cba 100644 --- a/src/bitpin/enums.py +++ b/src/bitpin/enums.py @@ -4,11 +4,13 @@ Enum classes for the BitPin API. """ -from typing import Any, List from enum import ( Enum as _Enum, +) +from enum import ( EnumMeta as _EnumMeta, ) +from typing import Any class EnumMeta(_EnumMeta): @@ -19,15 +21,15 @@ def __call__(cls, value: Any, *args: Any, **kwargs: Any) -> "Enum": # type: ign if isinstance(value, str): # pragma: no cover value = value.upper() - return super().__call__(value, *args, **kwargs) # type: ignore[no-any-return] # noqa + return super().__call__(value, *args, **kwargs) # type: ignore[no-any-return] def __contains__(cls, value: Any) -> bool: """Contains.""" if isinstance(value, str): - return value.upper() in cls._value2member_map_ # type: ignore[attr-defined] # noqa + return value.upper() in cls._value2member_map_ # type: ignore[attr-defined] - return super().__contains__(value) # type: ignore[call-arg] # noqa + return super().__contains__(value) # type: ignore[call-arg] class Enum(_Enum, metaclass=EnumMeta): @@ -65,8 +67,8 @@ def __eq__(self, other: Any) -> bool: # pragma: no cover """ if isinstance(other, Enum): - return self.value == other.value # type: ignore[no-any-return] # noqa - return self.value == other # type: ignore[no-any-return] # noqa + return self.value == other.value # type: ignore[no-any-return] + return self.value == other # type: ignore[no-any-return] def __hash__(self) -> int: # pragma: no cover """ @@ -91,10 +93,12 @@ def _missing_(cls, value: object) -> "Enum": """ for member in cls: - if isinstance(value, str) and member.value == value.upper(): # pragma: no cover + if ( + isinstance(value, str) and member.value == value.upper() + ): # pragma: no cover return member - return super()._missing_(value) # type: ignore[attribute-error, no-any-return] # noqa + return super()._missing_(value) # type: ignore[attribute-error, no-any-return] @classmethod def get_by_value(cls, value: Any) -> "Enum": @@ -148,17 +152,17 @@ def get_by_name_or_value(cls, name_or_value: Any) -> "Enum": return cls.get_by_value(name_or_value) @classmethod - def get_all_values(cls) -> List[Any]: + def get_all_values(cls) -> list[Any]: """Get all values.""" return [enum.value for enum in cls] @classmethod - def get_all_names(cls) -> List[str]: + def get_all_names(cls) -> list[str]: """Get all names.""" return [enum.name for enum in cls] @classmethod - def to_django_choices(cls) -> List[tuple]: + def to_django_choices(cls) -> list[tuple]: """Get all names.""" return [(enum.name, enum.name) for enum in cls] @@ -194,3 +198,10 @@ class RequestMethod(str, Enum): POST = "post" PUT = "put" DELETE = "delete" + + +class OrderBookQuoteAsset(str, Enum): + """Order Book Quote Assets""" + + USDT = "USDT" + IRT = "IRT" From d1e5774b33f7ff20c7ff0c7d4bdf331d5bf99165 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:05:59 +0330 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=94=96=20release:=201.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 9cb76a8..60453e6 100644 --- a/version +++ b/version @@ -1 +1 @@ -v0.0.11 \ No newline at end of file +v1.0.0 \ No newline at end of file From cb8770728e50dac8f11c7d7b4123ab4e7296f8cd Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:06:41 +0330 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=94=96=20release:=201.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f74a995..9e9115c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-bitpin" -version = "0.0.11" +version = "1.0.0" description = "Bitpin Exchange Python SDK" authors = ["AMiWR "] license = "MIT" @@ -14,6 +14,7 @@ python = ">=3.9,<4.0" requests = "^2.31.0" aiohttp = "^3.8.5" pysocks = "^1.7.1" +deprecated = "^1.2.14" [tool.poetry.group.dev.dependencies] @@ -25,6 +26,7 @@ dead = "^1.5.2" pytype = "^2023.8.22" pydocstyle = "^6.3.0" vulture = "^2.9.1" +ruff = "^0.7.0" [tool.poetry.group.docs.dependencies] From 7f07c2795d4b27820de8811b6e0e16ce51c892cb Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:08:00 +0330 Subject: [PATCH 09/12] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20requiremen?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1d7ae76..71d0f6d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohttp" @@ -385,6 +385,23 @@ files = [ [package.dependencies] identify = "*" +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "dill" version = "0.3.7" @@ -1776,6 +1793,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -1947,6 +1965,33 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "ruff" +version = "0.7.0" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"}, + {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, + {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"}, + {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"}, + {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, + {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, + {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, +] + [[package]] name = "setuptools" version = "69.0.3" @@ -2349,4 +2394,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "fa37e344ad87bc39410b203bc3ad364b4ec8131d8401407e97df42a30f836b9a" +content-hash = "f77cba89abaadf700ad5a7e508001a741eb1d5464257333fce46ea5ce3f0ecb1" From 8546c1504d86dec8412bd86871c05ef04eaad239 Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:19:49 +0330 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20deprecate:=20type?= =?UTF-8?q?s=20moved=20to=20deprecated/deprecated=5Ftypes.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/types.py | 226 -------------------------------------------- 1 file changed, 226 deletions(-) delete mode 100644 src/bitpin/types.py diff --git a/src/bitpin/types.py b/src/bitpin/types.py deleted file mode 100644 index a30e141..0000000 --- a/src/bitpin/types.py +++ /dev/null @@ -1,226 +0,0 @@ -""" -# Types. - -Types for Python Bitpin. - -## Description -This file contains all the types used in the project. -""" - -import typing as t -import asyncio -import requests -import aiohttp - -from . import enums - -# General Types: -OptionalStr = t.Optional[str] -OptionalInt = t.Optional[int] -OptionalFloat = t.Optional[float] - -DictStrAny = t.Dict[str, t.Any] -OptionalDictStrAny = t.Optional[DictStrAny] - -EventLoop = asyncio.AbstractEventLoop -OptionalEventLoop = t.Optional[EventLoop] - -# Client Types: -OrderTypeBuy = t.Literal["buy"] -OrderTypeSell = t.Literal["sell"] -OrderTypes = t.Union[OrderTypeBuy, OrderTypeSell, enums.OrderType] -OptionalOrderTypes = t.Optional[OrderTypes] - -OrderModeLimit = t.Literal["limit"] -OrderModeMarket = t.Literal["market"] -OrderModeOCO = t.Literal["oco"] -OrderModeStopLimit = t.Literal["stop_limit"] -OrderModes = t.Union[OrderModeLimit, OrderModeMarket, OrderModeOCO, OrderModeStopLimit, enums.OrderMode] -OptionalOrderModes = t.Optional[OrderModes] - -# HTTP Types: -HttpSession = t.Union[requests.Session, aiohttp.ClientSession] -HttpResponses = t.Union[requests.Response, aiohttp.ClientResponse] - -# Request Types: -RequestMethodGet = t.Literal["get"] -RequestMethodPost = t.Literal["post"] -RequestMethodPut = t.Literal["put"] -RequestMethodDelete = t.Literal["delete"] -RequestMethods = t.Union[ - RequestMethodGet, - RequestMethodPost, - RequestMethodPut, - RequestMethodDelete, - enums.RequestMethod, -] - -# Response Types: -ResultListResponse = t.TypedDict( - "ResultListResponse", - { - "count": t.Optional[int], - "next": t.Optional[str], - "previous": t.Optional[str], - "results": t.List[DictStrAny], - }, -) - -InnerOrderbookResponse = t.TypedDict( - "InnerOrderbookResponse", - { - "amount": str, - "price": str, - "remain": str, - "value": str, - }, -) - -OrderbookResponse = t.TypedDict("OrderbookResponse", {"orders": t.List[InnerOrderbookResponse], "volume": str}) - -InnerTradeResponse = t.TypedDict( - "InnerTradeResponse", - { - "time": float, - "price": str, - "value": str, - "match_amount": str, - "type": str, - "match_id": str, - }, -) - -TradeResponse = t.List[InnerTradeResponse] - -LoginResponse = t.TypedDict( - "LoginResponse", - { - "refresh": str, - "access": str, - }, -) - -RefreshTokenResponse = t.TypedDict( - "RefreshTokenResponse", - { - "access": str, - }, -) - -CurrencyInfo = t.TypedDict( - "CurrencyInfo", - { - "id": int, - "title": str, - "title_fa": str, - "code": str, - "tradable": bool, - "for_test": bool, - "image": str, - "decimal": int, - "decimal_amount": int, - "decimal_irt": int, - "color": str, - "high_risk": bool, - "show_high_risk": bool, - "withdraw_commission": str, - "tags": t.List[DictStrAny], - }, -) - -WalletInfo = t.TypedDict( - "WalletInfo", - { - "id": int, - "currency": CurrencyInfo, - "balance": str, - "frozen": str, - "total": str, - "value": str, - "value_frozen": str, - "value_total": str, - "usdt_value": str, - "usdt_value_frozen": str, - "usdt_value_total": str, - "address": str, - "inviter_commission": str, - "service": str, - "daily_withdraw": str, - }, -) - -MarketInfo = t.TypedDict( - "MarketInfo", - { - "id": int, - "currency1": CurrencyInfo, - "currency2": CurrencyInfo, - "code": str, - "title": str, - "title_fa": str, - "commissions": t.Dict[str, float], - }, -) - -CreateOrderResponse = t.TypedDict( - "CreateOrderResponse", - { - "id": int, - "market": MarketInfo, - "amount1": str, - "amount2": str, - "price": str, - "price_limit": str, - "price_stop": OptionalStr, - "price_limit_oco": OptionalStr, - "type": str, - "active_limit": str, - "identifier": OptionalStr, - "mode": str, - "expected_gain": str, - "expected_resource": str, - "commission_percent": float, - "user_share_percent": float, - "expected_commission": str, - "expected_user_gain": str, - "expected_user_price": str, - "gain_currency": CurrencyInfo, - "resource_currency": CurrencyInfo, - "fulfilled": float, - "exchanged1": str, - "exchanged2": str, - "gain": str, - "resource": str, - "remain_amount": str, - "average_price": str, - "average_user_price": str, - "commission": str, - "user_commission": str, - "user_gain": str, - "created_at": str, - "activated_at": str, - "state": str, - "req_to_cancel": bool, - "info": t.Dict[str, t.Any], - "closed_at": OptionalStr, - "external_address": str, - }, -) - -OpenOrdersResponse = t.TypedDict( - "OpenOrdersResponse", - { - "count": int, - "next": OptionalStr, - "previous": OptionalStr, - "results": t.List[CreateOrderResponse], - }, -) - -CancelOrderResponse = t.TypedDict( - "CancelOrderResponse", - { - "status": str, - "id": str, - }, -) From 70b7d3ecde2df71c8c34c5e259fba50f88f8ab3f Mon Sep 17 00:00:00 2001 From: darhelm Date: Thu, 24 Oct 2024 17:29:48 +0330 Subject: [PATCH 11/12] =?UTF-8?q?=E2=9C=A8=20feat:=20added=20quote=20asset?= =?UTF-8?q?s=20to=20enums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bitpin/enums.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitpin/enums.py b/src/bitpin/enums.py index 3637cba..9c848c5 100644 --- a/src/bitpin/enums.py +++ b/src/bitpin/enums.py @@ -1,7 +1,7 @@ """ # Enums. -Enum classes for the BitPin API. +Enum classes for the Bitpin API. """ from enum import ( From c4f6097779a8ab612af8df0738f47d0f1cc03024 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:03:56 +0000 Subject: [PATCH 12/12] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/bitpin/clients/async_client.py | 46 ++++--------------- src/bitpin/clients/client.py | 44 ++++-------------- src/bitpin/clients/core.py | 16 ++----- src/bitpin/deprecated/clients/async_client.py | 38 ++++----------- src/bitpin/deprecated/clients/client.py | 30 +++--------- src/bitpin/deprecated/clients/core.py | 16 ++----- src/bitpin/deprecated/deprecated_enums.py | 4 +- src/bitpin/enums.py | 4 +- src/bitpin/response_types.py | 8 +--- 9 files changed, 44 insertions(+), 162 deletions(-) diff --git a/src/bitpin/clients/async_client.py b/src/bitpin/clients/async_client.py index 172356d..f7fe300 100644 --- a/src/bitpin/clients/async_client.py +++ b/src/bitpin/clients/async_client.py @@ -201,9 +201,7 @@ async def _get( # type: ignore[no-untyped-def, override] dict: Response. """ - return await self._request_api( - enums.RequestMethod.GET, path, signed, version, **kwargs - ) + return await self._request_api(enums.RequestMethod.GET, path, signed, version, **kwargs) async def _post( # type: ignore[no-untyped-def, override] self, @@ -225,9 +223,7 @@ async def _post( # type: ignore[no-untyped-def, override] dict: Response. """ - return await self._request_api( - enums.RequestMethod.POST, path, signed, version, **kwargs - ) + return await self._request_api(enums.RequestMethod.POST, path, signed, version, **kwargs) async def _delete( # type: ignore[no-untyped-def, override] self, @@ -249,9 +245,7 @@ async def _delete( # type: ignore[no-untyped-def, override] dict: Response. """ - return await self._request_api( - enums.RequestMethod.DELETE, path, signed, version, **kwargs - ) + return await self._request_api(enums.RequestMethod.DELETE, path, signed, version, **kwargs) async def _request_api( # type: ignore[no-untyped-def, override] self, @@ -494,11 +488,7 @@ async def get_wallets( # type: ignore[no-untyped-def, override] Rate limit: 10000/day. """ - kwargs["params"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} return await self._get(self.WALLETS_URL, signed=True, **kwargs) async def get_orderbook( # type: ignore[no-untyped-def, override] @@ -526,9 +516,7 @@ async def get_orderbook( # type: ignore[no-untyped-def, override] version=self.PUBLIC_API_VERSION_1, ) - async def get_recent_trades( # type: ignore[no-untyped-def, override] - self, symbol: str - ) -> t.RecentTradesInfo: + async def get_recent_trades(self, symbol: str) -> t.RecentTradesInfo: # type: ignore[no-untyped-def, override] """ Get recent trades. @@ -591,11 +579,7 @@ async def get_user_orders( # type: ignore[no-untyped-def, override] kwargs["params"] = kwargs.get("params", {}) kwargs["params"].update( - { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} ) return await self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] @@ -718,11 +702,7 @@ async def create_order_bulk(self, orders: t.BulkOrderList, **kwargs): msg = "All orders must be in the same market! not creating order!" raise ValueError(msg) - kwargs["json"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + kwargs["json"] = {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} return await self._post(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] async def cancel_order_bulk( @@ -746,11 +726,7 @@ async def cancel_order_bulk( [API Docs](https://docs.bitpin.ir/v1/docs/order/Bulk%20Orders/Cancel_Bulk_Orders) """ - kwargs["json"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + kwargs["json"] = {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} return await self._delete(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] async def get_user_trades( # type: ignore[no-untyped-def, override] @@ -783,11 +759,7 @@ async def get_user_trades( # type: ignore[no-untyped-def, override] kwargs["params"] = kwargs.get("params", {}) kwargs["params"].update( - { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} ) return await self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] diff --git a/src/bitpin/clients/client.py b/src/bitpin/clients/client.py index 28c22e3..8fa4a33 100644 --- a/src/bitpin/clients/client.py +++ b/src/bitpin/clients/client.py @@ -134,9 +134,7 @@ def _get( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api( - enums.RequestMethod.GET, path, signed, version, **kwargs - ) + return self._request_api(enums.RequestMethod.GET, path, signed, version, **kwargs) def _post( # type: ignore[no-untyped-def] self, @@ -158,9 +156,7 @@ def _post( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api( - enums.RequestMethod.POST, path, signed, version, **kwargs - ) + return self._request_api(enums.RequestMethod.POST, path, signed, version, **kwargs) def _delete( # type: ignore[no-untyped-def] self, @@ -182,9 +178,7 @@ def _delete( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api( - enums.RequestMethod.DELETE, path, signed, version, **kwargs - ) + return self._request_api(enums.RequestMethod.DELETE, path, signed, version, **kwargs) def _request_api( # type: ignore[no-untyped-def] self, @@ -430,11 +424,7 @@ def get_wallets( # type: ignore[no-untyped-def, override] Rate limit: 10000/day. """ - kwargs["params"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} return self._get(self.WALLETS_URL, signed=True, **kwargs) def get_orderbook( # type: ignore[no-untyped-def, override] @@ -462,9 +452,7 @@ def get_orderbook( # type: ignore[no-untyped-def, override] version=self.PUBLIC_API_VERSION_1, ) - def get_recent_trades( # type: ignore[no-untyped-def, override] - self, symbol: str - ) -> t.RecentTradesInfo: + def get_recent_trades(self, symbol: str) -> t.RecentTradesInfo: # type: ignore[no-untyped-def, override] """ Get recent trades. @@ -527,11 +515,7 @@ def get_user_orders( # type: ignore[no-untyped-def, override] kwargs["params"] = kwargs.get("params", {}) kwargs["params"].update( - { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} ) return self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] @@ -588,9 +572,7 @@ def create_order( # type: ignore[no-untyped-def, override] kwargs["json"] = {k: v for k, v in kwargs["json"].items() if v is not None} return self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] - def cancel_order( # type: ignore[no-untyped-def, override] - self, order_id: str, **kwargs - ) -> t.CancelOrderResponse: + def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # type: ignore[no-untyped-def, override] """ Cancel order. @@ -654,11 +636,7 @@ def create_order_bulk(self, orders: t.BulkOrderList, **kwargs): msg = "All orders must be in the same market! not creating order!" raise ValueError(msg) - kwargs["json"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + kwargs["json"] = {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} return self._post(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] def cancel_order_bulk( @@ -682,11 +660,7 @@ def cancel_order_bulk( [API Docs](https://docs.bitpin.ir/v1/docs/order/Bulk%20Orders/Cancel_Bulk_Orders) """ - kwargs["json"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in {"self", "kwargs"} - } + kwargs["json"] = {k: str(v) for k, v in locals().items() if v is not None and k not in {"self", "kwargs"}} return self._delete(self.BULK_ORDER_URL, signed=True, **kwargs) # type: ignore[return-value] def get_user_trades( # type: ignore[no-untyped-def, override] diff --git a/src/bitpin/clients/core.py b/src/bitpin/clients/core.py index cd2951e..7d6593e 100644 --- a/src/bitpin/clients/core.py +++ b/src/bitpin/clients/core.py @@ -78,12 +78,8 @@ def __init__( # type: ignore[no-untyped-def] self.api_key = api_key or os.environ.get("BITPIN_API_KEY") self.api_secret = api_secret or os.environ.get("BITPIN_API_SECRET") - self.access_token: t.OptionalStr = access_token or os.environ.get( - "BITPIN_ACCESS_TOKEN" - ) - self.refresh_token: t.OptionalStr = refresh_token or os.environ.get( - "BITPIN_REFRESH_TOKEN" - ) + self.access_token: t.OptionalStr = access_token or os.environ.get("BITPIN_ACCESS_TOKEN") + self.refresh_token: t.OptionalStr = refresh_token or os.environ.get("BITPIN_REFRESH_TOKEN") self._background_relogin = background_relogin self._background_relogin_interval = background_relogin_interval @@ -115,17 +111,13 @@ def _get_request_kwargs( kwargs["headers"] = headers if data and method == "get": - kwargs["params"] = "&".join( - f"{data[0]}={data[1]}" for data in kwargs["data"] - ) + kwargs["params"] = "&".join(f"{data[0]}={data[1]}" for data in kwargs["data"]) del kwargs["data"] return kwargs @staticmethod - def _pick( - response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results" - ) -> t.DictStrAny: + def _pick(response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results") -> t.DictStrAny: for _ in response.get(result_key, []): if _[key] == value: response[result_key] = _ diff --git a/src/bitpin/deprecated/clients/async_client.py b/src/bitpin/deprecated/clients/async_client.py index 9ea03a8..f602e2a 100644 --- a/src/bitpin/deprecated/clients/async_client.py +++ b/src/bitpin/deprecated/clients/async_client.py @@ -206,9 +206,7 @@ async def _get( # type: ignore[no-untyped-def, override] dict: Response. """ - return await self._request_api( - deprecated_enums.RequestMethod.GET, path, signed, version, **kwargs - ) + return await self._request_api(deprecated_enums.RequestMethod.GET, path, signed, version, **kwargs) async def _post( # type: ignore[no-untyped-def, override] self, @@ -230,9 +228,7 @@ async def _post( # type: ignore[no-untyped-def, override] dict: Response. """ - return await self._request_api( - deprecated_enums.RequestMethod.POST, path, signed, version, **kwargs - ) + return await self._request_api(deprecated_enums.RequestMethod.POST, path, signed, version, **kwargs) async def _delete( # type: ignore[no-untyped-def, override] self, @@ -254,9 +250,7 @@ async def _delete( # type: ignore[no-untyped-def, override] dict: Response. """ - return await self._request_api( - deprecated_enums.RequestMethod.DELETE, path, signed, version, **kwargs - ) + return await self._request_api(deprecated_enums.RequestMethod.DELETE, path, signed, version, **kwargs) async def _request_api( # type: ignore[no-untyped-def, override] self, @@ -328,9 +322,7 @@ async def _handle_response(response: aiohttp.ClientResponse) -> t.DictStrAny: # return {"status": "success", "id": response.request_info.url.parts[-2]} return await response.json() # type: ignore[no-any-return] except ValueError as exc: - raise RequestException( - f"Invalid Response: {await response.text()}" - ) from exc + raise RequestException(f"Invalid Response: {await response.text()}") from exc async def _background_relogin_task(self) -> None: # type: ignore[override] """Background relogin task.""" @@ -561,11 +553,7 @@ async def get_user_orders( # type: ignore[no-untyped-def, override] [API Docs](https://docs.bitpin.ir/#8a7c2a2af5) """ - kwargs["params"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in ("self", "kwargs") - } + kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} return await self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] async def create_order( # type: ignore[no-untyped-def, override] @@ -605,11 +593,7 @@ async def create_order( # type: ignore[no-untyped-def, override] [API Docs](https://docs.bitpin.ir/#34b353d77b) """ - kwargs["json"] = { - k: v - for k, v in locals().items() - if v is not None and k not in ("self", "kwargs") - } + kwargs["json"] = {k: v for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} return await self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] async def cancel_order( # type: ignore[no-untyped-def, override] @@ -629,9 +613,7 @@ async def cancel_order( # type: ignore[no-untyped-def, override] [API Docs](https://docs.bitpin.ir/#3fe8d57657) """ - return await self._delete( - self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs - ) # type: ignore[return-value] + return await self._delete(self.ORDERS_URL + f"{order_id}/", signed=True, **kwargs) # type: ignore[return-value] async def get_user_trades( # type: ignore[no-untyped-def, override] self, @@ -656,11 +638,7 @@ async def get_user_trades( # type: ignore[no-untyped-def, override] [API Docs](https://docs.bitpin.ir/#3fe8d57657) """ - kwargs["params"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in ("self", "kwargs") - } + kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} return await self._get(self.USER_TRADES_URL, signed=True, **kwargs) async def close_connection(self) -> None: # type: ignore[override] diff --git a/src/bitpin/deprecated/clients/client.py b/src/bitpin/deprecated/clients/client.py index 5f2d927..5fa58a5 100644 --- a/src/bitpin/deprecated/clients/client.py +++ b/src/bitpin/deprecated/clients/client.py @@ -141,9 +141,7 @@ def _get( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api( - deprecated_enums.RequestMethod.GET, path, signed, version, **kwargs - ) + return self._request_api(deprecated_enums.RequestMethod.GET, path, signed, version, **kwargs) def _post( # type: ignore[no-untyped-def] self, @@ -165,9 +163,7 @@ def _post( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api( - deprecated_enums.RequestMethod.POST, path, signed, version, **kwargs - ) + return self._request_api(deprecated_enums.RequestMethod.POST, path, signed, version, **kwargs) def _delete( # type: ignore[no-untyped-def] self, @@ -189,9 +185,7 @@ def _delete( # type: ignore[no-untyped-def] dict: Response. """ - return self._request_api( - deprecated_enums.RequestMethod.DELETE, path, signed, version, **kwargs - ) + return self._request_api(deprecated_enums.RequestMethod.DELETE, path, signed, version, **kwargs) def _request_api( # type: ignore[no-untyped-def] self, @@ -490,11 +484,7 @@ def get_user_orders( # type: ignore[no-untyped-def] [API Docs](https://docs.bitpin.ir/#8a7c2a2af5) """ - kwargs["params"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in ("self", "kwargs") - } + kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} return self._get(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] def create_order( # type: ignore[no-untyped-def] @@ -534,11 +524,7 @@ def create_order( # type: ignore[no-untyped-def] [API Docs](https://docs.bitpin.ir/#34b353d77b) """ - kwargs["json"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in ("self", "kwargs") - } + kwargs["json"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} return self._post(self.ORDERS_URL, signed=True, **kwargs) # type: ignore[return-value] def cancel_order(self, order_id: str, **kwargs) -> t.CancelOrderResponse: # type: ignore[no-untyped-def] @@ -581,11 +567,7 @@ def get_user_trades( # type: ignore[no-untyped-def] [API Docs](https://docs.bitpin.ir/#3fe8d57657) """ - kwargs["params"] = { - k: str(v) - for k, v in locals().items() - if v is not None and k not in ("self", "kwargs") - } + kwargs["params"] = {k: str(v) for k, v in locals().items() if v is not None and k not in ("self", "kwargs")} return self._get(self.USER_TRADES_URL, signed=True, **kwargs) def close_connection(self) -> None: diff --git a/src/bitpin/deprecated/clients/core.py b/src/bitpin/deprecated/clients/core.py index 168ab8c..a1390ca 100644 --- a/src/bitpin/deprecated/clients/core.py +++ b/src/bitpin/deprecated/clients/core.py @@ -76,12 +76,8 @@ def __init__( # type: ignore[no-untyped-def] self.api_key = api_key or os.environ.get("BITPIN_API_KEY") self.api_secret = api_secret or os.environ.get("BITPIN_API_SECRET") - self.access_token: t.OptionalStr = access_token or os.environ.get( - "BITPIN_ACCESS_TOKEN" - ) - self.refresh_token: t.OptionalStr = refresh_token or os.environ.get( - "BITPIN_REFRESH_TOKEN" - ) + self.access_token: t.OptionalStr = access_token or os.environ.get("BITPIN_ACCESS_TOKEN") + self.refresh_token: t.OptionalStr = refresh_token or os.environ.get("BITPIN_REFRESH_TOKEN") self._background_relogin = background_relogin self._background_relogin_interval = background_relogin_interval @@ -113,17 +109,13 @@ def _get_request_kwargs( kwargs["headers"] = headers if data and method == "get": - kwargs["params"] = "&".join( - f"{data[0]}={data[1]}" for data in kwargs["data"] - ) + kwargs["params"] = "&".join(f"{data[0]}={data[1]}" for data in kwargs["data"]) del kwargs["data"] return kwargs @staticmethod - def _pick( - response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results" - ) -> t.DictStrAny: + def _pick(response: t.DictStrAny, key: str, value: t.t.Any, result_key: str = "results") -> t.DictStrAny: for _ in response.get(result_key, []): if _[key] == value: response[result_key] = _ diff --git a/src/bitpin/deprecated/deprecated_enums.py b/src/bitpin/deprecated/deprecated_enums.py index 9506a63..56525bd 100644 --- a/src/bitpin/deprecated/deprecated_enums.py +++ b/src/bitpin/deprecated/deprecated_enums.py @@ -93,9 +93,7 @@ def _missing_(cls, value: object) -> "Enum": """ for member in cls: - if ( - isinstance(value, str) and member.value == value.upper() - ): # pragma: no cover + if isinstance(value, str) and member.value == value.upper(): # pragma: no cover return member return super()._missing_(value) # type: ignore[attribute-error, no-any-return] diff --git a/src/bitpin/enums.py b/src/bitpin/enums.py index 9c848c5..c775cdb 100644 --- a/src/bitpin/enums.py +++ b/src/bitpin/enums.py @@ -93,9 +93,7 @@ def _missing_(cls, value: object) -> "Enum": """ for member in cls: - if ( - isinstance(value, str) and member.value == value.upper() - ): # pragma: no cover + if isinstance(value, str) and member.value == value.upper(): # pragma: no cover return member return super()._missing_(value) # type: ignore[attribute-error, no-any-return] diff --git a/src/bitpin/response_types.py b/src/bitpin/response_types.py index 98127b9..0f7495b 100644 --- a/src/bitpin/response_types.py +++ b/src/bitpin/response_types.py @@ -40,9 +40,7 @@ OrderStateInitial = t.Literal["initial"] OrderStateActive = t.Literal["active"] OrderStateClosed = t.Literal["closed"] -OrderState = t.Union[ - OrderStateInitial, OrderStateActive, OrderStateClosed, enums.OrderState -] +OrderState = t.Union[OrderStateInitial, OrderStateActive, OrderStateClosed, enums.OrderState] OptionalOrderState = t.Optional[OrderState] OptionalOrderStateList = t.Optional[list[OrderState]] @@ -55,9 +53,7 @@ OrderModeMarket = t.Literal["market"] OrderModeOCO = t.Literal["oco"] OrderModeStopLimit = t.Literal["stop_limit"] -OrderModes = t.Union[ - OrderModeLimit, OrderModeMarket, OrderModeOCO, OrderModeStopLimit, enums.OrderMode -] +OrderModes = t.Union[OrderModeLimit, OrderModeMarket, OrderModeOCO, OrderModeStopLimit, enums.OrderMode] OptionalOrderModes = t.Optional[OrderModes] OptionalOrderModesList = t.Optional[list[OrderModes]]