diff --git a/edgex_sdk/client.py b/edgex_sdk/client.py index 55654b9..8e7db06 100644 --- a/edgex_sdk/client.py +++ b/edgex_sdk/client.py @@ -53,6 +53,8 @@ def __init__(self, base_url: str, account_id: int, stark_private_key: str, self.transfer = TransferClient(self.async_client) self.asset = AssetClient(self.async_client) + self._cached_metadata: Dict[str, Any] | None = None + async def __aenter__(self): """Async context manager entry.""" await self.async_client._ensure_session() @@ -73,7 +75,9 @@ def internal_client(self): async def get_metadata(self) -> Dict[str, Any]: """Get the exchange metadata.""" - return await self.metadata.get_metadata() + if self._cached_metadata is None: + self._cached_metadata = await self.metadata.get_metadata() + return self._cached_metadata async def get_server_time(self) -> Dict[str, Any]: """Get the current server time.""" diff --git a/edgex_sdk/order/client.py b/edgex_sdk/order/client.py index b6ccfc3..f155231 100644 --- a/edgex_sdk/order/client.py +++ b/edgex_sdk/order/client.py @@ -8,6 +8,7 @@ CreateOrderParams, CancelOrderParams, GetActiveOrderParams, + GetHistoryOrderParams, OrderFillTransactionParams, TimeInForce, OrderType @@ -261,6 +262,61 @@ async def get_active_orders(self, params: GetActiveOrderParams) -> Dict[str, Any params=query_params ) + async def get_history_orders(self, params: GetHistoryOrderParams) -> Dict[str, Any]: + """ + Get historical orders with pagination and filters. + + Args: + params: History order query parameters + + Returns: + Dict[str, Any]: The historical orders + + Raises: + ValueError: If the request fails + """ + # Build query parameters + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + if params.filter_contract_id_list: + query_params["filterContractIdList"] = ",".join(params.filter_contract_id_list) + if params.filter_type_list: + query_params["filterTypeList"] = ",".join(params.filter_type_list) + if params.filter_status_list: + query_params["filterStatusList"] = ",".join(params.filter_status_list) + + # Add boolean filters + if params.filter_is_liquidate is not None: + query_params["filterIsLiquidateList"] = str(params.filter_is_liquidate).lower() + if params.filter_is_deleverage is not None: + query_params["filterIsDeleverageList"] = str(params.filter_is_deleverage).lower() + if params.filter_is_position_tpsl is not None: + query_params["filterIsPositionTpslList"] = str(params.filter_is_position_tpsl).lower() + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + # Execute request using async client + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/order/getHistoryOrderPage", + params=query_params + ) + async def get_order_fill_transactions(self, params: OrderFillTransactionParams) -> Dict[str, Any]: """ Get order fill transactions with pagination and filters. @@ -314,6 +370,60 @@ async def get_order_fill_transactions(self, params: OrderFillTransactionParams) params=query_params ) + async def get_orders_by_ids(self, order_id_list: List[str]) -> Dict[str, Any]: + """ + Get orders by account ID and order IDs (batch). + + Args: + order_id_list: List of order IDs to fetch + + Returns: + Dict[str, Any]: Orders matching the provided IDs + + Raises: + ValueError: If input is invalid or request fails + """ + if not order_id_list: + raise ValueError("order_id_list must not be empty") + + query_params = { + "accountId": str(self.async_client.get_account_id()), + "orderIdList": ",".join(order_id_list), + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/order/getOrderById", + params=query_params, + ) + + async def get_orders_by_client_order_ids(self, client_order_id_list: List[str]) -> Dict[str, Any]: + """ + Get orders by client order IDs (batch). + + Args: + client_order_id_list: List of client order IDs to fetch + + Returns: + Dict[str, Any]: Orders matching the provided client order IDs + + Raises: + ValueError: If input is invalid or request fails + """ + if not client_order_id_list: + raise ValueError("client_order_id_list must not be empty") + + query_params = { + "accountId": str(self.async_client.get_account_id()), + "clientOrderIdList": ",".join(client_order_id_list), + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/order/getOrderByClientOrderId", + params=query_params, + ) + async def get_max_order_size(self, contract_id: str, price: float) -> Dict[str, Any]: """ Get the maximum order size for a given contract and price. diff --git a/edgex_sdk/ws/client.py b/edgex_sdk/ws/client.py index 0571bce..27418c7 100644 --- a/edgex_sdk/ws/client.py +++ b/edgex_sdk/ws/client.py @@ -167,6 +167,20 @@ def _handle_messages(self): self.handlers[channel_type](message) continue + if msg.get("type") == "trade-event": + account_handler = self.handlers.get("account") + if account_handler is not None: + account_handler(message) + + order_handler = self.handlers.get("order") + if order_handler is not None: + order_handler(message) + + position_handler = self.handlers.get("position") + if position_handler is not None: + position_handler(message) + + # Call registered handlers for other message types msg_type = msg.get("type", "") if msg_type in self.handlers: diff --git a/pyproject.toml b/pyproject.toml index 10df441..f52c4bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "edgex-python-sdk" -version = "0.3.0" +version = "0.3.3" description = "A Python SDK for interacting with the EdgeX Exchange API" readme = "README.md" license = {text = "MIT"}