From 8f2cbbe6de60b3a48f04c091eb3bd7965e597616 Mon Sep 17 00:00:00 2001 From: nicelgueta Date: Tue, 20 Dec 2022 00:36:15 +0000 Subject: [PATCH] fixed array body logic to be super generic --- pyokx/Account.py | 29 +++++- pyokx/CopyTrading.py | 206 +++++++++++++++++++++++++++++++++++++++++++ pyokx/Earn.py | 1 + pyokx/Funding.py | 2 +- pyokx/Gridtrading.py | 14 +-- pyokx/Marketdata.py | 2 +- pyokx/Trade.py | 92 +++++-------------- pyokx/auth.py | 13 ++- pyokx/base.py | 8 +- pyproject.toml | 2 +- 10 files changed, 279 insertions(+), 90 deletions(-) create mode 100644 pyokx/CopyTrading.py diff --git a/pyokx/Account.py b/pyokx/Account.py index 8177324..3d66018 100644 --- a/pyokx/Account.py +++ b/pyokx/Account.py @@ -595,7 +595,7 @@ def get_manual_borrow_and_repay_history_in_quick_margin_mode( return self.request(details) def vip_loans_borrow_and_repay( - self, ccy: str, side: str, amt: str, use_proxy: bool = False + self, ccy: str, side: str, amt: str, ordId: str = None, use_proxy: bool = False ) -> APIReturn: """ VIP loans borrow and repay @@ -668,6 +668,33 @@ def get_vip_interest_accrued_data( ) return self.request(details) + def get_vip_interest_deducted_data( + self, + ccy: str = None, + ordId: str = None, + after: str = None, + before: str = None, + limit: str = None, + use_proxy: bool = False, + ) -> APIReturn: + """ + Get VIP interest deducted data + Rate Limit: 5 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/account/vip-interest-deducted", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + def get_vip_loan_order_list( self, ordId: str = None, diff --git a/pyokx/CopyTrading.py b/pyokx/CopyTrading.py new file mode 100644 index 0000000..a752a45 --- /dev/null +++ b/pyokx/CopyTrading.py @@ -0,0 +1,206 @@ +# auto-generated code # +from .base import APIComponent, APIReturn, EndpointDetails + + +class CopyTrading(APIComponent): + def get_existing_leading_positions( + self, instId: str = None, use_proxy: bool = False + ) -> APIReturn: + """ + Get existing leading positions + Returns reverse chronological order with openTime + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/current-subpositions", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def get_leading_position_history( + self, + instId: str = None, + after: str = None, + before: str = None, + limit: str = None, + use_proxy: bool = False, + ) -> APIReturn: + """ + Get leading position history + Returns reverse chronological order with closeTime. + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/subpositions-history", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def place_leading_stop_order( + self, + subPosId: str, + tpTriggerPxType: str = None, + slTriggerPxType: str = None, + tpTriggerPx: str = None, + slTriggerPx: str = None, + use_proxy: bool = False, + ) -> APIReturn: + """ + Place leading stop order + Rate limit: 1 request per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/algo-order", + method="POST", + body=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def close_leading_position( + self, subPosId: str, use_proxy: bool = False + ) -> APIReturn: + """ + Close leading position + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/close-subposition", + method="POST", + body=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def get_leading_instruments(self, use_proxy: bool = False) -> APIReturn: + """ + Get leading instruments + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/instruments", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def amend_leading_instruments( + self, instId: str, use_proxy: bool = False + ) -> APIReturn: + """ + Amend leading instruments + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/set-instruments", + method="POST", + body=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def get_profit_sharing_details( + self, + after: str = None, + before: str = None, + limit: str = None, + use_proxy: bool = False, + ) -> APIReturn: + """ + Get profit sharing details + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/profit-sharing-details", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def get_total_profit_sharing(self, use_proxy: bool = False) -> APIReturn: + """ + Get total profit sharing + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/total-profit-sharing", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) + + def get_unrealized_profit_sharing_details( + self, use_proxy: bool = False + ) -> APIReturn: + """ + Get unrealized profit sharing details + Rate limit: 2 requests per 2 seconds + Rate limit rule: UserID + """ + kwargs = { + k: v + for k, v in locals().items() + if k not in ["use_proxy", "self"] and v is not None + } + details = EndpointDetails( + request_path="/api/v5/copytrading/unrealized-profit-sharing-details", + method="GET", + params=kwargs, + use_proxy=use_proxy, + ) + return self.request(details) diff --git a/pyokx/Earn.py b/pyokx/Earn.py index 9fe3596..33da232 100644 --- a/pyokx/Earn.py +++ b/pyokx/Earn.py @@ -34,6 +34,7 @@ def purchase( investData: list, ccy: str, amt: str, + tag: str = None, term: str = None, use_proxy: bool = False, ) -> APIReturn: diff --git a/pyokx/Funding.py b/pyokx/Funding.py index 8f6e0d7..5b15d48 100644 --- a/pyokx/Funding.py +++ b/pyokx/Funding.py @@ -268,7 +268,7 @@ def withdrawal( ) -> APIReturn: """ Withdrawal - Withdrawal of tokens. Sub-account does not support withdrawal. + Withdrawal of tokens. Common sub-account does not support withdrawal. Rate Limit: 6 requests per second Rate limit rule: UserID """ diff --git a/pyokx/Gridtrading.py b/pyokx/Gridtrading.py index 1fd27aa..c899046 100644 --- a/pyokx/Gridtrading.py +++ b/pyokx/Gridtrading.py @@ -1,5 +1,6 @@ # auto-generated code # from .base import APIComponent, APIReturn, EndpointDetails +from typing import * class Gridtrading(APIComponent): @@ -69,12 +70,7 @@ def amend_grid_algo_order( return self.request(details) def stop_grid_algo_order( - self, - algoId: str, - instId: str, - algoOrdType: str, - stopType: str, - use_proxy: bool = False, + self, body: List[dict] = None, use_proxy: bool = False ) -> APIReturn: """ Stop grid algo order @@ -83,11 +79,7 @@ def stop_grid_algo_order( Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family """ - kwargs = { - k: v - for k, v in locals().items() - if k not in ["use_proxy", "self"] and v is not None - } + kwargs = body details = EndpointDetails( request_path="/api/v5/tradingBot/grid/stop-order-algo", method="POST", diff --git a/pyokx/Marketdata.py b/pyokx/Marketdata.py index 1596b65..f6e4eaf 100644 --- a/pyokx/Marketdata.py +++ b/pyokx/Marketdata.py @@ -77,7 +77,7 @@ def get_order_book( """ Get order book Retrieve order book of the instrument. - Rate Limit: 20 requests per 2 seconds + Rate Limit: 40 requests per 2 seconds Rate limit rule: IP """ kwargs = { diff --git a/pyokx/Trade.py b/pyokx/Trade.py index a920f8c..ed9b1c0 100644 --- a/pyokx/Trade.py +++ b/pyokx/Trade.py @@ -1,5 +1,6 @@ # auto-generated code # from .base import APIComponent, APIReturn, EndpointDetails +from typing import * class Trade(APIComponent): @@ -27,11 +28,13 @@ def place_order( use_proxy: bool = False, ) -> APIReturn: """ - Place order - You can place an order only if you have sufficient funds. - Rate Limit: 60 requests per 2 seconds - Rate limit rule (except Options): UserID + Instrument ID - Rate limit rule (Options only): UserID + Instrument Family + Place order + You can place an order only if you have sufficient funds. + For leading contracts, this endpoint supports placement, but can't close positions. + Rate Limit: 60 requests per 2 seconds + Rate Limit of leading contracts: 1 requests per 2 seconds + Rate limit rule (except Options): UserID + Instrument ID + Rate limit rule (Options only): UserID + Instrument Family """ kwargs = { k: v @@ -47,43 +50,21 @@ def place_order( return self.request(details) def place_multiple_orders( - self, - instId: str, - side: str, - ordType: str, - sz: str, - tdMode: str, - banAmend: bool = None, - tpTriggerPxType: str = None, - slOrdPx: str = None, - slTriggerPx: str = None, - tpOrdPx: str = None, - tpTriggerPx: str = None, - slTriggerPxType: str = None, - tgtCcy: str = None, - reduceOnly: bool = None, - tag: str = None, - clOrdId: str = None, - ccy: str = None, - posSide: str = None, - px: str = None, - use_proxy: bool = False, + self, body: List[dict] = None, use_proxy: bool = False ) -> APIReturn: """ Place multiple orders Place orders in batches. Maximum 20 orders can be placed per request. Request parameters should be passed in the form of an array. + For leading contracts, this endpoint supports placement, but can't close positions. Rate Limit: 300 orders per 2 seconds + Rate Limit of leading contracts: 1 requests per 2 seconds Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family Unlike other endpoints, the rate limit of this endpoint is determined by the number of orders. If there is only one order in the request, it will consume the rate limit of `Place order`. """ - kwargs = { - k: v - for k, v in locals().items() - if k not in ["use_proxy", "self"] and v is not None - } + kwargs = body details = EndpointDetails( request_path="/api/v5/trade/batch-orders", method="POST", @@ -120,11 +101,7 @@ def cancel_order( return self.request(details) def cancel_multiple_orders( - self, - instId: str, - ordId: str = None, - clOrdId: str = None, - use_proxy: bool = False, + self, body: List[dict] = None, use_proxy: bool = False ) -> APIReturn: """ Cancel multiple orders @@ -136,11 +113,7 @@ def cancel_multiple_orders( Unlike other endpoints, the rate limit of this endpoint is determined by the number of orders. If there is only one order in the request, it will consume the rate limit of `Cancel order`. """ - kwargs = { - k: v - for k, v in locals().items() - if k not in ["use_proxy", "self"] and v is not None - } + kwargs = body details = EndpointDetails( request_path="/api/v5/trade/cancel-batch-orders", method="POST", @@ -164,6 +137,7 @@ def amend_order( Amend order Amend an incomplete order. Rate Limit: 60 requests per 2 seconds + Rate Limit of leading contracts: 1 requests per 2 seconds Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family """ @@ -181,31 +155,20 @@ def amend_order( return self.request(details) def amend_multiple_orders( - self, - instId: str, - cxlOnFail: bool = None, - reqId: str = None, - ordId: str = None, - clOrdId: str = None, - newSz: str = None, - newPx: str = None, - use_proxy: bool = False, + self, body: List[dict] = None, use_proxy: bool = False ) -> APIReturn: """ Amend multiple orders Amend incomplete orders in batches. Maximum 20 orders can be amended per request. Request parameters should be passed in the form of an array. Rate Limit: 300 orders per 2 seconds + Rate Limit of leading contracts: 1 requests per 2 seconds Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family Unlike other endpoints, the rate limit of this endpoint is determined by the number of orders. If there is only one order in the request, it will consume the rate limit of `Amend order`. """ - kwargs = { - k: v - for k, v in locals().items() - if k not in ["use_proxy", "self"] and v is not None - } + kwargs = body details = EndpointDetails( request_path="/api/v5/trade/amend-batch-orders", method="POST", @@ -321,7 +284,7 @@ def get_order_history_last_7_days( use_proxy: bool = False, ) -> APIReturn: """ - Get order history (last 7 days) + Get order history (last 7 days) Retrieve the completed order data for the last 7 days, and the incomplete orders that have been canceled are only reserved for 2 hours. Rate Limit: 40 requests per 2 seconds Rate limit rule: UserID @@ -477,6 +440,7 @@ def place_algo_order( Place algo order The algo order includes trigger order, oco order, conditional order,iceberg order, twap order and trailing order. Rate Limit: 20 requests per 2 seconds + Rate Limit of leading contracts: 1 requests per 2 seconds Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family """ @@ -494,7 +458,7 @@ def place_algo_order( return self.request(details) def cancel_algo_order( - self, algoId: str, instId: str, use_proxy: bool = False + self, body: List[dict] = None, use_proxy: bool = False ) -> APIReturn: """ Cancel algo order @@ -503,11 +467,7 @@ def cancel_algo_order( Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family """ - kwargs = { - k: v - for k, v in locals().items() - if k not in ["use_proxy", "self"] and v is not None - } + kwargs = body details = EndpointDetails( request_path="/api/v5/trade/cancel-algos", method="POST", @@ -517,7 +477,7 @@ def cancel_algo_order( return self.request(details) def cancel_advance_algo_order( - self, algoId: str, instId: str, use_proxy: bool = False + self, body: List[dict] = None, use_proxy: bool = False ) -> APIReturn: """ Cancel advance algo order @@ -526,11 +486,7 @@ def cancel_advance_algo_order( Rate limit rule (except Options): UserID + Instrument ID Rate limit rule (Options only): UserID + Instrument Family """ - kwargs = { - k: v - for k, v in locals().items() - if k not in ["use_proxy", "self"] and v is not None - } + kwargs = body details = EndpointDetails( request_path="/api/v5/trade/cancel-advance-algos", method="POST", diff --git a/pyokx/auth.py b/pyokx/auth.py index f90acdb..fc203c0 100644 --- a/pyokx/auth.py +++ b/pyokx/auth.py @@ -6,6 +6,7 @@ import datetime import urllib.parse from typeguard import typechecked +from typing import Any, Dict, List, Optional, Union @dataclass @@ -49,7 +50,10 @@ def __init__(self, key: str, secret: str, phrase: str): self.ok_access_passphrase = phrase def get_auth( - self, method: str, request_path: str, body: dict = None + self, + method: str, + request_path: str, + body: Optional[Union[Dict[str, Any], List[dict]]] = None, ) -> OKXAuthHeaders: """ Get the auth headers used for making a request to OKX @@ -74,12 +78,15 @@ def _sign(self, prehash: str) -> str: @staticmethod def _prehash_str( - timestamp: str, method: str, request_path: str, body: dict = None + timestamp: str, + method: str, + request_path: str, + body: Optional[Union[Dict[str, Any], List[dict]]] = None, ) -> str: if body: if method == "GET": body_str = "?" - body_str += urllib.parse.urlencode(body) + body_str += urllib.parse.urlencode(body) # type: ignore elif method == "POST": body_str = json.dumps(body) else: diff --git a/pyokx/base.py b/pyokx/base.py index 879c200..2c7a0fb 100644 --- a/pyokx/base.py +++ b/pyokx/base.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field import pandas as pd -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional, Union from typeguard import typechecked from .auth import OKXAuth import requests @@ -43,7 +43,7 @@ def make_request( request_path: str, method: VALID_HTTP_METHOD, params: dict = None, - body: dict = None, + body: Optional[Union[Dict[str, Any], List[dict]]] = None, use_proxy: bool = False, ) -> requests.Response: """ @@ -59,7 +59,7 @@ def _prepare_request( request_path: str, method: VALID_HTTP_METHOD, params: dict = None, - body: dict = None, + body: Optional[Union[Dict[str, Any], List[dict]]] = None, ) -> requests.PreparedRequest: params_to_encode = params if method == "GET" else body headers = self.auth.get_auth(method, request_path, params_to_encode).to_dict() @@ -116,7 +116,7 @@ class EndpointDetails: request_path: str method: VALID_HTTP_METHOD params: Optional[Dict[str, Any]] = field(default=None) - body: Optional[Dict[str, Any]] = field(default=None) + body: Optional[Union[Dict[str, Any], List[dict]]] = field(default=None) use_proxy: bool = field(default=False) diff --git a/pyproject.toml b/pyproject.toml index 55db0ac..ec82ba9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyokx" -version = "0.5.0" +version = "0.6.0" description = "Unofficial python wrapper for the OKX V5 API" authors = ["nicelgueta"] license = "MIT"