From 4f63a0bc0b117bb212783b04d5ffe2db98d4cad5 Mon Sep 17 00:00:00 2001 From: sirouk <8901571+sirouk@users.noreply.github.com> Date: Tue, 4 Feb 2025 22:42:51 -0500 Subject: [PATCH 1/2] Update trading.py for copy trading --- src/blofin/api/trading.py | 69 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/blofin/api/trading.py b/src/blofin/api/trading.py index 2c5629d..7e87fec 100644 --- a/src/blofin/api/trading.py +++ b/src/blofin/api/trading.py @@ -3,21 +3,26 @@ from ..constants import ( FUTURES_ACCOUNT_BALANCE_ENDPOINT, ACCOUNT_POSITIONS_ENDPOINT, + ACCOUNT_POSITIONS_ENDPOINT_CT, ACCOUNT_MARGIN_MODE_ENDPOINT, ACCOUNT_SET_MARGIN_MODE_ENDPOINT, ACCOUNT_POSITION_MODE_ENDPOINT, ACCOUNT_SET_POSITION_MODE_ENDPOINT, ACCOUNT_BATCH_LEVERAGE_INFO_ENDPOINT, ACCOUNT_SET_LEVERAGE_ENDPOINT, + ACCOUNT_SET_LEVERAGE_ENDPOINT_CT, TRADE_ORDER_ENDPOINT, + TRADE_ORDER_ENDPOINT_CT, TRADE_BATCH_ORDERS_ENDPOINT, TRADE_ORDER_TPSL_ENDPOINT, TRADE_CANCEL_ORDER_ENDPOINT, TRADE_CANCEL_BATCH_ORDERS_ENDPOINT, TRADE_CANCEL_TPSL_ENDPOINT, TRADE_ORDERS_PENDING_ENDPOINT, + TRADE_ORDERS_PENDING_ENDPOINT_CT, TRADE_ORDERS_TPSL_PENDING_ENDPOINT, TRADE_CLOSE_POSITION_ENDPOINT, + TRADE_CLOSE_POSITION_ENDPOINT_CT, TRADE_ORDERS_HISTORY_ENDPOINT, TRADE_ORDERS_TPSL_HISTORY_ENDPOINT, TRADE_FILLS_HISTORY_ENDPOINT, @@ -50,6 +55,10 @@ def get_positions(self, inst_id: Optional[str] = None): params = {'instId': inst_id} if inst_id else {} return send_request('GET', ACCOUNT_POSITIONS_ENDPOINT, self.client.auth, params=params, authenticate=True) + def get_positions_ct(self, inst_id: Optional[str] = None): + params = {'instId': inst_id} if inst_id else {} + return send_request('GET', ACCOUNT_POSITIONS_ENDPOINT_CT, self.client.auth, params=params, authenticate=True) + def get_margin_mode(self): return send_request('GET', ACCOUNT_MARGIN_MODE_ENDPOINT, self.client.auth, authenticate=True) @@ -88,6 +97,18 @@ def set_leverage(self, inst_id: str, leverage: int, margin_mode: str, position_s data['positionSide'] = position_side return send_request('POST', ACCOUNT_SET_LEVERAGE_ENDPOINT, self.client.auth, data=data, authenticate=True) + def set_leverage_ct(self, inst_id: str, margin_mode: str, leverage: int, position_side: Optional[str] = None): + if margin_mode not in MARGIN_MODES: + raise BloFinParameterException(f"Invalid margin_mode. Must be one of: {', '.join(MARGIN_MODES)}") + data = { + 'instId': inst_id, + 'marginMode': margin_mode, + 'leverage': leverage + } + if position_side: + data['positionSide'] = position_side + return send_request('POST', ACCOUNT_SET_LEVERAGE_ENDPOINT_CT, self.client.auth, data=data, authenticate=True) + def place_order(self, inst_id: str, margin_mode: str, position_side: str, side: str, order_type: str, price: float, size: float, **kwargs): if margin_mode not in MARGIN_MODES: raise BloFinParameterException(f"Invalid margin_mode. Must be one of: {', '.join(MARGIN_MODES)}") @@ -110,6 +131,24 @@ def place_order(self, inst_id: str, margin_mode: str, position_side: str, side: } return send_request('POST', TRADE_ORDER_ENDPOINT, self.client.auth, data=data, authenticate=True) + def place_order_ct(self, inst_id: str, margin_mode: str, position_side: str, side: str, order_type: str, price: float, size: float, **kwargs): + if margin_mode not in MARGIN_MODES: + raise BloFinParameterException(f"Invalid margin_mode. Must be one of: {', '.join(MARGIN_MODES)}") + if position_side not in POSITION_SIDES: + raise BloFinParameterException(f"Invalid position_side. Must be one of: {', '.join(POSITION_SIDES)}") + + data = { + 'instId': inst_id, + 'marginMode': margin_mode, + 'positionSide': position_side, + 'side': side, + 'orderType': order_type, + 'price': price, + 'size': size, + **kwargs + } + return send_request('POST', TRADE_ORDER_ENDPOINT_CT, self.client.auth, data=data, authenticate=True) + def place_multiple_orders(self, orders: List[dict]): return send_request('POST', TRADE_BATCH_ORDERS_ENDPOINT, self.client.auth, data=orders, authenticate=True) @@ -159,6 +198,15 @@ def get_active_orders(self, inst_id: Optional[str] = None, order_type: Optional[ params = filter_none_params(instId=inst_id, orderType=order_type, state=state, after=after, before=before, limit=limit) return send_request('GET', TRADE_ORDERS_PENDING_ENDPOINT, self.client.auth, params=params, authenticate=True) + def get_active_orders_ct(self, inst_id: Optional[str] = None, order_type: Optional[str] = None, state: Optional[str] = None, after: Optional[str] = None, before: Optional[str] = None, limit: Optional[int] = None): + if order_type and order_type not in ORDER_TYPES: + raise BloFinParameterException(f"Invalid order_type. Must be one of: {', '.join(ORDER_TYPES)}") + if state and state not in ORDER_STATES: + raise BloFinParameterException(f"Invalid state. Must be one of: {', '.join(ORDER_STATES)}") + + params = filter_none_params(instId=inst_id, orderType=order_type, state=state, after=after, before=before, limit=limit) + return send_request('GET', TRADE_ORDERS_PENDING_ENDPOINT_CT, self.client.auth, params=params, authenticate=True) + def get_active_tpsl_orders(self, inst_id: Optional[str] = None, tpsl_id: Optional[str] = None, client_order_id: Optional[str] = None, after: Optional[str] = None, before: Optional[str] = None, limit: Optional[int] = None): params = filter_none_params(instId=inst_id, tpslId=tpsl_id, clientOrderId=client_order_id, after=after, before=before, limit=limit) return send_request('GET', TRADE_ORDERS_TPSL_PENDING_ENDPOINT, self.client.auth, params=params, authenticate=True) @@ -178,6 +226,25 @@ def close_positions(self, inst_id: str, margin_mode: str, position_side: str, cl data['clientOrderId'] = client_order_id return send_request('POST', TRADE_CLOSE_POSITION_ENDPOINT, self.client.auth, data=data, authenticate=True) + def close_positions_ct(self, inst_id: str, margin_mode: str, position_side: str, client_order_id: Optional[str] = None, close_type: str = "pnl", size: Optional[float] = None): + if margin_mode not in MARGIN_MODES: + raise BloFinParameterException(f"Invalid margin_mode. Must be one of: {', '.join(MARGIN_MODES)}") + if position_side not in POSITION_SIDES: + raise BloFinParameterException(f"Invalid position_side. Must be one of: {', '.join(POSITION_SIDES)}") + + data = { + 'instId': inst_id, + 'marginMode': margin_mode, + 'positionSide': position_side, + 'closeType': close_type + } + if client_order_id: + data['clientOrderId'] = client_order_id + if size: + data['size'] = size + + return send_request('POST', TRADE_CLOSE_POSITION_ENDPOINT_CT, self.client.auth, data=data, authenticate=True) + def get_order_history(self, inst_id: Optional[str] = None, order_type: Optional[str] = None, state: Optional[str] = None, after: Optional[str] = None, before: Optional[str] = None, begin: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None): if order_type and order_type not in ORDER_TYPES: raise BloFinParameterException(f"Invalid order_type. Must be one of: {', '.join(ORDER_TYPES)}") @@ -431,4 +498,4 @@ def get_algo_order_history(self, inst_id: Optional[str] = None, algo_id: Optiona limit=limit, orderType=order_type ) - return send_request('GET', TRADE_ORDERS_ALGO_HISTORY_ENDPOINT, self.client.auth, params=params, authenticate=True) \ No newline at end of file + return send_request('GET', TRADE_ORDERS_ALGO_HISTORY_ENDPOINT, self.client.auth, params=params, authenticate=True) From a906a3f2d7874991c20cef803fcc59b3e59dc7c4 Mon Sep 17 00:00:00 2001 From: sirouk <8901571+sirouk@users.noreply.github.com> Date: Tue, 4 Feb 2025 22:44:28 -0500 Subject: [PATCH 2/2] Update constants.py for copytrading --- src/blofin/constants.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/blofin/constants.py b/src/blofin/constants.py index 5c9b3de..de78e6f 100644 --- a/src/blofin/constants.py +++ b/src/blofin/constants.py @@ -15,27 +15,33 @@ # Trade endpoints FUTURES_ACCOUNT_BALANCE_ENDPOINT = '/api/v1/account/balance' ACCOUNT_POSITIONS_ENDPOINT = '/api/v1/account/positions' +ACCOUNT_POSITIONS_ENDPOINT_CT = '/api/v1/copytrading/account/positions-by-contract' ACCOUNT_MARGIN_MODE_ENDPOINT = '/api/v1/account/margin-mode' ACCOUNT_SET_MARGIN_MODE_ENDPOINT = '/api/v1/account/set-margin-mode' ACCOUNT_POSITION_MODE_ENDPOINT = '/api/v1/account/position-mode' ACCOUNT_SET_POSITION_MODE_ENDPOINT = '/api/v1/account/set-position-mode' ACCOUNT_BATCH_LEVERAGE_INFO_ENDPOINT = '/api/v1/account/batch-leverage-info' ACCOUNT_SET_LEVERAGE_ENDPOINT = '/api/v1/account/set-leverage' +ACCOUNT_SET_LEVERAGE_ENDPOINT_CT = '/api/v1/copytrading/account/set-leverage' TRADE_ORDER_ENDPOINT = '/api/v1/trade/order' +TRADE_ORDER_ENDPOINT_CT = '/api/v1/copytrading/trade/place-order' TRADE_BATCH_ORDERS_ENDPOINT = '/api/v1/trade/batch-orders' TRADE_ORDER_TPSL_ENDPOINT = '/api/v1/trade/order-tpsl' TRADE_CANCEL_ORDER_ENDPOINT = '/api/v1/trade/cancel-order' TRADE_CANCEL_BATCH_ORDERS_ENDPOINT = '/api/v1/trade/cancel-batch-orders' TRADE_CANCEL_TPSL_ENDPOINT = '/api/v1/trade/cancel-tpsl' TRADE_ORDERS_PENDING_ENDPOINT = '/api/v1/trade/orders-pending' +TRADE_ORDERS_PENDING_ENDPOINT_CT = '/api/v1/copytrading/trade/orders-pending' TRADE_ORDERS_TPSL_PENDING_ENDPOINT = '/api/v1/trade/orders-tpsl-pending' TRADE_CLOSE_POSITION_ENDPOINT = '/api/v1/trade/close-position' +TRADE_CLOSE_POSITION_ENDPOINT_CT = '/api/v1/copytrading/trade/close-position-by-contract' TRADE_ORDERS_HISTORY_ENDPOINT = '/api/v1/trade/orders-history' TRADE_ORDERS_TPSL_HISTORY_ENDPOINT = '/api/v1/trade/orders-tpsl-history' TRADE_FILLS_HISTORY_ENDPOINT = '/api/v1/trade/fills-history' # Public API Endpoints PUBLIC_INSTRUMENTS_ENDPOINT = '/api/v1/market/instruments' +#PUBLIC_INSTRUMENTS_ENDPOINT_CT = '/api/v1/copytrading/instruments' # useless PUBLIC_TICKERS_ENDPOINT = '/api/v1/market/tickers' PUBLIC_ORDER_BOOK_ENDPOINT = '/api/v1/market/books' PUBLIC_TRADES_ENDPOINT = '/api/v1/market/trades' @@ -218,4 +224,4 @@ TRADING_MAX_ORDER_SIZE = 1000 # Algo trading max order size -ALGO_MAX_LIMIT = 100 \ No newline at end of file +ALGO_MAX_LIMIT = 100