From 70ff008c45d2e0394731cacd0b5697f1f3f9ceac Mon Sep 17 00:00:00 2001 From: Abel Armoa <30988000+aarmoa@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:02:25 -0300 Subject: [PATCH] chore: added support for the new positions in market exchange query --- .../exchange/query/65_PositionsInMarket.py | 21 +++++++++ pyinjective/async_client_v2.py | 3 ++ .../chain/grpc/chain_grpc_exchange_v2_api.py | 6 +++ ...configurable_exchange_v2_query_servicer.py | 6 +++ .../grpc/test_chain_grpc_exchange_v2_api.py | 46 ++++++++++++++++++- 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 examples/chain_client/exchange/query/65_PositionsInMarket.py diff --git a/examples/chain_client/exchange/query/65_PositionsInMarket.py b/examples/chain_client/exchange/query/65_PositionsInMarket.py new file mode 100644 index 00000000..028d488d --- /dev/null +++ b/examples/chain_client/exchange/query/65_PositionsInMarket.py @@ -0,0 +1,21 @@ +import asyncio +import json + +from pyinjective.async_client_v2 import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6" + positions = await client.fetch_chain_positions_in_market(market_id=market_id) + print(json.dumps(positions, indent=2)) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/pyinjective/async_client_v2.py b/pyinjective/async_client_v2.py index f99b0cd5..6c7cf81f 100644 --- a/pyinjective/async_client_v2.py +++ b/pyinjective/async_client_v2.py @@ -734,6 +734,9 @@ async def fetch_chain_derivative_market( async def fetch_chain_positions(self) -> Dict[str, Any]: return await self.chain_exchange_v2_api.fetch_positions() + async def fetch_chain_positions_in_market(self, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_v2_api.fetch_positions_in_market(market_id=market_id) + async def fetch_chain_subaccount_positions(self, subaccount_id: str) -> Dict[str, Any]: return await self.chain_exchange_v2_api.fetch_subaccount_positions(subaccount_id=subaccount_id) diff --git a/pyinjective/client/chain/grpc/chain_grpc_exchange_v2_api.py b/pyinjective/client/chain/grpc/chain_grpc_exchange_v2_api.py index fd9df5fd..3690b3ef 100644 --- a/pyinjective/client/chain/grpc/chain_grpc_exchange_v2_api.py +++ b/pyinjective/client/chain/grpc/chain_grpc_exchange_v2_api.py @@ -378,6 +378,12 @@ async def fetch_positions(self) -> Dict[str, Any]: return response + async def fetch_positions_in_market(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryPositionsInMarketRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.PositionsInMarket, request=request) + + return response + async def fetch_subaccount_positions(self, subaccount_id: str) -> Dict[str, Any]: request = exchange_query_pb.QuerySubaccountPositionsRequest(subaccount_id=subaccount_id) response = await self._execute_call(call=self._stub.SubaccountPositions, request=request) diff --git a/tests/client/chain/grpc/configurable_exchange_v2_query_servicer.py b/tests/client/chain/grpc/configurable_exchange_v2_query_servicer.py index fa361011..e7187e21 100644 --- a/tests/client/chain/grpc/configurable_exchange_v2_query_servicer.py +++ b/tests/client/chain/grpc/configurable_exchange_v2_query_servicer.py @@ -41,6 +41,7 @@ def __init__(self): self.derivative_market_address_responses = deque() self.subaccount_trade_nonce_responses = deque() self.positions_responses = deque() + self.positions_in_market_responses = deque() self.subaccount_positions_responses = deque() self.subaccount_position_in_market_responses = deque() self.subaccount_effective_position_in_market_responses = deque() @@ -221,6 +222,11 @@ async def SubaccountTradeNonce( async def Positions(self, request: exchange_query_pb.QueryPositionsRequest, context=None, metadata=None): return self.positions_responses.pop() + async def PositionsInMarket( + self, request: exchange_query_pb.QueryPositionsInMarketRequest, context=None, metadata=None + ): + return self.positions_in_market_responses.pop() + async def SubaccountPositions( self, request: exchange_query_pb.QuerySubaccountPositionsRequest, context=None, metadata=None ): diff --git a/tests/client/chain/grpc/test_chain_grpc_exchange_v2_api.py b/tests/client/chain/grpc/test_chain_grpc_exchange_v2_api.py index ab049edf..d741043b 100644 --- a/tests/client/chain/grpc/test_chain_grpc_exchange_v2_api.py +++ b/tests/client/chain/grpc/test_chain_grpc_exchange_v2_api.py @@ -23,7 +23,7 @@ def exchange_servicer(): return ConfigurableExchangeV2QueryServicer() -class TestChainGrpcBankApi: +class TestChainGrpcExchangeV2Api: @pytest.mark.asyncio async def test_fetch_exchange_params( self, @@ -1554,6 +1554,50 @@ async def test_fetch_positions( assert positions == expected_positions + @pytest.mark.asyncio + async def test_fetch_positions_in_market( + self, + exchange_servicer, + ): + position = exchange_pb.Position( + isLong=True, + quantity="1000000000000000", + entry_price="2000000000000000000", + margin="2000000000000000000000000000000000", + cumulative_funding_entry="4000000", + ) + derivative_position = exchange_pb.DerivativePosition( + subaccount_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + position=position, + ) + exchange_servicer.positions_in_market_responses.append( + exchange_query_pb.QueryPositionsInMarketResponse(state=[derivative_position]) + ) + + api = self._api_instance(servicer=exchange_servicer) + + positions = await api.fetch_positions_in_market( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + expected_positions = { + "state": [ + { + "subaccountId": derivative_position.subaccount_id, + "marketId": derivative_position.market_id, + "position": { + "isLong": position.isLong, + "quantity": position.quantity, + "entryPrice": position.entry_price, + "margin": position.margin, + "cumulativeFundingEntry": position.cumulative_funding_entry, + }, + }, + ], + } + + assert positions == expected_positions + @pytest.mark.asyncio async def test_fetch_subaccount_positions( self,