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.
Files changed (104) hide show
  1. ddx/.gitignore +1 -0
  2. ddx/__init__.py +58 -0
  3. ddx/_rust/__init__.pyi +2009 -0
  4. ddx/_rust/common/__init__.pyi +17 -0
  5. ddx/_rust/common/accounting.pyi +6 -0
  6. ddx/_rust/common/enums.pyi +3 -0
  7. ddx/_rust/common/requests/__init__.pyi +21 -0
  8. ddx/_rust/common/requests/intents.pyi +19 -0
  9. ddx/_rust/common/specs.pyi +17 -0
  10. ddx/_rust/common/state/__init__.pyi +41 -0
  11. ddx/_rust/common/state/keys.pyi +29 -0
  12. ddx/_rust/common/transactions.pyi +7 -0
  13. ddx/_rust/decimal.pyi +3 -0
  14. ddx/_rust/h256.pyi +3 -0
  15. ddx/_rust.abi3.so +0 -0
  16. ddx/app_config/ethereum/addresses.json +541 -0
  17. ddx/auditor/README.md +32 -0
  18. ddx/auditor/__init__.py +0 -0
  19. ddx/auditor/auditor_driver.py +1034 -0
  20. ddx/auditor/websocket_message.py +54 -0
  21. ddx/common/__init__.py +0 -0
  22. ddx/common/epoch_params.py +28 -0
  23. ddx/common/fill_context.py +144 -0
  24. ddx/common/item_utils.py +38 -0
  25. ddx/common/logging.py +184 -0
  26. ddx/common/market_specs.py +64 -0
  27. ddx/common/trade_mining_params.py +19 -0
  28. ddx/common/transaction_utils.py +85 -0
  29. ddx/common/transactions/__init__.py +0 -0
  30. ddx/common/transactions/advance_epoch.py +91 -0
  31. ddx/common/transactions/advance_settlement_epoch.py +63 -0
  32. ddx/common/transactions/all_price_checkpoints.py +84 -0
  33. ddx/common/transactions/cancel.py +76 -0
  34. ddx/common/transactions/cancel_all.py +88 -0
  35. ddx/common/transactions/complete_fill.py +103 -0
  36. ddx/common/transactions/disaster_recovery.py +97 -0
  37. ddx/common/transactions/event.py +48 -0
  38. ddx/common/transactions/fee_distribution.py +119 -0
  39. ddx/common/transactions/funding.py +294 -0
  40. ddx/common/transactions/futures_expiry.py +123 -0
  41. ddx/common/transactions/genesis.py +108 -0
  42. ddx/common/transactions/inner/__init__.py +0 -0
  43. ddx/common/transactions/inner/adl_outcome.py +25 -0
  44. ddx/common/transactions/inner/fill.py +227 -0
  45. ddx/common/transactions/inner/liquidated_position.py +41 -0
  46. ddx/common/transactions/inner/liquidation_entry.py +41 -0
  47. ddx/common/transactions/inner/liquidation_fill.py +118 -0
  48. ddx/common/transactions/inner/outcome.py +32 -0
  49. ddx/common/transactions/inner/trade_fill.py +125 -0
  50. ddx/common/transactions/insurance_fund_update.py +142 -0
  51. ddx/common/transactions/insurance_fund_withdraw.py +99 -0
  52. ddx/common/transactions/liquidation.py +357 -0
  53. ddx/common/transactions/partial_fill.py +125 -0
  54. ddx/common/transactions/pnl_realization.py +122 -0
  55. ddx/common/transactions/post.py +72 -0
  56. ddx/common/transactions/post_order.py +95 -0
  57. ddx/common/transactions/price_checkpoint.py +96 -0
  58. ddx/common/transactions/signer_registered.py +62 -0
  59. ddx/common/transactions/specs_update.py +61 -0
  60. ddx/common/transactions/strategy_update.py +156 -0
  61. ddx/common/transactions/tradable_product_update.py +98 -0
  62. ddx/common/transactions/trade_mining.py +147 -0
  63. ddx/common/transactions/trader_update.py +105 -0
  64. ddx/common/transactions/withdraw.py +91 -0
  65. ddx/common/transactions/withdraw_ddx.py +74 -0
  66. ddx/common/utils.py +176 -0
  67. ddx/config.py +17 -0
  68. ddx/derivadex_client.py +254 -0
  69. ddx/py.typed +0 -0
  70. ddx/realtime_client/__init__.py +2 -0
  71. ddx/realtime_client/config.py +2 -0
  72. ddx/realtime_client/logs/pytest.log +0 -0
  73. ddx/realtime_client/models/__init__.py +683 -0
  74. ddx/realtime_client/realtime_client.py +567 -0
  75. ddx/rest_client/__init__.py +0 -0
  76. ddx/rest_client/clients/__init__.py +0 -0
  77. ddx/rest_client/clients/base_client.py +60 -0
  78. ddx/rest_client/clients/market_client.py +1241 -0
  79. ddx/rest_client/clients/on_chain_client.py +432 -0
  80. ddx/rest_client/clients/signed_client.py +301 -0
  81. ddx/rest_client/clients/system_client.py +843 -0
  82. ddx/rest_client/clients/trade_client.py +335 -0
  83. ddx/rest_client/constants/__init__.py +0 -0
  84. ddx/rest_client/constants/endpoints.py +67 -0
  85. ddx/rest_client/contracts/__init__.py +0 -0
  86. ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
  87. ddx/rest_client/contracts/ddx/__init__.py +1949 -0
  88. ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
  89. ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
  90. ddx/rest_client/contracts/i_stake/__init__.py +696 -0
  91. ddx/rest_client/exceptions/__init__.py +0 -0
  92. ddx/rest_client/exceptions/exceptions.py +32 -0
  93. ddx/rest_client/http/__init__.py +0 -0
  94. ddx/rest_client/http/http_client.py +305 -0
  95. ddx/rest_client/models/__init__.py +0 -0
  96. ddx/rest_client/models/market.py +683 -0
  97. ddx/rest_client/models/signed.py +60 -0
  98. ddx/rest_client/models/system.py +390 -0
  99. ddx/rest_client/models/trade.py +140 -0
  100. ddx/rest_client/utils/__init__.py +0 -0
  101. ddx/rest_client/utils/encryption_utils.py +26 -0
  102. ddx_python-1.0.5.dist-info/METADATA +63 -0
  103. ddx_python-1.0.5.dist-info/RECORD +104 -0
  104. ddx_python-1.0.5.dist-info/WHEEL +4 -0
@@ -0,0 +1,683 @@
1
+ from pydantic import BaseModel, Field, ConfigDict, conlist, field_validator, ConfigDict
2
+ from pydantic.alias_generators import to_camel
3
+ from pydantic.types import StringConstraints
4
+ from typing import Optional, Union, Annotated, Literal
5
+ from enum import Enum
6
+ from datetime import datetime
7
+
8
+ from ddx._rust.decimal import Decimal
9
+
10
+
11
+ class CamelModel(BaseModel):
12
+ model_config = ConfigDict(
13
+ alias_generator=to_camel,
14
+ validate_by_alias=True,
15
+ validate_by_name=True,
16
+ serialize_by_alias=True,
17
+ )
18
+
19
+
20
+ def validate_decimal_str(value: str, field_name: str, nonnegative: bool = True) -> str:
21
+ try:
22
+ d = Decimal(value)
23
+ except Exception:
24
+ raise ValueError(f"Invalid decimal value for {field_name}: {value}")
25
+ if nonnegative and d < 0:
26
+ raise ValueError(f"Value for {field_name} must be non-negative: {value}")
27
+ return value
28
+
29
+
30
+ HexStr = Annotated[str, StringConstraints(pattern=r"^0x[0-9a-f]+$")]
31
+
32
+
33
+ class Action(str, Enum):
34
+ SUBSCRIBE = "SUBSCRIBE"
35
+ UNSUBSCRIBE = "UNSUBSCRIBE"
36
+
37
+
38
+ class Feed(str, Enum):
39
+ ORDER_BOOK_L2 = "ORDER_BOOK_L2"
40
+ ORDER_BOOK_L3 = "ORDER_BOOK_L3"
41
+ MARK_PRICE = "MARK_PRICE"
42
+ ORDER_UPDATE = "ORDER_UPDATE"
43
+ STRATEGY_UPDATE = "STRATEGY_UPDATE"
44
+ TRADER_UPDATE = "TRADER_UPDATE"
45
+
46
+
47
+ class MessageType(str, Enum):
48
+ PARTIAL = "PARTIAL"
49
+ UPDATE = "UPDATE"
50
+
51
+
52
+ class TradeSide(int, Enum):
53
+ BID = 0
54
+ ASK = 1
55
+
56
+
57
+ class OrderType(int, Enum):
58
+ LIMIT = 0
59
+ MARKET = 1
60
+ STOP = 2
61
+ LIMIT_POST_ONLY = 3
62
+
63
+
64
+ class OrderRejection(int, Enum):
65
+ SELF_MATCH = 0
66
+ SOLVENCY_GUARD = 1
67
+ MAX_TAKER_PRICE_DEVIATION = 2
68
+ NO_LIQUIDITY = 3
69
+ INVALID_STRATEGY = 4
70
+ POST_ONLY_VIOLATION = 5
71
+
72
+
73
+ class CancelRejection(int, Enum):
74
+ INVALID_ORDER = 0
75
+
76
+
77
+ class OrderUpdateReason(int, Enum):
78
+ TRADE = 0
79
+ LIQUIDATION = 1
80
+ CANCELLATION = 2
81
+ ORDER_REJECTION = 3
82
+ CANCEL_REJECTION = 4
83
+
84
+
85
+ class WithdrawRejection(int, Enum):
86
+ INVALID_STRATEGY = 0
87
+ INVALID_INSURANCE_FUND_CONTRIBUTION = 1
88
+ MAX_WITHDRAWAL_AMOUNT = 2
89
+ INSUFFICIENT_INSURANCE_FUND_CONTRIBUTION = 3
90
+ INSUFFICIENT_REMAINING_INSURANCE_FUND = 4
91
+
92
+
93
+ class StrategyUpdateReason(int, Enum):
94
+ DEPOSIT = 0
95
+ WITHDRAW = 1
96
+ WITHDRAW_INTENT = 2
97
+ FUNDING_PAYMENT = 3
98
+ REALIZED_PNL = 4
99
+ LIQUIDATION = 5
100
+ ADL = 6
101
+ WITHDRAW_REJECTION = 7
102
+
103
+
104
+ class WithdrawDDXRejection(int, Enum):
105
+ INVALID_TRADER = 0
106
+ INSUFFICIENT_DDX_BALANCE = 1
107
+
108
+
109
+ class TraderUpdateReason(int, Enum):
110
+ DEPOSIT_DDX = 0
111
+ WITHDRAW_DDX = 1
112
+ WITHDRAW_DDX_INTENT = 2
113
+ TRADE_MINING_REWARD = 3
114
+ PROFILE_UPDATE = 4
115
+ FEE_DISTRIBUTION = 5
116
+ WITHDRAW_DDX_REJECTION = 6
117
+
118
+
119
+ class PositionSide(int, Enum):
120
+ LONG = 0
121
+ SHORT = 1
122
+
123
+
124
+ class OrderFilter(CamelModel):
125
+ trader_address: HexStr
126
+ strategy_id_hash: Optional[HexStr] = None
127
+ symbol: Optional[str] = None
128
+ reason: Optional["OrderUpdateReason"] = None
129
+
130
+
131
+ class StrategyFilter(CamelModel):
132
+ trader_address: HexStr
133
+ strategy_id_hash: Optional[HexStr] = None
134
+ reason: Optional["StrategyUpdateReason"] = None
135
+
136
+
137
+ class TraderFilter(CamelModel):
138
+ trader_address: HexStr
139
+ reason: Optional["TraderUpdateReason"] = None
140
+
141
+
142
+ class OrderBookL2Params(CamelModel):
143
+ symbol: str
144
+ aggregation: float = Field(..., gt=0, description="Aggregation level for prices.")
145
+
146
+
147
+ class OrderBookL3Params(CamelModel):
148
+ symbol: str
149
+
150
+
151
+ class MarkPriceParams(CamelModel):
152
+ symbols: Annotated[list[str], conlist(str, min_length=1)] = Field(
153
+ ..., description="The product symbols for which to retrieve mark prices."
154
+ )
155
+
156
+
157
+ class OrderIdentifier(CamelModel):
158
+ trader_address: HexStr
159
+ strategy_id_hash: Optional[HexStr] = None
160
+ symbol: Optional[str] = None
161
+
162
+
163
+ class OrderUpdateParams(CamelModel):
164
+ order_filters: Optional[list[OrderFilter]] = None
165
+
166
+
167
+ class StrategyIdentifier(CamelModel):
168
+ trader_address: HexStr
169
+ strategy_id_hash: Optional[HexStr] = None
170
+
171
+
172
+ class StrategyUpdateParams(CamelModel):
173
+ strategy_filters: Optional[list[StrategyFilter]] = None
174
+
175
+
176
+ class TraderUpdateParams(CamelModel):
177
+ trader_filters: Optional[list[TraderFilter]] = None
178
+
179
+
180
+ class FeedWithParams(CamelModel):
181
+ feed: Feed
182
+ # params must match one of the models used by the various feeds.
183
+ params: Union[
184
+ OrderBookL2Params,
185
+ OrderBookL3Params,
186
+ MarkPriceParams,
187
+ OrderUpdateParams,
188
+ StrategyUpdateParams,
189
+ TraderUpdateParams,
190
+ ]
191
+
192
+
193
+ class SubscribePayload(CamelModel):
194
+ action: Literal[Action.SUBSCRIBE]
195
+ nonce: str
196
+ feeds: Annotated[list[FeedWithParams], conlist(FeedWithParams, min_length=1)]
197
+
198
+
199
+ class UnsubscribePayload(CamelModel):
200
+ action: Literal[Action.UNSUBSCRIBE]
201
+ nonce: str
202
+ feeds: Annotated[list[Feed], conlist(Feed, min_length=1)]
203
+
204
+
205
+ class AcknowledgeResult(CamelModel):
206
+ error: Optional[str] = None
207
+
208
+
209
+ class AcknowledgePayload(CamelModel):
210
+ action: Optional[Action]
211
+ nonce: Optional[str]
212
+ result: AcknowledgeResult
213
+
214
+
215
+ class AggregatedOrder(CamelModel):
216
+ symbol: str
217
+ side: TradeSide = Field(..., description="Side of the order (BID=0, ASK=1).")
218
+ amount: str = Field(
219
+ ...,
220
+ description="The aggregated amount for this price level. Stored as string to preserve decimal precision.",
221
+ )
222
+ price: str = Field(
223
+ ...,
224
+ description="The aggregated price level. Stored as string to preserve decimal precision.",
225
+ )
226
+
227
+ @field_validator("amount", "price")
228
+ @classmethod
229
+ def validate_nonnegative_decimals(cls, v, info):
230
+ return validate_decimal_str(
231
+ v, f"AggregatedOrder.{info.field_name}", nonnegative=True
232
+ )
233
+
234
+
235
+ class OrderBookL2Contents(CamelModel):
236
+ message_type: MessageType
237
+ ordinal: int = Field(..., ge=0, description="Ordinal must be non-negative.")
238
+ data: list[AggregatedOrder]
239
+
240
+
241
+ class OrderBookL2Payload(CamelModel):
242
+ feed: Literal[Feed.ORDER_BOOK_L2] = Field(default=Feed.ORDER_BOOK_L2)
243
+ params: OrderBookL2Params
244
+ contents: OrderBookL2Contents
245
+
246
+
247
+ class OrderBookL3Order(CamelModel):
248
+ order_hash: HexStr
249
+ symbol: str
250
+ side: TradeSide
251
+ original_amount: str = Field(..., description="Original order amount")
252
+ amount: str = Field(..., description="Remaining order amount")
253
+ price: str = Field(..., description="Order price")
254
+ trader_address: HexStr
255
+ strategy_id_hash: HexStr
256
+ book_ordinal: int = Field(..., ge=0)
257
+
258
+ @field_validator("original_amount", "amount", "price")
259
+ @classmethod
260
+ def validate_nonnegative_decimals(cls, v, info):
261
+ return validate_decimal_str(
262
+ v, f"OrderBookL3Order.{info.field_name}", nonnegative=True
263
+ )
264
+
265
+
266
+ class OrderBookL3Contents(CamelModel):
267
+ message_type: MessageType
268
+ ordinal: int = Field(..., ge=0)
269
+ data: list[OrderBookL3Order]
270
+
271
+
272
+ class OrderBookL3Payload(CamelModel):
273
+ feed: Literal[Feed.ORDER_BOOK_L3] = Field(default=Feed.ORDER_BOOK_L3)
274
+ params: OrderBookL3Params
275
+ contents: OrderBookL3Contents
276
+
277
+
278
+ class MarkPriceEntry(CamelModel):
279
+ global_ordinal: int = Field(..., ge=0)
280
+ epoch_id: int = Field(..., ge=0)
281
+ price: str = Field(
282
+ ...,
283
+ description="The mark price, stored as a string to preserve decimal precision.",
284
+ )
285
+ funding_rate: str = Field(
286
+ ...,
287
+ description="The new funding rate for this symbol. Stored as a string to preserve decimal precision.",
288
+ )
289
+ symbol: str
290
+ created_at: datetime
291
+
292
+ @field_validator("price")
293
+ @classmethod
294
+ def validate_nonnegative_decimals(cls, v, info):
295
+ return validate_decimal_str(
296
+ v, f"MarkPriceEntry.{info.field_name}", nonnegative=True
297
+ )
298
+
299
+ @field_validator("funding_rate")
300
+ @classmethod
301
+ def validate_decimals(cls, v, info):
302
+ return validate_decimal_str(
303
+ v, f"MarkPriceEntry.{info.field_name}", nonnegative=False
304
+ )
305
+
306
+
307
+ class MarkPriceContents(CamelModel):
308
+ message_type: MessageType
309
+ ordinal: int = Field(..., ge=0)
310
+ data: list[MarkPriceEntry]
311
+
312
+
313
+ class MarkPricePayload(CamelModel):
314
+ feed: Literal[Feed.MARK_PRICE] = Field(default=Feed.MARK_PRICE)
315
+ params: MarkPriceParams
316
+ contents: MarkPriceContents
317
+
318
+
319
+ class OrderIntent(CamelModel):
320
+ epoch_id: int = Field(..., ge=0)
321
+ order_hash: HexStr
322
+ symbol: str
323
+ side: TradeSide
324
+ amount: str = Field(
325
+ ..., description="Amount stored as string to preserve decimal precision."
326
+ )
327
+ price: str = Field(
328
+ ..., description="Price stored as string to preserve decimal precision."
329
+ )
330
+ trader_address: HexStr
331
+ strategy_id_hash: HexStr
332
+ order_type: OrderType
333
+ stop_price: str = Field(
334
+ ..., description="Stop price stored as string to preserve decimal precision."
335
+ )
336
+ nonce: str
337
+ signature: HexStr
338
+ created_at: datetime
339
+
340
+ @field_validator("amount", "price", "stop_price")
341
+ @classmethod
342
+ def validate_nonnegative_decimals(cls, v, info):
343
+ return validate_decimal_str(
344
+ v, f"OrderIntent.{info.field_name}", nonnegative=True
345
+ )
346
+
347
+
348
+ class OrderUpdate(CamelModel):
349
+ global_ordinal: int = Field(..., ge=0)
350
+ epoch_id: int = Field(..., ge=0)
351
+ order_rejection: Optional[OrderRejection] = None
352
+ cancel_rejection: Optional[CancelRejection] = None
353
+ reason: OrderUpdateReason
354
+ amount: Optional[str] = Field(
355
+ default=None,
356
+ description="The filled amount for the update, as a string preserving decimal precision.",
357
+ )
358
+ quote_asset_amount: Optional[str] = Field(
359
+ default=None,
360
+ description="The quote asset transacted amount for this update, as a string preserving decimal precision.",
361
+ )
362
+ symbol: str
363
+ price: Optional[str] = Field(
364
+ default=None,
365
+ description="The fill price for the update, as a string preserving decimal precision.",
366
+ )
367
+ order_match_ordinal: Optional[int] = Field(
368
+ default=None,
369
+ ge=0,
370
+ description="Ordinal representing the order match outcome for this update.",
371
+ )
372
+ ordinal: int = Field(..., ge=0)
373
+ last_executed_amount: Optional[str] = Field(
374
+ default=None,
375
+ description="The last executed amount from the update, as a string preserving decimal precision.",
376
+ )
377
+ last_executed_price: Optional[str] = Field(
378
+ default=None,
379
+ description="The last executed price from the update, as a string preserving decimal precision.",
380
+ )
381
+ cumulative_filled_amount: Optional[str] = Field(
382
+ default=None,
383
+ description="The cumulative filled amount up to this update, as a string preserving decimal precision.",
384
+ )
385
+ cumulative_quote_asset_transacted_amount: Optional[str] = Field(
386
+ default=None,
387
+ description="The cumulative quote asset transacted amount up to this update, as a string preserving decimal precision.",
388
+ )
389
+ last_quote_asset_transacted_amount: Optional[str] = Field(
390
+ default=None,
391
+ description="The last quote asset transacted amount from this update, as a string preserving decimal precision.",
392
+ )
393
+ maker_fee_collateral: Optional[str] = Field(
394
+ default=None,
395
+ description="The maker fee in collateral, as a string preserving decimal precision.",
396
+ )
397
+ maker_fee_ddx: Optional[str] = Field(
398
+ default=None,
399
+ description="The maker fee in DDX, as a string preserving decimal precision.",
400
+ )
401
+ maker_realized_pnl: Optional[str] = Field(
402
+ default=None,
403
+ description="The realized PnL for the maker trade in the collateral currency, stored as string to preserve decimal precision.",
404
+ )
405
+ taker_order_intent: Optional[OrderIntent] = None
406
+ taker_fee_collateral: Optional[str] = Field(
407
+ default=None,
408
+ description="The taker fee in collateral, as a string preserving decimal precision.",
409
+ )
410
+ taker_fee_ddx: Optional[str] = Field(
411
+ default=None,
412
+ description="The taker fee in DDX, as a string preserving decimal precision.",
413
+ )
414
+ taker_realized_pnl: Optional[str] = None
415
+ liquidated_trader_address: Optional[str] = None
416
+ liquidated_strategy_id_hash: Optional[str] = None
417
+ maker_order_intent: OrderIntent
418
+ created_at: datetime
419
+
420
+ @field_validator(
421
+ "amount",
422
+ "quote_asset_amount",
423
+ "price",
424
+ "last_executed_amount",
425
+ "last_executed_price",
426
+ "cumulative_filled_amount",
427
+ "cumulative_quote_asset_transacted_amount",
428
+ "last_quote_asset_transacted_amount",
429
+ "maker_fee_collateral",
430
+ "maker_fee_ddx",
431
+ "taker_fee_collateral",
432
+ "taker_fee_ddx",
433
+ )
434
+ @classmethod
435
+ def validate_nonnegative_optional_decimals(cls, v, info):
436
+ if v is None:
437
+ return v
438
+ return validate_decimal_str(
439
+ v, f"OrderUpdate.{info.field_name}", nonnegative=True
440
+ )
441
+
442
+ @field_validator("maker_realized_pnl", "taker_realized_pnl")
443
+ @classmethod
444
+ def validate_optional_decimals(cls, v, info):
445
+ if v is None:
446
+ return v
447
+ return validate_decimal_str(
448
+ v, f"OrderUpdate.{info.field_name}", nonnegative=False
449
+ )
450
+
451
+
452
+ class OrderUpdateContents(CamelModel):
453
+ message_type: MessageType
454
+ ordinal: int = Field(..., ge=0)
455
+ data: list[OrderUpdate]
456
+
457
+
458
+ class OrderUpdatePayload(CamelModel):
459
+ feed: Literal[Feed.ORDER_UPDATE] = Field(default=Feed.ORDER_UPDATE)
460
+ params: OrderUpdateParams
461
+ contents: OrderUpdateContents
462
+
463
+
464
+ class Position(CamelModel):
465
+ symbol: str
466
+ balance: str = Field(
467
+ ...,
468
+ description="Position balance (after PnL was realized), stored as string to preserve decimal precision",
469
+ )
470
+ side: PositionSide
471
+ avg_entry_price: str = Field(
472
+ ...,
473
+ description="Average entry price, stored as string to preserve decimal precision",
474
+ )
475
+ realized_pnl: str = Field(
476
+ ..., description="Realized PnL, stored as string to preserve decimal precision"
477
+ )
478
+
479
+ @field_validator("balance", "avg_entry_price")
480
+ @classmethod
481
+ def validate_nonnegative_decimals(cls, v, info):
482
+ return validate_decimal_str(v, f"Position.{info.field_name}", nonnegative=True)
483
+
484
+ @field_validator("realized_pnl")
485
+ @classmethod
486
+ def validate_decimals(cls, v, info):
487
+ return validate_decimal_str(v, f"Position.{info.field_name}", nonnegative=False)
488
+
489
+
490
+ class StrategyUpdate(CamelModel):
491
+ global_ordinal: int = Field(..., ge=0)
492
+ epoch_id: int = Field(..., ge=0)
493
+ withdraw_rejection: Optional[WithdrawRejection] = None
494
+ reason: StrategyUpdateReason
495
+ trader_address: HexStr
496
+ strategy_id_hash: HexStr
497
+ collateral_address: HexStr
498
+ collateral_symbol: Literal["USDC"]
499
+ amount: Optional[str] = Field(
500
+ default=None,
501
+ description="The amount added to the strategy (may be negative for withdrawals). Stored as string to preserve decimal precision.",
502
+ )
503
+ new_avail_collateral: Optional[str] = Field(
504
+ default=None,
505
+ description="The available collateral after the update, as a string preserving decimal precision.",
506
+ )
507
+ new_locked_collateral: Optional[str] = Field(
508
+ default=None,
509
+ description="The locked collateral after the update, as a string preserving decimal precision.",
510
+ )
511
+ block_number: Optional[int] = Field(
512
+ default=None,
513
+ ge=0,
514
+ description="The block number when this update was processed.",
515
+ )
516
+ positions: Optional[list[Position]] = Field(
517
+ default=None,
518
+ description="The updated positions after a PnL realization, as an array of positions. Null if not applicable.",
519
+ )
520
+ created_at: datetime
521
+
522
+ @field_validator("new_avail_collateral", "new_locked_collateral")
523
+ @classmethod
524
+ def validate_nonnegative_optional_decimals(cls, v, info):
525
+ if v is None:
526
+ return v
527
+ return validate_decimal_str(
528
+ v, f"StrategyUpdate.{info.field_name}", nonnegative=True
529
+ )
530
+
531
+ @field_validator("amount")
532
+ @classmethod
533
+ def validate_optional_decimals(cls, v, info):
534
+ if v is None:
535
+ return v
536
+ return validate_decimal_str(
537
+ v, f"StrategyUpdate.{info.field_name}", nonnegative=False
538
+ )
539
+
540
+
541
+ class StrategyUpdateContents(CamelModel):
542
+ message_type: MessageType
543
+ ordinal: int = Field(..., ge=0)
544
+ data: list[StrategyUpdate]
545
+
546
+
547
+ class StrategyUpdatePayload(CamelModel):
548
+ feed: Literal[Feed.STRATEGY_UPDATE] = Field(default=Feed.STRATEGY_UPDATE)
549
+ params: StrategyUpdateParams
550
+ contents: StrategyUpdateContents
551
+
552
+
553
+ class TraderUpdate(CamelModel):
554
+ global_ordinal: int = Field(..., ge=0)
555
+ epoch_id: int = Field(..., ge=0)
556
+ withdraw_ddx_rejection: Optional[WithdrawDDXRejection] = None
557
+ reason: TraderUpdateReason
558
+ trader_address: HexStr
559
+ amount: Optional[str] = Field(
560
+ default=None,
561
+ description="The change in trader amount (may be negative), stored as string to preserve decimal precision.",
562
+ )
563
+ new_avail_ddx_balance: Optional[str] = Field(
564
+ default=None,
565
+ description="The new available DDX balance after the update, as a string preserving decimal precision.",
566
+ )
567
+ new_locked_ddx_balance: Optional[str] = Field(
568
+ default=None,
569
+ description="The new locked DDX balance after the update, as a string preserving decimal precision.",
570
+ )
571
+ pay_fees_in_ddx: Optional[bool] = None
572
+ block_number: Optional[int] = Field(
573
+ default=None,
574
+ ge=0,
575
+ description="The block number when this trader update was processed.",
576
+ )
577
+ created_at: datetime
578
+
579
+ @field_validator("new_avail_ddx_balance", "new_locked_ddx_balance")
580
+ @classmethod
581
+ def validate_nonnegative_optional_decimals(cls, v, info):
582
+ if v is None:
583
+ return v
584
+ return validate_decimal_str(
585
+ v, f"TraderUpdate.{info.field_name}", nonnegative=True
586
+ )
587
+
588
+ @field_validator("amount")
589
+ @classmethod
590
+ def validate_optional_decimals(cls, v, info):
591
+ if v is None:
592
+ return v
593
+ return validate_decimal_str(
594
+ v, f"TraderUpdate.{info.field_name}", nonnegative=False
595
+ )
596
+
597
+
598
+ class TraderUpdateContents(CamelModel):
599
+ message_type: MessageType
600
+ ordinal: int = Field(..., ge=0)
601
+ data: list[TraderUpdate]
602
+
603
+
604
+ class TraderUpdatePayload(CamelModel):
605
+ feed: Literal[Feed.TRADER_UPDATE] = Field(default=Feed.TRADER_UPDATE)
606
+ params: TraderUpdateParams
607
+ contents: TraderUpdateContents
608
+
609
+
610
+ FeedPayload = (
611
+ OrderBookL2Payload
612
+ | OrderBookL3Payload
613
+ | MarkPricePayload
614
+ | OrderUpdatePayload
615
+ | StrategyUpdatePayload
616
+ | TraderUpdatePayload
617
+ )
618
+
619
+ SubscriptionPayload = SubscribePayload | UnsubscribePayload | AcknowledgePayload
620
+
621
+ Contents = (
622
+ OrderBookL2Contents
623
+ | OrderBookL3Contents
624
+ | MarkPriceContents
625
+ | OrderUpdateContents
626
+ | StrategyUpdateContents
627
+ | TraderUpdateContents
628
+ )
629
+
630
+ __all__ = [
631
+ "Action",
632
+ "Feed",
633
+ "MessageType",
634
+ "TradeSide",
635
+ "OrderType",
636
+ "OrderRejection",
637
+ "CancelRejection",
638
+ "OrderUpdateReason",
639
+ "WithdrawRejection",
640
+ "StrategyUpdateReason",
641
+ "WithdrawDDXRejection",
642
+ "TraderUpdateReason",
643
+ "OrderBookL2Params",
644
+ "OrderBookL3Params",
645
+ "MarkPriceParams",
646
+ "OrderIdentifier",
647
+ "OrderUpdateParams",
648
+ "StrategyIdentifier",
649
+ "StrategyUpdateParams",
650
+ "TraderUpdateParams",
651
+ "FeedWithParams",
652
+ "SubscribePayload",
653
+ "UnsubscribePayload",
654
+ "AcknowledgeResult",
655
+ "AcknowledgePayload",
656
+ "AggregatedOrder",
657
+ "OrderBookL2Contents",
658
+ "OrderBookL2Payload",
659
+ "OrderBookL3Order",
660
+ "OrderBookL3Contents",
661
+ "OrderBookL3Payload",
662
+ "MarkPriceEntry",
663
+ "MarkPriceContents",
664
+ "MarkPricePayload",
665
+ "OrderIntent",
666
+ "OrderUpdate",
667
+ "OrderUpdateContents",
668
+ "OrderUpdatePayload",
669
+ "Position",
670
+ "StrategyUpdate",
671
+ "StrategyUpdateContents",
672
+ "StrategyUpdatePayload",
673
+ "TraderUpdate",
674
+ "TraderUpdateContents",
675
+ "TraderUpdatePayload",
676
+ "PositionSide",
677
+ "OrderFilter",
678
+ "StrategyFilter",
679
+ "TraderFilter",
680
+ "FeedPayload",
681
+ "SubscriptionPayload",
682
+ "Contents",
683
+ ]