avantis-trader-sdk 0.8.13__py3-none-any.whl → 0.8.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- avantis_trader_sdk/__init__.py +2 -1
- avantis_trader_sdk/abis/Trading.sol/Trading.json +1347 -1
- avantis_trader_sdk/config.py +2 -0
- avantis_trader_sdk/feed/feed_client.py +118 -6
- avantis_trader_sdk/rpc/pairs_cache.py +19 -0
- avantis_trader_sdk/rpc/trade.py +63 -28
- avantis_trader_sdk/types.py +72 -0
- avantis_trader_sdk-0.8.15.dist-info/METADATA +126 -0
- {avantis_trader_sdk-0.8.13.dist-info → avantis_trader_sdk-0.8.15.dist-info}/RECORD +11 -11
- avantis_trader_sdk-0.8.13.dist-info/METADATA +0 -124
- {avantis_trader_sdk-0.8.13.dist-info → avantis_trader_sdk-0.8.15.dist-info}/WHEEL +0 -0
- {avantis_trader_sdk-0.8.13.dist-info → avantis_trader_sdk-0.8.15.dist-info}/top_level.txt +0 -0
avantis_trader_sdk/config.py
CHANGED
|
@@ -11,5 +11,7 @@ MAINNET_ADDRESSES = {
|
|
|
11
11
|
|
|
12
12
|
AVANTIS_SOCKET_API = "https://socket-api-pub.avantisfi.com/socket-api/v1/data"
|
|
13
13
|
AVANTIS_CORE_API_BASE_URL = "https://core.avantisfi.com"
|
|
14
|
+
AVANTIS_FEED_V3_URL = "https://feed-v3.avantisfi.com"
|
|
15
|
+
PYTH_LAZER_SSE_URL = "https://pyth-lazer-proxy-3.dourolabs.app/v1/stream"
|
|
14
16
|
|
|
15
17
|
CONTRACT_ADDRESSES = MAINNET_ADDRESSES
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import websockets
|
|
3
|
-
from ..types import
|
|
4
|
-
|
|
3
|
+
from ..types import (
|
|
4
|
+
PriceFeedResponse,
|
|
5
|
+
PriceFeedUpdatesResponse,
|
|
6
|
+
PairInfoFeed,
|
|
7
|
+
FeedV3PriceResponse,
|
|
8
|
+
LazerPriceFeedResponse,
|
|
9
|
+
)
|
|
10
|
+
from typing import List, Callable, Optional
|
|
5
11
|
import requests
|
|
6
12
|
from pydantic import ValidationError
|
|
7
|
-
from ..config import AVANTIS_SOCKET_API
|
|
13
|
+
from ..config import AVANTIS_SOCKET_API, AVANTIS_FEED_V3_URL, PYTH_LAZER_SSE_URL
|
|
8
14
|
import asyncio
|
|
9
15
|
from concurrent.futures import ThreadPoolExecutor
|
|
16
|
+
import aiohttp
|
|
10
17
|
|
|
11
18
|
|
|
12
19
|
class FeedClient:
|
|
@@ -22,14 +29,21 @@ class FeedClient:
|
|
|
22
29
|
hermes_url="https://hermes.pyth.network/v2/updates/price/latest",
|
|
23
30
|
socket_api: str = AVANTIS_SOCKET_API,
|
|
24
31
|
pair_fetcher: Callable = None,
|
|
32
|
+
feed_v3_url: str = AVANTIS_FEED_V3_URL,
|
|
33
|
+
lazer_sse_url: str = PYTH_LAZER_SSE_URL,
|
|
25
34
|
):
|
|
26
35
|
"""
|
|
27
36
|
Constructor for the FeedClient class.
|
|
28
37
|
|
|
29
38
|
Args:
|
|
30
|
-
ws_url: Optional - The websocket URL to connect to.
|
|
31
|
-
on_error: Optional callback for handling websocket errors.
|
|
32
|
-
on_close: Optional callback for handling websocket close events.
|
|
39
|
+
ws_url: Optional - The websocket URL to connect to (Pyth Hermes).
|
|
40
|
+
on_error: Optional callback for handling websocket/SSE errors.
|
|
41
|
+
on_close: Optional callback for handling websocket/SSE close events.
|
|
42
|
+
hermes_url: Optional - The Hermes HTTP API URL.
|
|
43
|
+
socket_api: Optional - The Avantis socket API URL.
|
|
44
|
+
pair_fetcher: Optional - Custom pair fetcher function.
|
|
45
|
+
feed_v3_url: Optional - The feed-v3 API URL for price update data.
|
|
46
|
+
lazer_sse_url: Optional - The Pyth Lazer SSE URL for real-time prices.
|
|
33
47
|
"""
|
|
34
48
|
if (
|
|
35
49
|
ws_url is not None
|
|
@@ -40,11 +54,15 @@ class FeedClient:
|
|
|
40
54
|
|
|
41
55
|
self.ws_url = ws_url
|
|
42
56
|
self.hermes_url = hermes_url
|
|
57
|
+
self.feed_v3_url = feed_v3_url
|
|
58
|
+
self.lazer_sse_url = lazer_sse_url
|
|
43
59
|
self.pair_feeds = {}
|
|
44
60
|
self.feed_pairs = {}
|
|
45
61
|
self.price_feed_callbacks = {}
|
|
62
|
+
self.lazer_callbacks = {}
|
|
46
63
|
self._socket = None
|
|
47
64
|
self._connected = False
|
|
65
|
+
self._lazer_connected = False
|
|
48
66
|
self._on_error = on_error
|
|
49
67
|
self._on_close = on_close
|
|
50
68
|
self.socket_api = socket_api
|
|
@@ -267,3 +285,97 @@ class FeedClient:
|
|
|
267
285
|
return PriceFeedUpdatesResponse(**data)
|
|
268
286
|
else:
|
|
269
287
|
response.raise_for_status()
|
|
288
|
+
|
|
289
|
+
async def get_price_update_data(self, pair_index: int) -> FeedV3PriceResponse:
|
|
290
|
+
"""
|
|
291
|
+
Retrieves price update data from the feed-v3 API for a specific pair.
|
|
292
|
+
|
|
293
|
+
This returns both core (Pyth Hermes) and pro (Pyth Lazer) price data,
|
|
294
|
+
including the priceUpdateData bytes needed for contract calls.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
pair_index: The pair index to get price update data for.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
A FeedV3PriceResponse containing core and pro price data.
|
|
301
|
+
|
|
302
|
+
Raises:
|
|
303
|
+
requests.HTTPError: If the API request fails.
|
|
304
|
+
"""
|
|
305
|
+
url = f"{self.feed_v3_url}/v2/pairs/{pair_index}/price-update-data"
|
|
306
|
+
response = requests.get(url, timeout=10)
|
|
307
|
+
response.raise_for_status()
|
|
308
|
+
data = response.json()
|
|
309
|
+
return FeedV3PriceResponse(**data)
|
|
310
|
+
|
|
311
|
+
async def get_latest_lazer_price(
|
|
312
|
+
self, lazer_feed_ids: List[int]
|
|
313
|
+
) -> LazerPriceFeedResponse:
|
|
314
|
+
"""
|
|
315
|
+
Retrieves the latest prices from the Pyth Lazer API.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
lazer_feed_ids: List of Lazer feed IDs to get prices for.
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
A LazerPriceFeedResponse containing the latest prices.
|
|
322
|
+
|
|
323
|
+
Raises:
|
|
324
|
+
requests.HTTPError: If the API request fails.
|
|
325
|
+
"""
|
|
326
|
+
params = "&".join([f"price_feed_ids={fid}" for fid in lazer_feed_ids])
|
|
327
|
+
url = f"{self.lazer_sse_url.replace('/stream', '/latest_price')}?{params}"
|
|
328
|
+
response = requests.get(url, timeout=10)
|
|
329
|
+
response.raise_for_status()
|
|
330
|
+
data = response.json()
|
|
331
|
+
return LazerPriceFeedResponse(**data)
|
|
332
|
+
|
|
333
|
+
async def listen_for_lazer_price_updates(
|
|
334
|
+
self,
|
|
335
|
+
lazer_feed_ids: List[int],
|
|
336
|
+
callback: Callable[[LazerPriceFeedResponse], None],
|
|
337
|
+
):
|
|
338
|
+
"""
|
|
339
|
+
Listens for real-time price updates from the Pyth Lazer SSE stream.
|
|
340
|
+
|
|
341
|
+
This is the Pyth Pro alternative to the WebSocket-based listen_for_price_updates.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
lazer_feed_ids: List of Lazer feed IDs to subscribe to.
|
|
345
|
+
callback: Callback function to handle price updates.
|
|
346
|
+
|
|
347
|
+
Raises:
|
|
348
|
+
Exception: If an error occurs while listening for price updates.
|
|
349
|
+
"""
|
|
350
|
+
params = "&".join([f"price_feed_ids={fid}" for fid in lazer_feed_ids])
|
|
351
|
+
url = f"{self.lazer_sse_url}?{params}"
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
async with aiohttp.ClientSession() as session:
|
|
355
|
+
async with session.get(url) as response:
|
|
356
|
+
self._lazer_connected = True
|
|
357
|
+
async for line in response.content:
|
|
358
|
+
line = line.decode("utf-8").strip()
|
|
359
|
+
if line.startswith("data:"):
|
|
360
|
+
try:
|
|
361
|
+
data = json.loads(line[5:].strip())
|
|
362
|
+
price_response = LazerPriceFeedResponse(**data)
|
|
363
|
+
callback(price_response)
|
|
364
|
+
except json.JSONDecodeError as e:
|
|
365
|
+
if self._on_error:
|
|
366
|
+
self._on_error(e)
|
|
367
|
+
except ValidationError as e:
|
|
368
|
+
if self._on_error:
|
|
369
|
+
self._on_error(e)
|
|
370
|
+
except aiohttp.ClientError as e:
|
|
371
|
+
self._lazer_connected = False
|
|
372
|
+
if self._on_error:
|
|
373
|
+
self._on_error(e)
|
|
374
|
+
else:
|
|
375
|
+
raise e
|
|
376
|
+
except Exception as e:
|
|
377
|
+
self._lazer_connected = False
|
|
378
|
+
if self._on_close:
|
|
379
|
+
self._on_close(e)
|
|
380
|
+
else:
|
|
381
|
+
raise e
|
|
@@ -187,3 +187,22 @@ class PairsCache:
|
|
|
187
187
|
"""
|
|
188
188
|
pairs_info = await self.get_pairs_info()
|
|
189
189
|
return pairs_info[pair_index].from_ + "/" + pairs_info[pair_index].to
|
|
190
|
+
|
|
191
|
+
async def get_lazer_feed_id(self, pair_index: int) -> int:
|
|
192
|
+
"""
|
|
193
|
+
Retrieves the Pyth Lazer feed ID for a pair.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
pair_index: The pair index.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
The Lazer feed ID as an integer.
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
ValueError: If the pair does not have a Lazer feed configured.
|
|
203
|
+
"""
|
|
204
|
+
pair_info = await self.get_pair_info_from_socket(pair_index)
|
|
205
|
+
lazer_feed = pair_info.get("lazerFeed")
|
|
206
|
+
if not lazer_feed:
|
|
207
|
+
raise ValueError(f"Pair {pair_index} does not have a Lazer feed configured")
|
|
208
|
+
return lazer_feed.get("feedId")
|
avantis_trader_sdk/rpc/trade.py
CHANGED
|
@@ -8,6 +8,7 @@ from ..types import (
|
|
|
8
8
|
TradeInfo,
|
|
9
9
|
PendingLimitOrderExtendedResponse,
|
|
10
10
|
MarginUpdateType,
|
|
11
|
+
PriceSourcing,
|
|
11
12
|
)
|
|
12
13
|
from typing import Optional, List, Tuple
|
|
13
14
|
import math
|
|
@@ -73,11 +74,15 @@ class TradeRPC:
|
|
|
73
74
|
trade_input_order_type == TradeInputOrderType.MARKET
|
|
74
75
|
or trade_input_order_type == TradeInputOrderType.MARKET_ZERO_FEE
|
|
75
76
|
) and not trade_input.openPrice:
|
|
76
|
-
|
|
77
|
+
lazer_feed_id = await self.client.pairs_cache.get_lazer_feed_id(
|
|
77
78
|
trade_input.pairIndex
|
|
78
79
|
)
|
|
79
|
-
price_data = await self.feed_client.
|
|
80
|
-
|
|
80
|
+
price_data = await self.feed_client.get_latest_lazer_price([lazer_feed_id])
|
|
81
|
+
price_feed = next(
|
|
82
|
+
(f for f in price_data.price_feeds if f.price_feed_id == lazer_feed_id),
|
|
83
|
+
price_data.price_feeds[0],
|
|
84
|
+
)
|
|
85
|
+
price = int(price_feed.converted_price * 10**10)
|
|
81
86
|
trade_input.openPrice = price
|
|
82
87
|
|
|
83
88
|
if (
|
|
@@ -136,11 +141,15 @@ class TradeRPC:
|
|
|
136
141
|
trade_input_order_type == TradeInputOrderType.MARKET
|
|
137
142
|
or trade_input_order_type == TradeInputOrderType.MARKET_ZERO_FEE
|
|
138
143
|
) and not trade_input.openPrice:
|
|
139
|
-
|
|
144
|
+
lazer_feed_id = await self.client.pairs_cache.get_lazer_feed_id(
|
|
140
145
|
trade_input.pairIndex
|
|
141
146
|
)
|
|
142
|
-
price_data = await self.feed_client.
|
|
143
|
-
|
|
147
|
+
price_data = await self.feed_client.get_latest_lazer_price([lazer_feed_id])
|
|
148
|
+
price_feed = next(
|
|
149
|
+
(f for f in price_data.price_feeds if f.price_feed_id == lazer_feed_id),
|
|
150
|
+
price_data.price_feeds[0],
|
|
151
|
+
)
|
|
152
|
+
price = int(price_feed.converted_price * 10**10)
|
|
144
153
|
trade_input.openPrice = price
|
|
145
154
|
|
|
146
155
|
if (
|
|
@@ -162,8 +171,6 @@ class TradeRPC:
|
|
|
162
171
|
}
|
|
163
172
|
)
|
|
164
173
|
|
|
165
|
-
print("transaction: ", trade_input.trader)
|
|
166
|
-
|
|
167
174
|
delegate_transaction = await Trading.functions.delegatedAction(
|
|
168
175
|
trade_input.trader, transaction["data"]
|
|
169
176
|
).build_transaction(
|
|
@@ -595,6 +602,7 @@ class TradeRPC:
|
|
|
595
602
|
margin_update_type: MarginUpdateType,
|
|
596
603
|
collateral_change: float,
|
|
597
604
|
trader: Optional[str] = None,
|
|
605
|
+
price_sourcing: PriceSourcing = PriceSourcing.PRO,
|
|
598
606
|
):
|
|
599
607
|
"""
|
|
600
608
|
Builds a transaction to update the margin of a trade.
|
|
@@ -605,6 +613,7 @@ class TradeRPC:
|
|
|
605
613
|
margin_update_type: The margin update type.
|
|
606
614
|
collateral_change: The collateral change.
|
|
607
615
|
trader (optional): The trader's wallet address.
|
|
616
|
+
price_sourcing: The price sourcing to use. Defaults to PriceSourcing.PRO (Pyth Pro/Lazer).
|
|
608
617
|
Returns:
|
|
609
618
|
A transaction object.
|
|
610
619
|
"""
|
|
@@ -615,11 +624,15 @@ class TradeRPC:
|
|
|
615
624
|
|
|
616
625
|
collateral_change = int(collateral_change * 10**6)
|
|
617
626
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
627
|
+
if price_sourcing == PriceSourcing.PRO:
|
|
628
|
+
price_data = await self.feed_client.get_price_update_data(pair_index)
|
|
629
|
+
price_update_data = price_data.pro.price_update_data
|
|
630
|
+
else:
|
|
631
|
+
pair_name = await self.client.pairs_cache.get_pair_name_from_index(
|
|
632
|
+
pair_index
|
|
633
|
+
)
|
|
634
|
+
price_data = await self.feed_client.get_latest_price_updates([pair_name])
|
|
635
|
+
price_update_data = "0x" + price_data.binary.data[0]
|
|
623
636
|
|
|
624
637
|
transaction = await Trading.functions.updateMargin(
|
|
625
638
|
pair_index,
|
|
@@ -627,6 +640,7 @@ class TradeRPC:
|
|
|
627
640
|
margin_update_type.value,
|
|
628
641
|
collateral_change,
|
|
629
642
|
[price_update_data],
|
|
643
|
+
price_sourcing.value,
|
|
630
644
|
).build_transaction(
|
|
631
645
|
{
|
|
632
646
|
"from": trader,
|
|
@@ -645,6 +659,7 @@ class TradeRPC:
|
|
|
645
659
|
margin_update_type: MarginUpdateType,
|
|
646
660
|
collateral_change: float,
|
|
647
661
|
trader: Optional[str] = None,
|
|
662
|
+
price_sourcing: PriceSourcing = PriceSourcing.PRO,
|
|
648
663
|
):
|
|
649
664
|
"""
|
|
650
665
|
Builds a transaction to update the margin of a trade.
|
|
@@ -655,6 +670,7 @@ class TradeRPC:
|
|
|
655
670
|
margin_update_type: The margin update type.
|
|
656
671
|
collateral_change: The collateral change.
|
|
657
672
|
trader (optional): The trader's wallet address.
|
|
673
|
+
price_sourcing: The price sourcing to use. Defaults to PriceSourcing.PRO (Pyth Pro/Lazer).
|
|
658
674
|
Returns:
|
|
659
675
|
A transaction object.
|
|
660
676
|
"""
|
|
@@ -665,11 +681,15 @@ class TradeRPC:
|
|
|
665
681
|
|
|
666
682
|
collateral_change = int(collateral_change * 10**6)
|
|
667
683
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
684
|
+
if price_sourcing == PriceSourcing.PRO:
|
|
685
|
+
price_data = await self.feed_client.get_price_update_data(pair_index)
|
|
686
|
+
price_update_data = price_data.pro.price_update_data
|
|
687
|
+
else:
|
|
688
|
+
pair_name = await self.client.pairs_cache.get_pair_name_from_index(
|
|
689
|
+
pair_index
|
|
690
|
+
)
|
|
691
|
+
price_data = await self.feed_client.get_latest_price_updates([pair_name])
|
|
692
|
+
price_update_data = "0x" + price_data.binary.data[0]
|
|
673
693
|
|
|
674
694
|
transaction = await Trading.functions.updateMargin(
|
|
675
695
|
pair_index,
|
|
@@ -677,6 +697,7 @@ class TradeRPC:
|
|
|
677
697
|
margin_update_type.value,
|
|
678
698
|
collateral_change,
|
|
679
699
|
[price_update_data],
|
|
700
|
+
price_sourcing.value,
|
|
680
701
|
).build_transaction(
|
|
681
702
|
{
|
|
682
703
|
"from": trader,
|
|
@@ -708,6 +729,7 @@ class TradeRPC:
|
|
|
708
729
|
take_profit_price: float,
|
|
709
730
|
stop_loss_price: float,
|
|
710
731
|
trader: str = None,
|
|
732
|
+
price_sourcing: PriceSourcing = PriceSourcing.PRO,
|
|
711
733
|
):
|
|
712
734
|
"""
|
|
713
735
|
Builds a transaction to update the stop loss and take profit of a trade.
|
|
@@ -718,6 +740,7 @@ class TradeRPC:
|
|
|
718
740
|
take_profit_price: The take profit price.
|
|
719
741
|
stop_loss_price: The stop loss price. Pass 0 if you want to remove the stop loss.
|
|
720
742
|
trader (optional): The trader's wallet address.
|
|
743
|
+
price_sourcing: The price sourcing to use. Defaults to PriceSourcing.PRO (Pyth Pro/Lazer).
|
|
721
744
|
Returns:
|
|
722
745
|
A transaction object.
|
|
723
746
|
"""
|
|
@@ -729,11 +752,15 @@ class TradeRPC:
|
|
|
729
752
|
if trader is None:
|
|
730
753
|
trader = self.client.get_signer().get_ethereum_address()
|
|
731
754
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
755
|
+
if price_sourcing == PriceSourcing.PRO:
|
|
756
|
+
price_data = await self.feed_client.get_price_update_data(pair_index)
|
|
757
|
+
price_update_data = price_data.pro.price_update_data
|
|
758
|
+
else:
|
|
759
|
+
pair_name = await self.client.pairs_cache.get_pair_name_from_index(
|
|
760
|
+
pair_index
|
|
761
|
+
)
|
|
762
|
+
price_data = await self.feed_client.get_latest_price_updates([pair_name])
|
|
763
|
+
price_update_data = "0x" + price_data.binary.data[0]
|
|
737
764
|
|
|
738
765
|
take_profit_price = int(take_profit_price * 10**10)
|
|
739
766
|
stop_loss_price = int(stop_loss_price * 10**10)
|
|
@@ -744,6 +771,7 @@ class TradeRPC:
|
|
|
744
771
|
stop_loss_price,
|
|
745
772
|
take_profit_price,
|
|
746
773
|
[price_update_data],
|
|
774
|
+
price_sourcing.value,
|
|
747
775
|
).build_transaction(
|
|
748
776
|
{
|
|
749
777
|
"from": trader,
|
|
@@ -763,6 +791,7 @@ class TradeRPC:
|
|
|
763
791
|
take_profit_price: float,
|
|
764
792
|
stop_loss_price: float,
|
|
765
793
|
trader: str = None,
|
|
794
|
+
price_sourcing: PriceSourcing = PriceSourcing.PRO,
|
|
766
795
|
):
|
|
767
796
|
"""
|
|
768
797
|
Builds a transaction to update the stop loss and take profit of a trade.
|
|
@@ -773,6 +802,7 @@ class TradeRPC:
|
|
|
773
802
|
take_profit_price: The take profit price.
|
|
774
803
|
stop_loss_price: The stop loss price. Pass 0 if you want to remove the stop loss.
|
|
775
804
|
trader (optional): The trader's wallet address.
|
|
805
|
+
price_sourcing: The price sourcing to use. Defaults to PriceSourcing.PRO (Pyth Pro/Lazer).
|
|
776
806
|
Returns:
|
|
777
807
|
A transaction object.
|
|
778
808
|
"""
|
|
@@ -784,11 +814,15 @@ class TradeRPC:
|
|
|
784
814
|
if trader is None:
|
|
785
815
|
trader = self.client.get_signer().get_ethereum_address()
|
|
786
816
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
817
|
+
if price_sourcing == PriceSourcing.PRO:
|
|
818
|
+
price_data = await self.feed_client.get_price_update_data(pair_index)
|
|
819
|
+
price_update_data = price_data.pro.price_update_data
|
|
820
|
+
else:
|
|
821
|
+
pair_name = await self.client.pairs_cache.get_pair_name_from_index(
|
|
822
|
+
pair_index
|
|
823
|
+
)
|
|
824
|
+
price_data = await self.feed_client.get_latest_price_updates([pair_name])
|
|
825
|
+
price_update_data = "0x" + price_data.binary.data[0]
|
|
792
826
|
|
|
793
827
|
take_profit_price = int(take_profit_price * 10**10)
|
|
794
828
|
stop_loss_price = int(stop_loss_price * 10**10)
|
|
@@ -799,6 +833,7 @@ class TradeRPC:
|
|
|
799
833
|
stop_loss_price,
|
|
800
834
|
take_profit_price,
|
|
801
835
|
[price_update_data],
|
|
836
|
+
price_sourcing.value,
|
|
802
837
|
).build_transaction(
|
|
803
838
|
{
|
|
804
839
|
"from": trader,
|
avantis_trader_sdk/types.py
CHANGED
|
@@ -487,6 +487,78 @@ class MarginUpdateType(Enum):
|
|
|
487
487
|
WITHDRAW = 1
|
|
488
488
|
|
|
489
489
|
|
|
490
|
+
class PriceSourcing(Enum):
|
|
491
|
+
"""Price sourcing options for contract calls."""
|
|
492
|
+
|
|
493
|
+
HERMES = 0 # Pyth Hermes (legacy)
|
|
494
|
+
PRO = 1 # Pyth Pro / Lazer
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
class LazerPriceFeed(BaseModel):
|
|
498
|
+
"""Single price feed from Pyth Lazer SSE stream."""
|
|
499
|
+
|
|
500
|
+
price_feed_id: int = Field(..., alias="priceFeedId")
|
|
501
|
+
price: str
|
|
502
|
+
best_bid_price: str = Field(..., alias="bestBidPrice")
|
|
503
|
+
best_ask_price: str = Field(..., alias="bestAskPrice")
|
|
504
|
+
publisher_count: int = Field(..., alias="publisherCount")
|
|
505
|
+
exponent: int
|
|
506
|
+
confidence: int
|
|
507
|
+
|
|
508
|
+
@property
|
|
509
|
+
def converted_price(self) -> float:
|
|
510
|
+
return int(self.price) / 10 ** -self.exponent
|
|
511
|
+
|
|
512
|
+
class Config:
|
|
513
|
+
populate_by_name = True
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class LazerPriceFeedResponse(BaseModel):
|
|
517
|
+
"""Response from Pyth Lazer SSE stream."""
|
|
518
|
+
|
|
519
|
+
timestamp_us: str = Field(..., alias="timestampUs")
|
|
520
|
+
price_feeds: List[LazerPriceFeed] = Field(..., alias="priceFeeds")
|
|
521
|
+
|
|
522
|
+
@property
|
|
523
|
+
def timestamp_ms(self) -> int:
|
|
524
|
+
return int(self.timestamp_us) // 1000
|
|
525
|
+
|
|
526
|
+
class Config:
|
|
527
|
+
populate_by_name = True
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
class FeedV3CorePriceData(BaseModel):
|
|
531
|
+
"""Core price data from feed-v3 API (Pyth Hermes)."""
|
|
532
|
+
|
|
533
|
+
price_update_data: str = Field(..., alias="priceUpdateData")
|
|
534
|
+
price: float
|
|
535
|
+
publish_timestamp_ms: int = Field(..., alias="publishTimestampMs")
|
|
536
|
+
|
|
537
|
+
class Config:
|
|
538
|
+
populate_by_name = True
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
class FeedV3ProPriceData(BaseModel):
|
|
542
|
+
"""Pro price data from feed-v3 API (Pyth Pro/Lazer)."""
|
|
543
|
+
|
|
544
|
+
price_update_data: str = Field(..., alias="priceUpdateData")
|
|
545
|
+
price: float
|
|
546
|
+
publish_timestamp_ms: int = Field(..., alias="publishTimestampMs")
|
|
547
|
+
|
|
548
|
+
class Config:
|
|
549
|
+
populate_by_name = True
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
class FeedV3PriceResponse(BaseModel):
|
|
553
|
+
"""Response from feed-v3 API containing both core and pro price data."""
|
|
554
|
+
|
|
555
|
+
core: FeedV3CorePriceData
|
|
556
|
+
pro: FeedV3ProPriceData
|
|
557
|
+
|
|
558
|
+
class Config:
|
|
559
|
+
populate_by_name = True
|
|
560
|
+
|
|
561
|
+
|
|
490
562
|
class LossProtectionInfo(BaseModel):
|
|
491
563
|
percentage: float
|
|
492
564
|
amount: float
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: avantis_trader_sdk
|
|
3
|
+
Version: 0.8.15
|
|
4
|
+
Summary: SDK for interacting with Avantis trading contracts.
|
|
5
|
+
Home-page: https://avantisfi.com/
|
|
6
|
+
Author: Avantis Labs
|
|
7
|
+
Author-email: yug@avantisfi.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: trading sdk blockchain ethereum web3 avantis
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.6
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: web3<7,>=6.15.1
|
|
16
|
+
Requires-Dist: pydantic<3,>=2.8.2
|
|
17
|
+
Requires-Dist: websockets<14,>=12.0
|
|
18
|
+
Requires-Dist: boto3<2,>=1.35.44
|
|
19
|
+
Requires-Dist: eth_account<0.14,>=0.10.0
|
|
20
|
+
Requires-Dist: toolz<1,>=0.12.1
|
|
21
|
+
Requires-Dist: eth_utils<5,>=2.1.0
|
|
22
|
+
Requires-Dist: pyasn1<1,>=0.6.1
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: description-content-type
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: keywords
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: requires-dist
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
Dynamic: summary
|
|
34
|
+
|
|
35
|
+
# Avantis Trader SDK
|
|
36
|
+
|
|
37
|
+
Python SDK for trading on [Avantis](https://avantisfi.com/) - a perpetual trading platform on Base.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install avantis-trader-sdk
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import asyncio
|
|
49
|
+
from avantis_trader_sdk import TraderClient
|
|
50
|
+
from avantis_trader_sdk.types import TradeInput, TradeInputOrderType
|
|
51
|
+
|
|
52
|
+
async def main():
|
|
53
|
+
# Initialize client
|
|
54
|
+
trader_client = TraderClient("https://mainnet.base.org")
|
|
55
|
+
trader_client.set_local_signer("0xYOUR_PRIVATE_KEY")
|
|
56
|
+
|
|
57
|
+
trader = trader_client.get_signer().get_ethereum_address()
|
|
58
|
+
|
|
59
|
+
# Check and approve USDC allowance
|
|
60
|
+
allowance = await trader_client.get_usdc_allowance_for_trading(trader)
|
|
61
|
+
if allowance < 100:
|
|
62
|
+
await trader_client.approve_usdc_for_trading(100)
|
|
63
|
+
|
|
64
|
+
# Open a 10x long ETH position with $100 collateral
|
|
65
|
+
trade_input = TradeInput(
|
|
66
|
+
trader=trader,
|
|
67
|
+
pair_index=1, # ETH/USD
|
|
68
|
+
collateral_in_trade=100,
|
|
69
|
+
is_long=True,
|
|
70
|
+
leverage=10,
|
|
71
|
+
tp=5000, # take profit
|
|
72
|
+
sl=2500, # stop loss
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
tx = await trader_client.trade.build_trade_open_tx(
|
|
76
|
+
trade_input, TradeInputOrderType.MARKET, slippage_percentage=1
|
|
77
|
+
)
|
|
78
|
+
receipt = await trader_client.sign_and_get_receipt(tx)
|
|
79
|
+
print("Trade opened!", receipt.transactionHash.hex())
|
|
80
|
+
|
|
81
|
+
asyncio.run(main())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Get Open Trades
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
trades, pending_orders = await trader_client.trade.get_trades(trader)
|
|
88
|
+
|
|
89
|
+
for trade in trades:
|
|
90
|
+
print(f"Pair: {trade.trade.pair_index}, Leverage: {trade.trade.leverage}x")
|
|
91
|
+
print(f"Entry: {trade.trade.open_price}, Liq: {trade.liquidation_price}")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Close a Trade
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
trade = trades[0]
|
|
98
|
+
close_tx = await trader_client.trade.build_trade_close_tx(
|
|
99
|
+
pair_index=trade.trade.pair_index,
|
|
100
|
+
trade_index=trade.trade.trade_index,
|
|
101
|
+
collateral_to_close=trade.trade.collateral_in_trade,
|
|
102
|
+
trader=trader,
|
|
103
|
+
)
|
|
104
|
+
await trader_client.sign_and_get_receipt(close_tx)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## AI-Assisted Development
|
|
108
|
+
|
|
109
|
+
Building with AI tools? We provide optimized documentation:
|
|
110
|
+
|
|
111
|
+
- [AGENT.md](./AGENT.md) - Comprehensive guide for AI agents. Copy to your project or paste into AI chat.
|
|
112
|
+
- [.cursorrules](./.cursorrules) - Auto-loaded by Cursor IDE.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
curl -o AGENT.md https://raw.githubusercontent.com/Avantis-Labs/avantis_trader_sdk/main/AGENT.md
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Resources
|
|
119
|
+
|
|
120
|
+
- [Documentation](https://sdk.avantisfi.com/)
|
|
121
|
+
- [Examples](https://github.com/Avantis-Labs/avantis_trader_sdk/tree/main/examples)
|
|
122
|
+
- [Avantis Docs](https://docs.avantisfi.com/)
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
avantis_trader_sdk/__init__.py,sha256=
|
|
1
|
+
avantis_trader_sdk/__init__.py,sha256=qpix2Zruoll9hF1RrY9b0Z2na5gVuibZalTuAHJX_04,168
|
|
2
2
|
avantis_trader_sdk/client.py,sha256=M01uwXIdxJjmPEcFr1MsDuoxU9YGddxcGMTPucOOssA,10903
|
|
3
|
-
avantis_trader_sdk/config.py,sha256=
|
|
4
|
-
avantis_trader_sdk/types.py,sha256=
|
|
3
|
+
avantis_trader_sdk/config.py,sha256=sE9y9R1ql1UwpFSs-qTbNfvO5iffRRUdYtx_JVOaWyA,838
|
|
4
|
+
avantis_trader_sdk/types.py,sha256=spp3n7UedgVM-w9JyCzQuqi8JiJmSjm24j4cxg0-gx8,17274
|
|
5
5
|
avantis_trader_sdk/utils.py,sha256=gTYgNVVd5rLZEUf2eyJftjKYxn55wRm09xAXIF_xjXM,2831
|
|
6
6
|
avantis_trader_sdk/abis/AggregatorV3Interface.json,sha256=qUeDGZ55Akgu8zOv_Wzf21sTREEyZXF47K_TdPcliBM,26244
|
|
7
7
|
avantis_trader_sdk/abis/Sanctions.json,sha256=Fsn67jEGW4GdS15icrtxN_sur4e8-06SoijL7K79vAE,3857
|
|
@@ -128,7 +128,7 @@ avantis_trader_sdk/abis/Timelock.t.sol/Timelock.json,sha256=N58EBp-LRYuIUvs5s0ze
|
|
|
128
128
|
avantis_trader_sdk/abis/TimelockBase.t.sol/TimelockBase.json,sha256=Bx-U5If_X5Dd49-GBDQTst3gDp8hG1YptPxoDyw3auw,1111293
|
|
129
129
|
avantis_trader_sdk/abis/TimelockController.sol/TimelockController.json,sha256=k6uk3eErrOFWCIaSBno0aqziOi744NEdxnK8vG-ohrA,333379
|
|
130
130
|
avantis_trader_sdk/abis/TradeBase.t.sol/TradeBase.json,sha256=w3sGtHzPkd45TLRhRxQzlUPmwYfjay7_4rU6CyuFu9M,1328584
|
|
131
|
-
avantis_trader_sdk/abis/Trading.sol/Trading.json,sha256=
|
|
131
|
+
avantis_trader_sdk/abis/Trading.sol/Trading.json,sha256=IM8Uv4SSXq23L8W-eJSzPtmz_h10WaRWao5ks0uiOLo,41789
|
|
132
132
|
avantis_trader_sdk/abis/TradingCallbacks.sol/TradingCallbacks.json,sha256=m_tO-wHtc5hXMky0wbLj6lqFt12SgtjlBQZQnmShECU,199077
|
|
133
133
|
avantis_trader_sdk/abis/TradingStorage.sol/TradingStorage.json,sha256=ImaFCa_a4htBOw99ITGzfuWlUeN3CvIROv0f4KH-hqw,313012
|
|
134
134
|
avantis_trader_sdk/abis/Tranche.sol/Tranche.json,sha256=AZX5KxzyXI6ao93UrZsqQE9FpNZAArOqReJX0OzoT2E,392082
|
|
@@ -195,16 +195,16 @@ avantis_trader_sdk/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
195
195
|
avantis_trader_sdk/crypto/spki.py,sha256=CNy7A8TTwBHiNSzIj7uqiHKAeLcn1Q9MbszW_2mdXgI,3080
|
|
196
196
|
avantis_trader_sdk/feed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
197
197
|
avantis_trader_sdk/feed/feedIds.json,sha256=T77nww3eRiQt8rqZBDpdxA49USGyfI0dQBPnzo-H1dE,6697
|
|
198
|
-
avantis_trader_sdk/feed/feed_client.py,sha256=
|
|
198
|
+
avantis_trader_sdk/feed/feed_client.py,sha256=fwdmS_9b6N-CAbmu2Lq_26uKahMLsYKNglg6XF8xZHQ,13755
|
|
199
199
|
avantis_trader_sdk/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
200
200
|
avantis_trader_sdk/rpc/asset_parameters.py,sha256=ESx4eg0K3W9EigsEmN0a7Pym2hL4iJgMWmzhHmmWXyY,19323
|
|
201
201
|
avantis_trader_sdk/rpc/blended.py,sha256=tRWfO7XreY_YahL9ACpss7ylWz5fdk6G3bv4QpLpGms,2441
|
|
202
202
|
avantis_trader_sdk/rpc/category_parameters.py,sha256=ofsKct23E8DThCKqP627ol-_YPIdN5HAn09eLqyf6WM,7965
|
|
203
203
|
avantis_trader_sdk/rpc/fee_parameters.py,sha256=0UCf4FZQp26777o8aA75oOO-b3xFK88c-_glbqQ2-8M,9132
|
|
204
|
-
avantis_trader_sdk/rpc/pairs_cache.py,sha256=
|
|
204
|
+
avantis_trader_sdk/rpc/pairs_cache.py,sha256=wtxkvaemVbJclCSiOcyelbBcQzwJbIOP0JF0rfyE56M,7234
|
|
205
205
|
avantis_trader_sdk/rpc/rpc_helpers.py,sha256=Sywz6BIj4y2gkudkOhPEND2r2ILvtfq502A_pSEUDv8,284
|
|
206
206
|
avantis_trader_sdk/rpc/snapshot.py,sha256=hfLRfCbOqnqcuZncaiTmm0BJ2pgLFOEHgsgQ-92Xlcs,5352
|
|
207
|
-
avantis_trader_sdk/rpc/trade.py,sha256=
|
|
207
|
+
avantis_trader_sdk/rpc/trade.py,sha256=EB9JEAih2s0y3h2Y1VnYboc7-gQQe-aQa3RBoseAiwk,32220
|
|
208
208
|
avantis_trader_sdk/rpc/trading_parameters.py,sha256=_tpzgyMO_I-XebVWtiWSlmedtbr66elUxCRgegd1aao,4626
|
|
209
209
|
avantis_trader_sdk/signers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
210
210
|
avantis_trader_sdk/signers/base.py,sha256=QaOu0CxFq60oR4LegCp1XwONMQx8ZShXyiLZvfcbCPM,260
|
|
@@ -212,7 +212,7 @@ avantis_trader_sdk/signers/kms_signer.py,sha256=lxK3f9KQsdCDAvOE1SHleKjI8zD_3PTv
|
|
|
212
212
|
avantis_trader_sdk/signers/local_signer.py,sha256=kUx5vExiBfvFGmoMCFR6b7_4cXx2mvYOJNqZQDIEcG8,505
|
|
213
213
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
214
214
|
tests/test_client.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
215
|
-
avantis_trader_sdk-0.8.
|
|
216
|
-
avantis_trader_sdk-0.8.
|
|
217
|
-
avantis_trader_sdk-0.8.
|
|
218
|
-
avantis_trader_sdk-0.8.
|
|
215
|
+
avantis_trader_sdk-0.8.15.dist-info/METADATA,sha256=1XhW8Ak1oBkrDgb2UBASGfWIV2et7Pnt6tAliqbnUUo,3514
|
|
216
|
+
avantis_trader_sdk-0.8.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
217
|
+
avantis_trader_sdk-0.8.15.dist-info/top_level.txt,sha256=XffaQJ68SGT1KUz2HHXSGSEsmNy8-AGjgtO127xhzQA,25
|
|
218
|
+
avantis_trader_sdk-0.8.15.dist-info/RECORD,,
|