ddx-python 1.0.5__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.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.
- ddx/.gitignore +1 -0
- ddx/__init__.py +58 -0
- ddx/_rust/__init__.pyi +2009 -0
- ddx/_rust/common/__init__.pyi +17 -0
- ddx/_rust/common/accounting.pyi +6 -0
- ddx/_rust/common/enums.pyi +3 -0
- ddx/_rust/common/requests/__init__.pyi +21 -0
- ddx/_rust/common/requests/intents.pyi +19 -0
- ddx/_rust/common/specs.pyi +17 -0
- ddx/_rust/common/state/__init__.pyi +41 -0
- ddx/_rust/common/state/keys.pyi +29 -0
- ddx/_rust/common/transactions.pyi +7 -0
- ddx/_rust/decimal.pyi +3 -0
- ddx/_rust/h256.pyi +3 -0
- ddx/_rust.abi3.so +0 -0
- ddx/app_config/ethereum/addresses.json +541 -0
- ddx/auditor/README.md +32 -0
- ddx/auditor/__init__.py +0 -0
- ddx/auditor/auditor_driver.py +1034 -0
- ddx/auditor/websocket_message.py +54 -0
- ddx/common/__init__.py +0 -0
- ddx/common/epoch_params.py +28 -0
- ddx/common/fill_context.py +144 -0
- ddx/common/item_utils.py +38 -0
- ddx/common/logging.py +184 -0
- ddx/common/market_specs.py +64 -0
- ddx/common/trade_mining_params.py +19 -0
- ddx/common/transaction_utils.py +85 -0
- ddx/common/transactions/__init__.py +0 -0
- ddx/common/transactions/advance_epoch.py +91 -0
- ddx/common/transactions/advance_settlement_epoch.py +63 -0
- ddx/common/transactions/all_price_checkpoints.py +84 -0
- ddx/common/transactions/cancel.py +76 -0
- ddx/common/transactions/cancel_all.py +88 -0
- ddx/common/transactions/complete_fill.py +103 -0
- ddx/common/transactions/disaster_recovery.py +97 -0
- ddx/common/transactions/event.py +48 -0
- ddx/common/transactions/fee_distribution.py +119 -0
- ddx/common/transactions/funding.py +294 -0
- ddx/common/transactions/futures_expiry.py +123 -0
- ddx/common/transactions/genesis.py +108 -0
- ddx/common/transactions/inner/__init__.py +0 -0
- ddx/common/transactions/inner/adl_outcome.py +25 -0
- ddx/common/transactions/inner/fill.py +227 -0
- ddx/common/transactions/inner/liquidated_position.py +41 -0
- ddx/common/transactions/inner/liquidation_entry.py +41 -0
- ddx/common/transactions/inner/liquidation_fill.py +118 -0
- ddx/common/transactions/inner/outcome.py +32 -0
- ddx/common/transactions/inner/trade_fill.py +125 -0
- ddx/common/transactions/insurance_fund_update.py +142 -0
- ddx/common/transactions/insurance_fund_withdraw.py +99 -0
- ddx/common/transactions/liquidation.py +357 -0
- ddx/common/transactions/partial_fill.py +125 -0
- ddx/common/transactions/pnl_realization.py +122 -0
- ddx/common/transactions/post.py +72 -0
- ddx/common/transactions/post_order.py +95 -0
- ddx/common/transactions/price_checkpoint.py +96 -0
- ddx/common/transactions/signer_registered.py +62 -0
- ddx/common/transactions/specs_update.py +61 -0
- ddx/common/transactions/strategy_update.py +156 -0
- ddx/common/transactions/tradable_product_update.py +98 -0
- ddx/common/transactions/trade_mining.py +147 -0
- ddx/common/transactions/trader_update.py +105 -0
- ddx/common/transactions/withdraw.py +91 -0
- ddx/common/transactions/withdraw_ddx.py +74 -0
- ddx/common/utils.py +176 -0
- ddx/config.py +17 -0
- ddx/derivadex_client.py +254 -0
- ddx/py.typed +0 -0
- ddx/realtime_client/__init__.py +2 -0
- ddx/realtime_client/config.py +2 -0
- ddx/realtime_client/logs/pytest.log +0 -0
- ddx/realtime_client/models/__init__.py +683 -0
- ddx/realtime_client/realtime_client.py +567 -0
- ddx/rest_client/__init__.py +0 -0
- ddx/rest_client/clients/__init__.py +0 -0
- ddx/rest_client/clients/base_client.py +60 -0
- ddx/rest_client/clients/market_client.py +1241 -0
- ddx/rest_client/clients/on_chain_client.py +432 -0
- ddx/rest_client/clients/signed_client.py +301 -0
- ddx/rest_client/clients/system_client.py +843 -0
- ddx/rest_client/clients/trade_client.py +335 -0
- ddx/rest_client/constants/__init__.py +0 -0
- ddx/rest_client/constants/endpoints.py +67 -0
- ddx/rest_client/contracts/__init__.py +0 -0
- ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
- ddx/rest_client/contracts/ddx/__init__.py +1949 -0
- ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
- ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
- ddx/rest_client/contracts/i_stake/__init__.py +696 -0
- ddx/rest_client/exceptions/__init__.py +0 -0
- ddx/rest_client/exceptions/exceptions.py +32 -0
- ddx/rest_client/http/__init__.py +0 -0
- ddx/rest_client/http/http_client.py +305 -0
- ddx/rest_client/models/__init__.py +0 -0
- ddx/rest_client/models/market.py +683 -0
- ddx/rest_client/models/signed.py +60 -0
- ddx/rest_client/models/system.py +390 -0
- ddx/rest_client/models/trade.py +140 -0
- ddx/rest_client/utils/__init__.py +0 -0
- ddx/rest_client/utils/encryption_utils.py +26 -0
- ddx_python-1.0.5.dist-info/METADATA +63 -0
- ddx_python-1.0.5.dist-info/RECORD +104 -0
- ddx_python-1.0.5.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from typing import List, Optional, Dict
|
|
4
|
+
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
|
5
|
+
from pydantic.alias_generators import to_camel
|
|
6
|
+
from dateutil.parser import parse
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from itertools import groupby
|
|
9
|
+
from operator import attrgetter
|
|
10
|
+
|
|
11
|
+
from ddx._rust.decimal import Decimal
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def parse_datetime_string(value):
|
|
15
|
+
"""Utility function to parse datetime strings."""
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
return parse(value)
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Enums
|
|
22
|
+
class OrderSide(IntEnum):
|
|
23
|
+
"""Order side enum."""
|
|
24
|
+
|
|
25
|
+
BID = 0
|
|
26
|
+
ASK = 1
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class OrderType(IntEnum):
|
|
30
|
+
"""Order type enum."""
|
|
31
|
+
|
|
32
|
+
LIMIT = 0
|
|
33
|
+
MARKET = 1
|
|
34
|
+
STOP = 2
|
|
35
|
+
LIMIT_POST_ONLY = 3
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class OrderUpdateReason(IntEnum):
|
|
39
|
+
"""Order update reason enum."""
|
|
40
|
+
|
|
41
|
+
TRADE = 0
|
|
42
|
+
LIQUIDATION = 1
|
|
43
|
+
CANCEL = 2
|
|
44
|
+
ORDER_REJECTION = 3
|
|
45
|
+
CANCEL_REJECTION = 4
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class StrategyUpdateReason(IntEnum):
|
|
49
|
+
"""Strategy update reason enum."""
|
|
50
|
+
|
|
51
|
+
DEPOSIT = 0
|
|
52
|
+
WITHDRAW = 1
|
|
53
|
+
WITHDRAW_INTENT = 2
|
|
54
|
+
FUNDING_PAYMENT = 3
|
|
55
|
+
REALIZED_PNL = 4
|
|
56
|
+
LIQUIDATION = 5
|
|
57
|
+
ADL = 6
|
|
58
|
+
WITHDRAW_REJECTION = 7
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TraderUpdateReason(IntEnum):
|
|
62
|
+
"""Trader update reason enum."""
|
|
63
|
+
|
|
64
|
+
DEPOSIT = 0
|
|
65
|
+
WITHDRAW_DDX = 1
|
|
66
|
+
WITHDRAW_DDX_INTENT = 2
|
|
67
|
+
TRADE_MINING_REWARD = 3
|
|
68
|
+
PROFILE_UPDATE = 4
|
|
69
|
+
FEE_DISTRIBUTION = 5
|
|
70
|
+
WITHDRAW_DDX_REJECTION = 6
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Mark Price History Models
|
|
74
|
+
class MarkPrice(BaseModel):
|
|
75
|
+
"""Mark price data model."""
|
|
76
|
+
|
|
77
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
78
|
+
|
|
79
|
+
global_ordinal: int
|
|
80
|
+
epoch_id: int
|
|
81
|
+
symbol: str
|
|
82
|
+
price: str
|
|
83
|
+
funding_rate: str
|
|
84
|
+
created_at: datetime
|
|
85
|
+
|
|
86
|
+
@field_validator("created_at", mode="before")
|
|
87
|
+
@classmethod
|
|
88
|
+
def parse_datetime_field(cls, value):
|
|
89
|
+
return parse_datetime_string(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class MarkPriceHistoryResponse(BaseModel):
|
|
93
|
+
"""Response model for mark price history endpoint."""
|
|
94
|
+
|
|
95
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
96
|
+
|
|
97
|
+
next_global_ordinal: Optional[int] = None
|
|
98
|
+
value: List[MarkPrice]
|
|
99
|
+
success: bool
|
|
100
|
+
timestamp: int
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Order Book L3 Models
|
|
104
|
+
class OrderBookL3(BaseModel):
|
|
105
|
+
"""L3 order book entry model."""
|
|
106
|
+
|
|
107
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
108
|
+
|
|
109
|
+
book_ordinal: int
|
|
110
|
+
order_hash: str
|
|
111
|
+
symbol: str
|
|
112
|
+
side: int = Field(..., description="0: Bid, 1: Ask")
|
|
113
|
+
original_amount: str
|
|
114
|
+
amount: str
|
|
115
|
+
price: str
|
|
116
|
+
trader_address: str
|
|
117
|
+
strategy_id_hash: str
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class OrderBookL3Response(BaseModel):
|
|
121
|
+
"""Response model for L3 order book endpoint."""
|
|
122
|
+
|
|
123
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
124
|
+
|
|
125
|
+
value: List[OrderBookL3]
|
|
126
|
+
success: bool
|
|
127
|
+
timestamp: int
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# Order Update History Models
|
|
131
|
+
class OrderIntent(BaseModel):
|
|
132
|
+
"""Order intent data model."""
|
|
133
|
+
|
|
134
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
135
|
+
|
|
136
|
+
epoch_id: int
|
|
137
|
+
order_hash: str
|
|
138
|
+
symbol: str
|
|
139
|
+
side: int = Field(..., description="0: Bid, 1: Ask")
|
|
140
|
+
amount: str
|
|
141
|
+
price: str
|
|
142
|
+
trader_address: str
|
|
143
|
+
strategy_id_hash: str
|
|
144
|
+
order_type: int = Field(
|
|
145
|
+
..., description="0: Limit, 1: Market, 2: Stop, 3: LimitPostOnly"
|
|
146
|
+
)
|
|
147
|
+
stop_price: str
|
|
148
|
+
nonce: str
|
|
149
|
+
signature: str
|
|
150
|
+
created_at: datetime
|
|
151
|
+
|
|
152
|
+
@field_validator("created_at", mode="before")
|
|
153
|
+
@classmethod
|
|
154
|
+
def parse_datetime_field(cls, value):
|
|
155
|
+
return parse_datetime_string(value)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class OrderUpdate(BaseModel):
|
|
159
|
+
"""Order update data model."""
|
|
160
|
+
|
|
161
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
162
|
+
|
|
163
|
+
global_ordinal: int
|
|
164
|
+
epoch_id: int
|
|
165
|
+
tx_ordinal: Optional[int] = None
|
|
166
|
+
ordinal: Optional[int] = None
|
|
167
|
+
order_rejection: Optional[int] = Field(
|
|
168
|
+
None, description="0-5: only applicable when rejection"
|
|
169
|
+
)
|
|
170
|
+
cancel_rejection: Optional[int] = None
|
|
171
|
+
reason: int = Field(
|
|
172
|
+
...,
|
|
173
|
+
description="0: Trade, 1: Liquidation, 2: Cancel, 3: Order Rejection, 4: Cancel Rejection",
|
|
174
|
+
)
|
|
175
|
+
amount: str
|
|
176
|
+
quote_asset_amount: Optional[str] = None
|
|
177
|
+
symbol: str
|
|
178
|
+
price: Optional[str] = None
|
|
179
|
+
maker_fee_collateral: Optional[str] = None
|
|
180
|
+
maker_fee_ddx: Optional[str] = None
|
|
181
|
+
maker_realized_pnl: Optional[str] = None
|
|
182
|
+
taker_order_intent: Optional[OrderIntent] = None
|
|
183
|
+
taker_fee_collateral: Optional[str] = None
|
|
184
|
+
taker_fee_ddx: Optional[str] = None
|
|
185
|
+
taker_realized_pnl: Optional[str] = None
|
|
186
|
+
liquidated_trader_address: Optional[str] = None
|
|
187
|
+
liquidated_strategy_id_hash: Optional[str] = None
|
|
188
|
+
maker_order_intent: OrderIntent
|
|
189
|
+
created_at: datetime
|
|
190
|
+
|
|
191
|
+
@field_validator("created_at", mode="before")
|
|
192
|
+
@classmethod
|
|
193
|
+
def parse_datetime_field(cls, value):
|
|
194
|
+
return parse_datetime_string(value)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class OrderUpdateHistoryResponse(BaseModel):
|
|
198
|
+
"""Response model for order update history endpoint."""
|
|
199
|
+
|
|
200
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
201
|
+
|
|
202
|
+
next_global_ordinal: Optional[int] = None
|
|
203
|
+
value: List[OrderUpdate]
|
|
204
|
+
success: bool
|
|
205
|
+
timestamp: int
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# Strategy Update History Models
|
|
209
|
+
class StrategyPosition(BaseModel):
|
|
210
|
+
"""Strategy position data within a strategy update."""
|
|
211
|
+
|
|
212
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
213
|
+
|
|
214
|
+
symbol: str
|
|
215
|
+
side: Optional[int] = Field(
|
|
216
|
+
None, description="0: Bid, 1: Ask. Only present on ADL strategy update"
|
|
217
|
+
)
|
|
218
|
+
avg_entry_price: Optional[str] = Field(
|
|
219
|
+
None, description="New average entry price after RealizedPnl strategy update"
|
|
220
|
+
)
|
|
221
|
+
realized_pnl: str = Field(
|
|
222
|
+
..., description="Realized PnL after RealizedPnl or ADL strategy update"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class StrategyUpdate(BaseModel):
|
|
227
|
+
"""Strategy update data model."""
|
|
228
|
+
|
|
229
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
230
|
+
|
|
231
|
+
global_ordinal: int
|
|
232
|
+
epoch_id: int
|
|
233
|
+
tx_ordinal: Optional[int] = None
|
|
234
|
+
ordinal: Optional[int] = None
|
|
235
|
+
withdraw_rejection: Optional[int] = Field(
|
|
236
|
+
None, description="0-4: Withdraw rejection type. Only present on rejections"
|
|
237
|
+
)
|
|
238
|
+
reason: int = Field(
|
|
239
|
+
...,
|
|
240
|
+
description="0: Deposit, 1: Withdraw, 2: WithdrawIntent, 3: FundingPayment, 4: RealizedPnl, 5: Liquidation, 6: ADL, 7: Withdraw Rejection",
|
|
241
|
+
)
|
|
242
|
+
trader_address: str
|
|
243
|
+
strategy_id_hash: str
|
|
244
|
+
collateral_address: Optional[str] = None
|
|
245
|
+
collateral_symbol: Optional[str] = None
|
|
246
|
+
amount: Optional[str] = None
|
|
247
|
+
new_avail_collateral: Optional[str] = Field(
|
|
248
|
+
None, description="Not present on ADL reason"
|
|
249
|
+
)
|
|
250
|
+
new_locked_collateral: Optional[str] = Field(
|
|
251
|
+
None, description="Not present on ADL reason"
|
|
252
|
+
)
|
|
253
|
+
block_number: Optional[int] = Field(None, description="Not present on ADL reason")
|
|
254
|
+
tx_hash: Optional[str] = None
|
|
255
|
+
positions: Optional[List[StrategyPosition]] = None
|
|
256
|
+
created_at: datetime
|
|
257
|
+
|
|
258
|
+
@field_validator("created_at", mode="before")
|
|
259
|
+
@classmethod
|
|
260
|
+
def parse_datetime_field(cls, value):
|
|
261
|
+
return parse_datetime_string(value)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class StrategyUpdateHistoryResponse(BaseModel):
|
|
265
|
+
"""Response model for strategy update history endpoint."""
|
|
266
|
+
|
|
267
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
268
|
+
|
|
269
|
+
next_global_ordinal: Optional[int] = None
|
|
270
|
+
value: List[StrategyUpdate]
|
|
271
|
+
success: bool
|
|
272
|
+
timestamp: int
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Ticker Models
|
|
276
|
+
class Ticker(BaseModel):
|
|
277
|
+
"""Market ticker data model."""
|
|
278
|
+
|
|
279
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
280
|
+
|
|
281
|
+
symbol: str
|
|
282
|
+
high_price_24h: str
|
|
283
|
+
low_price_24h: str
|
|
284
|
+
prev_price_24h: str
|
|
285
|
+
last_price: str
|
|
286
|
+
mark_price: str
|
|
287
|
+
index_price: str
|
|
288
|
+
next_funding_time: datetime
|
|
289
|
+
volume_24h: str
|
|
290
|
+
amount_24h: Optional[str] = None
|
|
291
|
+
funding_rate: str
|
|
292
|
+
open_interest: str
|
|
293
|
+
open_interest_value: str
|
|
294
|
+
|
|
295
|
+
@field_validator("next_funding_time", mode="before")
|
|
296
|
+
@classmethod
|
|
297
|
+
def parse_datetime_field(cls, value):
|
|
298
|
+
return parse_datetime_string(value)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class TickersResponse(BaseModel):
|
|
302
|
+
"""Response model for tickers endpoint."""
|
|
303
|
+
|
|
304
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
305
|
+
|
|
306
|
+
value: List[Ticker]
|
|
307
|
+
success: bool
|
|
308
|
+
timestamp: int
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# Trader Update History Models
|
|
312
|
+
class TraderUpdate(BaseModel):
|
|
313
|
+
"""Trader update data model."""
|
|
314
|
+
|
|
315
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
316
|
+
|
|
317
|
+
global_ordinal: int
|
|
318
|
+
epoch_id: int
|
|
319
|
+
tx_ordinal: Optional[int] = None
|
|
320
|
+
ordinal: Optional[int] = None
|
|
321
|
+
withdraw_ddx_rejection: Optional[int] = Field(
|
|
322
|
+
None, description="0-1: only present on rejections"
|
|
323
|
+
)
|
|
324
|
+
reason: int = Field(
|
|
325
|
+
...,
|
|
326
|
+
description="0: Deposit, 1: WithdrawDDX, 2: WithdrawDDXIntent, 3: TradeMiningReward, 4: ProfileUpdate, 5: FeeDistribution, 6: WithdrawDDXRejection",
|
|
327
|
+
)
|
|
328
|
+
trader_address: str
|
|
329
|
+
amount: Optional[str] = None
|
|
330
|
+
new_avail_ddx_balance: Optional[str] = None
|
|
331
|
+
new_locked_ddx_balance: Optional[str] = None
|
|
332
|
+
pay_fees_in_ddx: Optional[bool] = None
|
|
333
|
+
block_number: Optional[int] = None
|
|
334
|
+
tx_hash: Optional[str] = None
|
|
335
|
+
created_at: datetime
|
|
336
|
+
|
|
337
|
+
@field_validator("created_at", mode="before")
|
|
338
|
+
@classmethod
|
|
339
|
+
def parse_datetime_field(cls, value):
|
|
340
|
+
return parse_datetime_string(value)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class TraderUpdateHistoryResponse(BaseModel):
|
|
344
|
+
"""Response model for trader update history endpoint."""
|
|
345
|
+
|
|
346
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
347
|
+
|
|
348
|
+
next_global_ordinal: Optional[int] = None
|
|
349
|
+
value: List[TraderUpdate]
|
|
350
|
+
success: bool
|
|
351
|
+
timestamp: int
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# Balance Aggregation Models
|
|
355
|
+
class BalanceAggregation(BaseModel):
|
|
356
|
+
"""Balance aggregation data model."""
|
|
357
|
+
|
|
358
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
359
|
+
|
|
360
|
+
trader: str
|
|
361
|
+
strategy_id_hash: str
|
|
362
|
+
amount: str
|
|
363
|
+
timestamp: int
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class BalanceAggregationResponse(BaseModel):
|
|
367
|
+
"""Response model for balance aggregation endpoint."""
|
|
368
|
+
|
|
369
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
370
|
+
|
|
371
|
+
value: List[BalanceAggregation]
|
|
372
|
+
success: bool
|
|
373
|
+
timestamp: int
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# Fees Aggregation Models
|
|
377
|
+
class FeesAggregation(BaseModel):
|
|
378
|
+
"""Fees aggregation data model."""
|
|
379
|
+
|
|
380
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
381
|
+
|
|
382
|
+
timestamp: int
|
|
383
|
+
|
|
384
|
+
def get_fees_value(self, fee_symbol: str) -> Optional[str]:
|
|
385
|
+
"""Get fees value for a specific fee symbol (e.g., 'USDC', 'DDX')."""
|
|
386
|
+
return getattr(self, f"fees_{fee_symbol}", None)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class FeesAggregationResponse(BaseModel):
|
|
390
|
+
"""Response model for fees aggregation endpoint."""
|
|
391
|
+
|
|
392
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
393
|
+
|
|
394
|
+
next_lookback_timestamp: Optional[int] = None
|
|
395
|
+
value: List[FeesAggregation]
|
|
396
|
+
success: bool
|
|
397
|
+
timestamp: int
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# Funding Rate Comparison Models
|
|
401
|
+
class FundingRateComparison(BaseModel):
|
|
402
|
+
"""Funding rate comparison data model."""
|
|
403
|
+
|
|
404
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
405
|
+
|
|
406
|
+
symbol: str
|
|
407
|
+
derivadex_funding_rate: float
|
|
408
|
+
binance_funding_rate: float
|
|
409
|
+
derivadex_binance_arbitrage: float
|
|
410
|
+
bybit_funding_rate: float
|
|
411
|
+
derivadex_bybit_arbitrage: float
|
|
412
|
+
hyperliquid_funding_rate: float
|
|
413
|
+
derivadex_hyperliquid_arbitrage: float
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class FundingRateComparisonResponse(BaseModel):
|
|
417
|
+
"""Response model for funding rate comparison aggregation endpoint."""
|
|
418
|
+
|
|
419
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
420
|
+
|
|
421
|
+
value: List[FundingRateComparison]
|
|
422
|
+
success: bool
|
|
423
|
+
timestamp: int
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# Top Traders Models
|
|
427
|
+
class TopTrader(BaseModel):
|
|
428
|
+
"""Top trader data model."""
|
|
429
|
+
|
|
430
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
431
|
+
|
|
432
|
+
trader: str
|
|
433
|
+
volume: Optional[str] = None
|
|
434
|
+
realized_pnl: Optional[str] = None
|
|
435
|
+
account_value: Optional[str] = None
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class TopTradersAggregationResponse(BaseModel):
|
|
439
|
+
"""Response model for top traders aggregation endpoint."""
|
|
440
|
+
|
|
441
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
442
|
+
|
|
443
|
+
value: List[TopTrader]
|
|
444
|
+
next_cursor: Optional[int] = None
|
|
445
|
+
success: bool
|
|
446
|
+
timestamp: int
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# Volume Aggregation Models
|
|
450
|
+
class VolumeAggregation(BaseModel):
|
|
451
|
+
"""Volume aggregation data model."""
|
|
452
|
+
|
|
453
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
454
|
+
|
|
455
|
+
timestamp: int
|
|
456
|
+
|
|
457
|
+
def get_volume_value(self, field_name: str) -> Optional[str]:
|
|
458
|
+
"""Get value for a specific volume field."""
|
|
459
|
+
return getattr(self, f"volume_{field_name}", None)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class VolumeAggregationResponse(BaseModel):
|
|
463
|
+
"""Response model for volume aggregation endpoint."""
|
|
464
|
+
|
|
465
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
466
|
+
|
|
467
|
+
next_lookback_timestamp: Optional[int] = None
|
|
468
|
+
value: List[VolumeAggregation]
|
|
469
|
+
success: bool
|
|
470
|
+
timestamp: int
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
# Funding Rate History Models
|
|
474
|
+
class FundingRateHistory(BaseModel):
|
|
475
|
+
"""Funding rate history data model."""
|
|
476
|
+
|
|
477
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
478
|
+
|
|
479
|
+
epoch_id: int
|
|
480
|
+
tx_ordinal: int
|
|
481
|
+
symbol: str
|
|
482
|
+
funding_rate: str
|
|
483
|
+
created_at: datetime
|
|
484
|
+
|
|
485
|
+
@field_validator("created_at", mode="before")
|
|
486
|
+
@classmethod
|
|
487
|
+
def parse_datetime_field(cls, value):
|
|
488
|
+
return parse_datetime_string(value)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
class FundingRateHistoryResponse(BaseModel):
|
|
492
|
+
"""Response model for funding rate history endpoint."""
|
|
493
|
+
|
|
494
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
495
|
+
|
|
496
|
+
next_epoch: Optional[int] = None
|
|
497
|
+
next_tx_ordinal: Optional[int] = None
|
|
498
|
+
next_ordinal: Optional[int] = None
|
|
499
|
+
value: List[FundingRateHistory]
|
|
500
|
+
success: bool
|
|
501
|
+
timestamp: int
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
# Open Interest History Models
|
|
505
|
+
class OpenInterestHistory(BaseModel):
|
|
506
|
+
"""Open interest history data model."""
|
|
507
|
+
|
|
508
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
509
|
+
|
|
510
|
+
symbol: str
|
|
511
|
+
amount: str
|
|
512
|
+
created_at: datetime
|
|
513
|
+
|
|
514
|
+
@field_validator("created_at", mode="before")
|
|
515
|
+
@classmethod
|
|
516
|
+
def parse_datetime_field(cls, value):
|
|
517
|
+
return parse_datetime_string(value)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class OpenInterestHistoryResponse(BaseModel):
|
|
521
|
+
"""Response model for open interest history endpoint."""
|
|
522
|
+
|
|
523
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
524
|
+
|
|
525
|
+
value: List[OpenInterestHistory]
|
|
526
|
+
success: bool
|
|
527
|
+
timestamp: int
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
# Order Book L2 Models
|
|
531
|
+
class OrderBookL2Item(BaseModel):
|
|
532
|
+
"""L2 order book item model."""
|
|
533
|
+
|
|
534
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
535
|
+
|
|
536
|
+
symbol: str
|
|
537
|
+
side: int = Field(..., description="0: Bid, 1: Ask")
|
|
538
|
+
amount: str
|
|
539
|
+
price: str
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class OrderBookL2Response(BaseModel):
|
|
543
|
+
"""Response model for L2 order book endpoint."""
|
|
544
|
+
|
|
545
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
546
|
+
|
|
547
|
+
value: List[OrderBookL2Item]
|
|
548
|
+
success: bool
|
|
549
|
+
timestamp: int
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
@dataclass
|
|
553
|
+
class OrderBook:
|
|
554
|
+
"""
|
|
555
|
+
Complete order book for a single market.
|
|
556
|
+
|
|
557
|
+
Attributes
|
|
558
|
+
----------
|
|
559
|
+
symbol : str
|
|
560
|
+
The symbol of the market
|
|
561
|
+
bids : List[OrderBookL2Item]
|
|
562
|
+
List of bid orders, sorted by price (descending)
|
|
563
|
+
asks : List[OrderBookL2Item]
|
|
564
|
+
List of ask orders, sorted by price (ascending)
|
|
565
|
+
timestamp : int
|
|
566
|
+
Timestamp of the order book snapshot
|
|
567
|
+
"""
|
|
568
|
+
|
|
569
|
+
symbol: str
|
|
570
|
+
bids: List[OrderBookL2Item]
|
|
571
|
+
asks: List[OrderBookL2Item]
|
|
572
|
+
timestamp: int
|
|
573
|
+
|
|
574
|
+
@classmethod
|
|
575
|
+
def from_order_book_l2_items(
|
|
576
|
+
cls, symbol: str, order_book_l2_items: List[OrderBookL2Item], timestamp: int
|
|
577
|
+
) -> "OrderBook":
|
|
578
|
+
"""
|
|
579
|
+
Create instance from a list of entries for a single symbol.
|
|
580
|
+
|
|
581
|
+
Parameters
|
|
582
|
+
----------
|
|
583
|
+
symbol : str
|
|
584
|
+
The market symbol
|
|
585
|
+
order_book_l2_items : List[OrderBookL2Item]
|
|
586
|
+
List of order book entries for this symbol
|
|
587
|
+
timestamp : int
|
|
588
|
+
Timestamp of the order book snapshot
|
|
589
|
+
|
|
590
|
+
Returns
|
|
591
|
+
-------
|
|
592
|
+
OrderBook
|
|
593
|
+
Initialized instance
|
|
594
|
+
"""
|
|
595
|
+
# Filter and sort bids (descending by price)
|
|
596
|
+
bids = [e for e in order_book_l2_items if e.side == OrderSide.BID]
|
|
597
|
+
bids.sort(key=lambda x: Decimal(x.price), reverse=True)
|
|
598
|
+
|
|
599
|
+
# Filter and sort asks (ascending by price)
|
|
600
|
+
asks = [e for e in order_book_l2_items if e.side == OrderSide.ASK]
|
|
601
|
+
asks.sort(key=lambda x: Decimal(x.price))
|
|
602
|
+
|
|
603
|
+
return cls(symbol=symbol, bids=bids, asks=asks, timestamp=timestamp)
|
|
604
|
+
|
|
605
|
+
@classmethod
|
|
606
|
+
def from_response(
|
|
607
|
+
cls, response: OrderBookL2Response, symbol: Optional[str] = None
|
|
608
|
+
) -> Dict[str, "OrderBook"]:
|
|
609
|
+
"""
|
|
610
|
+
Create OrderBook instance(s) from response data.
|
|
611
|
+
|
|
612
|
+
Parameters
|
|
613
|
+
----------
|
|
614
|
+
response : OrderBookL2Response
|
|
615
|
+
Parsed response from the API
|
|
616
|
+
symbol : Optional[str]
|
|
617
|
+
If provided, only return order book for this symbol
|
|
618
|
+
|
|
619
|
+
Returns
|
|
620
|
+
-------
|
|
621
|
+
Dict[str, OrderBook]
|
|
622
|
+
Dictionary mapping symbols to their respective order books
|
|
623
|
+
"""
|
|
624
|
+
if not response.value:
|
|
625
|
+
return {}
|
|
626
|
+
|
|
627
|
+
# If specific symbol requested, filter first
|
|
628
|
+
items = response.value
|
|
629
|
+
if symbol:
|
|
630
|
+
items = [item for item in items if item.symbol == symbol]
|
|
631
|
+
if not items:
|
|
632
|
+
return {}
|
|
633
|
+
|
|
634
|
+
# Group entries by symbol
|
|
635
|
+
items_sorted = sorted(items, key=attrgetter("symbol"))
|
|
636
|
+
grouped = groupby(items_sorted, key=attrgetter("symbol"))
|
|
637
|
+
|
|
638
|
+
# Create order books for each symbol
|
|
639
|
+
order_books = {}
|
|
640
|
+
for sym, entries in grouped:
|
|
641
|
+
entry_list = list(entries)
|
|
642
|
+
if entry_list: # Only create order book if there are entries
|
|
643
|
+
order_books[sym] = cls.from_order_book_l2_items(
|
|
644
|
+
sym, entry_list, response.timestamp
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
return order_books
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
# Price Checkpoint History Models
|
|
651
|
+
class PriceCheckpoint(BaseModel):
|
|
652
|
+
"""Price checkpoint data model."""
|
|
653
|
+
|
|
654
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
655
|
+
|
|
656
|
+
epoch_id: int
|
|
657
|
+
tx_ordinal: int
|
|
658
|
+
index_price_hash: str
|
|
659
|
+
symbol: str
|
|
660
|
+
index_price: str
|
|
661
|
+
mark_price: str
|
|
662
|
+
time: str
|
|
663
|
+
ema: Optional[str] = None
|
|
664
|
+
price_ordinal: int
|
|
665
|
+
created_at: datetime
|
|
666
|
+
|
|
667
|
+
@field_validator("created_at", mode="before")
|
|
668
|
+
@classmethod
|
|
669
|
+
def parse_datetime_field(cls, value):
|
|
670
|
+
return parse_datetime_string(value)
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
class PriceCheckpointHistoryResponse(BaseModel):
|
|
674
|
+
"""Response model for price checkpoint history endpoint."""
|
|
675
|
+
|
|
676
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
|
|
677
|
+
|
|
678
|
+
next_epoch: Optional[int] = None
|
|
679
|
+
next_tx_ordinal: Optional[int] = None
|
|
680
|
+
next_ordinal: Optional[int] = None
|
|
681
|
+
value: List[PriceCheckpoint]
|
|
682
|
+
success: bool
|
|
683
|
+
timestamp: int
|