walrasquant-lib 0.4.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. walrasquant/__init__.py +7 -0
  2. walrasquant/aggregation.py +449 -0
  3. walrasquant/backends/__init__.py +5 -0
  4. walrasquant/backends/db.py +109 -0
  5. walrasquant/backends/db_memory.py +61 -0
  6. walrasquant/backends/db_postgresql.py +321 -0
  7. walrasquant/backends/db_sqlite.py +310 -0
  8. walrasquant/base/__init__.py +24 -0
  9. walrasquant/base/api_client.py +46 -0
  10. walrasquant/base/connector.py +863 -0
  11. walrasquant/base/ems.py +794 -0
  12. walrasquant/base/exchange.py +213 -0
  13. walrasquant/base/oms.py +428 -0
  14. walrasquant/base/retry.py +220 -0
  15. walrasquant/base/sms.py +545 -0
  16. walrasquant/base/ws_client.py +408 -0
  17. walrasquant/config.py +284 -0
  18. walrasquant/constants.py +413 -0
  19. walrasquant/core/__init__.py +0 -0
  20. walrasquant/core/cache.py +688 -0
  21. walrasquant/core/clock.py +59 -0
  22. walrasquant/core/connection.py +41 -0
  23. walrasquant/core/entity.py +504 -0
  24. walrasquant/core/nautilius_core.py +103 -0
  25. walrasquant/core/registry.py +41 -0
  26. walrasquant/engine.py +745 -0
  27. walrasquant/error.py +34 -0
  28. walrasquant/exchange/__init__.py +13 -0
  29. walrasquant/exchange/base_factory.py +172 -0
  30. walrasquant/exchange/binance/__init__.py +30 -0
  31. walrasquant/exchange/binance/connector.py +1093 -0
  32. walrasquant/exchange/binance/constants.py +934 -0
  33. walrasquant/exchange/binance/ems.py +140 -0
  34. walrasquant/exchange/binance/error.py +48 -0
  35. walrasquant/exchange/binance/exchange.py +144 -0
  36. walrasquant/exchange/binance/factory.py +115 -0
  37. walrasquant/exchange/binance/oms.py +1807 -0
  38. walrasquant/exchange/binance/rest_api.py +1653 -0
  39. walrasquant/exchange/binance/schema.py +1063 -0
  40. walrasquant/exchange/binance/websockets.py +389 -0
  41. walrasquant/exchange/bitget/__init__.py +28 -0
  42. walrasquant/exchange/bitget/connector.py +578 -0
  43. walrasquant/exchange/bitget/constants.py +392 -0
  44. walrasquant/exchange/bitget/ems.py +202 -0
  45. walrasquant/exchange/bitget/error.py +36 -0
  46. walrasquant/exchange/bitget/exchange.py +128 -0
  47. walrasquant/exchange/bitget/factory.py +135 -0
  48. walrasquant/exchange/bitget/oms.py +1619 -0
  49. walrasquant/exchange/bitget/rest_api.py +610 -0
  50. walrasquant/exchange/bitget/schema.py +885 -0
  51. walrasquant/exchange/bitget/websockets.py +753 -0
  52. walrasquant/exchange/bybit/__init__.py +32 -0
  53. walrasquant/exchange/bybit/connector.py +819 -0
  54. walrasquant/exchange/bybit/constants.py +479 -0
  55. walrasquant/exchange/bybit/ems.py +93 -0
  56. walrasquant/exchange/bybit/error.py +36 -0
  57. walrasquant/exchange/bybit/exchange.py +108 -0
  58. walrasquant/exchange/bybit/factory.py +128 -0
  59. walrasquant/exchange/bybit/oms.py +1195 -0
  60. walrasquant/exchange/bybit/rest_api.py +570 -0
  61. walrasquant/exchange/bybit/schema.py +867 -0
  62. walrasquant/exchange/bybit/websockets.py +307 -0
  63. walrasquant/exchange/hyperliquid/__init__.py +28 -0
  64. walrasquant/exchange/hyperliquid/connector.py +370 -0
  65. walrasquant/exchange/hyperliquid/constants.py +371 -0
  66. walrasquant/exchange/hyperliquid/ems.py +156 -0
  67. walrasquant/exchange/hyperliquid/error.py +48 -0
  68. walrasquant/exchange/hyperliquid/exchange.py +120 -0
  69. walrasquant/exchange/hyperliquid/factory.py +135 -0
  70. walrasquant/exchange/hyperliquid/oms.py +1081 -0
  71. walrasquant/exchange/hyperliquid/rest_api.py +348 -0
  72. walrasquant/exchange/hyperliquid/schema.py +583 -0
  73. walrasquant/exchange/hyperliquid/websockets.py +592 -0
  74. walrasquant/exchange/okx/__init__.py +25 -0
  75. walrasquant/exchange/okx/connector.py +931 -0
  76. walrasquant/exchange/okx/constants.py +518 -0
  77. walrasquant/exchange/okx/ems.py +144 -0
  78. walrasquant/exchange/okx/error.py +66 -0
  79. walrasquant/exchange/okx/exchange.py +102 -0
  80. walrasquant/exchange/okx/factory.py +138 -0
  81. walrasquant/exchange/okx/oms.py +1199 -0
  82. walrasquant/exchange/okx/rest_api.py +799 -0
  83. walrasquant/exchange/okx/schema.py +1449 -0
  84. walrasquant/exchange/okx/websockets.py +420 -0
  85. walrasquant/exchange/registry.py +201 -0
  86. walrasquant/execution/__init__.py +24 -0
  87. walrasquant/execution/algorithm.py +968 -0
  88. walrasquant/execution/algorithms/__init__.py +3 -0
  89. walrasquant/execution/algorithms/twap.py +392 -0
  90. walrasquant/execution/config.py +34 -0
  91. walrasquant/execution/constants.py +27 -0
  92. walrasquant/execution/schema.py +62 -0
  93. walrasquant/indicator.py +382 -0
  94. walrasquant/push.py +77 -0
  95. walrasquant/schema.py +755 -0
  96. walrasquant/strategy.py +1805 -0
  97. walrasquant/tools/__init__.py +0 -0
  98. walrasquant/tools/pm2_wrapper.py +1016 -0
  99. walrasquant/web/__init__.py +26 -0
  100. walrasquant/web/app.py +157 -0
  101. walrasquant/web/server.py +92 -0
  102. walrasquant_lib-0.4.20.dist-info/METADATA +162 -0
  103. walrasquant_lib-0.4.20.dist-info/RECORD +105 -0
  104. walrasquant_lib-0.4.20.dist-info/WHEEL +4 -0
  105. walrasquant_lib-0.4.20.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,220 @@
