avantis-trader-sdk 0.8.2__py3-none-any.whl → 0.8.3__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.
Files changed (39) hide show
  1. avantis_trader_sdk/__init__.py +5 -5
  2. avantis_trader_sdk/abis/AggregatorV3Interface.json +606 -606
  3. avantis_trader_sdk/abis/IPyth.sol/IPyth.dbg.json +4 -4
  4. avantis_trader_sdk/abis/Referral.sol/ReferralStorage.json +7132 -7132
  5. avantis_trader_sdk/abis/Sanctions.json +190 -190
  6. avantis_trader_sdk/abis/USDC.sol/USDC.dbg.json +4 -4
  7. avantis_trader_sdk/abis/interfaces/ICallbacks.sol/ICallbacks.json +2637 -2637
  8. avantis_trader_sdk/abis/interfaces/IExecute.sol/IExecute.json +1628 -1628
  9. avantis_trader_sdk/abis/interfaces/IPairInfos.sol/IPairInfos.json +2781 -2781
  10. avantis_trader_sdk/abis/interfaces/IPairStorage.sol/IPairStorage.json +3729 -3729
  11. avantis_trader_sdk/abis/interfaces/IPriceAggregator.sol/IPriceAggregator.json +2330 -2330
  12. avantis_trader_sdk/abis/interfaces/IReferral.sol/IReferral.json +1890 -1890
  13. avantis_trader_sdk/abis/interfaces/ITradingStorage.sol/ITradingStorage.json +7022 -7022
  14. avantis_trader_sdk/abis/interfaces/ITranche.sol/ITranche.json +1283 -1283
  15. avantis_trader_sdk/abis/interfaces/IVaultManager.sol/IVaultManager.json +2424 -2424
  16. avantis_trader_sdk/abis/interfaces/IVeTranche.sol/IVeTranche.json +855 -855
  17. avantis_trader_sdk/abis/library/PositionMath.sol/PositionMath.dbg.json +4 -4
  18. avantis_trader_sdk/abis/library/PositionMath.sol/PositionMath.json +10 -10
  19. avantis_trader_sdk/abis/testnet/USDC.sol/USDC.dbg.json +4 -4
  20. avantis_trader_sdk/abis/testnet/USDC.sol/USDC.json +320 -320
  21. avantis_trader_sdk/client.py +369 -367
  22. avantis_trader_sdk/config.py +14 -14
  23. avantis_trader_sdk/feed/feed_client.py +263 -261
  24. avantis_trader_sdk/rpc/asset_parameters.py +499 -499
  25. avantis_trader_sdk/rpc/blended.py +71 -71
  26. avantis_trader_sdk/rpc/category_parameters.py +216 -216
  27. avantis_trader_sdk/rpc/fee_parameters.py +237 -237
  28. avantis_trader_sdk/rpc/pairs_cache.py +130 -130
  29. avantis_trader_sdk/rpc/rpc_helpers.py +8 -8
  30. avantis_trader_sdk/rpc/snapshot.py +142 -142
  31. avantis_trader_sdk/rpc/trade.py +701 -710
  32. avantis_trader_sdk/rpc/trading_parameters.py +139 -139
  33. avantis_trader_sdk/types.py +462 -462
  34. avantis_trader_sdk/utils.py +78 -78
  35. {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/METADATA +124 -113
  36. {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/RECORD +38 -39
  37. {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/WHEEL +1 -1
  38. avantis_trader_sdk/feed/feedIds.json +0 -214
  39. {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/top_level.txt +0 -0
@@ -1,462 +1,462 @@
1
- from pydantic import (
2
- BaseModel,
3
- Field,
4
- conint,
5
- field_validator,
6
- ValidationError,
7
- model_validator,
8
- )
9
- from enum import Enum
10
- from typing import Dict, Optional, List, Union
11
- import time
12
- import re
13
-
14
-
15
- class PairInfoFeed(BaseModel):
16
- max_open_deviation_percentage: float = Field(..., alias="maxOpenDeviationP")
17
- max_close_deviation_percentage: float = Field(..., alias="maxCloseDeviationP")
18
-
19
- feed_id: str = Field(..., alias="feedId")
20
-
21
- @field_validator(
22
- "max_open_deviation_percentage", "max_close_deviation_percentage", mode="before"
23
- )
24
- def convert_max_deviation(cls, v):
25
- return v / 10**10
26
-
27
- class Config:
28
- populate_by_name = True
29
-
30
-
31
- class PairInfoBackupFeed(BaseModel):
32
- max_deviation_percentage: float = Field(..., alias="maxDeviationP")
33
- feed_id: str = Field(..., alias="feedId")
34
-
35
- @field_validator("max_deviation_percentage", mode="before")
36
- def convert_max_deviation(cls, v):
37
- return v / 10**10
38
-
39
- class Config:
40
- populate_by_name = True
41
-
42
-
43
- class PairInfoLeverages(BaseModel):
44
- min_leverage: float = Field(..., alias="minLeverage")
45
- max_leverage: float = Field(..., alias="maxLeverage")
46
- pnl_min_leverage: float = Field(..., alias="pnlMinLeverage")
47
- pnl_max_leverage: float = Field(..., alias="pnlMaxLeverage")
48
-
49
- @field_validator(
50
- "min_leverage",
51
- "max_leverage",
52
- "pnl_min_leverage",
53
- "pnl_max_leverage",
54
- mode="before",
55
- )
56
- def convert_max_deviation(cls, v):
57
- return v / 10**10
58
-
59
-
60
- class PairInfoValues(BaseModel):
61
- max_gain_percentage: float = Field(..., alias="maxGainP")
62
- max_sl_percentage: float = Field(..., alias="maxSlP")
63
- max_long_oi_percentage: float = Field(..., alias="maxLongOiP")
64
- max_short_oi_percentage: float = Field(..., alias="maxShortOiP")
65
- group_open_interest_percentage: float = Field(
66
- ..., alias="groupOpenInterestPecentage"
67
- )
68
- max_wallet_oi_percentage: float = Field(..., alias="maxWalletOI")
69
- is_usdc_aligned: bool = Field(..., alias="isUSDCAligned")
70
-
71
-
72
- class PairInfo(BaseModel):
73
- feed: PairInfoFeed
74
- backup_feed: PairInfoBackupFeed = Field(..., alias="backupFeed")
75
- constant_spread_bps: float = Field(..., alias="spreadP")
76
- constant_pnl_spread_bps: float = Field(..., alias="pnlSpreadP")
77
- leverages: PairInfoLeverages = Field(..., alias="leverages")
78
- price_impact_parameter: float = Field(..., alias="priceImpactMultiplier")
79
- skew_impact_parameter: float = Field(..., alias="skewImpactMultiplier")
80
- group_index: int = Field(..., alias="groupIndex")
81
- fee_index: int = Field(..., alias="feeIndex")
82
- values: PairInfoValues = Field(..., alias="values")
83
-
84
- @field_validator("price_impact_parameter", "skew_impact_parameter", mode="before")
85
- def convert_to_float_10(cls, v):
86
- return v / 10**10
87
-
88
- @field_validator("constant_spread_bps", "constant_pnl_spread_bps", mode="before")
89
- def convert_to_float_10_bps(cls, v):
90
- return v / 10**10 * 100
91
-
92
- class Config:
93
- populate_by_name = True
94
-
95
-
96
- class PairData(BaseModel):
97
- from_: str = Field(..., alias="from")
98
- to: str
99
- num_tiers: int = Field(..., alias="numTiers")
100
- tier_thresholds: Dict[str, float] = Field(..., alias="tierThresholds")
101
- tier_timers: Dict[str, float] = Field(..., alias="timer")
102
-
103
- @field_validator("tier_thresholds", "tier_timers", mode="before")
104
- def convert_tuple_to_dict(cls, v):
105
- if isinstance(v, tuple):
106
- return {str(i): j for i, j in enumerate(v)}
107
- return v
108
-
109
-
110
- class PairInfoWithData(PairInfo, PairData):
111
- class Config:
112
- populate_by_name = True
113
- from_attributes = True
114
-
115
-
116
- class PairInfoFeed(BaseModel):
117
- from_: str = Field(..., alias="from")
118
- to: str
119
- feed: PairInfoFeed
120
- backup_feed: PairInfoBackupFeed = Field(..., alias="backupFeed")
121
-
122
- class Config:
123
- populate_by_name = True
124
-
125
-
126
- class OpenInterest(BaseModel):
127
- long: Dict[str, float]
128
-
129
- short: Dict[str, float]
130
-
131
-
132
- class OpenInterestLimits(BaseModel):
133
- limits: Dict[str, float]
134
-
135
-
136
- class Utilization(BaseModel):
137
- utilization: Dict[str, float]
138
-
139
-
140
- class Skew(BaseModel):
141
- skew: Dict[str, float]
142
-
143
-
144
- class MarginFee(BaseModel):
145
- hourly_base_fee_parameter: Dict[str, float]
146
- hourly_margin_fee_long_bps: Dict[str, float]
147
- hourly_margin_fee_short_bps: Dict[str, float]
148
-
149
-
150
- class MarginFeeSingle(BaseModel):
151
- hourly_base_fee_parameter: float
152
- hourly_margin_fee_long_bps: float
153
- hourly_margin_fee_short_bps: float
154
-
155
-
156
- class DepthSingle(BaseModel):
157
- above: float
158
- below: float
159
-
160
-
161
- class PairInfoExtended(PairInfoWithData):
162
- asset_open_interest_limit: float
163
- asset_open_interest: Dict[str, float]
164
- asset_utilization: float
165
- asset_skew: float
166
- blended_utilization: float
167
- blended_skew: float
168
- margin_fee: MarginFeeSingle
169
- one_percent_depth: DepthSingle
170
- new_1k_long_opening_fee_bps: float # Opening fee for new $1,000 long position
171
- new_1k_short_opening_fee_bps: float # Opening fee for new $1,000 short position
172
- new_1k_long_opening_spread_bps: float # Opening spread for new $1,000 long position
173
- new_1k_short_opening_spread_bps: (
174
- float # Opening spread for new $1,000 short position
175
- )
176
- price_impact_spread_long_bps: float
177
- price_impact_spread_short_bps: float
178
- skew_impact_spread_long_bps: float
179
- skew_impact_spread_short_bps: float
180
-
181
- class Config:
182
- populate_by_name = True
183
-
184
-
185
- class PairSpread(BaseModel):
186
- spread: Dict[str, float]
187
-
188
-
189
- class PriceFeedResponse(BaseModel):
190
- id: str
191
- price: Optional[Union[Dict[str, str], Dict[str, float]]] = None
192
- ema_price: Optional[Union[Dict[str, str], Dict[str, float]]] = None
193
- pair: Optional[str] = None
194
- metadata: Optional[Union[Dict[str, str], Dict[str, float]]] = None
195
- converted_price: float = 0.0
196
- converted_ema_price: float = 0.0
197
-
198
- @model_validator(mode="before")
199
- def convert_price(cls, values):
200
- price_info = values.get("price")
201
- if price_info:
202
- values["converted_price"] = float(price_info["price"]) / 10 ** -int(
203
- price_info["expo"]
204
- )
205
-
206
- ema_price_info = values.get("ema_price")
207
- if ema_price_info:
208
- values["converted_ema_price"] = float(ema_price_info["price"]) / 10 ** -int(
209
- ema_price_info["expo"]
210
- )
211
-
212
- return values
213
-
214
-
215
- class PriceFeesUpdateBinary(BaseModel):
216
- encoding: str
217
- data: List[str]
218
-
219
-
220
- class PriceFeedUpdatesResponse(BaseModel):
221
- binary: PriceFeesUpdateBinary
222
- parsed: List[PriceFeedResponse]
223
-
224
-
225
- class Spread(BaseModel):
226
- long: Optional[Dict[str, float]] = None
227
- short: Optional[Dict[str, float]] = None
228
-
229
- @model_validator(mode="before")
230
- def check_at_least_one(cls, values):
231
- if not values.get("long") and not values.get("short"):
232
- raise ValueError('At least one of "long" or "short" must be present.')
233
- return values
234
-
235
-
236
- class Depth(BaseModel):
237
- above: Optional[Dict[str, float]] = None
238
- below: Optional[Dict[str, float]] = None
239
-
240
- @model_validator(mode="before")
241
- def check_at_least_one(cls, values):
242
- if not values.get("above") and not values.get("below"):
243
- raise ValueError('At least one of "above" or "below" must be present.')
244
- return values
245
-
246
-
247
- class Fee(BaseModel):
248
- long: Optional[Dict[str, float]] = None
249
- short: Optional[Dict[str, float]] = None
250
-
251
- @model_validator(mode="before")
252
- def check_at_least_one(cls, values):
253
- if not values.get("long") and not values.get("short"):
254
- raise ValueError('At least one of "long" or "short" must be present.')
255
- return values
256
-
257
-
258
- class TradeInput(BaseModel):
259
- trader: str = "0x1234567890123456789012345678901234567890"
260
- pairIndex: int = Field(..., alias="pair_index")
261
- index: int = Field(0, alias="trade_index")
262
- initialPosToken: Optional[int] = Field(None, alias="open_collateral")
263
- positionSizeUSDC: Optional[int] = Field(None, alias="collateral_in_trade")
264
- openPrice: int = Field(0, alias="open_price")
265
- buy: bool = Field(..., alias="is_long")
266
- leverage: int
267
- tp: Optional[int] = 0
268
- sl: Optional[int] = 0
269
- timestamp: Optional[int] = 0
270
-
271
- @field_validator("trader")
272
- def validate_eth_address(cls, v):
273
- if not re.match(r"^0x[a-fA-F0-9]{40}$", v):
274
- raise ValueError("Invalid Ethereum address")
275
- return v
276
-
277
- @field_validator("tp")
278
- def validate_tp(cls, v, values):
279
- if values.data.get("openPrice") not in [0, None] and v in [0, None]:
280
- raise ValueError("tp is required when openPrice is provided and is not 0")
281
- return v
282
-
283
- @field_validator("openPrice", "tp", "sl", "leverage", mode="before")
284
- def convert_to_float_10(cls, v):
285
- if v is None:
286
- return 0
287
- return int(v * 10**10)
288
-
289
- @model_validator(mode="before")
290
- def assign_and_validate_collateral_and_position_size_usdc(cls, values):
291
- collateral_in_trade = values.pop("position_size_usdc", None)
292
- if collateral_in_trade is not None:
293
- values["positionSizeUSDC"] = collateral_in_trade
294
-
295
- initialPosToken = values.get("initialPosToken") or values.get("open_collateral")
296
- positionSizeUSDC = values.get("positionSizeUSDC") or values.get(
297
- "collateral_in_trade"
298
- )
299
-
300
- if initialPosToken is None and positionSizeUSDC is None:
301
- raise ValueError(
302
- "Either 'open_collateral' or 'collateral_in_trade' must be provided."
303
- )
304
-
305
- if initialPosToken is not None and positionSizeUSDC is None:
306
- values["positionSizeUSDC"] = 0
307
- elif positionSizeUSDC is not None and initialPosToken is None:
308
- values["initialPosToken"] = 0
309
-
310
- return values
311
-
312
- @field_validator("initialPosToken", "positionSizeUSDC", mode="before")
313
- def convert_to_float_6(cls, v):
314
- return int(v * 10**6)
315
-
316
- @field_validator("timestamp", mode="before")
317
- def set_default_timestamp(cls, v):
318
- if v is None:
319
- return int(time.time())
320
- return v
321
-
322
- class Config:
323
- populate_by_name = True
324
-
325
-
326
- class TradeInputOrderType(Enum):
327
- MARKET = 0
328
- STOP_LIMIT = 1
329
- LIMIT = 2
330
- MARKET_ZERO_FEE = 3
331
-
332
-
333
- class TradeResponse(BaseModel):
334
- trader: str
335
- pair_index: int = Field(..., alias="pairIndex")
336
- trade_index: int = Field(0, alias="index")
337
- open_collateral: float = Field(None, alias="initialPosToken")
338
- collateral_in_trade: float = Field(None, alias="positionSizeUSDC")
339
- open_price: float = Field(0, alias="openPrice")
340
- is_long: bool = Field(..., alias="buy")
341
- leverage: float
342
- tp: float
343
- sl: float
344
- timestamp: int
345
-
346
- @field_validator("trader")
347
- def validate_eth_address(cls, v):
348
- if not re.match(r"^0x[a-fA-F0-9]{40}$", v):
349
- raise ValueError("Invalid Ethereum address")
350
- return v
351
-
352
- @field_validator("open_price", "tp", "sl", "leverage", mode="before")
353
- def convert_to_float_10(cls, v):
354
- return v / 10**10
355
-
356
- @field_validator("open_collateral", "collateral_in_trade", mode="before")
357
- def convert_to_float_6(cls, v):
358
- return v / 10**6
359
-
360
- class Config:
361
- populate_by_name = True
362
-
363
-
364
- class TradeInfo(BaseModel):
365
- open_interest_usdc: float = Field(..., alias="openInterestUSDC")
366
- tp_last_updated: float = Field(..., alias="tpLastUpdated")
367
- sl_last_updated: float = Field(..., alias="slLastUpdated")
368
- being_market_closed: bool = Field(..., alias="beingMarketClosed")
369
- loss_protection_percentage: float = Field(..., alias="lossProtectionPercentage")
370
-
371
- @field_validator("open_interest_usdc", mode="before")
372
- def convert_to_float_6(cls, v):
373
- return v / 10**6
374
-
375
- class Config:
376
- populate_by_name = True
377
-
378
-
379
- class TradeExtendedResponse(BaseModel):
380
- trade: TradeResponse
381
- additional_info: TradeInfo
382
- margin_fee: float
383
- liquidation_price: float
384
-
385
- @field_validator("margin_fee", mode="before")
386
- def convert_to_float_6(cls, v):
387
- return v / 10**6
388
-
389
- @field_validator("liquidation_price", mode="before")
390
- def convert_to_float_10(cls, v):
391
- return v / 10**10
392
-
393
- class Config:
394
- populate_by_name = True
395
-
396
-
397
- class PendingLimitOrderResponse(BaseModel):
398
- trader: str
399
- pair_index: int = Field(..., alias="pairIndex")
400
- trade_index: int = Field(0, alias="index")
401
- open_collateral: float = Field(..., alias="positionSize")
402
- buy: bool
403
- leverage: int
404
- tp: float
405
- sl: float
406
- price: float
407
- slippage_percentage: float = Field(..., alias="slippageP")
408
- block: int
409
-
410
- @field_validator("trader")
411
- def validate_eth_address(cls, v):
412
- if not re.match(r"^0x[a-fA-F0-9]{40}$", v):
413
- raise ValueError("Invalid Ethereum address")
414
- return v
415
-
416
- @field_validator(
417
- "price", "tp", "sl", "leverage", "slippage_percentage", mode="before"
418
- )
419
- def convert_to_float_10(cls, v):
420
- return v / 10**10
421
-
422
- @field_validator("open_collateral", mode="before")
423
- def convert_to_float_6(cls, v):
424
- return v / 10**6
425
-
426
- class Config:
427
- populate_by_name = True
428
-
429
-
430
- class PendingLimitOrderExtendedResponse(PendingLimitOrderResponse):
431
- liquidation_price: float
432
-
433
- @field_validator("liquidation_price", mode="before")
434
- def convert_liq_to_float_10(cls, v):
435
- return v / 10**10
436
-
437
-
438
- class MarginUpdateType(Enum):
439
- DEPOSIT = 0
440
- WITHDRAW = 1
441
-
442
-
443
- class LossProtectionInfo(BaseModel):
444
- percentage: float
445
- amount: float
446
-
447
-
448
- class SnapshotOpenInterest(BaseModel):
449
- long: float
450
- short: float
451
-
452
-
453
- class SnapshotGroup(BaseModel):
454
- group_open_interest_limit: float
455
- group_open_interest: SnapshotOpenInterest
456
- group_utilization: float
457
- group_skew: float
458
- pairs: Dict[str, PairInfoExtended]
459
-
460
-
461
- class Snapshot(BaseModel):
462
- groups: Dict[conint(ge=0), SnapshotGroup]
1
+ from pydantic import (
2
+ BaseModel,
3
+ Field,
4
+ conint,
5
+ field_validator,
6
+ ValidationError,
7
+ model_validator,
8
+ )
9
+ from enum import Enum
10
+ from typing import Dict, Optional, List, Union
11
+ import time
12
+ import re
13
+
14
+
15
+ class PairInfoFeed(BaseModel):
16
+ max_open_deviation_percentage: float = Field(..., alias="maxOpenDeviationP")
17
+ max_close_deviation_percentage: float = Field(..., alias="maxCloseDeviationP")
18
+
19
+ feed_id: str = Field(..., alias="feedId")
20
+
21
+ @field_validator(
22
+ "max_open_deviation_percentage", "max_close_deviation_percentage", mode="before"
23
+ )
24
+ def convert_max_deviation(cls, v):
25
+ return v / 10**10
26
+
27
+ class Config:
28
+ populate_by_name = True
29
+
30
+
31
+ class PairInfoBackupFeed(BaseModel):
32
+ max_deviation_percentage: float = Field(..., alias="maxDeviationP")
33
+ feed_id: str = Field(..., alias="feedId")
34
+
35
+ @field_validator("max_deviation_percentage", mode="before")
36
+ def convert_max_deviation(cls, v):
37
+ return v / 10**10
38
+
39
+ class Config:
40
+ populate_by_name = True
41
+
42
+
43
+ class PairInfoLeverages(BaseModel):
44
+ min_leverage: float = Field(..., alias="minLeverage")
45
+ max_leverage: float = Field(..., alias="maxLeverage")
46
+ pnl_min_leverage: float = Field(..., alias="pnlMinLeverage")
47
+ pnl_max_leverage: float = Field(..., alias="pnlMaxLeverage")
48
+
49
+ @field_validator(
50
+ "min_leverage",
51
+ "max_leverage",
52
+ "pnl_min_leverage",
53
+ "pnl_max_leverage",
54
+ mode="before",
55
+ )
56
+ def convert_max_deviation(cls, v):
57
+ return v / 10**10
58
+
59
+
60
+ class PairInfoValues(BaseModel):
61
+ max_gain_percentage: float = Field(..., alias="maxGainP")
62
+ max_sl_percentage: float = Field(..., alias="maxSlP")
63
+ max_long_oi_percentage: float = Field(..., alias="maxLongOiP")
64
+ max_short_oi_percentage: float = Field(..., alias="maxShortOiP")
65
+ group_open_interest_percentage: float = Field(
66
+ ..., alias="groupOpenInterestPecentage"
67
+ )
68
+ max_wallet_oi_percentage: float = Field(..., alias="maxWalletOI")
69
+ is_usdc_aligned: bool = Field(..., alias="isUSDCAligned")
70
+
71
+
72
+ class PairInfo(BaseModel):
73
+ feed: PairInfoFeed
74
+ backup_feed: PairInfoBackupFeed = Field(..., alias="backupFeed")
75
+ constant_spread_bps: float = Field(..., alias="spreadP")
76
+ constant_pnl_spread_bps: float = Field(..., alias="pnlSpreadP")
77
+ leverages: PairInfoLeverages = Field(..., alias="leverages")
78
+ price_impact_parameter: float = Field(..., alias="priceImpactMultiplier")
79
+ skew_impact_parameter: float = Field(..., alias="skewImpactMultiplier")
80
+ group_index: int = Field(..., alias="groupIndex")
81
+ fee_index: int = Field(..., alias="feeIndex")
82
+ values: PairInfoValues = Field(..., alias="values")
83
+
84
+ @field_validator("price_impact_parameter", "skew_impact_parameter", mode="before")
85
+ def convert_to_float_10(cls, v):
86
+ return v / 10**10
87
+
88
+ @field_validator("constant_spread_bps", "constant_pnl_spread_bps", mode="before")
89
+ def convert_to_float_10_bps(cls, v):
90
+ return v / 10**10 * 100
91
+
92
+ class Config:
93
+ populate_by_name = True
94
+
95
+
96
+ class PairData(BaseModel):
97
+ from_: str = Field(..., alias="from")
98
+ to: str
99
+ num_tiers: int = Field(..., alias="numTiers")
100
+ tier_thresholds: Dict[str, float] = Field(..., alias="tierThresholds")
101
+ tier_timers: Dict[str, float] = Field(..., alias="timer")
102
+
103
+ @field_validator("tier_thresholds", "tier_timers", mode="before")
104
+ def convert_tuple_to_dict(cls, v):
105
+ if isinstance(v, tuple):
106
+ return {str(i): j for i, j in enumerate(v)}
107
+ return v
108
+
109
+
110
+ class PairInfoWithData(PairInfo, PairData):
111
+ class Config:
112
+ populate_by_name = True
113
+ from_attributes = True
114
+
115
+
116
+ class PairInfoFeed(BaseModel):
117
+ from_: str = Field(..., alias="from")
118
+ to: str
119
+ feed: PairInfoFeed
120
+ backup_feed: PairInfoBackupFeed = Field(..., alias="backupFeed")
121
+
122
+ class Config:
123
+ populate_by_name = True
124
+
125
+
126
+ class OpenInterest(BaseModel):
127
+ long: Dict[str, float]
128
+
129
+ short: Dict[str, float]
130
+
131
+
132
+ class OpenInterestLimits(BaseModel):
133
+ limits: Dict[str, float]
134
+
135
+
136
+ class Utilization(BaseModel):
137
+ utilization: Dict[str, float]
138
+
139
+
140
+ class Skew(BaseModel):
141
+ skew: Dict[str, float]
142
+
143
+
144
+ class MarginFee(BaseModel):
145
+ hourly_base_fee_parameter: Dict[str, float]
146
+ hourly_margin_fee_long_bps: Dict[str, float]
147
+ hourly_margin_fee_short_bps: Dict[str, float]
148
+
149
+
150
+ class MarginFeeSingle(BaseModel):
151
+ hourly_base_fee_parameter: float
152
+ hourly_margin_fee_long_bps: float
153
+ hourly_margin_fee_short_bps: float
154
+
155
+
156
+ class DepthSingle(BaseModel):
157
+ above: float
158
+ below: float
159
+
160
+
161
+ class PairInfoExtended(PairInfoWithData):
162
+ asset_open_interest_limit: float
163
+ asset_open_interest: Dict[str, float]
164
+ asset_utilization: float
165
+ asset_skew: float
166
+ blended_utilization: float
167
+ blended_skew: float
168
+ margin_fee: MarginFeeSingle
169
+ one_percent_depth: DepthSingle
170
+ new_1k_long_opening_fee_bps: float # Opening fee for new $1,000 long position
171
+ new_1k_short_opening_fee_bps: float # Opening fee for new $1,000 short position
172
+ new_1k_long_opening_spread_bps: float # Opening spread for new $1,000 long position
173
+ new_1k_short_opening_spread_bps: (
174
+ float # Opening spread for new $1,000 short position
175
+ )
176
+ price_impact_spread_long_bps: float
177
+ price_impact_spread_short_bps: float
178
+ skew_impact_spread_long_bps: float
179
+ skew_impact_spread_short_bps: float
180
+
181
+ class Config:
182
+ populate_by_name = True
183
+
184
+
185
+ class PairSpread(BaseModel):
186
+ spread: Dict[str, float]
187
+
188
+
189
+ class PriceFeedResponse(BaseModel):
190
+ id: str
191
+ price: Optional[Union[Dict[str, str], Dict[str, float]]] = None
192
+ ema_price: Optional[Union[Dict[str, str], Dict[str, float]]] = None
193
+ pair: Optional[str] = None
194
+ metadata: Optional[Union[Dict[str, str], Dict[str, float]]] = None
195
+ converted_price: float = 0.0
196
+ converted_ema_price: float = 0.0
197
+
198
+ @model_validator(mode="before")
199
+ def convert_price(cls, values):
200
+ price_info = values.get("price")
201
+ if price_info:
202
+ values["converted_price"] = float(price_info["price"]) / 10 ** -int(
203
+ price_info["expo"]
204
+ )
205
+
206
+ ema_price_info = values.get("ema_price")
207
+ if ema_price_info:
208
+ values["converted_ema_price"] = float(ema_price_info["price"]) / 10 ** -int(
209
+ ema_price_info["expo"]
210
+ )
211
+
212
+ return values
213
+
214
+
215
+ class PriceFeesUpdateBinary(BaseModel):
216
+ encoding: str
217
+ data: List[str]
218
+
219
+
220
+ class PriceFeedUpdatesResponse(BaseModel):
221
+ binary: PriceFeesUpdateBinary
222
+ parsed: List[PriceFeedResponse]
223
+
224
+
225
+ class Spread(BaseModel):
226
+ long: Optional[Dict[str, float]] = None
227
+ short: Optional[Dict[str, float]] = None
228
+
229
+ @model_validator(mode="before")
230
+ def check_at_least_one(cls, values):
231
+ if not values.get("long") and not values.get("short"):
232
+ raise ValueError('At least one of "long" or "short" must be present.')
233
+ return values
234
+
235
+
236
+ class Depth(BaseModel):
237
+ above: Optional[Dict[str, float]] = None
238
+ below: Optional[Dict[str, float]] = None
239
+
240
+ @model_validator(mode="before")
241
+ def check_at_least_one(cls, values):
242
+ if not values.get("above") and not values.get("below"):
243
+ raise ValueError('At least one of "above" or "below" must be present.')
244
+ return values
245
+
246
+
247
+ class Fee(BaseModel):
248
+ long: Optional[Dict[str, float]] = None
249
+ short: Optional[Dict[str, float]] = None
250
+
251
+ @model_validator(mode="before")
252
+ def check_at_least_one(cls, values):
253
+ if not values.get("long") and not values.get("short"):
254
+ raise ValueError('At least one of "long" or "short" must be present.')
255
+ return values
256
+
257
+
258
+ class TradeInput(BaseModel):
259
+ trader: str = "0x1234567890123456789012345678901234567890"
260
+ pairIndex: int = Field(..., alias="pair_index")
261
+ index: int = Field(0, alias="trade_index")
262
+ initialPosToken: Optional[int] = Field(None, alias="open_collateral")
263
+ positionSizeUSDC: Optional[int] = Field(None, alias="collateral_in_trade")
264
+ openPrice: int = Field(0, alias="open_price")
265
+ buy: bool = Field(..., alias="is_long")
266
+ leverage: int
267
+ tp: Optional[int] = 0
268
+ sl: Optional[int] = 0
269
+ timestamp: Optional[int] = 0
270
+
271
+ @field_validator("trader")
272
+ def validate_eth_address(cls, v):
273
+ if not re.match(r"^0x[a-fA-F0-9]{40}$", v):
274
+ raise ValueError("Invalid Ethereum address")
275
+ return v
276
+
277
+ @field_validator("tp")
278
+ def validate_tp(cls, v, values):
279
+ if values.data.get("openPrice") not in [0, None] and v in [0, None]:
280
+ raise ValueError("tp is required when openPrice is provided and is not 0")
281
+ return v
282
+
283
+ @field_validator("openPrice", "tp", "sl", "leverage", mode="before")
284
+ def convert_to_float_10(cls, v):
285
+ if v is None:
286
+ return 0
287
+ return int(v * 10**10)
288
+
289
+ @model_validator(mode="before")
290
+ def assign_and_validate_collateral_and_position_size_usdc(cls, values):
291
+ collateral_in_trade = values.pop("position_size_usdc", None)
292
+ if collateral_in_trade is not None:
293
+ values["positionSizeUSDC"] = collateral_in_trade
294
+
295
+ initialPosToken = values.get("initialPosToken") or values.get("open_collateral")
296
+ positionSizeUSDC = values.get("positionSizeUSDC") or values.get(
297
+ "collateral_in_trade"
298
+ )
299
+
300
+ if initialPosToken is None and positionSizeUSDC is None:
301
+ raise ValueError(
302
+ "Either 'open_collateral' or 'collateral_in_trade' must be provided."
303
+ )
304
+
305
+ if initialPosToken is not None and positionSizeUSDC is None:
306
+ values["positionSizeUSDC"] = 0
307
+ elif positionSizeUSDC is not None and initialPosToken is None:
308
+ values["initialPosToken"] = 0
309
+
310
+ return values
311
+
312
+ @field_validator("initialPosToken", "positionSizeUSDC", mode="before")
313
+ def convert_to_float_6(cls, v):
314
+ return int(v * 10**6)
315
+
316
+ @field_validator("timestamp", mode="before")
317
+ def set_default_timestamp(cls, v):
318
+ if v is None:
319
+ return int(time.time())
320
+ return v
321
+
322
+ class Config:
323
+ populate_by_name = True
324
+
325
+
326
+ class TradeInputOrderType(Enum):
327
+ MARKET = 0
328
+ STOP_LIMIT = 1
329
+ LIMIT = 2
330
+ MARKET_ZERO_FEE = 3
331
+
332
+
333
+ class TradeResponse(BaseModel):
334
+ trader: str
335
+ pair_index: int = Field(..., alias="pairIndex")
336
+ trade_index: int = Field(0, alias="index")
337
+ open_collateral: float = Field(None, alias="initialPosToken")
338
+ collateral_in_trade: float = Field(None, alias="positionSizeUSDC")
339
+ open_price: float = Field(0, alias="openPrice")
340
+ is_long: bool = Field(..., alias="buy")
341
+ leverage: float
342
+ tp: float
343
+ sl: float
344
+ timestamp: int
345
+
346
+ @field_validator("trader")
347
+ def validate_eth_address(cls, v):
348
+ if not re.match(r"^0x[a-fA-F0-9]{40}$", v):
349
+ raise ValueError("Invalid Ethereum address")
350
+ return v
351
+
352
+ @field_validator("open_price", "tp", "sl", "leverage", mode="before")
353
+ def convert_to_float_10(cls, v):
354
+ return v / 10**10
355
+
356
+ @field_validator("open_collateral", "collateral_in_trade", mode="before")
357
+ def convert_to_float_6(cls, v):
358
+ return v / 10**6
359
+
360
+ class Config:
361
+ populate_by_name = True
362
+
363
+
364
+ class TradeInfo(BaseModel):
365
+ open_interest_usdc: float = Field(..., alias="openInterestUSDC")
366
+ tp_last_updated: float = Field(..., alias="tpLastUpdated")
367
+ sl_last_updated: float = Field(..., alias="slLastUpdated")
368
+ being_market_closed: bool = Field(..., alias="beingMarketClosed")
369
+ loss_protection_percentage: float = Field(..., alias="lossProtectionPercentage")
370
+
371
+ @field_validator("open_interest_usdc", mode="before")
372
+ def convert_to_float_6(cls, v):
373
+ return v / 10**6
374
+
375
+ class Config:
376
+ populate_by_name = True
377
+
378
+
379
+ class TradeExtendedResponse(BaseModel):
380
+ trade: TradeResponse
381
+ additional_info: TradeInfo
382
+ margin_fee: float
383
+ liquidation_price: float
384
+
385
+ @field_validator("margin_fee", mode="before")
386
+ def convert_to_float_6(cls, v):
387
+ return v / 10**6
388
+
389
+ @field_validator("liquidation_price", mode="before")
390
+ def convert_to_float_10(cls, v):
391
+ return v / 10**10
392
+
393
+ class Config:
394
+ populate_by_name = True
395
+
396
+
397
+ class PendingLimitOrderResponse(BaseModel):
398
+ trader: str
399
+ pair_index: int = Field(..., alias="pairIndex")
400
+ trade_index: int = Field(0, alias="index")
401
+ open_collateral: float = Field(..., alias="positionSize")
402
+ buy: bool
403
+ leverage: int
404
+ tp: float
405
+ sl: float
406
+ price: float
407
+ slippage_percentage: float = Field(..., alias="slippageP")
408
+ block: int
409
+
410
+ @field_validator("trader")
411
+ def validate_eth_address(cls, v):
412
+ if not re.match(r"^0x[a-fA-F0-9]{40}$", v):
413
+ raise ValueError("Invalid Ethereum address")
414
+ return v
415
+
416
+ @field_validator(
417
+ "price", "tp", "sl", "leverage", "slippage_percentage", mode="before"
418
+ )
419
+ def convert_to_float_10(cls, v):
420
+ return v / 10**10
421
+
422
+ @field_validator("open_collateral", mode="before")
423
+ def convert_to_float_6(cls, v):
424
+ return v / 10**6
425
+
426
+ class Config:
427
+ populate_by_name = True
428
+
429
+
430
+ class PendingLimitOrderExtendedResponse(PendingLimitOrderResponse):
431
+ liquidation_price: float
432
+
433
+ @field_validator("liquidation_price", mode="before")
434
+ def convert_liq_to_float_10(cls, v):
435
+ return v / 10**10
436
+
437
+
438
+ class MarginUpdateType(Enum):
439
+ DEPOSIT = 0
440
+ WITHDRAW = 1
441
+
442
+
443
+ class LossProtectionInfo(BaseModel):
444
+ percentage: float
445
+ amount: float
446
+
447
+
448
+ class SnapshotOpenInterest(BaseModel):
449
+ long: float
450
+ short: float
451
+
452
+
453
+ class SnapshotGroup(BaseModel):
454
+ group_open_interest_limit: float
455
+ group_open_interest: SnapshotOpenInterest
456
+ group_utilization: float
457
+ group_skew: float
458
+ pairs: Dict[str, PairInfoExtended]
459
+
460
+
461
+ class Snapshot(BaseModel):
462
+ groups: Dict[conint(ge=0), SnapshotGroup]