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.
- avantis_trader_sdk/__init__.py +5 -5
- avantis_trader_sdk/abis/AggregatorV3Interface.json +606 -606
- avantis_trader_sdk/abis/IPyth.sol/IPyth.dbg.json +4 -4
- avantis_trader_sdk/abis/Referral.sol/ReferralStorage.json +7132 -7132
- avantis_trader_sdk/abis/Sanctions.json +190 -190
- avantis_trader_sdk/abis/USDC.sol/USDC.dbg.json +4 -4
- avantis_trader_sdk/abis/interfaces/ICallbacks.sol/ICallbacks.json +2637 -2637
- avantis_trader_sdk/abis/interfaces/IExecute.sol/IExecute.json +1628 -1628
- avantis_trader_sdk/abis/interfaces/IPairInfos.sol/IPairInfos.json +2781 -2781
- avantis_trader_sdk/abis/interfaces/IPairStorage.sol/IPairStorage.json +3729 -3729
- avantis_trader_sdk/abis/interfaces/IPriceAggregator.sol/IPriceAggregator.json +2330 -2330
- avantis_trader_sdk/abis/interfaces/IReferral.sol/IReferral.json +1890 -1890
- avantis_trader_sdk/abis/interfaces/ITradingStorage.sol/ITradingStorage.json +7022 -7022
- avantis_trader_sdk/abis/interfaces/ITranche.sol/ITranche.json +1283 -1283
- avantis_trader_sdk/abis/interfaces/IVaultManager.sol/IVaultManager.json +2424 -2424
- avantis_trader_sdk/abis/interfaces/IVeTranche.sol/IVeTranche.json +855 -855
- avantis_trader_sdk/abis/library/PositionMath.sol/PositionMath.dbg.json +4 -4
- avantis_trader_sdk/abis/library/PositionMath.sol/PositionMath.json +10 -10
- avantis_trader_sdk/abis/testnet/USDC.sol/USDC.dbg.json +4 -4
- avantis_trader_sdk/abis/testnet/USDC.sol/USDC.json +320 -320
- avantis_trader_sdk/client.py +369 -367
- avantis_trader_sdk/config.py +14 -14
- avantis_trader_sdk/feed/feed_client.py +263 -261
- avantis_trader_sdk/rpc/asset_parameters.py +499 -499
- avantis_trader_sdk/rpc/blended.py +71 -71
- avantis_trader_sdk/rpc/category_parameters.py +216 -216
- avantis_trader_sdk/rpc/fee_parameters.py +237 -237
- avantis_trader_sdk/rpc/pairs_cache.py +130 -130
- avantis_trader_sdk/rpc/rpc_helpers.py +8 -8
- avantis_trader_sdk/rpc/snapshot.py +142 -142
- avantis_trader_sdk/rpc/trade.py +701 -710
- avantis_trader_sdk/rpc/trading_parameters.py +139 -139
- avantis_trader_sdk/types.py +462 -462
- avantis_trader_sdk/utils.py +78 -78
- {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/METADATA +124 -113
- {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/RECORD +38 -39
- {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/WHEEL +1 -1
- avantis_trader_sdk/feed/feedIds.json +0 -214
- {avantis_trader_sdk-0.8.2.dist-info → avantis_trader_sdk-0.8.3.dist-info}/top_level.txt +0 -0
avantis_trader_sdk/types.py
CHANGED
|
@@ -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]
|