1
+ import asyncio
2
+ import nexuslog as logging
3
+
4
+ from typing import Callable, Awaitable, Generic, TypeVar
5
+ from random import randint
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ def get_exponential_backoff(
11
+ num_attempts: int,
12
+ delay_initial_ms: int = 500,
13
+ delay_max_ms: int = 2_000,
14
+ backoff_factor: int = 2,
15
+ jitter: bool = True,
16
+ ) -> int:
17
+ """
18
+ Compute the backoff using exponential backoff and jitter.
19
+
20
+ Parameters
21
+ ----------
22
+ num_attempts : int, default 1
23
+ The number of attempts that have already been made.
24
+ delay_initial_ms : int, default 500
25
+ The time to sleep in the first attempt.
26
+ delay_max_ms : int, default 2_000
27
+ The maximum delay.
28
+ backoff_factor : int, default 2
29
+ The exponential backoff factor for delays.
30
+ jitter : bool, default True
31
+ Whether or not to apply jitter.
32
+
33
+ Notes
34
+ -----
35
+ https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
36
+
37
+ Returns
38
+ -------
39
+ int
40
+ Delay in milliseconds.
41
+
42
+ """
43
+ delay = min(delay_max_ms, delay_initial_ms * backoff_factor ** (num_attempts - 1))
44
+
45
+ if jitter:
46
+ return randint(delay_initial_ms, delay) # noqa: S311
47
+
48
+ return delay
49
+
50
+
51
+ class RetryManager(Generic[T]):
52
+ """
53
+ Provides retry state management for an HTTP request.
54
+
55
+ This class is generic over `T`, where `T` is the return type of the
56
+ function passed to the `run` method.
57
+
58
+ Parameters
59
+ ----------
60
+ max_retries : int
61
+ The maximum number of retries before failure.
62
+ delay_initial_ms : int
63
+ The initial delay (milliseconds) for retries.
64
+ delay_max_ms : int
65
+ The maximum delay (milliseconds) for exponential backoff.
66
+ backoff_factor : int
67
+ The exponential backoff factor for retry delays.
68
+ exc_types : tuple[Type[BaseException], ...]
69
+ The exception types to handle for retries.
70
+ retry_check : Callable[[BaseException], None], optional
71
+ A function that performs additional checks on the exception.
72
+ If the function returns `False`, a retry will not be attempted.
73
+
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ max_retries: int,
79
+ delay_initial_ms: int,
80
+ delay_max_ms: int,
81
+ backoff_factor: int,
82
+ exc_types: tuple[type[BaseException], ...],
83
+ retry_check: Callable[[BaseException], bool] | None = None,
84
+ ) -> None:
85
+ self.max_retries = max_retries
86
+ self.delay_initial_ms = delay_initial_ms
87
+ self.delay_max_ms = delay_max_ms
88
+ self.backoff_factor = backoff_factor
89
+ self.exc_types = exc_types
90
+ self.retry_check = retry_check
91
+ self.cancel_event = asyncio.Event()
92
+ self._log = logging.getLogger(name=type(self).__name__)
93
+
94
+ self.name: str | None = None
95
+ self.details: list[object] | None = None
96
+ self.details_str: str | None = None
97
+ self.result: bool = False
98
+ self.message: str | None = None
99
+
100
+ def __repr__(self) -> str:
101
+ return f"<{type(self).__name__}(name='{self.name}', details={self.details}) at {hex(id(self))}>"
102
+
103
+ async def run(
104
+ self,
105
+ name: str,
106
+ func: Callable[..., Awaitable[T]],
107
+ details: list[object] | None = None,
108
+ jitter: bool = True,
109
+ *args,
110
+ **kwargs,
111
+ ) -> T | None:
112
+ """
113
+ Execute the given `func` with retry management.
114
+
115
+ If an exception in `self.exc_types` is raised, a warning is logged, and the function is
116
+ retried after a delay until the maximum retries are reached, at which point an error is logged.
117
+
118
+ Parameters
119
+ ----------
120
+ name : str
121
+ The name of the operation to run.
122
+ details : list[object], optional
123
+ The operation details such as identifiers.
124
+ func : Callable[..., Awaitable[T]]
125
+ The function to execute.
126
+ jitter : bool, default True
127
+ Whether to apply jitter to the retry delay.
128
+ args : Any
129
+ Positional arguments to pass to the function `func`.
130
+ kwargs : Any
131
+ Keyword arguments to pass to the function `func`.
132
+
133
+ Returns
134
+ -------
135
+ T | None
136
+ The result of the executed function, or ``None`` if the retries fail.
137
+
138
+ """
139
+ retries = 0
140
+ self.name = name
141
+ self.details = details
142
+
143
+ try:
144
+ while True:
145
+ if self.cancel_event.is_set():
146
+ self._cancel()
147
+ return None
148
+
149
+ try:
150
+ response = await func(*args, **kwargs)
151
+ self.result = True
152
+
153
+ return response # Successful request
154
+ except self.exc_types as e:
155
+ self._log.warning(repr(e))
156
+ if (
157
+ (self.retry_check and not self.retry_check(e))
158
+ or not self.max_retries
159
+ or retries >= self.max_retries
160
+ ):
161
+ self._log_error()
162
+ self.result = False
163
+ self.message = str(e)
164
+ raise e
165
+
166
+ retries += 1
167
+ retry_delay_ms = get_exponential_backoff(
168
+ delay_initial_ms=self.delay_initial_ms,
169
+ delay_max_ms=self.delay_max_ms,
170
+ backoff_factor=self.backoff_factor,
171
+ num_attempts=retries,
172
+ jitter=jitter,
173
+ )
174
+ self._log_retry(retries, retry_delay_ms=retry_delay_ms)
175
+ await asyncio.sleep(retry_delay_ms / 1000)
176
+ except asyncio.CancelledError:
177
+ self._cancel()
178
+ return None
179
+
180
+ def cancel(self) -> None:
181
+ """
182
+ Cancel the retry operation.
183
+ """
184
+ self._log.debug(f"Canceling {self!r}")
185
+ self.cancel_event.set()
186
+
187
+ def clear(self) -> None:
188
+ """
189
+ Clear all state from this retry manager.
190
+ """
191
+ self.name = None
192
+ self.details = None
193
+ self.details_str = None
194
+ self.result = False
195
+ self.message = None
196
+
197
+ def _cancel(self) -> None:
198
+ self._log.warning(f"Canceled retry for '{self.name}'")
199
+ self.result = False
200
+ self.message = "Canceled retry"
201
+
202
+ def _log_retry(self, retry: int, retry_delay_ms: int) -> None:
203
+ self._log.warning(
204
+ f"Retrying {retry}/{self.max_retries} for '{self.name}' "
205
+ f"in {retry_delay_ms / 1000}s{self._details_str()}",
206
+ )
207
+
208
+ def _log_error(self) -> None:
209
+ self._log.error(
210
+ f"Failed on '{self.name}'{self._details_str()}",
211
+ )
212
+
213
+ def _details_str(self) -> str:
214
+ if not self.details:
215
+ return ""
216
+
217
+ if not self.details_str:
218
+ self.details_str = ": " + ", ".join([repr(x) for x in self.details])
219
+
220
+ return self.details_str