-
Notifications
You must be signed in to change notification settings - Fork 34
Release/chain v1.17.0 support to master #402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 24 commits
71b34aa
8c6b3f5
95f9b21
65f0426
a7d26e6
987e82b
1c7fb55
26b9e06
bdd321b
251ae51
10509e6
c22a799
403ccff
76e43be
6a54b6a
0c7e085
15adf6d
05d6a6e
226c092
e42bd38
4d134c8
74bd9d2
8645426
629d62e
162ad40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,76 @@ | ||||||||||||||||||||||
| import asyncio | ||||||||||||||||||||||
| from decimal import Decimal | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| from pyinjective.async_client_v2 import AsyncClient | ||||||||||||||||||||||
| from pyinjective.core.network import Network | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def adjusted_margin( | ||||||||||||||||||||||
| quantity: Decimal, margin: Decimal, is_long: bool, cumulative_funding_entry: Decimal, cumulative_funding: Decimal | ||||||||||||||||||||||
| ) -> Decimal: | ||||||||||||||||||||||
| unrealized_funding_payment = (cumulative_funding - cumulative_funding_entry) * quantity * (-1 if is_long else 1) | ||||||||||||||||||||||
| return margin + unrealized_funding_payment | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| async def main() -> None: | ||||||||||||||||||||||
| # select network: local, testnet, mainnet | ||||||||||||||||||||||
| network = Network.mainnet() | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # initialize grpc client | ||||||||||||||||||||||
| client = AsyncClient(network) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| positions_per_market = dict() | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| positions_dict = await client.fetch_chain_positions() | ||||||||||||||||||||||
| liquidable_positions = [] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| for position in positions_dict["state"]: | ||||||||||||||||||||||
| if position["marketId"] not in positions_per_market: | ||||||||||||||||||||||
| positions_per_market[position["marketId"]] = [] | ||||||||||||||||||||||
| positions_per_market[position["marketId"]].append(position) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| derivative_markets = await client.fetch_chain_derivative_markets( | ||||||||||||||||||||||
| status="Active", | ||||||||||||||||||||||
| market_ids=list(positions_per_market.keys()), | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| for market in derivative_markets["markets"]: | ||||||||||||||||||||||
| client_market = (await client.all_derivative_markets())[market["market"]["marketId"]] | ||||||||||||||||||||||
| market_mark_price = client_market._from_extended_chain_format(Decimal(market["markPrice"])) | ||||||||||||||||||||||
| for position in positions_per_market[client_market.id]: | ||||||||||||||||||||||
| is_long = position["position"]["isLong"] | ||||||||||||||||||||||
| quantity = client_market._from_extended_chain_format(Decimal(position["position"]["quantity"])) | ||||||||||||||||||||||
| entry_price = client_market._from_extended_chain_format(Decimal(position["position"]["entryPrice"])) | ||||||||||||||||||||||
| margin = client_market._from_extended_chain_format(Decimal(position["position"]["margin"])) | ||||||||||||||||||||||
| cumulative_funding_entry = client_market._from_extended_chain_format( | ||||||||||||||||||||||
| Decimal(position["position"]["cumulativeFundingEntry"]) | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| market_cumulative_funding = client_market._from_extended_chain_format( | ||||||||||||||||||||||
| Decimal(market["perpetualInfo"]["fundingInfo"]["cumulativeFunding"]) | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
Comment on lines
+48
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against missing funding info on non‑perpetual markets
Apply something along these lines to avoid the crash: - market_cumulative_funding = client_market._from_extended_chain_format(
- Decimal(market["perpetualInfo"]["fundingInfo"]["cumulativeFunding"])
- )
+ perpetual_info = market.get("perpetualInfo")
+ if not perpetual_info or "fundingInfo" not in perpetual_info:
+ continue # no funding data available for this market type
+
+ market_cumulative_funding = client_market._from_extended_chain_format(
+ Decimal(perpetual_info["fundingInfo"]["cumulativeFunding"])
+ )📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| adj_margin = adjusted_margin(quantity, margin, is_long, cumulative_funding_entry, market_cumulative_funding) | ||||||||||||||||||||||
| adjusted_unit_margin = (adj_margin / quantity) * (-1 if is_long else 1) | ||||||||||||||||||||||
| maintenance_margin_ratio = client_market.maintenance_margin_ratio * (-1 if is_long else 1) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| liquidation_price = (entry_price + adjusted_unit_margin) / (Decimal(1) + maintenance_margin_ratio) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| should_be_liquidated = (is_long and market_mark_price <= liquidation_price) or ( | ||||||||||||||||||||||
| not is_long and market_mark_price >= liquidation_price | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if should_be_liquidated: | ||||||||||||||||||||||
| position_side = "Long" if is_long else "Short" | ||||||||||||||||||||||
| print( | ||||||||||||||||||||||
| f"{position_side} position for market {client_market.id} and subaccount " | ||||||||||||||||||||||
| f"{position['subaccountId']} should be liquidated (liquidation price: " | ||||||||||||||||||||||
| f"{liquidation_price.normalize()} / mark price: {market_mark_price.normalize()})" | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| liquidable_positions.append(position) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # print(f"\n\n\n") | ||||||||||||||||||||||
| # print(json.dumps(liquidable_positions, indent=4)) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||||||
| asyncio.get_event_loop().run_until_complete(main()) | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import asyncio | ||
| import json | ||
| import os | ||
|
|
||
| import dotenv | ||
|
|
||
| from pyinjective.async_client_v2 import AsyncClient | ||
| from pyinjective.core.broadcaster import MsgBroadcasterWithPk | ||
| from pyinjective.core.network import Network | ||
| from pyinjective.wallet import PrivateKey | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| dotenv.load_dotenv() | ||
| configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") | ||
|
|
||
| # select network: local, testnet, mainnet | ||
| network = Network.testnet() | ||
|
|
||
| # initialize grpc client | ||
| client = AsyncClient(network) | ||
| await client.initialize_tokens_from_chain_denoms() | ||
| composer = await client.composer() | ||
|
|
||
| gas_price = await client.current_chain_gas_price() | ||
| # adjust gas price to make it valid even if it changes between the time it is requested and the TX is broadcasted | ||
| gas_price = int(gas_price * 1.1) | ||
|
|
||
| message_broadcaster = MsgBroadcasterWithPk.new_using_simulation( | ||
| network=network, | ||
| private_key=configured_private_key, | ||
| gas_price=gas_price, | ||
| client=client, | ||
| composer=composer, | ||
| ) | ||
|
|
||
| # load account | ||
| priv_key = PrivateKey.from_hex(configured_private_key) | ||
| pub_key = priv_key.to_public_key() | ||
| address = pub_key.to_address() | ||
| await client.fetch_account(address.to_acc_bech32()) | ||
|
|
||
| offsetting_subaccount_ids = { | ||
| "0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000", | ||
| "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000", | ||
| } | ||
|
|
||
| # prepare tx msg | ||
| message = composer.msg_offset_position( | ||
| sender=address.to_acc_bech32(), | ||
| subaccount_id=address.get_subaccount_id(index=0), | ||
| market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", | ||
| offsetting_subaccount_ids=offsetting_subaccount_ids, | ||
| ) | ||
|
|
||
| # broadcast the transaction | ||
| result = await message_broadcaster.broadcast([message]) | ||
| print("---Transaction Response---") | ||
| print(json.dumps(result, indent=2)) | ||
|
|
||
| gas_price = await client.current_chain_gas_price() | ||
| # adjust gas price to make it valid even if it changes between the time it is requested and the TX is broadcasted | ||
| gas_price = int(gas_price * 1.1) | ||
| message_broadcaster.update_gas_price(gas_price=gas_price) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.get_event_loop().run_until_complete(main()) |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||
| import asyncio | ||||||||||||||
| import json | ||||||||||||||
|
|
||||||||||||||
| from pyinjective.async_client_v2 import AsyncClient | ||||||||||||||
| from pyinjective.core.network import Network | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| async def main() -> None: | ||||||||||||||
| """ | ||||||||||||||
| Demonstrate fetching denom min notionals using AsyncClient. | ||||||||||||||
| """ | ||||||||||||||
|
Comment on lines
+9
to
+11
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the docstring to match the actual functionality. The docstring states "Demonstrate fetching denom min notionals" but the code actually fetches open interest using Apply this diff to correct the docstring: - """
- Demonstrate fetching denom min notionals using AsyncClient.
- """
+ """
+ Demonstrate fetching open interest using AsyncClient.
+ """📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| # Select network: choose between Network.mainnet(), Network.testnet(), or Network.devnet() | ||||||||||||||
| network = Network.testnet() | ||||||||||||||
|
|
||||||||||||||
| # Initialize the Async Client | ||||||||||||||
| client = AsyncClient(network) | ||||||||||||||
|
|
||||||||||||||
| market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6" | ||||||||||||||
| open_interest = await client.fetch_open_interest(market_id=market_id) | ||||||||||||||
| print(json.dumps(open_interest, indent=2)) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| if __name__ == "__main__": | ||||||||||||||
| asyncio.get_event_loop().run_until_complete(main()) | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo and trailing whitespace.
Line 10 contains a typo: "chainstrem" should be "chainstream". Additionally, the pre-commit hook detected trailing whitespace that must be removed.
Apply this diff to fix both issues:
📝 Committable suggestion
🧰 Tools
🪛 LanguageTool
[grammar] ~10-~10: Ensure spelling is correct
Context: ...ditional orders trigger failures in the chainstrem updates ## [1.11.2] - 2025-09-24 ### A...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~10-~10: Ensure spelling is correct
Context: ...igger failures in the chainstrem updates ## [1.11.2] - 2025-09-24 ### Added - Added ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents