ddx-python 1.0.4__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 (106) hide show
  1. ddx/.gitignore +1 -0
  2. ddx/__init__.py +58 -0
  3. ddx/_rust/__init__.pyi +2685 -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 +23 -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 +526 -0
  17. ddx/auditor/README.md +32 -0
  18. ddx/auditor/__init__.py +0 -0
  19. ddx/auditor/auditor_driver.py +1043 -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 +141 -0
  24. ddx/common/logging.py +184 -0
  25. ddx/common/market_aware_account.py +259 -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 +96 -0
  37. ddx/common/transactions/event.py +48 -0
  38. ddx/common/transactions/fee_distribution.py +119 -0
  39. ddx/common/transactions/funding.py +292 -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 +232 -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 +292 -0
  50. ddx/common/transactions/insurance_fund_update.py +138 -0
  51. ddx/common/transactions/insurance_fund_withdraw.py +100 -0
  52. ddx/common/transactions/liquidation.py +353 -0
  53. ddx/common/transactions/partial_fill.py +125 -0
  54. ddx/common/transactions/pnl_realization.py +120 -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 +97 -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 +158 -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 +131 -0
  64. ddx/common/transactions/withdraw.py +90 -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 +270 -0
  69. ddx/models/__init__.py +0 -0
  70. ddx/models/base.py +132 -0
  71. ddx/py.typed +0 -0
  72. ddx/realtime_client/__init__.py +2 -0
  73. ddx/realtime_client/config.py +2 -0
  74. ddx/realtime_client/models/__init__.py +611 -0
  75. ddx/realtime_client/realtime_client.py +646 -0
  76. ddx/rest_client/__init__.py +0 -0
  77. ddx/rest_client/clients/__init__.py +0 -0
  78. ddx/rest_client/clients/base_client.py +60 -0
  79. ddx/rest_client/clients/market_client.py +1243 -0
  80. ddx/rest_client/clients/on_chain_client.py +439 -0
  81. ddx/rest_client/clients/signed_client.py +292 -0
  82. ddx/rest_client/clients/system_client.py +843 -0
  83. ddx/rest_client/clients/trade_client.py +357 -0
  84. ddx/rest_client/constants/__init__.py +0 -0
  85. ddx/rest_client/constants/endpoints.py +66 -0
  86. ddx/rest_client/contracts/__init__.py +0 -0
  87. ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
  88. ddx/rest_client/contracts/ddx/__init__.py +1949 -0
  89. ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
  90. ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
  91. ddx/rest_client/contracts/i_stake/__init__.py +696 -0
  92. ddx/rest_client/exceptions/__init__.py +0 -0
  93. ddx/rest_client/exceptions/exceptions.py +32 -0
  94. ddx/rest_client/http/__init__.py +0 -0
  95. ddx/rest_client/http/http_client.py +336 -0
  96. ddx/rest_client/models/__init__.py +0 -0
  97. ddx/rest_client/models/market.py +693 -0
  98. ddx/rest_client/models/signed.py +61 -0
  99. ddx/rest_client/models/system.py +311 -0
  100. ddx/rest_client/models/trade.py +185 -0
  101. ddx/rest_client/utils/__init__.py +0 -0
  102. ddx/rest_client/utils/encryption_utils.py +26 -0
  103. ddx/utils/__init__.py +0 -0
  104. ddx_python-1.0.4.dist-info/METADATA +63 -0
  105. ddx_python-1.0.4.dist-info/RECORD +106 -0
  106. ddx_python-1.0.4.dist-info/WHEEL +5 -0
File without changes
@@ -0,0 +1,32 @@
1
+ from typing import Optional, Dict, Any
2
+
3
+
4
+ class HTTPClientError(Exception):
5
+ """Base exception for HTTP client errors."""
6
+
7
+ def __init__(
8
+ self,
9
+ request: str,
10
+ message: str,
11
+ status_code: int,
12
+ time: str,
13
+ response: Optional[Dict[str, Any]] = None,
14
+ ):
15
+ self.request = request
16
+ self.message = message
17
+ self.status_code = status_code
18
+ self.time = time
19
+ self.response = response
20
+ super().__init__(f"{message} (Status: {status_code}, Time: {time})")
21
+
22
+
23
+ class InvalidRequestError(HTTPClientError):
24
+ """Exception raised for API-specific errors."""
25
+
26
+ pass
27
+
28
+
29
+ class FailedRequestError(HTTPClientError):
30
+ """Exception raised for HTTP request failures."""
31
+
32
+ pass
File without changes
@@ -0,0 +1,336 @@
1
+ import asyncio
2
+ import json
3
+ from datetime import datetime
4
+ from typing import Optional, Dict, Any
5
+ import logging
6
+ from httpx import AsyncClient, Limits, RequestError
7
+
8
+ from ddx.rest_client.exceptions.exceptions import (
9
+ InvalidRequestError,
10
+ FailedRequestError,
11
+ )
12
+
13
+
14
+ class HTTPClient:
15
+ """
16
+ HTTP client for DerivaDEX API.
17
+
18
+ Handles request preparation and response processing.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ timeout: int = 10,
24
+ max_retries: int = 3,
25
+ retry_delay: int = 1,
26
+ max_connections: int = 100,
27
+ max_keepalive: int = 20,
28
+ keepalive_expiry: int = 5,
29
+ logger: Optional[logging.Logger] = None,
30
+ ):
31
+ """
32
+ Initialize the HTTP client.
33
+
34
+ Parameters
35
+ ----------
36
+ timeout : int
37
+ Request timeout in seconds
38
+ max_retries : int
39
+ Maximum number of retry attempts
40
+ retry_delay : int
41
+ Delay between retries in seconds
42
+ max_connections : int, default=100
43
+ Max concurrent connections allowed
44
+ max_keepalive : int, optional
45
+ Max allowable keep-alive connections, None means always allow
46
+ keepalive_expiry: int, default=5
47
+ Time in seconds to keep HTTP connection alive. None means connection never expires
48
+ logger : Optional[logging.Logger]
49
+ Logger instance
50
+ """
51
+
52
+ self.timeout = timeout
53
+ self.max_retries = max_retries
54
+ self.retry_delay = retry_delay
55
+ self.max_connections = max_connections
56
+ self.max_keepalive = max_keepalive
57
+ self.keepalive_expiry = keepalive_expiry
58
+
59
+ # Setup logging
60
+ self.logger = logger or logging.getLogger(__name__)
61
+
62
+ # Initialize client to None - will be created in __aenter__
63
+ self.client: Optional[AsyncClient] = None
64
+
65
+ # Define retry status codes
66
+ self.retry_codes = {408, 429, 500, 502, 503, 504}
67
+
68
+ async def __aenter__(self):
69
+ """
70
+ Context manager entry point. Initializes the HTTP client.
71
+
72
+ Returns
73
+ -------
74
+ BaseHTTPClient
75
+ The client instance
76
+ """
77
+
78
+ self.client = AsyncClient(
79
+ timeout=self.timeout,
80
+ follow_redirects=True,
81
+ headers={"Content-Type": "application/json"},
82
+ limits=Limits(
83
+ max_keepalive_connections=self.max_keepalive,
84
+ max_connections=self.max_connections,
85
+ keepalive_expiry=self.keepalive_expiry
86
+ )
87
+ )
88
+ self.logger.debug("HTTP client initialized")
89
+
90
+ return self
91
+
92
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
93
+ """
94
+ Context manager exit point. Ensures proper cleanup of resources.
95
+
96
+ Parameters
97
+ ----------
98
+ exc_type : Type[Exception], optional
99
+ The exception type if an exception was raised
100
+ exc_val : Exception, optional
101
+ The exception value if an exception was raised
102
+ exc_tb : TracebackType, optional
103
+ The traceback if an exception was raised
104
+ """
105
+
106
+ if self.client:
107
+ await self.client.aclose()
108
+ self.client = None
109
+ self.logger.debug("HTTP client closed")
110
+
111
+ async def get(
112
+ self,
113
+ url: str,
114
+ params: Optional[Dict[str, Any]] = None,
115
+ headers: Optional[Dict[str, str]] = None,
116
+ ) -> Dict[str, Any]:
117
+ """
118
+ Send a GET request.
119
+
120
+ Parameters
121
+ ----------
122
+ url : str
123
+ The URL to send the request to
124
+ params : Optional[Dict[str, Any]]
125
+ Query parameters
126
+ headers : Optional[Dict[str, str]]
127
+ Additional headers
128
+
129
+ Returns
130
+ -------
131
+ Dict[str, Any]
132
+ The response data
133
+
134
+ Raises
135
+ ------
136
+ FailedRequestError
137
+ If the request fails after retries
138
+ InvalidRequestError
139
+ If the API returns an error response
140
+ """
141
+
142
+ return await self._request("GET", url, params=params, headers=headers)
143
+
144
+ async def post(
145
+ self,
146
+ url: str,
147
+ data: Any = None,
148
+ json_data: Optional[Dict[str, Any]] = None,
149
+ headers: Optional[Dict[str, str]] = None,
150
+ ) -> Dict[str, Any]:
151
+ """
152
+ Send a POST request.
153
+
154
+ Parameters
155
+ ----------
156
+ url : str
157
+ The URL to send the request to
158
+ data : Any
159
+ Raw request body
160
+ json_data : Optional[Dict[str, Any]]
161
+ JSON data to serialize
162
+ headers : Optional[Dict[str, str]]
163
+ Additional headers
164
+
165
+ Returns
166
+ -------
167
+ Dict[str, Any]
168
+ The response data
169
+
170
+ Raises
171
+ ------
172
+ FailedRequestError
173
+ If the request fails after retries
174
+ InvalidRequestError
175
+ If the API returns an error response
176
+ """
177
+
178
+ return await self._request(
179
+ "POST", url, data=data, json_data=json_data, headers=headers
180
+ )
181
+
182
+ def _parse_json_or_failed(self, response, request_info) -> Dict[str, Any]:
183
+ try:
184
+ return response.json()
185
+ except json.JSONDecodeError:
186
+ raise FailedRequestError(
187
+ request=request_info,
188
+ message="Failed to parse JSON response",
189
+ status_code=response.status_code,
190
+ time=datetime.now().strftime("%H:%M:%S"),
191
+ response={"text": response.text},
192
+ )
193
+
194
+ def _raise_from_response(self, response, request_info) -> None:
195
+ try:
196
+ body = response.json()
197
+ except json.JSONDecodeError:
198
+ raise FailedRequestError(
199
+ request=request_info,
200
+ message=f"Request failed with status {response.status_code}",
201
+ status_code=response.status_code,
202
+ time=datetime.now().strftime("%H:%M:%S"),
203
+ response={"text": response.text},
204
+ )
205
+ if isinstance(body, dict):
206
+ reason = body.get("error_reason")
207
+ safety = body.get("safety_failure")
208
+ message = body.get("message") or f"Request failed with status {response.status_code}"
209
+ details = f"{reason}: {message}" if reason else message
210
+ if safety:
211
+ details = f"{details} (safety_failure={safety})"
212
+ exc_cls = FailedRequestError if response.status_code >= 500 else InvalidRequestError
213
+ raise exc_cls(
214
+ request=request_info,
215
+ message=details,
216
+ status_code=response.status_code,
217
+ time=datetime.now().strftime("%H:%M:%S"),
218
+ response=body,
219
+ )
220
+ exc_cls = FailedRequestError if response.status_code >= 500 else InvalidRequestError
221
+ raise exc_cls(
222
+ request=request_info,
223
+ message=f"Request failed with status {response.status_code}",
224
+ status_code=response.status_code,
225
+ time=datetime.now().strftime("%H:%M:%S"),
226
+ response={"text": response.text},
227
+ )
228
+
229
+ async def _request(
230
+ self,
231
+ method: str,
232
+ url: str,
233
+ params: Optional[Dict[str, Any]] = None,
234
+ data: Any = None,
235
+ json_data: Optional[Dict[str, Any]] = None,
236
+ headers: Optional[Dict[str, str]] = None,
237
+ ) -> Dict[str, Any]:
238
+ """
239
+ Execute HTTP request with retries and error handling.
240
+
241
+ Parameters
242
+ ----------
243
+ method : str
244
+ HTTP method (GET, POST, etc.)
245
+ url : str
246
+ The URL to send the request to
247
+ params : Optional[Dict[str, Any]]
248
+ Query parameters
249
+ data : Any
250
+ Raw request body
251
+ json_data : Optional[Dict[str, Any]]
252
+ JSON data to serialize
253
+ headers : Optional[Dict[str, str]]
254
+ Additional headers
255
+
256
+ Returns
257
+ -------
258
+ Dict[str, Any]
259
+ The response data
260
+
261
+ Raises
262
+ ------
263
+ FailedRequestError
264
+ If the request fails after retries
265
+ InvalidRequestError
266
+ If the API returns an error response
267
+ RuntimeError
268
+ If the client is not initialized
269
+ """
270
+
271
+ if self.client is None:
272
+ raise RuntimeError(
273
+ "HTTP client not initialized. Use 'async with' context manager."
274
+ )
275
+
276
+ request_info = (
277
+ f"{method} {url}; params: {params}; json: {json_data}; data: {data}"
278
+ )
279
+
280
+ self.logger.debug(f"Request: {request_info}")
281
+
282
+ retries = 0
283
+ while retries <= self.max_retries:
284
+ try:
285
+ response = await self.client.request(
286
+ method=method,
287
+ url=url,
288
+ params=params,
289
+ content=data,
290
+ json=json_data,
291
+ headers=headers,
292
+ )
293
+
294
+ # Log response status
295
+ self.logger.debug(f"Response status: {response.status_code}")
296
+
297
+ # Check if the request was successful
298
+ if response.status_code == 200:
299
+ return self._parse_json_or_failed(response, request_info)
300
+
301
+ # Handle retry-able status codes
302
+ if (
303
+ response.status_code in self.retry_codes
304
+ and retries < self.max_retries
305
+ ):
306
+ wait_time = self.retry_delay * (2**retries)
307
+ self.logger.warning(
308
+ f"Request failed with status {response.status_code}. "
309
+ f"Retrying in {wait_time}s ({retries+1}/{self.max_retries})"
310
+ )
311
+ await asyncio.sleep(wait_time)
312
+ retries += 1
313
+ continue
314
+
315
+ # Non-retryable error or max retries reached
316
+ self._raise_from_response(response, request_info)
317
+
318
+ except RequestError as e:
319
+ # Network-related errors
320
+ if retries < self.max_retries:
321
+ wait_time = self.retry_delay * (2**retries)
322
+ self.logger.warning(
323
+ f"Request error: {str(e)}. "
324
+ f"Retrying in {wait_time}s ({retries+1}/{self.max_retries})"
325
+ )
326
+ await asyncio.sleep(wait_time)
327
+ retries += 1
328
+ continue
329
+
330
+ raise FailedRequestError(
331
+ request=request_info,
332
+ message=f"Request error: {str(e)}",
333
+ status_code=0,
334
+ time=datetime.now().strftime("%H:%M:%S"),
335
+ response=None,
336
+ )
File without changes