bitvavo-api-upgraded 4.3.1__tar.gz → 4.4.0__tar.gz
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.
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/PKG-INFO +1 -1
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/pyproject.toml +2 -2
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/auth/rate_limit.py +65 -7
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/transport/http.py +49 -1
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/README.md +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/bitvavo.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/dataframe_utils.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/helper_funcs.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/py.typed +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/settings.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/type_aliases.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/adapters/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/adapters/returns_adapter.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/auth/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/auth/signing.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/errors.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/model_preferences.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/private_models.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/public_models.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/settings.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/types.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/validation_helpers.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/df/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/df/convert.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/base.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/common.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/private.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/public.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/facade.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/py.typed +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/schemas/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/schemas/private_schemas.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/schemas/public_schemas.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/transport/__init__.py +0 -0
- {bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/ws/__init__.py +0 -0
@@ -6,7 +6,7 @@ build-backend = "uv_build"
|
|
6
6
|
# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
|
7
7
|
[project]
|
8
8
|
name = "bitvavo-api-upgraded"
|
9
|
-
version = "4.
|
9
|
+
version = "4.4.0"
|
10
10
|
description = "A unit-tested fork of the Bitvavo API"
|
11
11
|
readme = "README.md"
|
12
12
|
requires-python = ">=3.10"
|
@@ -108,7 +108,7 @@ dev-dependencies = [
|
|
108
108
|
]
|
109
109
|
|
110
110
|
[tool.bumpversion]
|
111
|
-
current_version = "4.
|
111
|
+
current_version = "4.4.0"
|
112
112
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
113
113
|
serialize = ["{major}.{minor}.{patch}"]
|
114
114
|
search = "{current_version}"
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/auth/rate_limit.py
RENAMED
@@ -20,10 +20,9 @@ class DefaultRateLimitStrategy(RateLimitStrategy):
|
|
20
20
|
|
21
21
|
|
22
22
|
class RateLimitManager:
|
23
|
-
"""Manages rate limiting for multiple API keys
|
23
|
+
"""Manages rate limiting for multiple API keys.
|
24
24
|
|
25
|
-
Each API key index has its own rate limit state.
|
26
|
-
for keyless requests.
|
25
|
+
Each API key index has its own rate limit state.
|
27
26
|
"""
|
28
27
|
|
29
28
|
def __init__(self, default_remaining: int, buffer: int, strategy: RateLimitStrategy | None = None) -> None:
|
@@ -35,7 +34,7 @@ class RateLimitManager:
|
|
35
34
|
strategy: Optional strategy callback when rate limit exceeded
|
36
35
|
"""
|
37
36
|
self.default_remaining: int = default_remaining
|
38
|
-
self.state: dict[int, dict[str, int]] = {
|
37
|
+
self.state: dict[int, dict[str, int]] = {}
|
39
38
|
self.buffer: int = buffer
|
40
39
|
|
41
40
|
self._strategy: RateLimitStrategy = strategy or DefaultRateLimitStrategy()
|
@@ -43,13 +42,13 @@ class RateLimitManager:
|
|
43
42
|
def ensure_key(self, idx: int) -> None:
|
44
43
|
"""Ensure a key index exists in the state."""
|
45
44
|
if idx not in self.state:
|
46
|
-
self.state[idx] = {"remaining": self.
|
45
|
+
self.state[idx] = {"remaining": self.default_remaining, "resetAt": 0}
|
47
46
|
|
48
47
|
def has_budget(self, idx: int, weight: int) -> bool:
|
49
48
|
"""Check if there's enough rate limit budget for a request.
|
50
49
|
|
51
50
|
Args:
|
52
|
-
idx: API key index
|
51
|
+
idx: API key index
|
53
52
|
weight: Weight of the request
|
54
53
|
|
55
54
|
Returns:
|
@@ -66,7 +65,7 @@ class RateLimitManager:
|
|
66
65
|
the response doesn't include rate limit headers.
|
67
66
|
|
68
67
|
Args:
|
69
|
-
idx: API key index
|
68
|
+
idx: API key index
|
70
69
|
weight: Weight of the request
|
71
70
|
"""
|
72
71
|
self.ensure_key(idx)
|
@@ -144,3 +143,62 @@ class RateLimitManager:
|
|
144
143
|
"""
|
145
144
|
self.ensure_key(idx)
|
146
145
|
return self.state[idx]["resetAt"]
|
146
|
+
|
147
|
+
def find_best_available_key(self, available_keys: list[int], weight: int) -> int | None:
|
148
|
+
"""Find the best API key for a request with given weight.
|
149
|
+
|
150
|
+
Prioritizes keys by:
|
151
|
+
1. Keys with sufficient budget (remaining - weight >= buffer)
|
152
|
+
2. Keys with the most remaining budget
|
153
|
+
3. Keys with the earliest reset time (if all are rate limited)
|
154
|
+
|
155
|
+
Args:
|
156
|
+
available_keys: List of available key indices
|
157
|
+
weight: Weight of the request
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
Best key index or None if no keys are suitable
|
161
|
+
"""
|
162
|
+
if not available_keys:
|
163
|
+
return None
|
164
|
+
|
165
|
+
suitable_keys = []
|
166
|
+
fallback_keys = []
|
167
|
+
|
168
|
+
for idx in available_keys:
|
169
|
+
self.ensure_key(idx)
|
170
|
+
if self.has_budget(idx, weight):
|
171
|
+
suitable_keys.append((idx, self.state[idx]["remaining"]))
|
172
|
+
else:
|
173
|
+
fallback_keys.append((idx, self.state[idx]["resetAt"]))
|
174
|
+
|
175
|
+
# Return key with most remaining budget if any have sufficient budget
|
176
|
+
if suitable_keys:
|
177
|
+
return max(suitable_keys, key=lambda x: x[1])[0]
|
178
|
+
|
179
|
+
# If no keys have budget, return the one that resets earliest
|
180
|
+
if fallback_keys:
|
181
|
+
return min(fallback_keys, key=lambda x: x[1])[0]
|
182
|
+
|
183
|
+
return None
|
184
|
+
|
185
|
+
def get_earliest_reset_time(self, key_indices: list[int]) -> int:
|
186
|
+
"""Get the earliest reset time among the given keys.
|
187
|
+
|
188
|
+
Args:
|
189
|
+
key_indices: List of key indices to check
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
Earliest reset timestamp in milliseconds, or 0 if no keys have reset times
|
193
|
+
"""
|
194
|
+
if not key_indices:
|
195
|
+
return 0
|
196
|
+
|
197
|
+
reset_times = []
|
198
|
+
for idx in key_indices:
|
199
|
+
self.ensure_key(idx)
|
200
|
+
reset_at = self.state[idx]["resetAt"]
|
201
|
+
if reset_at > 0: # Only consider keys that actually have a reset time
|
202
|
+
reset_times.append(reset_at)
|
203
|
+
|
204
|
+
return min(reset_times) if reset_times else 0
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/transport/http.py
RENAMED
@@ -86,6 +86,44 @@ class HTTPClient:
|
|
86
86
|
self.select_key(next_idx)
|
87
87
|
return True
|
88
88
|
|
89
|
+
def _find_available_key(self, weight: int) -> int | None:
|
90
|
+
"""Find the best available API key for a request with given weight.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
weight: Weight of the request
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
Best key index or None if all keys are rate limited
|
97
|
+
"""
|
98
|
+
available_keys = list(range(len(self._keys)))
|
99
|
+
return self.rate_limiter.find_best_available_key(available_keys, weight)
|
100
|
+
|
101
|
+
def _handle_rate_limit_exhaustion(self, weight: int) -> None:
|
102
|
+
"""Handle situation when all API keys are rate limited.
|
103
|
+
|
104
|
+
Sleeps until the earliest key reset time and then resets that key.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
weight: Weight of the original request
|
108
|
+
"""
|
109
|
+
all_keys = list(range(len(self._keys)))
|
110
|
+
earliest_reset = self.rate_limiter.get_earliest_reset_time(all_keys)
|
111
|
+
|
112
|
+
if earliest_reset > 0:
|
113
|
+
# Find which key has the earliest reset time
|
114
|
+
now = int(time.time() * 1000)
|
115
|
+
for idx in all_keys:
|
116
|
+
if self.rate_limiter.get_reset_at(idx) == earliest_reset:
|
117
|
+
if now < earliest_reset:
|
118
|
+
# Sleep until this key resets
|
119
|
+
self.rate_limiter.sleep_until_reset(idx)
|
120
|
+
self.rate_limiter.reset_key(idx)
|
121
|
+
self.select_key(idx)
|
122
|
+
return
|
123
|
+
|
124
|
+
# Fallback to current key's rate limit strategy
|
125
|
+
self.rate_limiter.handle_limit(self.key_index, weight)
|
126
|
+
|
89
127
|
def request(
|
90
128
|
self,
|
91
129
|
method: str,
|
@@ -111,6 +149,13 @@ class HTTPClient:
|
|
111
149
|
idx = self.key_index
|
112
150
|
self._ensure_rate_limit_initialized()
|
113
151
|
|
152
|
+
# Try to find the best available key for this request
|
153
|
+
best_key = self._find_available_key(weight)
|
154
|
+
if best_key is not None and best_key != idx:
|
155
|
+
self.select_key(best_key)
|
156
|
+
idx = best_key
|
157
|
+
|
158
|
+
# If current key doesn't have budget, try rotation
|
114
159
|
if not self.rate_limiter.has_budget(idx, weight):
|
115
160
|
for _ in range(len(self._keys)):
|
116
161
|
if self.rate_limiter.has_budget(idx, weight):
|
@@ -119,8 +164,11 @@ class HTTPClient:
|
|
119
164
|
idx = self.key_index
|
120
165
|
if not rotated:
|
121
166
|
break
|
167
|
+
|
168
|
+
# If still no budget after trying all keys, handle exhaustion smartly
|
122
169
|
if not self.rate_limiter.has_budget(idx, weight):
|
123
|
-
self.
|
170
|
+
self._handle_rate_limit_exhaustion(weight)
|
171
|
+
idx = self.key_index # Update idx after potential key change
|
124
172
|
|
125
173
|
url = f"{self.settings.rest_url}{endpoint}"
|
126
174
|
headers = self._create_auth_headers(method, endpoint, body)
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/__init__.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/bitvavo.py
RENAMED
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/helper_funcs.py
RENAMED
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/settings.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_api_upgraded/type_aliases.py
RENAMED
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/adapters/__init__.py
RENAMED
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/auth/__init__.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/auth/signing.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/private_models.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/public_models.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/core/settings.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/__init__.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/base.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/common.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/private.py
RENAMED
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/endpoints/public.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/schemas/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{bitvavo_api_upgraded-4.3.1 → bitvavo_api_upgraded-4.4.0}/src/bitvavo_client/transport/__init__.py
RENAMED
File without changes
|
File without changes
|