From ffca52463ad4eef1e4ddfdd849725b82075ddceb Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Mon, 19 Aug 2024 09:58:14 +0300 Subject: [PATCH 01/10] Add async --- pybit/async/__init__.py | 0 pybit/async/_async_http_manager.py | 379 ++++++++++++++++++++ pybit/async/_v5_account.py | 297 ++++++++++++++++ pybit/async/_v5_asset.py | 472 +++++++++++++++++++++++++ pybit/async/_v5_broker.py | 19 + pybit/async/_v5_institutional_loan.py | 100 ++++++ pybit/async/_v5_market.py | 299 ++++++++++++++++ pybit/async/_v5_misc.py | 40 +++ pybit/async/_v5_position.py | 248 +++++++++++++ pybit/async/_v5_pre_upgrade.py | 130 +++++++ pybit/async/_v5_spot_leverage_token.py | 95 +++++ pybit/async/_v5_spot_margin_trade.py | 242 +++++++++++++ pybit/async/_v5_trade.py | 244 +++++++++++++ pybit/async/_v5_user.py | 196 ++++++++++ pybit/async/unified_trading.py | 43 +++ requirements.txt | 1 + 16 files changed, 2805 insertions(+) create mode 100644 pybit/async/__init__.py create mode 100644 pybit/async/_async_http_manager.py create mode 100644 pybit/async/_v5_account.py create mode 100644 pybit/async/_v5_asset.py create mode 100644 pybit/async/_v5_broker.py create mode 100644 pybit/async/_v5_institutional_loan.py create mode 100644 pybit/async/_v5_market.py create mode 100644 pybit/async/_v5_misc.py create mode 100644 pybit/async/_v5_position.py create mode 100644 pybit/async/_v5_pre_upgrade.py create mode 100644 pybit/async/_v5_spot_leverage_token.py create mode 100644 pybit/async/_v5_spot_margin_trade.py create mode 100644 pybit/async/_v5_trade.py create mode 100644 pybit/async/_v5_user.py create mode 100644 pybit/async/unified_trading.py diff --git a/pybit/async/__init__.py b/pybit/async/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pybit/async/_async_http_manager.py b/pybit/async/_async_http_manager.py new file mode 100644 index 0000000..b239cee --- /dev/null +++ b/pybit/async/_async_http_manager.py @@ -0,0 +1,379 @@ +from collections import defaultdict +from dataclasses import dataclass, field +import time +import hmac +import hashlib +from Crypto.Hash import SHA256 +from Crypto.PublicKey import RSA +from Crypto.Signature import PKCS1_v1_5 +import base64 +import json +import logging +import aiohttp +import asyncio + +from datetime import datetime as dt + +from ..exceptions import FailedRequestError, InvalidRequestError +from .. import _helpers + +# Requests will use simplejson if available. +try: + from simplejson.errors import JSONDecodeError +except ImportError: + from json.decoder import JSONDecodeError + +HTTP_URL = "https://{SUBDOMAIN}.{DOMAIN}.{TLD}" +SUBDOMAIN_TESTNET = "api-testnet" +SUBDOMAIN_MAINNET = "api" +DEMO_SUBDOMAIN_TESTNET = "api-demo-testnet" +DEMO_SUBDOMAIN_MAINNET = "api-demo" +DOMAIN_MAIN = "bybit" +DOMAIN_ALT = "bytick" +TLD_MAIN = "com" +TLD_NL = "nl" +TLD_HK = "com.hk" + + +def generate_signature(use_rsa_authentication, secret, param_str): + def generate_hmac(): + hash = hmac.new( + bytes(secret, "utf-8"), + param_str.encode("utf-8"), + hashlib.sha256, + ) + return hash.hexdigest() + + def generate_rsa(): + hash = SHA256.new(param_str.encode("utf-8")) + encoded_signature = base64.b64encode( + PKCS1_v1_5.new(RSA.importKey(secret)).sign( + hash + ) + ) + return encoded_signature.decode() + + if not use_rsa_authentication: + return generate_hmac() + else: + return generate_rsa() + + +@dataclass +class _V5ASYNCHTTPManager: + testnet: bool = field(default=False) + domain: str = field(default=DOMAIN_MAIN) + tld: str = field(default=TLD_MAIN) + demo: bool = field(default=False) + rsa_authentication: str = field(default=False) + api_key: str = field(default=None) + api_secret: str = field(default=None) + logging_level: logging = field(default=logging.INFO) + log_requests: bool = field(default=False) + timeout: int = field(default=10) + recv_window: bool = field(default=5000) + force_retry: bool = field(default=False) + retry_codes: defaultdict[dict] = field( + default_factory=dict, + init=False, + ) + ignore_codes: dict = field( + default_factory=dict, + init=False, + ) + max_retries: bool = field(default=3) + retry_delay: bool = field(default=3) + referral_id: bool = field(default=None) + record_request_time: bool = field(default=False) + return_response_headers: bool = field(default=False) + + def __post_init__(self): + subdomain = SUBDOMAIN_TESTNET if self.testnet else SUBDOMAIN_MAINNET + domain = DOMAIN_MAIN if not self.domain else self.domain + if self.demo: + if self.testnet: + subdomain = DEMO_SUBDOMAIN_TESTNET + else: + subdomain = DEMO_SUBDOMAIN_MAINNET + url = HTTP_URL.format(SUBDOMAIN=subdomain, DOMAIN=domain, TLD=self.tld) + self.endpoint = url + + if not self.ignore_codes: + self.ignore_codes = set() + if not self.retry_codes: + self.retry_codes = {10002, 10006, 30034, 30035, 130035, 130150} + self.logger = logging.getLogger(__name__) + if len(logging.root.handlers) == 0: + # no handler on root logger set -> we add handler just for this logger to not mess with custom logic from outside + handler = logging.StreamHandler() + handler.setFormatter( + logging.Formatter( + fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + ) + handler.setLevel(self.logging_level) + self.logger.addHandler(handler) + + self.logger.debug("Initializing HTTP session.") + + @staticmethod + def prepare_payload(method, parameters): + """ + Prepares the request payload and validates parameter value types. + """ + + def cast_values(): + string_params = [ + "qty", + "price", + "triggerPrice", + "takeProfit", + "stopLoss", + ] + integer_params = ["positionIdx"] + for key, value in parameters.items(): + if key in string_params: + if type(value) != str: + parameters[key] = str(value) + elif key in integer_params: + if type(value) != int: + parameters[key] = int(value) + + if method == "GET": + payload = "&".join( + [ + str(k) + "=" + str(v) + for k, v in sorted(parameters.items()) + if v is not None + ] + ) + return payload + else: + cast_values() + return json.dumps(parameters) + + def _auth(self, payload, recv_window, timestamp): + """ + Prepares authentication signature per Bybit API specifications. + """ + + if self.api_key is None or self.api_secret is None: + raise PermissionError("Authenticated endpoints require keys.") + + param_str = str(timestamp) + self.api_key + str(recv_window) + payload + + return generate_signature( + self.rsa_authentication, self.api_secret, param_str + ) + + @staticmethod + def _verify_string(params, key): + if key in params: + if not isinstance(params[key], str): + return False + else: + return True + return True + + async def _submit_request(self, method=None, path=None, query=None, auth=False): + """ + Submits the request to the API. + + Notes + ------------------- + We use the params argument for the GET method, and data argument for + the POST method. Dicts passed to the data argument must be + JSONified prior to submitting request. + + """ + + if query is None: + query = {} + + # Store original recv_window. + recv_window = self.recv_window + + # Bug fix: change floating whole numbers to integers to prevent + # auth signature errors. + if query is not None: + for i in query.keys(): + if isinstance(query[i], float) and query[i] == int(query[i]): + query[i] = int(query[i]) + + # Send request and return headers with body. Retry if failed. + retries_attempted = self.max_retries + req_params = None + + async with aiohttp.ClientSession() as client: + while True: + retries_attempted -= 1 + if retries_attempted < 0: + raise FailedRequestError( + request=f"{method} {path}: {req_params}", + message="Bad Request. Retries exceeded maximum.", + status_code=400, + time=dt.utcnow().strftime("%H:%M:%S"), + resp_headers=None, + ) + + retries_remaining = f"{retries_attempted} retries remain." + + req_params = self.prepare_payload(method, query) + + # Authenticate if we are using a private endpoint. + if auth: + # Prepare signature. + timestamp = _helpers.generate_timestamp() + signature = self._auth( + payload=req_params, + recv_window=recv_window, + timestamp=timestamp, + ) + headers = { + "Content-Type": "application/json", + "X-BAPI-API-KEY": self.api_key, + "X-BAPI-SIGN": signature, + "X-BAPI-SIGN-TYPE": "2", + "X-BAPI-TIMESTAMP": str(timestamp), + "X-BAPI-RECV-WINDOW": str(recv_window), + } + else: + headers = {} + + try: + if method == "GET": + if req_params: + url = path + f"?{req_params}" + else: + url = path + request_func = client.get + request_kwargs = {"url": url, "headers": headers} + else: + request_func = client.post + request_kwargs = {"url": path, "data": req_params, "headers": headers} + + async with request_func(**request_kwargs, timeout=self.timeout) as response: + # Check HTTP status code before trying to decode JSON. + if response.status != 200: + if response.status == 403: + error_msg = "You have breached the IP rate limit or your IP is from the USA." + else: + error_msg = "HTTP status code is not 200." + self.logger.debug(f"Response text: {await response.text()}") + raise FailedRequestError( + request=f"{method} {path}: {req_params}", + message=error_msg, + status_code=response.status, + time=dt.utcnow().strftime("%H:%M:%S"), + resp_headers=response.headers, + ) + + try: + s = await response.json() + except JSONDecodeError as e: + if self.force_retry: + self.logger.error(f"{e}. {retries_remaining}") + await asyncio.sleep(self.retry_delay) + continue + else: + self.logger.debug(f"Response text: {await response.text()}") + raise FailedRequestError( + request=f"{method} {path}: {req_params}", + message="Conflict. Could not decode JSON.", + status_code=409, + time=dt.utcnow().strftime("%H:%M:%S"), + resp_headers=response.headers, + ) + except ( + aiohttp.ClientConnectionError, + aiohttp.ClientError, + aiohttp.ClientResponseError, + aiohttp.ClientSSLError, + aiohttp.ClientTimeout, + ) as e: + if self.force_retry: + self.logger.error(f"{e}. {retries_remaining}") + await asyncio.sleep(self.retry_delay) + continue + else: + raise e + + # Log the request. + if self.log_requests: + if req_params: + self.logger.debug( + f"Request -> {method} {path}. Body: {req_params}. " + f"Headers: {headers}" + ) + else: + self.logger.debug( + f"Request -> {method} {path}. Headers: {headers}" + ) + + + + ret_code = "retCode" + ret_msg = "retMsg" + + # If Bybit returns an error, raise. + if s[ret_code]: + # Generate error message. + error_msg = f"{s[ret_msg]} (ErrCode: {s[ret_code]})" + + # Set default retry delay. + delay_time = self.retry_delay + + # Retry non-fatal whitelisted error requests. + if s[ret_code] in self.retry_codes: + # 10002, recv_window error; add 2.5 seconds and retry. + if s[ret_code] == 10002: + error_msg += ". Added 2.5 seconds to recv_window" + recv_window += 2500 + + # 10006, rate limit error; wait until + # X-Bapi-Limit-Reset-Timestamp and retry. + elif s[ret_code] == 10006: + self.logger.error( + f"{error_msg}. Hit the API rate limit. " + f"Sleeping, then trying again. Request: {path}" + ) + + # Calculate how long we need to wait in milliseconds. + limit_reset_time = int(response.headers["X-Bapi-Limit-Reset-Timestamp"]) + limit_reset_str = dt.fromtimestamp(limit_reset_time / 10**3).strftime( + "%H:%M:%S.%f")[:-3] + delay_time = (int(limit_reset_time) - _helpers.generate_timestamp()) / 10**3 + error_msg = ( + f"API rate limit will reset at {limit_reset_str}. " + f"Sleeping for {int(delay_time * 10**3)} milliseconds" + ) + + # Log the error. + self.logger.error(f"{error_msg}. {retries_remaining}") + await asyncio.sleep(delay_time) + continue + + elif s[ret_code] in self.ignore_codes: + pass + + else: + raise InvalidRequestError( + request=f"{method} {path}: {req_params}", + message=s[ret_msg], + status_code=s[ret_code], + time=dt.utcnow().strftime("%H:%M:%S"), + resp_headers=response.headers, + ) + else: + if self.log_requests: + self.logger.debug( + f"Response headers: {response.headers}" + ) + + if self.return_response_headers: + return s, response.elapsed, response.headers, + elif self.record_request_time: + return s, response.elapsed + else: + return s \ No newline at end of file diff --git a/pybit/async/_v5_account.py b/pybit/async/_v5_account.py new file mode 100644 index 0000000..bc1a9a8 --- /dev/null +++ b/pybit/async/_v5_account.py @@ -0,0 +1,297 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..account import Account + + +class AccountHTTP(_V5ASYNCHTTPManager): + async def get_wallet_balance(self, **kwargs): + """Obtain wallet balance, query asset information of each currency, and account risk rate information under unified margin mode. + By default, currency information with assets or liabilities of 0 is not returned. + + Required args: + accountType (string): Account type + Unified account: UNIFIED + Normal account: CONTRACT + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/wallet-balance + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_WALLET_BALANCE}", + query=kwargs, + auth=True, + ) + + async def upgrade_to_unified_trading_account(self, **kwargs): + """Upgrade Unified Account + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/upgrade-unified-account + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.UPGRADE_TO_UNIFIED_ACCOUNT}", + query=kwargs, + auth=True, + ) + + async def get_borrow_history(self, **kwargs): + """Get interest records, sorted in reverse order of creation time. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/borrow-history + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_BORROW_HISTORY}", + query=kwargs, + auth=True, + ) + + async def repay_liability(self, **kwargs): + """You can manually repay the liabilities of the Unified account + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/repay-liability + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.REPAY_LIABILITY}", + query=kwargs, + auth=True, + ) + + async def get_collateral_info(self, **kwargs): + """Get the collateral information of the current unified margin account, including loan interest rate, loanable amount, collateral conversion rate, whether it can be mortgaged as margin, etc. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/collateral-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_COLLATERAL_INFO}", + query=kwargs, + auth=True, + ) + + async def set_collateral_coin(self, **kwargs): + """You can decide whether the assets in the Unified account needs to be collateral coins. + + Required args: + coin (string): Coin name + collateralSwitch (string): ON: switch on collateral, OFF: switch off collateral + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/set-collateral + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.SET_COLLATERAL_COIN}", + query=kwargs, + auth=True, + ) + + async def batch_set_collateral_coin(self, **kwargs): + """You can decide whether the assets in the Unified account needs to be collateral coins. + + Required args: + request (array): Object + > coin (string): Coin name + > collateralSwitch (string): ON: switch on collateral, OFF: switch off collateral + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/batch-set-collateral + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.BATCH_SET_COLLATERAL_COIN}", + query=kwargs, + auth=True, + ) + + async def get_coin_greeks(self, **kwargs): + """Get current account Greeks information + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/coin-greeks + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_COIN_GREEKS}", + query=kwargs, + auth=True, + ) + + async def get_fee_rates(self, **kwargs): + """Get the trading fee rate of derivatives. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/fee-rate + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_FEE_RATE}", + query=kwargs, + auth=True, + ) + + async def get_account_info(self, **kwargs): + """Query the margin mode configuration of the account. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/account-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_ACCOUNT_INFO}", + query=kwargs, + auth=True, + ) + + async def get_transaction_log(self, **kwargs): + """Query transaction logs in Unified account. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/transaction-log + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_TRANSACTION_LOG}", + query=kwargs, + auth=True, + ) + + async def get_contract_transaction_log(self, **kwargs): + """Query transaction logs in Classic account. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/contract-transaction-log + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_CONTRACT_TRANSACTION_LOG}", + query=kwargs, + auth=True, + ) + + async def set_margin_mode(self, **kwargs): + """Default is regular margin mode. This mode is valid for USDT Perp, USDC Perp and USDC Option. + + Required args: + setMarginMode (string): REGULAR_MARGIN, PORTFOLIO_MARGIN + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/set-margin-mode + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.SET_MARGIN_MODE}", + query=kwargs, + auth=True, + ) + + async def set_mmp(self, **kwargs): + """ + Market Maker Protection (MMP) is an automated mechanism designed to protect market makers (MM) against liquidity risks + and over-exposure in the market. It prevents simultaneous trade executions on quotes provided by the MM within a short time span. + The MM can automatically pull their quotes if the number of contracts traded for an underlying asset exceeds the configured + threshold within a certain time frame. Once MMP is triggered, any pre-existing MMP orders will be automatically canceled, + and new orders tagged as MMP will be rejected for a specific duration — known as the frozen period — so that MM can + reassess the market and modify the quotes. + + Required args: + baseCoin (strin): Base coin + window (string): Time window (ms) + frozenPeriod (string): Frozen period (ms). "0" means the trade will remain frozen until manually reset + qtyLimit (string): Trade qty limit (positive and up to 2 decimal places) + deltaLimit (string): Delta limit (positive and up to 2 decimal places) + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/set-mmp + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.SET_MMP}", + query=kwargs, + auth=True, + ) + + async def reset_mmp(self, **kwargs): + """Once the mmp triggered, you can unfreeze the account by this endpoint + + Required args: + baseCoin (string): Base coin + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/reset-mmp + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Account.RESET_MMP}", + query=kwargs, + auth=True, + ) + + async def get_mmp_state(self, **kwargs): + """Get MMP state + + Required args: + baseCoin (string): Base coin + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/account/get-mmp-state + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Account.GET_MMP_STATE}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_asset.py b/pybit/async/_v5_asset.py new file mode 100644 index 0000000..379a735 --- /dev/null +++ b/pybit/async/_v5_asset.py @@ -0,0 +1,472 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..asset import Asset + + +class AssetHTTP(_V5ASYNCHTTPManager): + async def get_coin_exchange_records(self, **kwargs): + """Query the coin exchange records. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/exchange + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_COIN_EXCHANGE_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_option_delivery_record(self, **kwargs): + """Query option delivery records, sorted by deliveryTime in descending order + + Required args: + category (string): Product type. option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/option-delivery + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_OPTION_DELIVERY_RECORD}", + query=kwargs, + auth=True, + ) + + async def get_usdc_contract_settlement(self, **kwargs): + """Query session settlement records of USDC perpetual and futures + + Required args: + category (string): Product type. linear + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/settlement + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_USDC_CONTRACT_SETTLEMENT}", + query=kwargs, + auth=True, + ) + + async def get_spot_asset_info(self, **kwargs): + """Query asset information + + Required args: + accountType (string): Account type. SPOT + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/asset-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_SPOT_ASSET_INFO}", + query=kwargs, + auth=True, + ) + + async def get_coins_balance(self, **kwargs): + """You could get all coin balance of all account types under the master account, and sub account. + + Required args: + memberId (string): User Id. It is required when you use master api key to check sub account coin balance + accountType (string): Account type + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/all-balance + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_ALL_COINS_BALANCE}", + query=kwargs, + auth=True, + ) + + async def get_coin_balance(self, **kwargs): + """Query the balance of a specific coin in a specific account type. Supports querying sub UID's balance. + + Required args: + memberId (string): UID. Required when querying sub UID balance + accountType (string): Account type + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/account-coin-balance + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_SINGLE_COIN_BALANCE}", + query=kwargs, + auth=True, + ) + + async def get_transferable_coin(self, **kwargs): + """Query the transferable coin list between each account type + + Required args: + fromAccountType (string): From account type + toAccountType (string): To account type + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/transferable-coin + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_TRANSFERABLE_COIN}", + query=kwargs, + auth=True, + ) + + async def create_internal_transfer(self, **kwargs): + """Create the internal transfer between different account types under the same UID. + + Required args: + transferId (string): UUID. Please manually generate a UUID + coin (string): Coin + amount (string): Amount + fromAccountType (string): From account type + toAccountType (string): To account type + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/create-inter-transfer + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Asset.CREATE_INTERNAL_TRANSFER}", + query=kwargs, + auth=True, + ) + + async def get_internal_transfer_records(self, **kwargs): + """Query the internal transfer records between different account types under the same UID. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_INTERNAL_TRANSFER_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_sub_uid(self, **kwargs): + """Query the sub UIDs under a main UID + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/sub-uid-list + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_SUB_UID}", + query=kwargs, + auth=True, + ) + + async def enable_universal_transfer_for_sub_uid(self, **kwargs): + """Transfer between sub-sub or main-sub + + Required args: + subMemberIds (array): This list has a single item. Separate multiple UIDs by comma, e.g., "uid1,uid2,uid3" + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/enable-unitransfer-subuid + """ + self.logger.warning("enable_universal_transfer_for_sub_uid() is depreciated. You no longer need to configure transferable sub UIDs. Now, all sub UIDs are automatically enabled for universal transfer.") + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Asset.ENABLE_UT_FOR_SUB_UID}", + query=kwargs, + auth=True, + ) + + async def create_universal_transfer(self, **kwargs): + """Transfer between sub-sub or main-sub. Please make sure you have enabled universal transfer on your sub UID in advance. + + Required args: + transferId (string): UUID. Please manually generate a UUID + coin (string): Coin + amount (string): Amount + fromMemberId (integer): From UID + toMemberId (integer): To UID + fromAccountType (string): From account type + toAccountType (string): To account type + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/unitransfer + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Asset.CREATE_UNIVERSAL_TRANSFER}", + query=kwargs, + auth=True, + ) + + async def get_universal_transfer_records(self, **kwargs): + """Query universal transfer records + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/unitransfer-list + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_UNIVERSAL_TRANSFER_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_allowed_deposit_coin_info(self, **kwargs): + """Query allowed deposit coin information. To find out paired chain of coin, please refer coin info api. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/deposit-coin-spec + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_ALLOWED_DEPOSIT_COIN_INFO}", + query=kwargs, + auth=True, + ) + + async def set_deposit_account(self, **kwargs): + """Set auto transfer account after deposit. The same function as the setting for Deposit on web GUI + + Required args: + accountType (string): Account type: UNIFIED,SPOT,OPTION,CONTRACT,FUND + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/set-deposit-acct + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Asset.SET_DEPOSIT_ACCOUNT}", + query=kwargs, + auth=True, + ) + + async def get_deposit_records(self, **kwargs): + """Query deposit records. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/deposit-record + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_DEPOSIT_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_sub_deposit_records(self, **kwargs): + """Query subaccount's deposit records by MAIN UID's API key. + + Required args: + subMemberId (string): Sub UID + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/sub-deposit-record + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_SUB_ACCOUNT_DEPOSIT_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_internal_deposit_records(self, **kwargs): + """Query deposit records within the Bybit platform. These transactions are not on the blockchain. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/internal-deposit-record + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_INTERNAL_DEPOSIT_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_master_deposit_address(self, **kwargs): + """Query the deposit address information of MASTER account. + + Required args: + coin (string): Coin + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_MASTER_DEPOSIT_ADDRESS}", + query=kwargs, + auth=True, + ) + + async def get_sub_deposit_address(self, **kwargs): + """Query the deposit address information of SUB account. + + Required args: + coin (string): Coin + chainType (string): Chain, e.g.,ETH + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/sub-deposit-addr + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_SUB_DEPOSIT_ADDRESS}", + query=kwargs, + auth=True, + ) + + async def get_coin_info(self, **kwargs): + """Query coin information, including chain information, withdraw and deposit status. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/coin-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_COIN_INFO}", + query=kwargs, + auth=True, + ) + + async def get_withdrawal_records(self, **kwargs): + """Query withdrawal records. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/withdraw-record + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_WITHDRAWAL_RECORDS}", + query=kwargs, + auth=True, + ) + + async def get_withdrawable_amount(self, **kwargs): + """Get withdrawable amount + + Required args: + coin (string): Coin name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/delay-amount + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Asset.GET_WITHDRAWABLE_AMOUNT}", + query=kwargs, + auth=True, + ) + + async def withdraw(self, **kwargs): + """Withdraw assets from your Bybit account. You can make an off-chain transfer if the target wallet address is from Bybit. This means that no blockchain fee will be charged. + + Required args: + coin (string): Coin + chain (string): Chain + address (string): Wallet address + tag (string): Tag. Required if tag exists in the wallet address list + amount (string): Withdraw amount + timestamp (integer): Current timestamp (ms). Used for preventing from withdraw replay + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/withdraw + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Asset.WITHDRAW}", + query=kwargs, + auth=True, + ) + + async def cancel_withdrawal(self, **kwargs): + """Cancel the withdrawal + + Required args: + id (string): Withdrawal ID + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/asset/cancel-withdraw + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Asset.CANCEL_WITHDRAWAL}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_broker.py b/pybit/async/_v5_broker.py new file mode 100644 index 0000000..f3ae28c --- /dev/null +++ b/pybit/async/_v5_broker.py @@ -0,0 +1,19 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..broker import Broker + + +class BrokerHTTP(_V5ASYNCHTTPManager): + async def get_broker_earnings(self, **kwargs) -> dict: + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/broker/earning + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Broker.GET_BROKER_EARNINGS}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_institutional_loan.py b/pybit/async/_v5_institutional_loan.py new file mode 100644 index 0000000..4e0cd40 --- /dev/null +++ b/pybit/async/_v5_institutional_loan.py @@ -0,0 +1,100 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..institutional_loan import InstitutionalLoan as InsLoan + + +class InstitutionalLoanHTTP(_V5ASYNCHTTPManager): + async def get_product_info(self, **kwargs) -> dict: + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/otc/margin-product-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{InsLoan.GET_PRODUCT_INFO}", + query=kwargs, + ) + + async def get_margin_coin_info(self, **kwargs) -> dict: + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/otc/margin-coin-convert-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{InsLoan.GET_MARGIN_COIN_INFO}", + query=kwargs, + ) + + async def get_loan_orders(self, **kwargs) -> dict: + """ + Get loan orders information + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/otc/loan-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{InsLoan.GET_LOAN_ORDERS}", + query=kwargs, + auth=True, + ) + + async def get_repayment_info(self, **kwargs) -> dict: + """ + Get a list of your loan repayment orders (orders which repaid the loan). + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/otc/repay-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{InsLoan.GET_REPAYMENT_ORDERS}", + query=kwargs, + auth=True, + ) + + async def get_ltv(self, **kwargs) -> dict: + """ + Get your loan-to-value ratio. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/otc/ltv-convert + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{InsLoan.GET_LTV}", + query=kwargs, + auth=True, + ) + + async def bind_or_unbind_uid(self, **kwargs) -> dict: + """ + For the institutional loan product, you can bind new UIDs to the risk + unit or unbind UID from the risk unit. + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/otc/bind-uid + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{InsLoan.BIND_OR_UNBIND_UID}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_market.py b/pybit/async/_v5_market.py new file mode 100644 index 0000000..269881e --- /dev/null +++ b/pybit/async/_v5_market.py @@ -0,0 +1,299 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..market import Market + + +class MarketHTTP(_V5ASYNCHTTPManager): + async def get_server_time(self) -> dict: + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/time + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_SERVER_TIME}", + ) + + async def get_kline(self, **kwargs) -> dict: + """Query the kline data. Charts are returned in groups based on the requested interval. + + Required args: + category (string): Product type: spot,linear,inverse + symbol (string): Symbol name + interval (string): Kline interval. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/kline + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_KLINE}", + query=kwargs, + ) + + async def get_mark_price_kline(self, **kwargs): + """Query the mark price kline data. Charts are returned in groups based on the requested interval. + + Required args: + category (string): Product type. linear,inverse + symbol (string): Symbol name + interval (string): Kline interval. 1,3,5,15,30,60,120,240,360,720,D,M,W + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/mark-kline + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_MARK_PRICE_KLINE}", + query=kwargs, + ) + + async def get_index_price_kline(self, **kwargs): + """Query the index price kline data. Charts are returned in groups based on the requested interval. + + Required args: + category (string): Product type. linear,inverse + symbol (string): Symbol name + interval (string): Kline interval. 1,3,5,15,30,60,120,240,360,720,D,M,W + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/index-kline + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_INDEX_PRICE_KLINE}", + query=kwargs, + ) + + async def get_premium_index_price_kline(self, **kwargs): + """Retrieve the premium index price kline data. Charts are returned in groups based on the requested interval. + + Required args: + category (string): Product type. linear + symbol (string): Symbol name + interval (string): Kline interval + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/preimum-index-kline + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_PREMIUM_INDEX_PRICE_KLINE}", + query=kwargs, + ) + + async def get_instruments_info(self, **kwargs): + """Query a list of instruments of online trading pair. + + Required args: + category (string): Product type. spot,linear,inverse,option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/instrument + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_INSTRUMENTS_INFO}", + query=kwargs, + ) + + async def get_orderbook(self, **kwargs): + """Query orderbook data + + Required args: + category (string): Product type. spot, linear, inverse, option + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/orderbook + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_ORDERBOOK}", + query=kwargs, + ) + + async def get_tickers(self, **kwargs): + """Query the latest price snapshot, best bid/ask price, and trading volume in the last 24 hours. + + Required args: + category (string): Product type. spot,linear,inverse,option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/tickers + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_TICKERS}", + query=kwargs, + ) + + async def get_funding_rate_history(self, **kwargs): + """ + Query historical funding rate. Each symbol has a different funding interval. + For example, if the interval is 8 hours and the current time is UTC 12, then it returns the last funding rate, which settled at UTC 8. + To query the funding rate interval, please refer to instruments-info. + + Required args: + category (string): Product type. linear,inverse + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/history-fund-rate + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_FUNDING_RATE_HISTORY}", + query=kwargs, + ) + + async def get_public_trade_history(self, **kwargs): + """Query recent public trading data in Bybit. + + Required args: + category (string): Product type. spot,linear,inverse,option + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/recent-trade + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_PUBLIC_TRADING_HISTORY}", + query=kwargs, + ) + + async def get_open_interest(self, **kwargs): + """Get open interest of each symbol. + + Required args: + category (string): Product type. linear,inverse + symbol (string): Symbol name + intervalTime (string): Interval. 5min,15min,30min,1h,4h,1d + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/open-interest + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_OPEN_INTEREST}", + query=kwargs, + ) + + async def get_historical_volatility(self, **kwargs): + """Query option historical volatility + + Required args: + category (string): Product type. option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/iv + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_HISTORICAL_VOLATILITY}", + query=kwargs, + ) + + async def get_insurance(self, **kwargs): + """ + Query Bybit insurance pool data (BTC/USDT/USDC etc). + The data is updated every 24 hours. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/insurance + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_INSURANCE}", + query=kwargs, + ) + + async def get_risk_limit(self, **kwargs): + """Query risk limit of futures + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/risk-limit + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_RISK_LIMIT}", + query=kwargs, + ) + + async def get_option_delivery_price(self, **kwargs): + """Get the delivery price for option + + Required args: + category (string): Product type. option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/delivery-price + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_OPTION_DELIVERY_PRICE}", + query=kwargs, + ) + + async def get_long_short_ratio(self, **kwargs): + """ + Required args: + category (string): Product type. linear (USDT Perpetual only), inverse + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/market/long-short-ratio + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Market.GET_LONG_SHORT_RATIO}", + query=kwargs, + ) diff --git a/pybit/async/_v5_misc.py b/pybit/async/_v5_misc.py new file mode 100644 index 0000000..d372047 --- /dev/null +++ b/pybit/async/_v5_misc.py @@ -0,0 +1,40 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..misc import Misc + + +class MiscHTTP(_V5ASYNCHTTPManager): + async def get_announcement(self, **kwargs) -> dict: + """ + Required args: + locale (string): Language symbol + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/announcement + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Misc.GET_ANNOUNCEMENT}", + query=kwargs, + ) + + async def request_demo_trading_funds(self) -> dict: + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/demo + """ + if not self.demo: + raise Exception( + "You must pass demo=True to the pybit HTTP session to use this " + "method." + ) + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Misc.REQUEST_DEMO_TRADING_FUNDS}", + auth=True, + ) diff --git a/pybit/async/_v5_position.py b/pybit/async/_v5_position.py new file mode 100644 index 0000000..7115df2 --- /dev/null +++ b/pybit/async/_v5_position.py @@ -0,0 +1,248 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..position import Position + + +class PositionHTTP(_V5ASYNCHTTPManager): + async def get_positions(self, **kwargs): + """Query real-time position data, such as position size, cumulative realizedPNL. + + Required args: + category (string): Product type + Unified account: linear, option + Normal account: linear, inverse. + + Please note that category is not involved with business logic + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Position.GET_POSITIONS}", + query=kwargs, + auth=True, + ) + + async def set_leverage(self, **kwargs): + """Set the leverage + + Required args: + category (string): Product type + Unified account: linear + Normal account: linear, inverse. + + Please note that category is not involved with business logic + symbol (string): Symbol name + buyLeverage (string): [0, max leverage of corresponding risk limit]. + Note: Under one-way mode, buyLeverage must be the same as sellLeverage + sellLeverage (string): [0, max leverage of corresponding risk limit]. + Note: Under one-way mode, buyLeverage must be the same as sellLeverage + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/leverage + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SET_LEVERAGE}", + query=kwargs, + auth=True, + ) + + async def switch_margin_mode(self, **kwargs): + """Select cross margin mode or isolated margin mode + + Required args: + category (string): Product type. linear,inverse + + Please note that category is not involved with business logicUnified account is not applicable + symbol (string): Symbol name + tradeMode (integer): 0: cross margin. 1: isolated margin + buyLeverage (string): The value must be equal to sellLeverage value + sellLeverage (string): The value must be equal to buyLeverage value + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/cross-isolate + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SWITCH_MARGIN_MODE}", + query=kwargs, + auth=True, + ) + + async def set_tp_sl_mode(self, **kwargs): + """Set TP/SL mode to Full or Partial + + Required args: + category (string): Product type + Unified account: linear + Normal account: linear, inverse. + + Please note that category is not involved with business logic + symbol (string): Symbol name + tpSlMode (string): TP/SL mode. Full,Partial + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/tpsl-mode + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SET_TP_SL_MODE}", + query=kwargs, + auth=True, + ) + + async def switch_position_mode(self, **kwargs): + """ + It supports to switch the position mode for USDT perpetual and Inverse futures. + If you are in one-way Mode, you can only open one position on Buy or Sell side. + If you are in hedge mode, you can open both Buy and Sell side positions simultaneously. + + Required args: + category (string): Product type. linear,inverse + + Please note that category is not involved with business logicUnified account is not applicable + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/position-mode + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SWITCH_POSITION_MODE}", + query=kwargs, + auth=True, + ) + + async def set_risk_limit(self, **kwargs): + """ + The risk limit will limit the maximum position value you can hold under different margin requirements. + If you want to hold a bigger position size, you need more margin. This interface can set the risk limit of a single position. + If the order exceeds the current risk limit when placing an order, it will be rejected. Click here to learn more about risk limit. + + Required args: + category (string): Product type + Unified account: linear + Normal account: linear, inverse. + + Please note that category is not involved with business logic + symbol (string): Symbol name + riskId (integer): Risk limit ID + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/set-risk-limit + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SET_RISK_LIMIT}", + query=kwargs, + auth=True, + ) + + async def set_trading_stop(self, **kwargs): + """Set the take profit, stop loss or trailing stop for the position. + + Required args: + category (string): Product type + Unified account: linear + Normal account: linear, inverse. + + Please note that category is not involved with business logic + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/trading-stop + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SET_TRADING_STOP}", + query=kwargs, + auth=True, + ) + + async def set_auto_add_margin(self, **kwargs): + """Turn on/off auto-add-margin for isolated margin position + + Required args: + category (string): Product type. linear + symbol (string): Symbol name + autoAddMargin (integer): Turn on/off. 0: off. 1: on + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/add-margin + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Position.SET_AUTO_ADD_MARGIN}", + query=kwargs, + auth=True, + ) + + async def get_executions(self, **kwargs): + """Query users' execution records, sorted by execTime in descending order + + Required args: + category (string): + Product type Unified account: spot, linear, option + Normal account: linear, inverse. + + Please note that category is not involved with business logic + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/execution + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Position.GET_EXECUTIONS}", + query=kwargs, + auth=True, + ) + + async def get_closed_pnl(self, **kwargs): + """Query user's closed profit and loss records. The results are sorted by createdTime in descending order. + + Required args: + category (string): + Product type Unified account: linear + Normal account: linear, inverse. + + Please note that category is not involved with business logic + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/position/close-pnl + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Position.GET_CLOSED_PNL}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_pre_upgrade.py b/pybit/async/_v5_pre_upgrade.py new file mode 100644 index 0000000..8309a9d --- /dev/null +++ b/pybit/async/_v5_pre_upgrade.py @@ -0,0 +1,130 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..pre_upgrade import PreUpgrade + + +class PreUpgradeHTTP(_V5ASYNCHTTPManager): + async def get_pre_upgrade_order_history(self, **kwargs) -> dict: + """ + After the account is upgraded to a Unified account, you can get the + orders which occurred before the upgrade. + + Required args: + category (string): Product type. linear, inverse, option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/pre-upgrade/order-list + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_ORDER_HISTORY}", + query=kwargs, + auth=True, + ) + + async def get_pre_upgrade_trade_history(self, **kwargs) -> dict: + """ + Get users' execution records which occurred before you upgraded the + account to a Unified account, sorted by execTime in descending order + + Required args: + category (string): Product type. linear, inverse, option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/pre-upgrade/execution + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_TRADE_HISTORY}", + query=kwargs, + auth=True, + ) + + async def get_pre_upgrade_closed_pnl(self, **kwargs) -> dict: + """ + Query user's closed profit and loss records from before you upgraded the + account to a Unified account. The results are sorted by createdTime in + descending order. + + Required args: + category (string): Product type linear, inverse + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/pre-upgrade/close-pnl + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_CLOSED_PNL}", + query=kwargs, + auth=True, + ) + + async def get_pre_upgrade_transaction_log(self, **kwargs) -> dict: + """ + Query transaction logs which occurred in the USDC Derivatives wallet + before the account was upgraded to a Unified account. + + Required args: + category (string): Product type. linear,option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/pre-upgrade/transaction-log + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_TRANSACTION_LOG}", + query=kwargs, + auth=True, + ) + + async def get_pre_upgrade_option_delivery_record(self, **kwargs) -> dict: + """ + Query delivery records of Option before you upgraded the account to a + Unified account, sorted by deliveryTime in descending order + Required args: + category (string): Product type. option + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/pre-upgrade/delivery + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_OPTION_DELIVERY_RECORD}", + query=kwargs, + auth=True, + ) + + async def get_pre_upgrade_usdc_session_settlement(self, **kwargs) -> dict: + """ + Required args: + category (string): Product type. linear + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/pre-upgrade/settlement + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_USDC_SESSION_SETTLEMENT}", + query=kwargs, + auth=True, + ) + + diff --git a/pybit/async/_v5_spot_leverage_token.py b/pybit/async/_v5_spot_leverage_token.py new file mode 100644 index 0000000..1f7f56a --- /dev/null +++ b/pybit/async/_v5_spot_leverage_token.py @@ -0,0 +1,95 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..spot_leverage_token import SpotLeverageToken + + +class SpotLeverageHTTP(_V5ASYNCHTTPManager): + async def get_leveraged_token_info(self, **kwargs): + """Query leverage token information + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/lt/leverage-token-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotLeverageToken.GET_LEVERAGED_TOKEN_INFO}", + query=kwargs, + ) + + async def get_leveraged_token_market(self, **kwargs): + """Get leverage token market information + + Required args: + ltCoin (string): Abbreviation of the LT, such as BTC3L + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/lt/leverage-token-reference + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotLeverageToken.GET_LEVERAGED_TOKEN_MARKET}", + query=kwargs, + ) + + async def purchase_leveraged_token(self, **kwargs): + """Purchase levearge token + + Required args: + ltCoin (string): Abbreviation of the LT, such as BTC3L + ltAmount (string): Purchase amount + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/lt/purchase + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotLeverageToken.PURCHASE}", + query=kwargs, + auth=True, + ) + + async def redeem_leveraged_token(self, **kwargs): + """Redeem leverage token + + Required args: + ltCoin (string): Abbreviation of the LT, such as BTC3L + quantity (string): Redeem quantity of LT + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/lt/redeem + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotLeverageToken.REDEEM}", + query=kwargs, + auth=True, + ) + + async def get_purchase_redemption_records(self, **kwargs): + """Get purchase or redeem history + + Required args: + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/lt/order-record + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotLeverageToken.GET_PURCHASE_REDEMPTION_RECORDS}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_spot_margin_trade.py b/pybit/async/_v5_spot_margin_trade.py new file mode 100644 index 0000000..e8a55f1 --- /dev/null +++ b/pybit/async/_v5_spot_margin_trade.py @@ -0,0 +1,242 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..spot_margin_trade import SpotMarginTrade + + +class SpotMarginTradeHTTP(_V5ASYNCHTTPManager): + async def spot_margin_trade_get_vip_margin_data(self, **kwargs): + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-uta/vip-margin + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.VIP_MARGIN_DATA}", + query=kwargs, + ) + + async def spot_margin_trade_toggle_margin_trade(self, **kwargs): + """UTA only. Turn spot margin trade on / off. + + Required args: + spotMarginMode (string): 1: on, 0: off + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-uta/switch-mode + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotMarginTrade.TOGGLE_MARGIN_TRADE}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_set_leverage(self, **kwargs): + """UTA only. Set the user's maximum leverage in spot cross margin + + Required args: + leverage (string): Leverage. [2, 5]. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-uta/set-leverage + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotMarginTrade.SET_LEVERAGE}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_get_status_and_leverage(self): + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-uta/status + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.STATUS_AND_LEVERAGE}", + auth=True, + ) + + async def spot_margin_trade_normal_get_vip_margin_data(self, **kwargs): + """ + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/vip-margin + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_MARGIN_COIN_INFO}", + query=kwargs, + ) + + async def spot_margin_trade_normal_get_margin_coin_info(self, **kwargs): + """Normal (non-UTA) account only. Turn on / off spot margin trade + + Required args: + switch (string): 1: on, 0: off + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/margin-data + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_MARGIN_COIN_INFO}", + query=kwargs, + ) + + async def spot_margin_trade_normal_get_borrowable_coin_info(self, **kwargs): + """Normal (non-UTA) account only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrowable-data + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_BORROWABLE_COIN_INFO}", + query=kwargs, + ) + + async def spot_margin_trade_normal_get_interest_quota(self, **kwargs): + """Normal (non-UTA) account only. + + Required args: + coin (string): Coin name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/interest-quota + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_INTEREST_QUOTA}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_normal_get_loan_account_info(self, **kwargs): + """Normal (non-UTA) account only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/account-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_LOAN_ACCOUNT_INFO}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_normal_borrow(self, **kwargs): + """Normal (non-UTA) account only. + + Required args: + coin (string): Coin name + qty (string): Amount to borrow + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_BORROW}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_normal_repay(self, **kwargs): + """Normal (non-UTA) account only. + + Required args: + coin (string): Coin name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_REPAY}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_normal_get_borrow_order_detail(self, **kwargs): + """Normal (non-UTA) account only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow-order + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_BORROW_ORDER_DETAIL}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_normal_get_repayment_order_detail(self, **kwargs): + """Normal (non-UTA) account only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay-order + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_REPAYMENT_ORDER_DETAIL}", + query=kwargs, + auth=True, + ) + + async def spot_margin_trade_normal_toggle_margin_trade(self, **kwargs): + """Normal (non-UTA) account only. + + Required args: + switch (integer): 1: on, 0: off + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/spot-margin-normal/switch-mode + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{SpotMarginTrade.NORMAL_TOGGLE_MARGIN_TRADE}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_trade.py b/pybit/async/_v5_trade.py new file mode 100644 index 0000000..5ca2b3f --- /dev/null +++ b/pybit/async/_v5_trade.py @@ -0,0 +1,244 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..trade import Trade + + +class TradeHTTP(_V5ASYNCHTTPManager): + async def place_order(self, **kwargs): + """This method supports to create the order for spot, spot margin, linear perpetual, inverse futures and options. + + Required args: + category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic + symbol (string): Symbol name + side (string): Buy, Sell + orderType (string): Market, Limit + qty (string): Order quantity + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/create-order + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.PLACE_ORDER}", + query=kwargs, + auth=True, + ) + + async def amend_order(self, **kwargs): + """Unified account covers: Linear contract / Options + Normal account covers: USDT perpetual / Inverse perpetual / Inverse futures + + Required args: + category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic + symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/amend-order + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.AMEND_ORDER}", + query=kwargs, + auth=True, + ) + + async def cancel_order(self, **kwargs): + """Unified account covers: Spot / Linear contract / Options + Normal account covers: USDT perpetual / Inverse perpetual / Inverse futures + + Required args: + category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic + symbol (string): Symbol name + orderId (string): Order ID. Either orderId or orderLinkId is required + orderLinkId (string): User customised order ID. Either orderId or orderLinkId is required + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/cancel-order + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.CANCEL_ORDER}", + query=kwargs, + auth=True, + ) + + async def get_open_orders(self, **kwargs): + """Query unfilled or partially filled orders in real-time. To query older order records, please use the order history interface. + + Required args: + category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/open-order + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Trade.GET_OPEN_ORDERS}", + query=kwargs, + auth=True, + ) + + async def cancel_all_orders(self, **kwargs): + """Cancel all open orders + + Required args: + category (string): Product type + Unified account: spot, linear, option + Normal account: linear, inverse. + + Please note that category is not involved with business logic. If cancel all by baseCoin, it will cancel all linear & inverse orders + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/cancel-all + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.CANCEL_ALL_ORDERS}", + query=kwargs, + auth=True, + ) + + async def get_order_history(self, **kwargs): + """Query order history. As order creation/cancellation is asynchronous, the data returned from this endpoint may delay. + If you want to get real-time order information, you could query this endpoint or rely on the websocket stream (recommended). + + Required args: + category (string): Product type + Unified account: spot, linear, option + Normal account: linear, inverse. + + Please note that category is not involved with business logic + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/order-list + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Trade.GET_ORDER_HISTORY}", + query=kwargs, + auth=True, + ) + + async def place_batch_order(self, **kwargs): + """Covers: Option (Unified Account) + + Required args: + category (string): Product type. option + request (array): Object + > symbol (string): Symbol name + > side (string): Buy, Sell + > orderType (string): Market, Limit + > qty (string): Order quantity + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/batch-place + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.BATCH_PLACE_ORDER}", + query=kwargs, + auth=True, + ) + + async def amend_batch_order(self, **kwargs): + """Covers: Option (Unified Account) + + Required args: + category (string): Product type. option + request (array): Object + > symbol (string): Symbol name + > orderId (string): Order ID. Either orderId or orderLinkId is required + > orderLinkId (string): User customised order ID. Either orderId or orderLinkId is required + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/batch-amend + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.BATCH_AMEND_ORDER}", + query=kwargs, + auth=True, + ) + + async def cancel_batch_order(self, **kwargs): + """This endpoint allows you to cancel more than one open order in a single request. + + Required args: + category (string): Product type. option + request (array): Object + > symbol (string): Symbol name + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/batch-cancel + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.BATCH_CANCEL_ORDER}", + query=kwargs, + auth=True, + ) + + async def get_borrow_quota(self, **kwargs): + """Query the qty and amount of borrowable coins in spot account. + + Required args: + category (string): Product type. spot + symbol (string): Symbol name + side (string): Transaction side. Buy,Sell + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/spot-borrow-quota + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{Trade.GET_BORROW_QUOTA}", + query=kwargs, + auth=True, + ) + + async def set_dcp(self, **kwargs): + """Covers: Option (Unified Account) + + Required args: + timeWindow (integer): Disconnection timing window time. [10, 300], unit: second + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/order/dcp + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{Trade.SET_DCP}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/_v5_user.py b/pybit/async/_v5_user.py new file mode 100644 index 0000000..e74efd0 --- /dev/null +++ b/pybit/async/_v5_user.py @@ -0,0 +1,196 @@ +from ._async_http_manager import _V5ASYNCHTTPManager +from ..user import User + + +class UserHTTP(_V5ASYNCHTTPManager): + async def create_sub_uid(self, **kwargs): + """Create a new sub user id. Use master user's api key only. + + Required args: + username (string): Give a username of the new sub user id. 6-16 characters, must include both numbers and letters.cannot be the same as the exist or deleted one. + memberType (integer): 1: normal sub account, 6: custodial sub account + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/create-subuid + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.CREATE_SUB_UID}", + query=kwargs, + auth=True, + ) + + async def create_sub_api_key(self, **kwargs): + """To create new API key for those newly created sub UID. Use master user's api key only. + + Required args: + subuid (integer): Sub user Id + readOnly (integer): 0: Read and Write. 1: Read only + permissions (Object): Tick the types of permission. one of below types must be passed, otherwise the error is thrown + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/create-subuid-apikey + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.CREATE_SUB_API_KEY}", + query=kwargs, + auth=True, + ) + + async def get_sub_uid_list(self, **kwargs): + """Get all sub uid of master account. Use master user's api key only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/subuid-list + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{User.GET_SUB_UID_LIST}", + query=kwargs, + auth=True, + ) + + async def freeze_sub_uid(self, **kwargs): + """Froze sub uid. Use master user's api key only. + + Required args: + subuid (integer): Sub user Id + frozen (integer): 0: unfreeze, 1: freeze + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/froze-subuid + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.FREEZE_SUB_UID}", + query=kwargs, + auth=True, + ) + + async def get_api_key_information(self, **kwargs): + """Get the information of the api key. Use the api key pending to be checked to call the endpoint. Both master and sub user's api key are applicable. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/apikey-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{User.GET_API_KEY_INFORMATION}", + query=kwargs, + auth=True, + ) + + async def modify_master_api_key(self, **kwargs): + """Modify the settings of master api key. Use the api key pending to be modified to call the endpoint. Use master user's api key only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/modify-master-apikey + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.MODIFY_MASTER_API_KEY}", + query=kwargs, + auth=True, + ) + + async def modify_sub_api_key(self, **kwargs): + """Modify the settings of sub api key. Use the api key pending to be modified to call the endpoint. Use sub user's api key only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/modify-sub-apikey + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.MODIFY_SUB_API_KEY}", + query=kwargs, + auth=True, + ) + + async def delete_master_api_key(self, **kwargs): + """Delete the api key of master account. Use the api key pending to be delete to call the endpoint. Use master user's api key only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/rm-master-apikey + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.DELETE_MASTER_API_KEY}", + query=kwargs, + auth=True, + ) + + async def delete_sub_api_key(self, **kwargs): + """Delete the api key of sub account. Use the api key pending to be delete to call the endpoint. Use sub user's api key only. + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/rm-sub-apikey + """ + return await self._submit_request( + method="POST", + path=f"{self.endpoint}{User.DELETE_SUB_API_KEY}", + query=kwargs, + auth=True, + ) + + async def get_affiliate_user_info(self, **kwargs): + """This API is used for affiliate to get their users information + + Required args: + uid (integer): The master account uid of affiliate's client + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/affiliate-info + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{User.GET_AFFILIATE_USER_INFO}", + query=kwargs, + auth=True, + ) + + async def get_uid_wallet_type(self, **kwargs): + """Get available wallet types for the master account or sub account + + Returns: + Request results as dictionary. + + Additional information: + https://bybit-exchange.github.io/docs/v5/user/wallet-type + """ + return await self._submit_request( + method="GET", + path=f"{self.endpoint}{User.GET_UID_WALLET_TYPE}", + query=kwargs, + auth=True, + ) diff --git a/pybit/async/unified_trading.py b/pybit/async/unified_trading.py new file mode 100644 index 0000000..770a3b8 --- /dev/null +++ b/pybit/async/unified_trading.py @@ -0,0 +1,43 @@ +from dataclasses import dataclass +from ._v5_misc import MiscHTTP +from ._v5_market import MarketHTTP +from ._v5_trade import TradeHTTP +from ._v5_account import AccountHTTP +from ._v5_asset import AssetHTTP +from ._v5_position import PositionHTTP +from ._v5_pre_upgrade import PreUpgradeHTTP +from ._v5_spot_leverage_token import SpotLeverageHTTP +from ._v5_spot_margin_trade import SpotMarginTradeHTTP +from ._v5_user import UserHTTP +from ._v5_broker import BrokerHTTP +from ._v5_institutional_loan import InstitutionalLoanHTTP + + +WSS_NAME = "Unified V5" +PRIVATE_WSS = "wss://{SUBDOMAIN}.{DOMAIN}.com/v5/private" +PUBLIC_WSS = "wss://{SUBDOMAIN}.{DOMAIN}.com/v5/public/{CHANNEL_TYPE}" +AVAILABLE_CHANNEL_TYPES = [ + "inverse", + "linear", + "spot", + "option", + "private", +] + +@dataclass +class HTTP( + MiscHTTP, + MarketHTTP, + TradeHTTP, + AccountHTTP, + AssetHTTP, + PositionHTTP, + PreUpgradeHTTP, + SpotLeverageHTTP, + SpotMarginTradeHTTP, + UserHTTP, + BrokerHTTP, + InstitutionalLoanHTTP, +): + def __init__(self, **args): + super().__init__(**args) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0da22d3..0fd5e8b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ requests>=2.22.0 +aiohttp==3.10.4 websocket-client==1.5.0 pycryptodome==3.20.0 \ No newline at end of file From 82ece43626a46cd15e3d737f2d6f19a197596f61 Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Mon, 19 Aug 2024 10:17:35 +0300 Subject: [PATCH 02/10] update --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 99d8f7b..388f07d 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='pybit', - version='5.8.0', + version='5.8.1', description='Python3 Bybit HTTP/WebSocket API Connector', long_description=long_description, long_description_content_type="text/markdown", @@ -25,7 +25,7 @@ "Programming Language :: Python :: 3.10", ], keywords="bybit api connector", - packages=["pybit", "pybit.legacy"], + packages=["pybit", "pybit.legacy", "pybit.async"], python_requires=">=3.6", install_requires=[ "requests", From 2f687f52bee231bf2010447c9d5c7512185f3d72 Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Mon, 19 Aug 2024 10:19:24 +0300 Subject: [PATCH 03/10] rename version --- CHANGELOG.md | 3 +++ pybit/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac03411..b887fdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.8.1] - 2024-08-19 +### Added +- Async HTTP module: `bybit.async` ## [5.8.0] - 2024-06-26 ### Added diff --git a/pybit/__init__.py b/pybit/__init__.py index fb86400..d1e8613 100644 --- a/pybit/__init__.py +++ b/pybit/__init__.py @@ -1 +1 @@ -VERSION = "5.8.0" +VERSION = "5.8.1" From 43c53f7d54b0010739ea5be093fe390bc35266af Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Mon, 19 Aug 2024 10:37:49 +0300 Subject: [PATCH 04/10] upd --- CHANGELOG.md | 2 +- pybit/{async => asyncio}/__init__.py | 0 pybit/{async => asyncio}/_async_http_manager.py | 0 pybit/{async => asyncio}/_v5_account.py | 0 pybit/{async => asyncio}/_v5_asset.py | 0 pybit/{async => asyncio}/_v5_broker.py | 0 pybit/{async => asyncio}/_v5_institutional_loan.py | 0 pybit/{async => asyncio}/_v5_market.py | 0 pybit/{async => asyncio}/_v5_misc.py | 0 pybit/{async => asyncio}/_v5_position.py | 0 pybit/{async => asyncio}/_v5_pre_upgrade.py | 0 pybit/{async => asyncio}/_v5_spot_leverage_token.py | 0 pybit/{async => asyncio}/_v5_spot_margin_trade.py | 0 pybit/{async => asyncio}/_v5_trade.py | 0 pybit/{async => asyncio}/_v5_user.py | 0 pybit/{async => asyncio}/unified_trading.py | 0 setup.py | 2 +- 17 files changed, 2 insertions(+), 2 deletions(-) rename pybit/{async => asyncio}/__init__.py (100%) rename pybit/{async => asyncio}/_async_http_manager.py (100%) rename pybit/{async => asyncio}/_v5_account.py (100%) rename pybit/{async => asyncio}/_v5_asset.py (100%) rename pybit/{async => asyncio}/_v5_broker.py (100%) rename pybit/{async => asyncio}/_v5_institutional_loan.py (100%) rename pybit/{async => asyncio}/_v5_market.py (100%) rename pybit/{async => asyncio}/_v5_misc.py (100%) rename pybit/{async => asyncio}/_v5_position.py (100%) rename pybit/{async => asyncio}/_v5_pre_upgrade.py (100%) rename pybit/{async => asyncio}/_v5_spot_leverage_token.py (100%) rename pybit/{async => asyncio}/_v5_spot_margin_trade.py (100%) rename pybit/{async => asyncio}/_v5_trade.py (100%) rename pybit/{async => asyncio}/_v5_user.py (100%) rename pybit/{async => asyncio}/unified_trading.py (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b887fdc..cbe65a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.8.1] - 2024-08-19 ### Added -- Async HTTP module: `bybit.async` +- Async HTTP module: `bybit.asyncio` ## [5.8.0] - 2024-06-26 ### Added diff --git a/pybit/async/__init__.py b/pybit/asyncio/__init__.py similarity index 100% rename from pybit/async/__init__.py rename to pybit/asyncio/__init__.py diff --git a/pybit/async/_async_http_manager.py b/pybit/asyncio/_async_http_manager.py similarity index 100% rename from pybit/async/_async_http_manager.py rename to pybit/asyncio/_async_http_manager.py diff --git a/pybit/async/_v5_account.py b/pybit/asyncio/_v5_account.py similarity index 100% rename from pybit/async/_v5_account.py rename to pybit/asyncio/_v5_account.py diff --git a/pybit/async/_v5_asset.py b/pybit/asyncio/_v5_asset.py similarity index 100% rename from pybit/async/_v5_asset.py rename to pybit/asyncio/_v5_asset.py diff --git a/pybit/async/_v5_broker.py b/pybit/asyncio/_v5_broker.py similarity index 100% rename from pybit/async/_v5_broker.py rename to pybit/asyncio/_v5_broker.py diff --git a/pybit/async/_v5_institutional_loan.py b/pybit/asyncio/_v5_institutional_loan.py similarity index 100% rename from pybit/async/_v5_institutional_loan.py rename to pybit/asyncio/_v5_institutional_loan.py diff --git a/pybit/async/_v5_market.py b/pybit/asyncio/_v5_market.py similarity index 100% rename from pybit/async/_v5_market.py rename to pybit/asyncio/_v5_market.py diff --git a/pybit/async/_v5_misc.py b/pybit/asyncio/_v5_misc.py similarity index 100% rename from pybit/async/_v5_misc.py rename to pybit/asyncio/_v5_misc.py diff --git a/pybit/async/_v5_position.py b/pybit/asyncio/_v5_position.py similarity index 100% rename from pybit/async/_v5_position.py rename to pybit/asyncio/_v5_position.py diff --git a/pybit/async/_v5_pre_upgrade.py b/pybit/asyncio/_v5_pre_upgrade.py similarity index 100% rename from pybit/async/_v5_pre_upgrade.py rename to pybit/asyncio/_v5_pre_upgrade.py diff --git a/pybit/async/_v5_spot_leverage_token.py b/pybit/asyncio/_v5_spot_leverage_token.py similarity index 100% rename from pybit/async/_v5_spot_leverage_token.py rename to pybit/asyncio/_v5_spot_leverage_token.py diff --git a/pybit/async/_v5_spot_margin_trade.py b/pybit/asyncio/_v5_spot_margin_trade.py similarity index 100% rename from pybit/async/_v5_spot_margin_trade.py rename to pybit/asyncio/_v5_spot_margin_trade.py diff --git a/pybit/async/_v5_trade.py b/pybit/asyncio/_v5_trade.py similarity index 100% rename from pybit/async/_v5_trade.py rename to pybit/asyncio/_v5_trade.py diff --git a/pybit/async/_v5_user.py b/pybit/asyncio/_v5_user.py similarity index 100% rename from pybit/async/_v5_user.py rename to pybit/asyncio/_v5_user.py diff --git a/pybit/async/unified_trading.py b/pybit/asyncio/unified_trading.py similarity index 100% rename from pybit/async/unified_trading.py rename to pybit/asyncio/unified_trading.py diff --git a/setup.py b/setup.py index 388f07d..2b07176 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ "Programming Language :: Python :: 3.10", ], keywords="bybit api connector", - packages=["pybit", "pybit.legacy", "pybit.async"], + packages=["pybit", "pybit.legacy", "pybit.asyncio"], python_requires=">=3.6", install_requires=[ "requests", From 47fb3932eb777628331660099bb649135b7eed46 Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Mon, 19 Aug 2024 11:21:48 +0300 Subject: [PATCH 05/10] upd --- pybit/_helpers.py | 75 ------------------------ pybit/_http_manager.py | 6 +- pybit/_websocket_stream.py | 3 +- pybit/_websocket_trading.py | 3 +- pybit/asyncio/_async_http_manager.py | 2 +- pybit/asyncio/_v5_account.py | 2 +- pybit/asyncio/_v5_asset.py | 2 +- pybit/asyncio/_v5_broker.py | 2 +- pybit/asyncio/_v5_institutional_loan.py | 2 +- pybit/asyncio/_v5_market.py | 2 +- pybit/asyncio/_v5_misc.py | 2 +- pybit/asyncio/_v5_position.py | 2 +- pybit/asyncio/_v5_pre_upgrade.py | 2 +- pybit/asyncio/_v5_spot_leverage_token.py | 2 +- pybit/asyncio/_v5_spot_margin_trade.py | 2 +- pybit/asyncio/_v5_trade.py | 2 +- pybit/asyncio/_v5_user.py | 2 +- pybit/asyncio/unified_trading.py | 48 +++++++-------- pybit/exceptions.py | 58 ------------------ pybit/unified_trading.py | 2 +- tests/test_pybit.py | 2 +- 21 files changed, 44 insertions(+), 179 deletions(-) delete mode 100644 pybit/_helpers.py delete mode 100644 pybit/exceptions.py diff --git a/pybit/_helpers.py b/pybit/_helpers.py deleted file mode 100644 index a613db7..0000000 --- a/pybit/_helpers.py +++ /dev/null @@ -1,75 +0,0 @@ -import time -import re -import copy - - -def generate_timestamp(): - """ - Return a millisecond integer timestamp. - """ - return int(time.time() * 10**3) - - -def identify_ws_method(input_wss_url, wss_dictionary): - """ - This method matches the input_wss_url with a particular WSS method. This - helps ensure that, when subscribing to a custom topic, the topic - subscription message is sent down the correct WSS connection. - """ - path = re.compile("(wss://)?([^/\s]+)(.*)") - input_wss_url_path = path.match(input_wss_url).group(3) - for wss_url, function_call in wss_dictionary.items(): - wss_url_path = path.match(wss_url).group(3) - if input_wss_url_path == wss_url_path: - return function_call - - -def find_index(source, target, key): - """ - Find the index in source list of the targeted ID. - """ - return next(i for i, j in enumerate(source) if j[key] == target[key]) - - -def make_private_args(args): - """ - Exists to pass on the user's arguments to a lower-level class without - giving the user access to that classes attributes (ie, passing on args - without inheriting the parent class). - """ - args.pop("self") - return args - - -def make_public_kwargs(private_kwargs): - public_kwargs = copy.deepcopy(private_kwargs) - public_kwargs.pop("api_key", "") - public_kwargs.pop("api_secret", "") - return public_kwargs - - -def are_connections_connected(active_connections): - for connection in active_connections: - if not connection.is_connected(): - return False - return True - - -def is_inverse_contract(symbol: str): - if re.search(r"(USD)([HMUZ]\d\d|$)", symbol): - return True - - -def is_usdt_perpetual(symbol: str): - if symbol.endswith("USDT"): - return True - - -def is_usdc_perpetual(symbol: str): - if symbol.endswith("USDC"): - return True - - -def is_usdc_option(symbol: str): - if re.search(r"[A-Z]{3}-.*-[PC]$", symbol): - return True diff --git a/pybit/_http_manager.py b/pybit/_http_manager.py index 3d3d3d2..9299496 100644 --- a/pybit/_http_manager.py +++ b/pybit/_http_manager.py @@ -13,8 +13,8 @@ from datetime import datetime as dt -from .exceptions import FailedRequestError, InvalidRequestError -from . import _helpers +from pybit.asyncio.exceptions import FailedRequestError, InvalidRequestError +from .asyncio import _helpers # Requests will use simplejson if available. try: @@ -361,7 +361,7 @@ def _submit_request(self, method=None, path=None, query=None, auth=False): limit_reset_time = int(s.headers["X-Bapi-Limit-Reset-Timestamp"]) limit_reset_str = dt.fromtimestamp(limit_reset_time / 10**3).strftime( "%H:%M:%S.%f")[:-3] - delay_time = (int(limit_reset_time) - _helpers.generate_timestamp()) / 10**3 + delay_time = (int(limit_reset_time) - _helpers.generate_timestamp()) / 10 ** 3 error_msg = ( f"API rate limit will reset at {limit_reset_str}. " f"Sleeping for {int(delay_time * 10**3)} milliseconds" diff --git a/pybit/_websocket_stream.py b/pybit/_websocket_stream.py index ff9714d..e79f238 100644 --- a/pybit/_websocket_stream.py +++ b/pybit/_websocket_stream.py @@ -6,8 +6,7 @@ import logging import copy from uuid import uuid4 -from . import _helpers - +from .asyncio import _helpers logger = logging.getLogger(__name__) diff --git a/pybit/_websocket_trading.py b/pybit/_websocket_trading.py index f7523ee..6802af0 100644 --- a/pybit/_websocket_trading.py +++ b/pybit/_websocket_trading.py @@ -3,8 +3,7 @@ import uuid import logging from ._websocket_stream import _WebSocketManager -from . import _helpers - +from .asyncio import _helpers logger = logging.getLogger(__name__) diff --git a/pybit/asyncio/_async_http_manager.py b/pybit/asyncio/_async_http_manager.py index b239cee..0a01d44 100644 --- a/pybit/asyncio/_async_http_manager.py +++ b/pybit/asyncio/_async_http_manager.py @@ -343,7 +343,7 @@ async def _submit_request(self, method=None, path=None, query=None, auth=False): limit_reset_time = int(response.headers["X-Bapi-Limit-Reset-Timestamp"]) limit_reset_str = dt.fromtimestamp(limit_reset_time / 10**3).strftime( "%H:%M:%S.%f")[:-3] - delay_time = (int(limit_reset_time) - _helpers.generate_timestamp()) / 10**3 + delay_time = (int(limit_reset_time) - _helpers.generate_timestamp()) / 10 ** 3 error_msg = ( f"API rate limit will reset at {limit_reset_str}. " f"Sleeping for {int(delay_time * 10**3)} milliseconds" diff --git a/pybit/asyncio/_v5_account.py b/pybit/asyncio/_v5_account.py index bc1a9a8..ca0a7b7 100644 --- a/pybit/asyncio/_v5_account.py +++ b/pybit/asyncio/_v5_account.py @@ -2,7 +2,7 @@ from ..account import Account -class AccountHTTP(_V5ASYNCHTTPManager): +class AccountASYNCHTTP(_V5ASYNCHTTPManager): async def get_wallet_balance(self, **kwargs): """Obtain wallet balance, query asset information of each currency, and account risk rate information under unified margin mode. By default, currency information with assets or liabilities of 0 is not returned. diff --git a/pybit/asyncio/_v5_asset.py b/pybit/asyncio/_v5_asset.py index 379a735..e69b883 100644 --- a/pybit/asyncio/_v5_asset.py +++ b/pybit/asyncio/_v5_asset.py @@ -2,7 +2,7 @@ from ..asset import Asset -class AssetHTTP(_V5ASYNCHTTPManager): +class AssetASYNCHTTP(_V5ASYNCHTTPManager): async def get_coin_exchange_records(self, **kwargs): """Query the coin exchange records. diff --git a/pybit/asyncio/_v5_broker.py b/pybit/asyncio/_v5_broker.py index f3ae28c..80d0617 100644 --- a/pybit/asyncio/_v5_broker.py +++ b/pybit/asyncio/_v5_broker.py @@ -2,7 +2,7 @@ from ..broker import Broker -class BrokerHTTP(_V5ASYNCHTTPManager): +class BrokerASYNCHTTP(_V5ASYNCHTTPManager): async def get_broker_earnings(self, **kwargs) -> dict: """ Returns: diff --git a/pybit/asyncio/_v5_institutional_loan.py b/pybit/asyncio/_v5_institutional_loan.py index 4e0cd40..c107f27 100644 --- a/pybit/asyncio/_v5_institutional_loan.py +++ b/pybit/asyncio/_v5_institutional_loan.py @@ -2,7 +2,7 @@ from ..institutional_loan import InstitutionalLoan as InsLoan -class InstitutionalLoanHTTP(_V5ASYNCHTTPManager): +class InstitutionalLoanASYNCHTTP(_V5ASYNCHTTPManager): async def get_product_info(self, **kwargs) -> dict: """ Returns: diff --git a/pybit/asyncio/_v5_market.py b/pybit/asyncio/_v5_market.py index 269881e..c0cec8a 100644 --- a/pybit/asyncio/_v5_market.py +++ b/pybit/asyncio/_v5_market.py @@ -2,7 +2,7 @@ from ..market import Market -class MarketHTTP(_V5ASYNCHTTPManager): +class MarketASYNCHTTP(_V5ASYNCHTTPManager): async def get_server_time(self) -> dict: """ Returns: diff --git a/pybit/asyncio/_v5_misc.py b/pybit/asyncio/_v5_misc.py index d372047..6d5904d 100644 --- a/pybit/asyncio/_v5_misc.py +++ b/pybit/asyncio/_v5_misc.py @@ -2,7 +2,7 @@ from ..misc import Misc -class MiscHTTP(_V5ASYNCHTTPManager): +class MiscASYNCHTTP(_V5ASYNCHTTPManager): async def get_announcement(self, **kwargs) -> dict: """ Required args: diff --git a/pybit/asyncio/_v5_position.py b/pybit/asyncio/_v5_position.py index 7115df2..8bd935a 100644 --- a/pybit/asyncio/_v5_position.py +++ b/pybit/asyncio/_v5_position.py @@ -2,7 +2,7 @@ from ..position import Position -class PositionHTTP(_V5ASYNCHTTPManager): +class PositionASYNCHTTP(_V5ASYNCHTTPManager): async def get_positions(self, **kwargs): """Query real-time position data, such as position size, cumulative realizedPNL. diff --git a/pybit/asyncio/_v5_pre_upgrade.py b/pybit/asyncio/_v5_pre_upgrade.py index 8309a9d..7392d22 100644 --- a/pybit/asyncio/_v5_pre_upgrade.py +++ b/pybit/asyncio/_v5_pre_upgrade.py @@ -2,7 +2,7 @@ from ..pre_upgrade import PreUpgrade -class PreUpgradeHTTP(_V5ASYNCHTTPManager): +class PreUpgradeASYNCHTTP(_V5ASYNCHTTPManager): async def get_pre_upgrade_order_history(self, **kwargs) -> dict: """ After the account is upgraded to a Unified account, you can get the diff --git a/pybit/asyncio/_v5_spot_leverage_token.py b/pybit/asyncio/_v5_spot_leverage_token.py index 1f7f56a..01dbd74 100644 --- a/pybit/asyncio/_v5_spot_leverage_token.py +++ b/pybit/asyncio/_v5_spot_leverage_token.py @@ -2,7 +2,7 @@ from ..spot_leverage_token import SpotLeverageToken -class SpotLeverageHTTP(_V5ASYNCHTTPManager): +class SpotLeverageASYNCHTTP(_V5ASYNCHTTPManager): async def get_leveraged_token_info(self, **kwargs): """Query leverage token information diff --git a/pybit/asyncio/_v5_spot_margin_trade.py b/pybit/asyncio/_v5_spot_margin_trade.py index e8a55f1..a36d4c8 100644 --- a/pybit/asyncio/_v5_spot_margin_trade.py +++ b/pybit/asyncio/_v5_spot_margin_trade.py @@ -2,7 +2,7 @@ from ..spot_margin_trade import SpotMarginTrade -class SpotMarginTradeHTTP(_V5ASYNCHTTPManager): +class SpotMarginTradeASYNCHTTP(_V5ASYNCHTTPManager): async def spot_margin_trade_get_vip_margin_data(self, **kwargs): """ Returns: diff --git a/pybit/asyncio/_v5_trade.py b/pybit/asyncio/_v5_trade.py index 5ca2b3f..360bb58 100644 --- a/pybit/asyncio/_v5_trade.py +++ b/pybit/asyncio/_v5_trade.py @@ -2,7 +2,7 @@ from ..trade import Trade -class TradeHTTP(_V5ASYNCHTTPManager): +class TradeASYNCHTTP(_V5ASYNCHTTPManager): async def place_order(self, **kwargs): """This method supports to create the order for spot, spot margin, linear perpetual, inverse futures and options. diff --git a/pybit/asyncio/_v5_user.py b/pybit/asyncio/_v5_user.py index e74efd0..fe0264e 100644 --- a/pybit/asyncio/_v5_user.py +++ b/pybit/asyncio/_v5_user.py @@ -2,7 +2,7 @@ from ..user import User -class UserHTTP(_V5ASYNCHTTPManager): +class UserASYNCHTTP(_V5ASYNCHTTPManager): async def create_sub_uid(self, **kwargs): """Create a new sub user id. Use master user's api key only. diff --git a/pybit/asyncio/unified_trading.py b/pybit/asyncio/unified_trading.py index 770a3b8..326216d 100644 --- a/pybit/asyncio/unified_trading.py +++ b/pybit/asyncio/unified_trading.py @@ -1,16 +1,16 @@ from dataclasses import dataclass -from ._v5_misc import MiscHTTP -from ._v5_market import MarketHTTP -from ._v5_trade import TradeHTTP -from ._v5_account import AccountHTTP -from ._v5_asset import AssetHTTP -from ._v5_position import PositionHTTP -from ._v5_pre_upgrade import PreUpgradeHTTP -from ._v5_spot_leverage_token import SpotLeverageHTTP -from ._v5_spot_margin_trade import SpotMarginTradeHTTP -from ._v5_user import UserHTTP -from ._v5_broker import BrokerHTTP -from ._v5_institutional_loan import InstitutionalLoanHTTP +from ._v5_misc import MiscASYNCHTTP +from ._v5_market import MarketASYNCHTTP +from ._v5_trade import TradeASYNCHTTP +from ._v5_account import AccountASYNCHTTP +from ._v5_asset import AssetASYNCHTTP +from ._v5_position import PositionASYNCHTTP +from ._v5_pre_upgrade import PreUpgradeASYNCHTTP +from ._v5_spot_leverage_token import SpotLeverageASYNCHTTP +from ._v5_spot_margin_trade import SpotMarginTradeASYNCHTTP +from ._v5_user import UserASYNCHTTP +from ._v5_broker import BrokerASYNCHTTP +from ._v5_institutional_loan import InstitutionalLoanASYNCHTTP WSS_NAME = "Unified V5" @@ -26,18 +26,18 @@ @dataclass class HTTP( - MiscHTTP, - MarketHTTP, - TradeHTTP, - AccountHTTP, - AssetHTTP, - PositionHTTP, - PreUpgradeHTTP, - SpotLeverageHTTP, - SpotMarginTradeHTTP, - UserHTTP, - BrokerHTTP, - InstitutionalLoanHTTP, + MiscASYNCHTTP, + MarketASYNCHTTP, + TradeASYNCHTTP, + AccountASYNCHTTP, + AssetASYNCHTTP, + PositionASYNCHTTP, + PreUpgradeASYNCHTTP, + SpotLeverageASYNCHTTP, + SpotMarginTradeASYNCHTTP, + UserASYNCHTTP, + BrokerASYNCHTTP, + InstitutionalLoanASYNCHTTP, ): def __init__(self, **args): super().__init__(**args) \ No newline at end of file diff --git a/pybit/exceptions.py b/pybit/exceptions.py deleted file mode 100644 index 67aaa02..0000000 --- a/pybit/exceptions.py +++ /dev/null @@ -1,58 +0,0 @@ -class UnauthorizedExceptionError(Exception): - pass - - -class InvalidChannelTypeError(Exception): - pass - - -class TopicMismatchError(Exception): - pass - - -class FailedRequestError(Exception): - """ - Exception raised for failed requests. - - Attributes: - request -- The original request that caused the error. - message -- Explanation of the error. - status_code -- The code number returned. - time -- The time of the error. - resp_headers -- The response headers from API. None, if the request caused an error locally. - """ - - def __init__(self, request, message, status_code, time, resp_headers): - self.request = request - self.message = message - self.status_code = status_code - self.time = time - self.resp_headers = resp_headers - super().__init__( - f"{message.capitalize()} (ErrCode: {status_code}) (ErrTime: {time})" - f".\nRequest → {request}." - ) - - -class InvalidRequestError(Exception): - """ - Exception raised for returned Bybit errors. - - Attributes: - request -- The original request that caused the error. - message -- Explanation of the error. - status_code -- The code number returned. - time -- The time of the error. - resp_headers -- The response headers from API. None, if the request caused an error locally. - """ - - def __init__(self, request, message, status_code, time, resp_headers): - self.request = request - self.message = message - self.status_code = status_code - self.time = time - self.resp_headers = resp_headers - super().__init__( - f"{message} (ErrCode: {status_code}) (ErrTime: {time})" - f".\nRequest → {request}." - ) diff --git a/pybit/unified_trading.py b/pybit/unified_trading.py index 38eca70..5a63be2 100644 --- a/pybit/unified_trading.py +++ b/pybit/unified_trading.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from pybit.exceptions import ( +from pybit.asyncio.exceptions import ( InvalidChannelTypeError, TopicMismatchError, UnauthorizedExceptionError, diff --git a/tests/test_pybit.py b/tests/test_pybit.py index 67159d1..6304253 100644 --- a/tests/test_pybit.py +++ b/tests/test_pybit.py @@ -1,5 +1,5 @@ import unittest, time -from pybit.exceptions import InvalidChannelTypeError, TopicMismatchError +from pybit.asyncio.exceptions import InvalidChannelTypeError, TopicMismatchError from pybit.unified_trading import HTTP, WebSocket # session uses Bybit's mainnet endpoint From 8bfebc1a353d4b2fb92b124c37b663b0641780de Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Mon, 19 Aug 2024 11:25:56 +0300 Subject: [PATCH 06/10] -f --- pybit/_helpers.py | 75 +++++++++++++++++++++++++++++++++++++++++++++ pybit/exceptions.py | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 pybit/_helpers.py create mode 100644 pybit/exceptions.py diff --git a/pybit/_helpers.py b/pybit/_helpers.py new file mode 100644 index 0000000..5722bea --- /dev/null +++ b/pybit/_helpers.py @@ -0,0 +1,75 @@ +import time +import re +import copy + + +def generate_timestamp(): + """ + Return a millisecond integer timestamp. + """ + return int(time.time() * 10**3) + + +def identify_ws_method(input_wss_url, wss_dictionary): + """ + This method matches the input_wss_url with a particular WSS method. This + helps ensure that, when subscribing to a custom topic, the topic + subscription message is sent down the correct WSS connection. + """ + path = re.compile("(wss://)?([^/\s]+)(.*)") + input_wss_url_path = path.match(input_wss_url).group(3) + for wss_url, function_call in wss_dictionary.items(): + wss_url_path = path.match(wss_url).group(3) + if input_wss_url_path == wss_url_path: + return function_call + + +def find_index(source, target, key): + """ + Find the index in source list of the targeted ID. + """ + return next(i for i, j in enumerate(source) if j[key] == target[key]) + + +def make_private_args(args): + """ + Exists to pass on the user's arguments to a lower-level class without + giving the user access to that classes attributes (ie, passing on args + without inheriting the parent class). + """ + args.pop("self") + return args + + +def make_public_kwargs(private_kwargs): + public_kwargs = copy.deepcopy(private_kwargs) + public_kwargs.pop("api_key", "") + public_kwargs.pop("api_secret", "") + return public_kwargs + + +def are_connections_connected(active_connections): + for connection in active_connections: + if not connection.is_connected(): + return False + return True + + +def is_inverse_contract(symbol: str): + if re.search(r"(USD)([HMUZ]\d\d|$)", symbol): + return True + + +def is_usdt_perpetual(symbol: str): + if symbol.endswith("USDT"): + return True + + +def is_usdc_perpetual(symbol: str): + if symbol.endswith("USDC"): + return True + + +def is_usdc_option(symbol: str): + if re.search(r"[A-Z]{3}-.*-[PC]$", symbol): + return True \ No newline at end of file diff --git a/pybit/exceptions.py b/pybit/exceptions.py new file mode 100644 index 0000000..78216c0 --- /dev/null +++ b/pybit/exceptions.py @@ -0,0 +1,58 @@ +class UnauthorizedExceptionError(Exception): + pass + + +class InvalidChannelTypeError(Exception): + pass + + +class TopicMismatchError(Exception): + pass + + +class FailedRequestError(Exception): + """ + Exception raised for failed requests. + + Attributes: + request -- The original request that caused the error. + message -- Explanation of the error. + status_code -- The code number returned. + time -- The time of the error. + resp_headers -- The response headers from API. None, if the request caused an error locally. + """ + + def __init__(self, request, message, status_code, time, resp_headers): + self.request = request + self.message = message + self.status_code = status_code + self.time = time + self.resp_headers = resp_headers + super().__init__( + f"{message.capitalize()} (ErrCode: {status_code}) (ErrTime: {time})" + f".\nRequest → {request}." + ) + + +class InvalidRequestError(Exception): + """ + Exception raised for returned Bybit errors. + + Attributes: + request -- The original request that caused the error. + message -- Explanation of the error. + status_code -- The code number returned. + time -- The time of the error. + resp_headers -- The response headers from API. None, if the request caused an error locally. + """ + + def __init__(self, request, message, status_code, time, resp_headers): + self.request = request + self.message = message + self.status_code = status_code + self.time = time + self.resp_headers = resp_headers + super().__init__( + f"{message} (ErrCode: {status_code}) (ErrTime: {time})" + f".\nRequest → {request}." + ) \ No newline at end of file From 9aeff94079c7331a8f67fb3ff96beb879cd17a4a Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Sun, 1 Sep 2024 13:03:44 +0300 Subject: [PATCH 07/10] fix bug with except BaseException --- pybit/asyncio/_async_http_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pybit/asyncio/_async_http_manager.py b/pybit/asyncio/_async_http_manager.py index 0a01d44..eeb0de3 100644 --- a/pybit/asyncio/_async_http_manager.py +++ b/pybit/asyncio/_async_http_manager.py @@ -290,7 +290,7 @@ async def _submit_request(self, method=None, path=None, query=None, auth=False): aiohttp.ClientError, aiohttp.ClientResponseError, aiohttp.ClientSSLError, - aiohttp.ClientTimeout, + aiohttp.ConnectionTimeoutError, ) as e: if self.force_retry: self.logger.error(f"{e}. {retries_remaining}") From 02f6aa77fea4df1b48c58fd4665b6acc27ec1135 Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Fri, 6 Sep 2024 09:15:42 +0300 Subject: [PATCH 08/10] fix import mistake --- pybit/_http_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pybit/_http_manager.py b/pybit/_http_manager.py index 9299496..53597f0 100644 --- a/pybit/_http_manager.py +++ b/pybit/_http_manager.py @@ -13,8 +13,8 @@ from datetime import datetime as dt -from pybit.asyncio.exceptions import FailedRequestError, InvalidRequestError -from .asyncio import _helpers +from .exceptions import FailedRequestError, InvalidRequestError +from . import _helpers # Requests will use simplejson if available. try: From 0fc617a2a6b3d6cafec61992e90266017cd97c98 Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Fri, 6 Sep 2024 10:50:36 +0300 Subject: [PATCH 09/10] fix import mistake in `pybit/unified_trading.py` --- examples/test.py | 11 +++++++++++ pybit/unified_trading.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 examples/test.py diff --git a/examples/test.py b/examples/test.py new file mode 100644 index 0000000..8882e60 --- /dev/null +++ b/examples/test.py @@ -0,0 +1,11 @@ +from pybit.unified_trading import HTTP +from dotenv import load_dotenv +import os + +load_dotenv() +proxy = os.getenv("PROXY") +print(proxy) + +http = HTTP( + +) \ No newline at end of file diff --git a/pybit/unified_trading.py b/pybit/unified_trading.py index 5a63be2..38eca70 100644 --- a/pybit/unified_trading.py +++ b/pybit/unified_trading.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from pybit.asyncio.exceptions import ( +from pybit.exceptions import ( InvalidChannelTypeError, TopicMismatchError, UnauthorizedExceptionError, From ae16318392fb6da87db6132a9dfbaec079bb41a2 Mon Sep 17 00:00:00 2001 From: Roman505050 Date: Fri, 6 Sep 2024 13:36:29 +0300 Subject: [PATCH 10/10] del test.py --- examples/test.py | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 examples/test.py diff --git a/examples/test.py b/examples/test.py deleted file mode 100644 index 8882e60..0000000 --- a/examples/test.py +++ /dev/null @@ -1,11 +0,0 @@ -from pybit.unified_trading import HTTP -from dotenv import load_dotenv -import os - -load_dotenv() -proxy = os.getenv("PROXY") -print(proxy) - -http = HTTP( - -) \ No newline at end of file