bitvavo-api-upgraded 2.0.0__py3-none-any.whl → 2.3.0__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.
- bitvavo_api_upgraded/__init__.py +2 -0
- bitvavo_api_upgraded/bitvavo.py +1087 -182
- bitvavo_api_upgraded/dataframe_utils.py +175 -0
- bitvavo_api_upgraded/settings.py +81 -16
- bitvavo_api_upgraded/type_aliases.py +34 -0
- {bitvavo_api_upgraded-2.0.0.dist-info → bitvavo_api_upgraded-2.3.0.dist-info}/METADATA +363 -101
- bitvavo_api_upgraded-2.3.0.dist-info/RECORD +10 -0
- {bitvavo_api_upgraded-2.0.0.dist-info → bitvavo_api_upgraded-2.3.0.dist-info}/WHEEL +1 -1
- bitvavo_api_upgraded-2.0.0.dist-info/RECORD +0 -9
bitvavo_api_upgraded/bitvavo.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import contextlib
|
3
4
|
import datetime as dt
|
4
5
|
import hashlib
|
5
6
|
import hmac
|
@@ -10,20 +11,27 @@ from threading import Thread
|
|
10
11
|
from typing import Any, Callable
|
11
12
|
|
12
13
|
import websocket as ws_lib
|
14
|
+
from deprecated import deprecated
|
13
15
|
from requests import delete, get, post, put
|
14
16
|
from structlog.stdlib import get_logger
|
15
17
|
from websocket import WebSocketApp # missing stubs for WebSocketApp
|
16
18
|
|
19
|
+
from bitvavo_api_upgraded.dataframe_utils import convert_candles_to_dataframe, convert_to_dataframe
|
17
20
|
from bitvavo_api_upgraded.helper_funcs import configure_loggers, time_ms, time_to_wait
|
18
|
-
from bitvavo_api_upgraded.settings import bitvavo_upgraded_settings
|
19
|
-
from bitvavo_api_upgraded.type_aliases import anydict, errordict, intdict, ms, s_f, strdict, strintdict
|
21
|
+
from bitvavo_api_upgraded.settings import bitvavo_settings, bitvavo_upgraded_settings
|
22
|
+
from bitvavo_api_upgraded.type_aliases import OutputFormat, anydict, errordict, intdict, ms, s_f, strdict, strintdict
|
20
23
|
|
21
24
|
configure_loggers()
|
22
25
|
|
23
26
|
logger = get_logger(__name__)
|
24
27
|
|
25
28
|
|
29
|
+
@deprecated(version="2.2.0", reason="Use create_signature instead")
|
26
30
|
def createSignature(timestamp: ms, method: str, url: str, body: anydict | None, api_secret: str) -> str:
|
31
|
+
return create_signature(timestamp, method, url, body, api_secret)
|
32
|
+
|
33
|
+
|
34
|
+
def create_signature(timestamp: ms, method: str, url: str, body: anydict | None, api_secret: str) -> str:
|
27
35
|
string = f"{timestamp}{method}/v2{url}"
|
28
36
|
if body is not None and len(body.keys()) > 0:
|
29
37
|
string += json.dumps(body, separators=(",", ":"))
|
@@ -31,7 +39,12 @@ def createSignature(timestamp: ms, method: str, url: str, body: anydict | None,
|
|
31
39
|
return signature
|
32
40
|
|
33
41
|
|
42
|
+
@deprecated(version="2.2.0", reason="Use create_postfix instead")
|
34
43
|
def createPostfix(options: anydict | None) -> str:
|
44
|
+
return create_postfix(options)
|
45
|
+
|
46
|
+
|
47
|
+
def create_postfix(options: anydict | None) -> str:
|
35
48
|
"""Generate a URL postfix, based on the `options` dict.
|
36
49
|
|
37
50
|
---
|
@@ -65,18 +78,37 @@ def _epoch_millis(dt: dt.datetime) -> int:
|
|
65
78
|
return int(dt.timestamp() * 1000)
|
66
79
|
|
67
80
|
|
81
|
+
@deprecated(version="2.2.0", reason="Use asks_compare instead")
|
68
82
|
def asksCompare(a: float, b: float) -> bool:
|
83
|
+
return asks_compare(a, b)
|
84
|
+
|
85
|
+
|
86
|
+
def asks_compare(a: float, b: float) -> bool:
|
69
87
|
return a < b
|
70
88
|
|
71
89
|
|
90
|
+
@deprecated(version="2.2.0", reason="Use bids_compare instead")
|
72
91
|
def bidsCompare(a: float, b: float) -> bool:
|
92
|
+
return bids_compare(a, b)
|
93
|
+
|
94
|
+
|
95
|
+
def bids_compare(a: float, b: float) -> bool:
|
73
96
|
return a > b
|
74
97
|
|
75
98
|
|
99
|
+
@deprecated(version="2.2.0", reason="Use sort_and_insert instead")
|
76
100
|
def sortAndInsert(
|
77
101
|
asks_or_bids: list[list[str]],
|
78
102
|
update: list[list[str]],
|
79
103
|
compareFunc: Callable[[float, float], bool],
|
104
|
+
) -> list[list[str]] | errordict:
|
105
|
+
return sort_and_insert(asks_or_bids, update, compareFunc)
|
106
|
+
|
107
|
+
|
108
|
+
def sort_and_insert(
|
109
|
+
asks_or_bids: list[list[str]],
|
110
|
+
update: list[list[str]],
|
111
|
+
compareFunc: Callable[[float, float], bool],
|
80
112
|
) -> list[list[str]] | errordict:
|
81
113
|
for updateEntry in update:
|
82
114
|
entrySet: bool = False
|
@@ -99,7 +131,12 @@ def sortAndInsert(
|
|
99
131
|
return asks_or_bids
|
100
132
|
|
101
133
|
|
134
|
+
@deprecated(version="2.2.0", reason="Use process_local_book instead")
|
102
135
|
def processLocalBook(ws: Bitvavo.WebSocketAppFacade, message: anydict) -> None:
|
136
|
+
return process_local_book(ws, message)
|
137
|
+
|
138
|
+
|
139
|
+
def process_local_book(ws: Bitvavo.WebSocketAppFacade, message: anydict) -> None:
|
103
140
|
market: str = ""
|
104
141
|
if "action" in message:
|
105
142
|
if message["action"] == "getBook":
|
@@ -113,10 +150,10 @@ def processLocalBook(ws: Bitvavo.WebSocketAppFacade, message: anydict) -> None:
|
|
113
150
|
|
114
151
|
if message["nonce"] != ws.localBook[market]["nonce"] + 1:
|
115
152
|
# I think I've fixed this, by looking at the other Bitvavo repos (search for 'nonce' or '!=' 😆)
|
116
|
-
ws.
|
153
|
+
ws.subscription_book(market, ws.callbacks[market])
|
117
154
|
return
|
118
|
-
ws.localBook[market]["bids"] =
|
119
|
-
ws.localBook[market]["asks"] =
|
155
|
+
ws.localBook[market]["bids"] = sort_and_insert(ws.localBook[market]["bids"], message["bids"], bids_compare)
|
156
|
+
ws.localBook[market]["asks"] = sort_and_insert(ws.localBook[market]["asks"], message["asks"], asks_compare)
|
120
157
|
ws.localBook[market]["nonce"] = message["nonce"]
|
121
158
|
|
122
159
|
if market != "":
|
@@ -216,6 +253,7 @@ class Bitvavo:
|
|
216
253
|
Example code to get your started:
|
217
254
|
|
218
255
|
```python
|
256
|
+
# Single API key (backward compatible)
|
219
257
|
bitvavo = Bitvavo(
|
220
258
|
{
|
221
259
|
"APIKEY": "$YOUR_API_KEY",
|
@@ -227,24 +265,204 @@ class Bitvavo:
|
|
227
265
|
},
|
228
266
|
)
|
229
267
|
time_dict = bitvavo.time()
|
268
|
+
|
269
|
+
# Multiple API keys with keyless preference
|
270
|
+
bitvavo = Bitvavo(
|
271
|
+
{
|
272
|
+
"APIKEYS": [
|
273
|
+
{"key": "$YOUR_API_KEY_1", "secret": "$YOUR_API_SECRET_1"},
|
274
|
+
{"key": "$YOUR_API_KEY_2", "secret": "$YOUR_API_SECRET_2"},
|
275
|
+
{"key": "$YOUR_API_KEY_3", "secret": "$YOUR_API_SECRET_3"},
|
276
|
+
],
|
277
|
+
"PREFER_KEYLESS": True, # Use keyless requests first, then API keys
|
278
|
+
"RESTURL": "https://api.bitvavo.com/v2",
|
279
|
+
"WSURL": "wss://ws.bitvavo.com/v2/",
|
280
|
+
"ACCESSWINDOW": 10000,
|
281
|
+
"DEBUGGING": True,
|
282
|
+
},
|
283
|
+
)
|
284
|
+
time_dict = bitvavo.time()
|
285
|
+
|
286
|
+
# Keyless only (no API keys)
|
287
|
+
bitvavo = Bitvavo(
|
288
|
+
{
|
289
|
+
"PREFER_KEYLESS": True,
|
290
|
+
"RESTURL": "https://api.bitvavo.com/v2",
|
291
|
+
"WSURL": "wss://ws.bitvavo.com/v2/",
|
292
|
+
"ACCESSWINDOW": 10000,
|
293
|
+
"DEBUGGING": True,
|
294
|
+
},
|
295
|
+
)
|
296
|
+
markets = bitvavo.markets() # Only public endpoints will work
|
230
297
|
```
|
231
298
|
"""
|
232
299
|
|
233
|
-
def __init__(self, options: dict[str, str | int] | None = None) -> None:
|
300
|
+
def __init__(self, options: dict[str, str | int | list[dict[str, str]]] | None = None) -> None:
|
234
301
|
if options is None:
|
235
302
|
options = {}
|
236
303
|
_options = {k.upper(): v for k, v in options.items()}
|
237
|
-
|
238
|
-
|
239
|
-
self.
|
240
|
-
self.
|
241
|
-
self.
|
242
|
-
|
304
|
+
|
305
|
+
# Options take precedence over settings
|
306
|
+
self.base: str = str(_options.get("RESTURL", bitvavo_settings.RESTURL))
|
307
|
+
self.wsUrl: str = str(_options.get("WSURL", bitvavo_settings.WSURL))
|
308
|
+
self.ACCESSWINDOW: int = int(_options.get("ACCESSWINDOW", bitvavo_settings.ACCESSWINDOW))
|
309
|
+
|
310
|
+
# Support for multiple API keys - options take absolute precedence
|
311
|
+
if "APIKEY" in _options and "APISECRET" in _options:
|
312
|
+
# Single API key explicitly provided in options - takes precedence
|
313
|
+
single_key = str(_options["APIKEY"])
|
314
|
+
single_secret = str(_options["APISECRET"])
|
315
|
+
self.api_keys: list[dict[str, str]] = [{"key": single_key, "secret": single_secret}]
|
316
|
+
elif "APIKEYS" in _options:
|
317
|
+
# Multiple API keys provided in options - takes precedence
|
318
|
+
api_keys = _options["APIKEYS"]
|
319
|
+
if isinstance(api_keys, list) and api_keys:
|
320
|
+
self.api_keys = api_keys
|
321
|
+
else:
|
322
|
+
self.api_keys = []
|
323
|
+
else:
|
324
|
+
# Fall back to settings only if no API key options provided
|
325
|
+
api_keys = bitvavo_settings.APIKEYS
|
326
|
+
if isinstance(api_keys, list) and api_keys:
|
327
|
+
self.api_keys = api_keys
|
328
|
+
else:
|
329
|
+
# Single API key from settings (backward compatibility)
|
330
|
+
single_key = str(bitvavo_settings.APIKEY)
|
331
|
+
single_secret = str(bitvavo_settings.APISECRET)
|
332
|
+
if single_key and single_secret:
|
333
|
+
self.api_keys = [{"key": single_key, "secret": single_secret}]
|
334
|
+
else:
|
335
|
+
self.api_keys = []
|
336
|
+
|
337
|
+
# Current API key index and keyless preference - options take precedence
|
338
|
+
self.current_api_key_index: int = 0
|
339
|
+
self.prefer_keyless: bool = bool(_options.get("PREFER_KEYLESS", bitvavo_upgraded_settings.PREFER_KEYLESS))
|
340
|
+
|
341
|
+
# Rate limiting per API key (keyless has index -1)
|
342
|
+
self.rate_limits: dict[int, dict[str, int | ms]] = {}
|
343
|
+
# Get default rate limit from options or settings
|
344
|
+
default_rate_limit_option = _options.get("DEFAULT_RATE_LIMIT", bitvavo_upgraded_settings.DEFAULT_RATE_LIMIT)
|
345
|
+
default_rate_limit = (
|
346
|
+
int(default_rate_limit_option)
|
347
|
+
if isinstance(default_rate_limit_option, (int, str))
|
348
|
+
else bitvavo_upgraded_settings.DEFAULT_RATE_LIMIT
|
349
|
+
)
|
350
|
+
|
351
|
+
self.rate_limits[-1] = {"remaining": default_rate_limit, "resetAt": ms(0)} # keyless
|
352
|
+
for i in range(len(self.api_keys)):
|
353
|
+
self.rate_limits[i] = {"remaining": default_rate_limit, "resetAt": ms(0)}
|
354
|
+
|
355
|
+
# Legacy properties for backward compatibility
|
356
|
+
self.APIKEY: str = self.api_keys[0]["key"] if self.api_keys else ""
|
357
|
+
self.APISECRET: str = self.api_keys[0]["secret"] if self.api_keys else ""
|
358
|
+
self._current_api_key: str = self.APIKEY
|
359
|
+
self._current_api_secret: str = self.APISECRET
|
360
|
+
self.rateLimitRemaining: int = default_rate_limit
|
243
361
|
self.rateLimitResetAt: ms = 0
|
244
|
-
# TODO(NostraDavid): for v2: remove this functionality - logger.debug is a level that can be set
|
245
|
-
self.debugging = bool(_options.get("DEBUGGING", False))
|
246
362
|
|
363
|
+
# Options take precedence over settings for debugging
|
364
|
+
self.debugging: bool = bool(_options.get("DEBUGGING", bitvavo_settings.DEBUGGING))
|
365
|
+
|
366
|
+
def get_best_api_key_config(self, rateLimitingWeight: int = 1) -> tuple[str, str, int]:
|
367
|
+
"""
|
368
|
+
Get the best API key configuration to use for a request.
|
369
|
+
|
370
|
+
Returns:
|
371
|
+
tuple: (api_key, api_secret, key_index) where key_index is -1 for keyless
|
372
|
+
"""
|
373
|
+
# If prefer keyless and keyless has enough rate limit, use keyless
|
374
|
+
if self.prefer_keyless and self._has_rate_limit_available(-1, rateLimitingWeight):
|
375
|
+
return "", "", -1
|
376
|
+
|
377
|
+
# Try to find an API key with enough rate limit
|
378
|
+
for i in range(len(self.api_keys)):
|
379
|
+
if self._has_rate_limit_available(i, rateLimitingWeight):
|
380
|
+
return self.api_keys[i]["key"], self.api_keys[i]["secret"], i
|
381
|
+
|
382
|
+
# If keyless is available, use it as fallback
|
383
|
+
if self._has_rate_limit_available(-1, rateLimitingWeight):
|
384
|
+
return "", "", -1
|
385
|
+
|
386
|
+
# No keys available, use current key and let rate limiting handle the wait
|
387
|
+
if self.api_keys:
|
388
|
+
return (
|
389
|
+
self.api_keys[self.current_api_key_index]["key"],
|
390
|
+
self.api_keys[self.current_api_key_index]["secret"],
|
391
|
+
self.current_api_key_index,
|
392
|
+
)
|
393
|
+
return "", "", -1
|
394
|
+
|
395
|
+
def _has_rate_limit_available(self, key_index: int, weight: int) -> bool:
|
396
|
+
"""Check if a specific API key (or keyless) has enough rate limit."""
|
397
|
+
if key_index not in self.rate_limits:
|
398
|
+
return False
|
399
|
+
remaining = self.rate_limits[key_index]["remaining"]
|
400
|
+
return (remaining - weight) > bitvavo_upgraded_settings.RATE_LIMITING_BUFFER
|
401
|
+
|
402
|
+
def _update_rate_limit_for_key(self, key_index: int, response: anydict | errordict) -> None:
|
403
|
+
"""Update rate limit for a specific API key index."""
|
404
|
+
if key_index not in self.rate_limits:
|
405
|
+
self.rate_limits[key_index] = {"remaining": 1000, "resetAt": ms(0)}
|
406
|
+
|
407
|
+
if "errorCode" in response and response["errorCode"] == 105: # noqa: PLR2004
|
408
|
+
self.rate_limits[key_index]["remaining"] = 0
|
409
|
+
# rateLimitResetAt is a value that's stripped from a string.
|
410
|
+
reset_time_str = str(response.get("error", "")).split(" at ")
|
411
|
+
if len(reset_time_str) > 1:
|
412
|
+
try:
|
413
|
+
reset_time = ms(int(reset_time_str[1].split(".")[0]))
|
414
|
+
self.rate_limits[key_index]["resetAt"] = reset_time
|
415
|
+
except (ValueError, IndexError):
|
416
|
+
# Fallback to current time + 60 seconds if parsing fails
|
417
|
+
self.rate_limits[key_index]["resetAt"] = ms(time_ms() + 60000)
|
418
|
+
else:
|
419
|
+
self.rate_limits[key_index]["resetAt"] = ms(time_ms() + 60000)
|
420
|
+
|
421
|
+
timeToWait = time_to_wait(ms(self.rate_limits[key_index]["resetAt"]))
|
422
|
+
key_name = f"API_KEY_{key_index}" if key_index >= 0 else "KEYLESS"
|
423
|
+
logger.warning(
|
424
|
+
"api-key-banned",
|
425
|
+
info={
|
426
|
+
"key_name": key_name,
|
427
|
+
"wait_time_seconds": timeToWait + 1,
|
428
|
+
"until": (dt.datetime.now(tz=dt.timezone.utc) + dt.timedelta(seconds=timeToWait + 1)).isoformat(),
|
429
|
+
},
|
430
|
+
)
|
431
|
+
|
432
|
+
if "bitvavo-ratelimit-remaining" in response:
|
433
|
+
with contextlib.suppress(ValueError, TypeError):
|
434
|
+
self.rate_limits[key_index]["remaining"] = int(response["bitvavo-ratelimit-remaining"])
|
435
|
+
|
436
|
+
if "bitvavo-ratelimit-resetat" in response:
|
437
|
+
with contextlib.suppress(ValueError, TypeError):
|
438
|
+
self.rate_limits[key_index]["resetAt"] = ms(int(response["bitvavo-ratelimit-resetat"]))
|
439
|
+
|
440
|
+
def _sleep_for_key(self, key_index: int) -> None:
|
441
|
+
"""Sleep until the specified API key's rate limit resets."""
|
442
|
+
if key_index not in self.rate_limits:
|
443
|
+
return
|
444
|
+
|
445
|
+
reset_at = ms(self.rate_limits[key_index]["resetAt"])
|
446
|
+
napTime = time_to_wait(reset_at)
|
447
|
+
key_name = f"API_KEY_{key_index}" if key_index >= 0 else "KEYLESS"
|
448
|
+
|
449
|
+
logger.warning(
|
450
|
+
"rate-limit-reached", key_name=key_name, rateLimitRemaining=self.rate_limits[key_index]["remaining"]
|
451
|
+
)
|
452
|
+
logger.info(
|
453
|
+
"napping-until-reset",
|
454
|
+
key_name=key_name,
|
455
|
+
napTime=napTime,
|
456
|
+
currentTime=dt.datetime.now(tz=dt.timezone.utc).isoformat(),
|
457
|
+
targetDatetime=dt.datetime.fromtimestamp(reset_at / 1000.0, tz=dt.timezone.utc).isoformat(),
|
458
|
+
)
|
459
|
+
time.sleep(napTime + 1) # +1 to add a tiny bit of buffer time
|
460
|
+
|
461
|
+
@deprecated(version="2.2.0", reason="Use calc_lag instead")
|
247
462
|
def calcLag(self) -> ms:
|
463
|
+
return self.calc_lag()
|
464
|
+
|
465
|
+
def calc_lag(self) -> ms:
|
248
466
|
"""
|
249
467
|
Calculate the time difference between the client and server; use this value with BITVAVO_API_UPGRADED_LAG,
|
250
468
|
when you make an api call, to precent 304 errors.
|
@@ -266,8 +484,12 @@ class Bitvavo:
|
|
266
484
|
|
267
485
|
return ms(sum(lag_list) / len(lag_list))
|
268
486
|
|
487
|
+
@deprecated(version="2.2.0", reason="Use get_remaining_limit instead")
|
269
488
|
def getRemainingLimit(self) -> int:
|
270
|
-
|
489
|
+
return self.get_remaining_limit()
|
490
|
+
|
491
|
+
def get_remaining_limit(self) -> int:
|
492
|
+
"""Get the remaining rate limit
|
271
493
|
|
272
494
|
---
|
273
495
|
Returns:
|
@@ -277,21 +499,31 @@ class Bitvavo:
|
|
277
499
|
"""
|
278
500
|
return self.rateLimitRemaining
|
279
501
|
|
502
|
+
@deprecated(version="2.2.0", reason="Use get_remaining_limit instead")
|
280
503
|
def updateRateLimit(self, response: anydict | errordict) -> None:
|
504
|
+
return self.update_rate_limit(response)
|
505
|
+
|
506
|
+
def update_rate_limit(self, response: anydict | errordict) -> None:
|
281
507
|
"""
|
282
508
|
Update the rate limited
|
283
509
|
|
284
510
|
If you're banned, use the errordict to sleep until you're not banned
|
285
511
|
|
286
512
|
If you're not banned, then use the received headers to update the variables.
|
513
|
+
|
514
|
+
This method maintains backward compatibility by updating the legacy properties.
|
287
515
|
"""
|
516
|
+
# Update rate limit for the current API key being used
|
517
|
+
current_key = self.current_api_key_index if self.APIKEY else -1
|
518
|
+
self._update_rate_limit_for_key(current_key, response)
|
519
|
+
|
520
|
+
# Update legacy properties for backward compatibility
|
521
|
+
if current_key in self.rate_limits:
|
522
|
+
self.rateLimitRemaining = int(self.rate_limits[current_key]["remaining"])
|
523
|
+
self.rateLimitResetAt = ms(self.rate_limits[current_key]["resetAt"])
|
524
|
+
|
525
|
+
# Handle ban with sleep (legacy behavior)
|
288
526
|
if "errorCode" in response and response["errorCode"] == 105: # noqa: PLR2004
|
289
|
-
self.rateLimitRemaining = 0
|
290
|
-
# rateLimitResetAt is a value that's stripped from a string.
|
291
|
-
# Kind of a terrible way to pass that information, but eh, whatever, I guess...
|
292
|
-
# Anyway, here is the string that's being pulled apart:
|
293
|
-
# "Your IP or API key has been banned for not respecting the rate limit. The ban expires at ${expiryInMs}""
|
294
|
-
self.rateLimitResetAt = ms(response["error"].split(" at ")[1].split(".")[0])
|
295
527
|
timeToWait = time_to_wait(self.rateLimitResetAt)
|
296
528
|
logger.warning(
|
297
529
|
"banned",
|
@@ -302,15 +534,19 @@ class Bitvavo:
|
|
302
534
|
)
|
303
535
|
logger.info("napping-until-ban-lifted")
|
304
536
|
time.sleep(timeToWait + 1) # plus one second to ENSURE we're able to run again.
|
305
|
-
if "bitvavo-ratelimit-remaining" in response:
|
306
|
-
self.rateLimitRemaining = int(response["bitvavo-ratelimit-remaining"])
|
307
|
-
if "bitvavo-ratelimit-resetat" in response:
|
308
|
-
self.rateLimitResetAt = int(response["bitvavo-ratelimit-resetat"])
|
309
537
|
|
538
|
+
@deprecated(version="2.2.0", reason="Use public_request instead")
|
310
539
|
def publicRequest(
|
311
540
|
self,
|
312
541
|
url: str,
|
313
542
|
rateLimitingWeight: int = 1,
|
543
|
+
) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | errordict:
|
544
|
+
return self.public_request(url, rateLimitingWeight)
|
545
|
+
|
546
|
+
def public_request(
|
547
|
+
self,
|
548
|
+
url: str,
|
549
|
+
rateLimitingWeight: int = 1,
|
314
550
|
) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | errordict:
|
315
551
|
"""Execute a request to the public part of the API; no API key and/or SECRET necessary.
|
316
552
|
Will return the reponse as one of three types.
|
@@ -330,22 +566,39 @@ class Bitvavo:
|
|
330
566
|
list[list[str]]
|
331
567
|
```
|
332
568
|
"""
|
333
|
-
|
334
|
-
|
569
|
+
# Get the best API key configuration (keyless preferred, then available keys)
|
570
|
+
api_key, api_secret, key_index = self.get_best_api_key_config(rateLimitingWeight)
|
571
|
+
|
572
|
+
# Check if we need to wait for rate limit
|
573
|
+
if not self._has_rate_limit_available(key_index, rateLimitingWeight):
|
574
|
+
self._sleep_for_key(key_index)
|
575
|
+
|
576
|
+
# Update current API key for legacy compatibility
|
577
|
+
if api_key:
|
578
|
+
self._current_api_key = api_key
|
579
|
+
self._current_api_secret = api_secret
|
580
|
+
self.current_api_key_index = key_index
|
581
|
+
else:
|
582
|
+
# Using keyless
|
583
|
+
self._current_api_key = ""
|
584
|
+
self._current_api_secret = ""
|
585
|
+
|
335
586
|
if self.debugging:
|
336
587
|
logger.debug(
|
337
588
|
"api-request",
|
338
589
|
info={
|
339
590
|
"url": url,
|
340
|
-
"with_api_key": bool(
|
591
|
+
"with_api_key": bool(api_key != ""),
|
341
592
|
"public_or_private": "public",
|
593
|
+
"key_index": key_index,
|
342
594
|
},
|
343
595
|
)
|
344
|
-
|
596
|
+
|
597
|
+
if api_key:
|
345
598
|
now = time_ms() + bitvavo_upgraded_settings.LAG
|
346
|
-
sig =
|
599
|
+
sig = create_signature(now, "GET", url.replace(self.base, ""), None, api_secret)
|
347
600
|
headers = {
|
348
|
-
"bitvavo-access-key":
|
601
|
+
"bitvavo-access-key": api_key,
|
349
602
|
"bitvavo-access-signature": sig,
|
350
603
|
"bitvavo-access-timestamp": str(now),
|
351
604
|
"bitvavo-access-window": str(self.ACCESSWINDOW),
|
@@ -353,12 +606,19 @@ class Bitvavo:
|
|
353
606
|
r = get(url, headers=headers, timeout=(self.ACCESSWINDOW / 1000))
|
354
607
|
else:
|
355
608
|
r = get(url, timeout=(self.ACCESSWINDOW / 1000))
|
609
|
+
|
610
|
+
# Update rate limit for the specific key used
|
356
611
|
if "error" in r.json():
|
357
|
-
self.
|
612
|
+
self._update_rate_limit_for_key(key_index, r.json())
|
358
613
|
else:
|
359
|
-
self.
|
614
|
+
self._update_rate_limit_for_key(key_index, dict(r.headers))
|
615
|
+
|
616
|
+
# Also update legacy rate limit tracking
|
617
|
+
self.update_rate_limit(r.json() if "error" in r.json() else dict(r.headers))
|
618
|
+
|
360
619
|
return r.json() # type:ignore[no-any-return]
|
361
620
|
|
621
|
+
@deprecated(version="2.2.0", reason="Use private_request instead")
|
362
622
|
def privateRequest(
|
363
623
|
self,
|
364
624
|
endpoint: str,
|
@@ -366,7 +626,17 @@ class Bitvavo:
|
|
366
626
|
body: anydict | None = None,
|
367
627
|
method: str = "GET",
|
368
628
|
rateLimitingWeight: int = 1,
|
369
|
-
) -> list[anydict] | list[list[str]] | intdict | strdict | anydict |
|
629
|
+
) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | errordict:
|
630
|
+
return self.private_request(endpoint, postfix, body, method, rateLimitingWeight)
|
631
|
+
|
632
|
+
def private_request(
|
633
|
+
self,
|
634
|
+
endpoint: str,
|
635
|
+
postfix: str,
|
636
|
+
body: anydict | None = None,
|
637
|
+
method: str = "GET",
|
638
|
+
rateLimitingWeight: int = 1,
|
639
|
+
) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | errordict:
|
370
640
|
"""Execute a request to the private part of the API. API key and SECRET are required.
|
371
641
|
Will return the reponse as one of three types.
|
372
642
|
|
@@ -389,14 +659,33 @@ class Bitvavo:
|
|
389
659
|
list[list[str]]
|
390
660
|
```
|
391
661
|
"""
|
392
|
-
|
393
|
-
|
394
|
-
|
662
|
+
# Private requests require an API key, so get the best available one
|
663
|
+
api_key, api_secret, key_index = self.get_best_api_key_config(rateLimitingWeight)
|
664
|
+
|
665
|
+
# If no API keys available, use the configured one (may fail)
|
666
|
+
if not api_key and self.api_keys:
|
667
|
+
api_key = self.api_keys[self.current_api_key_index]["key"]
|
668
|
+
api_secret = self.api_keys[self.current_api_key_index]["secret"]
|
669
|
+
key_index = self.current_api_key_index
|
670
|
+
elif not api_key:
|
671
|
+
# No API keys configured at all
|
672
|
+
api_key = self.APIKEY
|
673
|
+
api_secret = self.APISECRET
|
674
|
+
key_index = 0 if api_key else -1
|
675
|
+
|
676
|
+
# Check if we need to wait for rate limit
|
677
|
+
if not self._has_rate_limit_available(key_index, rateLimitingWeight):
|
678
|
+
self._sleep_for_key(key_index)
|
679
|
+
|
680
|
+
# Update current API key for legacy compatibility
|
681
|
+
self._current_api_key = api_key
|
682
|
+
self._current_api_secret = api_secret
|
683
|
+
|
395
684
|
now = time_ms() + bitvavo_upgraded_settings.LAG
|
396
|
-
sig =
|
685
|
+
sig = create_signature(now, method, (endpoint + postfix), body, api_secret)
|
397
686
|
url = self.base + endpoint + postfix
|
398
687
|
headers = {
|
399
|
-
"bitvavo-access-key":
|
688
|
+
"bitvavo-access-key": api_key,
|
400
689
|
"bitvavo-access-signature": sig,
|
401
690
|
"bitvavo-access-timestamp": str(now),
|
402
691
|
"bitvavo-access-window": str(self.ACCESSWINDOW),
|
@@ -406,9 +695,10 @@ class Bitvavo:
|
|
406
695
|
"api-request",
|
407
696
|
info={
|
408
697
|
"url": url,
|
409
|
-
"with_api_key": bool(
|
698
|
+
"with_api_key": bool(api_key != ""),
|
410
699
|
"public_or_private": "private",
|
411
700
|
"method": method,
|
701
|
+
"key_index": key_index,
|
412
702
|
},
|
413
703
|
)
|
414
704
|
if method == "DELETE":
|
@@ -419,10 +709,16 @@ class Bitvavo:
|
|
419
709
|
r = put(url, headers=headers, json=body, timeout=(self.ACCESSWINDOW / 1000))
|
420
710
|
else: # method == "GET"
|
421
711
|
r = get(url, headers=headers, timeout=(self.ACCESSWINDOW / 1000))
|
712
|
+
|
713
|
+
# Update rate limit for the specific key used
|
422
714
|
if "error" in r.json():
|
423
|
-
self.
|
715
|
+
self._update_rate_limit_for_key(key_index, r.json())
|
424
716
|
else:
|
425
|
-
self.
|
717
|
+
self._update_rate_limit_for_key(key_index, dict(r.headers))
|
718
|
+
|
719
|
+
# Also update legacy rate limit tracking
|
720
|
+
self.update_rate_limit(r.json() if "error" in r.json() else dict(r.headers))
|
721
|
+
|
426
722
|
return r.json()
|
427
723
|
|
428
724
|
def sleep_until_can_continue(self) -> None:
|
@@ -455,9 +751,13 @@ class Bitvavo:
|
|
455
751
|
{"time": 1539180275424 }
|
456
752
|
```
|
457
753
|
"""
|
458
|
-
return self.
|
754
|
+
return self.public_request(f"{self.base}/time") # type: ignore[return-value]
|
459
755
|
|
460
|
-
def markets(
|
756
|
+
def markets(
|
757
|
+
self,
|
758
|
+
options: strdict | None = None,
|
759
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
760
|
+
) -> list[anydict] | anydict | errordict | Any:
|
461
761
|
"""Get all available markets with some meta-information, unless options is given a `market` key.
|
462
762
|
Then you will get a single market, instead of a list of markets.
|
463
763
|
|
@@ -474,6 +774,23 @@ class Bitvavo:
|
|
474
774
|
options={} # returns all markets
|
475
775
|
options={"market": "BTC-EUR"} # returns only the BTC-EUR market
|
476
776
|
# If you want multiple markets, but not all, make multiple calls
|
777
|
+
|
778
|
+
# Output format selection:
|
779
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
780
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
781
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
782
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
783
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
784
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
785
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
786
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
787
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
788
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
789
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
790
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
791
|
+
|
792
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
793
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
477
794
|
```
|
478
795
|
|
479
796
|
---
|
@@ -485,6 +802,7 @@ class Bitvavo:
|
|
485
802
|
---
|
486
803
|
Returns:
|
487
804
|
```python
|
805
|
+
# When output_format=OutputFormat.DICT (default):
|
488
806
|
[
|
489
807
|
{
|
490
808
|
"market": "BTC-EUR",
|
@@ -504,12 +822,22 @@ class Bitvavo:
|
|
504
822
|
]
|
505
823
|
}
|
506
824
|
]
|
825
|
+
|
826
|
+
# When output_format is any DataFrame format (pandas, polars, cudf, etc.):
|
827
|
+
# Returns a DataFrame with columns: market, status, base, quote, pricePrecision,
|
828
|
+
# minOrderInQuoteAsset, minOrderInBaseAsset, orderTypes
|
829
|
+
# The specific DataFrame type depends on the selected format.
|
507
830
|
```
|
508
831
|
"""
|
509
|
-
postfix =
|
510
|
-
|
832
|
+
postfix = create_postfix(options)
|
833
|
+
result = self.public_request(f"{self.base}/markets{postfix}") # type: ignore[return-value]
|
834
|
+
return convert_to_dataframe(result, output_format)
|
511
835
|
|
512
|
-
def assets(
|
836
|
+
def assets(
|
837
|
+
self,
|
838
|
+
options: strdict | None = None,
|
839
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
840
|
+
) -> list[anydict] | anydict | Any:
|
513
841
|
"""Get all available assets, unless `options` is given a `symbol` key.
|
514
842
|
Then you will get a single asset, instead of a list of assets.
|
515
843
|
|
@@ -527,6 +855,23 @@ class Bitvavo:
|
|
527
855
|
# pick one
|
528
856
|
options={} # returns all assets
|
529
857
|
options={"symbol": "BTC"} # returns a single asset (the one of Bitcoin)
|
858
|
+
|
859
|
+
# Output format selection:
|
860
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
861
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
862
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
863
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
864
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
865
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
866
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
867
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
868
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
869
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
870
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
871
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
872
|
+
|
873
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
874
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
530
875
|
```
|
531
876
|
|
532
877
|
---
|
@@ -538,6 +883,7 @@ class Bitvavo:
|
|
538
883
|
---
|
539
884
|
Returns:
|
540
885
|
```python
|
886
|
+
# When output_format=OutputFormat.DICT (default):
|
541
887
|
[
|
542
888
|
{
|
543
889
|
"symbol": "BTC",
|
@@ -553,10 +899,17 @@ class Bitvavo:
|
|
553
899
|
"message": ""
|
554
900
|
}
|
555
901
|
]
|
902
|
+
|
903
|
+
# When output_format is any DataFrame format (pandas, polars, cudf, etc.):
|
904
|
+
# Returns a DataFrame with columns: symbol, name, decimals, depositFee,
|
905
|
+
# depositConfirmations, depositStatus, withdrawalFee, withdrawalMinAmount,
|
906
|
+
# withdrawalStatus, networks, message
|
907
|
+
# The specific DataFrame type depends on the selected format.
|
556
908
|
```
|
557
909
|
"""
|
558
|
-
postfix =
|
559
|
-
|
910
|
+
postfix = create_postfix(options)
|
911
|
+
result = self.public_request(f"{self.base}/assets{postfix}") # type: ignore[return-value]
|
912
|
+
return convert_to_dataframe(result, output_format)
|
560
913
|
|
561
914
|
def book(self, market: str, options: intdict | None = None) -> dict[str, str | int | list[str]] | errordict:
|
562
915
|
"""Get a book (with two lists: asks and bids, as they're called)
|
@@ -600,10 +953,24 @@ class Bitvavo:
|
|
600
953
|
assert result == 714.48 # EUR can be gained from this bid if it's sold (minus the fee)
|
601
954
|
```
|
602
955
|
"""
|
603
|
-
postfix =
|
604
|
-
return self.
|
956
|
+
postfix = create_postfix(options)
|
957
|
+
return self.public_request(f"{self.base}/{market}/book{postfix}") # type: ignore[return-value]
|
958
|
+
|
959
|
+
@deprecated(version="2.2.0", reason="Use public_trades instead")
|
960
|
+
def publicTrades(
|
961
|
+
self,
|
962
|
+
market: str,
|
963
|
+
options: strintdict | None = None,
|
964
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
965
|
+
) -> list[anydict] | errordict | Any:
|
966
|
+
return self.public_trades(market, options, output_format)
|
605
967
|
|
606
|
-
def
|
968
|
+
def public_trades(
|
969
|
+
self,
|
970
|
+
market: str,
|
971
|
+
options: strintdict | None = None,
|
972
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
973
|
+
) -> list[anydict] | errordict | Any:
|
607
974
|
"""Publically available trades
|
608
975
|
|
609
976
|
---
|
@@ -628,6 +995,23 @@ class Bitvavo:
|
|
628
995
|
"tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
|
629
996
|
"tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
|
630
997
|
}
|
998
|
+
|
999
|
+
# Output format selection:
|
1000
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
1001
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
1002
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
1003
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
1004
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
1005
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
1006
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
1007
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
1008
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
1009
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
1010
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
1011
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
1012
|
+
|
1013
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
1014
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
631
1015
|
```
|
632
1016
|
|
633
1017
|
---
|
@@ -639,6 +1023,7 @@ class Bitvavo:
|
|
639
1023
|
---
|
640
1024
|
Returns:
|
641
1025
|
```python
|
1026
|
+
# When output_format='dict' (default):
|
642
1027
|
[
|
643
1028
|
{
|
644
1029
|
"timestamp": 1542967486256,
|
@@ -648,10 +1033,14 @@ class Bitvavo:
|
|
648
1033
|
"side": "sell"
|
649
1034
|
}
|
650
1035
|
]
|
1036
|
+
|
1037
|
+
# When output_format is any DataFrame format:
|
1038
|
+
# Returns the above data as a DataFrame in the requested format (pandas, polars, etc.)
|
651
1039
|
```
|
652
1040
|
"""
|
653
|
-
postfix =
|
654
|
-
|
1041
|
+
postfix = create_postfix(options)
|
1042
|
+
result = self.public_request(f"{self.base}/{market}/trades{postfix}", 5) # type: ignore[return-value]
|
1043
|
+
return convert_to_dataframe(result, output_format)
|
655
1044
|
|
656
1045
|
def candles(
|
657
1046
|
self,
|
@@ -661,7 +1050,8 @@ class Bitvavo:
|
|
661
1050
|
limit: int | None = None,
|
662
1051
|
start: dt.datetime | None = None,
|
663
1052
|
end: dt.datetime | None = None,
|
664
|
-
|
1053
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1054
|
+
) -> list[list[str]] | errordict | Any:
|
665
1055
|
"""Get up to 1440 candles for a market, with a specific interval (candle size)
|
666
1056
|
|
667
1057
|
Extra reading material: https://en.wikipedia.org/wiki/Candlestick_chart
|
@@ -684,6 +1074,23 @@ class Bitvavo:
|
|
684
1074
|
"start": int timestamp in ms >= 0
|
685
1075
|
"end": int timestamp in ms <= 8640000000000000
|
686
1076
|
}
|
1077
|
+
|
1078
|
+
# Output format selection:
|
1079
|
+
output_format=OutputFormat.DICT # Default: returns standard Python list/dict
|
1080
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
1081
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
1082
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
1083
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
1084
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
1085
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
1086
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
1087
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
1088
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
1089
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
1090
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
1091
|
+
|
1092
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
1093
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
687
1094
|
```
|
688
1095
|
|
689
1096
|
---
|
@@ -695,6 +1102,7 @@ class Bitvavo:
|
|
695
1102
|
---
|
696
1103
|
Returns:
|
697
1104
|
```python
|
1105
|
+
# When output_format='dict' (default):
|
698
1106
|
[
|
699
1107
|
# For whatever reason, you're getting a list of lists; no keys,
|
700
1108
|
# so here is the explanation of what's what.
|
@@ -705,6 +1113,11 @@ class Bitvavo:
|
|
705
1113
|
[1640804400000, "41937", "41955", "41449", "41540", "23.64498292"],
|
706
1114
|
[1640800800000, "41955", "42163", "41807", "41939", "10.40093845"],
|
707
1115
|
]
|
1116
|
+
|
1117
|
+
# When output_format is any DataFrame format:
|
1118
|
+
# Returns the above data as a DataFrame in the requested format (pandas, polars, etc.)
|
1119
|
+
# with columns: timestamp, open, high, low, close, volume
|
1120
|
+
# timestamp is converted to datetime, numeric columns to float
|
708
1121
|
```
|
709
1122
|
"""
|
710
1123
|
options = _default(options, {})
|
@@ -715,10 +1128,23 @@ class Bitvavo:
|
|
715
1128
|
options["start"] = _epoch_millis(start)
|
716
1129
|
if end is not None:
|
717
1130
|
options["end"] = _epoch_millis(end)
|
718
|
-
postfix =
|
719
|
-
|
1131
|
+
postfix = create_postfix(options)
|
1132
|
+
result = self.public_request(f"{self.base}/{market}/candles{postfix}") # type: ignore[return-value]
|
1133
|
+
return convert_candles_to_dataframe(result, output_format)
|
1134
|
+
|
1135
|
+
@deprecated(version="2.2.0", reason="Use ticker_price instead")
|
1136
|
+
def tickerPrice(
|
1137
|
+
self,
|
1138
|
+
options: strdict | None = None,
|
1139
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1140
|
+
) -> list[strdict] | strdict | Any:
|
1141
|
+
return self.ticker_price(options, output_format)
|
720
1142
|
|
721
|
-
def
|
1143
|
+
def ticker_price(
|
1144
|
+
self,
|
1145
|
+
options: strdict | None = None,
|
1146
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1147
|
+
) -> list[strdict] | strdict | Any:
|
722
1148
|
"""Get the current price for each market
|
723
1149
|
|
724
1150
|
---
|
@@ -735,6 +1161,23 @@ class Bitvavo:
|
|
735
1161
|
```python
|
736
1162
|
options={}
|
737
1163
|
options={"market": "BTC-EUR"}
|
1164
|
+
|
1165
|
+
# Output format selection:
|
1166
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
1167
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
1168
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
1169
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
1170
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
1171
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
1172
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
1173
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
1174
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
1175
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
1176
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
1177
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
1178
|
+
|
1179
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
1180
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
738
1181
|
```
|
739
1182
|
|
740
1183
|
---
|
@@ -746,6 +1189,7 @@ class Bitvavo:
|
|
746
1189
|
---
|
747
1190
|
Returns:
|
748
1191
|
```python
|
1192
|
+
# When output_format=OutputFormat.DICT (default):
|
749
1193
|
# Note that `price` is unconverted
|
750
1194
|
[
|
751
1195
|
{"market": "1INCH-EUR", "price": "2.1594"},
|
@@ -761,12 +1205,28 @@ class Bitvavo:
|
|
761
1205
|
{"market": "ALGO-EUR", "price": "1.3942"},
|
762
1206
|
# and another 210 markets below this point
|
763
1207
|
]
|
1208
|
+
|
1209
|
+
# When output_format is any DataFrame format (pandas, polars, cudf, etc.):
|
1210
|
+
# Returns a DataFrame with columns: market, price
|
764
1211
|
```
|
765
1212
|
"""
|
766
|
-
postfix =
|
767
|
-
|
1213
|
+
postfix = create_postfix(options)
|
1214
|
+
result = self.public_request(f"{self.base}/ticker/price{postfix}") # type: ignore[return-value]
|
1215
|
+
return convert_to_dataframe(result, output_format)
|
1216
|
+
|
1217
|
+
@deprecated(version="2.2.0", reason="Use ticker_book instead")
|
1218
|
+
def tickerBook(
|
1219
|
+
self,
|
1220
|
+
options: strdict | None = None,
|
1221
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1222
|
+
) -> list[strdict] | strdict | Any:
|
1223
|
+
return self.ticker_book(options, output_format)
|
768
1224
|
|
769
|
-
def
|
1225
|
+
def ticker_book(
|
1226
|
+
self,
|
1227
|
+
options: strdict | None = None,
|
1228
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1229
|
+
) -> list[strdict] | strdict | Any:
|
770
1230
|
"""Get current bid/ask, bidsize/asksize per market
|
771
1231
|
|
772
1232
|
---
|
@@ -783,6 +1243,23 @@ class Bitvavo:
|
|
783
1243
|
```python
|
784
1244
|
options={}
|
785
1245
|
options={"market": "BTC-EUR"}
|
1246
|
+
|
1247
|
+
# Output format selection:
|
1248
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
1249
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
1250
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
1251
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
1252
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
1253
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
1254
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
1255
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
1256
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
1257
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
1258
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
1259
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
1260
|
+
|
1261
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
1262
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
786
1263
|
```
|
787
1264
|
|
788
1265
|
---
|
@@ -794,6 +1271,7 @@ class Bitvavo:
|
|
794
1271
|
---
|
795
1272
|
Returns:
|
796
1273
|
```python
|
1274
|
+
# When output_format=OutputFormat.DICT (default):
|
797
1275
|
[
|
798
1276
|
{"market": "1INCH-EUR", "bid": "2.1534", "ask": "2.1587", "bidSize": "194.8", "askSize": "194.8"},
|
799
1277
|
{"market": "AAVE-EUR", "bid": "213.7", "ask": "214.05", "bidSize": "212.532", "askSize": "4.77676965"},
|
@@ -802,12 +1280,20 @@ class Bitvavo:
|
|
802
1280
|
{"market": "AION-EUR", "bid": "0.12531", "ask": "0.12578", "bidSize": "3345", "askSize": "10958.49228653"},
|
803
1281
|
# and another 215 markets below this point
|
804
1282
|
]
|
1283
|
+
|
1284
|
+
# When output_format is any DataFrame format (pandas, polars, cudf, etc.):
|
1285
|
+
# Returns a DataFrame with columns: market, bid, ask, bidSize, askSize
|
805
1286
|
```
|
806
1287
|
"""
|
807
|
-
postfix =
|
808
|
-
|
1288
|
+
postfix = create_postfix(options)
|
1289
|
+
result = self.public_request(f"{self.base}/ticker/book{postfix}") # type: ignore[return-value]
|
1290
|
+
return convert_to_dataframe(result, output_format)
|
809
1291
|
|
810
|
-
def ticker24h(
|
1292
|
+
def ticker24h(
|
1293
|
+
self,
|
1294
|
+
options: strdict | None = None,
|
1295
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1296
|
+
) -> list[anydict] | anydict | errordict | Any:
|
811
1297
|
"""Get current bid/ask, bidsize/asksize per market
|
812
1298
|
|
813
1299
|
---
|
@@ -824,15 +1310,30 @@ class Bitvavo:
|
|
824
1310
|
```python
|
825
1311
|
options={}
|
826
1312
|
options={"market": "BTC-EUR"}
|
827
|
-
```
|
828
1313
|
|
1314
|
+
# Output format selection:
|
1315
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
1316
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
1317
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
1318
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
1319
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
1320
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
1321
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
1322
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
1323
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
1324
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
1325
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
1326
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
1327
|
+
|
1328
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
1329
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
1330
|
+
```
|
829
1331
|
---
|
830
1332
|
Rate Limit Weight:
|
831
1333
|
```python
|
832
1334
|
25 # if no market option is used
|
833
1335
|
1 # if a market option is used
|
834
1336
|
```
|
835
|
-
|
836
1337
|
---
|
837
1338
|
Returns:
|
838
1339
|
```python
|
@@ -873,10 +1374,25 @@ class Bitvavo:
|
|
873
1374
|
rateLimitingWeight = 25
|
874
1375
|
if "market" in options:
|
875
1376
|
rateLimitingWeight = 1
|
876
|
-
postfix =
|
877
|
-
|
1377
|
+
postfix = create_postfix(options)
|
1378
|
+
result = self.public_request(f"{self.base}/ticker/24h{postfix}", rateLimitingWeight) # type: ignore[return-value]
|
1379
|
+
return convert_to_dataframe(result, output_format)
|
1380
|
+
|
1381
|
+
@deprecated(version="2.2.0", reason="Use report_trades instead")
|
1382
|
+
def reportTrades(
|
1383
|
+
self,
|
1384
|
+
market: str,
|
1385
|
+
options: strintdict | None = None,
|
1386
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1387
|
+
) -> list[anydict] | errordict | Any:
|
1388
|
+
return self.report_trades(market, options, output_format)
|
878
1389
|
|
879
|
-
def
|
1390
|
+
def report_trades(
|
1391
|
+
self,
|
1392
|
+
market: str,
|
1393
|
+
options: strintdict | None = None,
|
1394
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
1395
|
+
) -> list[anydict] | errordict | Any:
|
880
1396
|
"""Get MiCA-compliant trades report for a specific market
|
881
1397
|
|
882
1398
|
Returns trades from the specified market and time period made by all Bitvavo users.
|
@@ -893,14 +1409,27 @@ class Bitvavo:
|
|
893
1409
|
```python
|
894
1410
|
market="BTC-EUR"
|
895
1411
|
options={
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
1412
|
+
"limit": [ 1 .. 1000 ], default 500
|
1413
|
+
"start": int timestamp in ms >= 0
|
1414
|
+
"end": int timestamp in ms <= 8_640_000_000_000_000 # Cannot be more than 24 hours after start
|
1415
|
+
"tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
|
1416
|
+
"tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
|
901
1417
|
}
|
902
|
-
```
|
903
1418
|
|
1419
|
+
# Output format selection:
|
1420
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
1421
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
1422
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
1423
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
1424
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
1425
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
1426
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
1427
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
1428
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
1429
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
1430
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
1431
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
1432
|
+
```
|
904
1433
|
---
|
905
1434
|
Rate Limit Weight:
|
906
1435
|
```python
|
@@ -912,19 +1441,24 @@ class Bitvavo:
|
|
912
1441
|
```python
|
913
1442
|
[
|
914
1443
|
{
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
1444
|
+
"timestamp": 1542967486256,
|
1445
|
+
"id": "57b1159b-6bf5-4cde-9e2c-6bd6a5678baf",
|
1446
|
+
"amount": "0.1",
|
1447
|
+
"price": "5012",
|
1448
|
+
"side": "sell"
|
920
1449
|
}
|
921
1450
|
]
|
922
1451
|
```
|
923
1452
|
"""
|
924
|
-
postfix =
|
925
|
-
|
1453
|
+
postfix = create_postfix(options)
|
1454
|
+
result = self.public_request(f"{self.base}/report/{market}/trades{postfix}", 5) # type: ignore[return-value]
|
1455
|
+
return convert_to_dataframe(result, output_format)
|
926
1456
|
|
1457
|
+
@deprecated(version="2.2.0", reason="Use report_book instead")
|
927
1458
|
def reportBook(self, market: str, options: intdict | None = None) -> dict[str, str | int | list[str]] | errordict:
|
1459
|
+
return self.report_book(market, options)
|
1460
|
+
|
1461
|
+
def report_book(self, market: str, options: intdict | None = None) -> dict[str, str | int | list[str]] | errordict:
|
928
1462
|
"""Get MiCA-compliant order book report for a specific market
|
929
1463
|
|
930
1464
|
Returns the list of all bids and asks for the specified market, sorted by price.
|
@@ -961,10 +1495,14 @@ class Bitvavo:
|
|
961
1495
|
}
|
962
1496
|
```
|
963
1497
|
"""
|
964
|
-
postfix =
|
965
|
-
return self.
|
1498
|
+
postfix = create_postfix(options)
|
1499
|
+
return self.public_request(f"{self.base}/report/{market}/book{postfix}") # type: ignore[return-value]
|
966
1500
|
|
1501
|
+
@deprecated(version="2.2.0", reason="Use place_order instead")
|
967
1502
|
def placeOrder(self, market: str, side: str, orderType: str, operatorId: int, body: anydict) -> anydict:
|
1503
|
+
return self.place_order(market, side, orderType, operatorId, body)
|
1504
|
+
|
1505
|
+
def place_order(self, market: str, side: str, orderType: str, operatorId: int, body: anydict) -> anydict:
|
968
1506
|
"""Place a new order on the exchange
|
969
1507
|
|
970
1508
|
---
|
@@ -1093,9 +1631,13 @@ class Bitvavo:
|
|
1093
1631
|
body["side"] = side
|
1094
1632
|
body["orderType"] = orderType
|
1095
1633
|
body["operatorId"] = operatorId
|
1096
|
-
return self.
|
1634
|
+
return self.private_request("/order", "", body, "POST") # type: ignore[return-value]
|
1097
1635
|
|
1636
|
+
@deprecated(version="2.2.0", reason="Use update_order instead")
|
1098
1637
|
def updateOrder(self, market: str, orderId: str, operatorId: int, body: anydict) -> anydict:
|
1638
|
+
return self.update_order(market, orderId, operatorId, body)
|
1639
|
+
|
1640
|
+
def update_order(self, market: str, orderId: str, operatorId: int, body: anydict) -> anydict:
|
1099
1641
|
"""Update an existing order for a specific market. Make sure that at least one of the optional parameters is set, otherwise nothing will be updated.
|
1100
1642
|
|
1101
1643
|
---
|
@@ -1179,14 +1721,24 @@ class Bitvavo:
|
|
1179
1721
|
body["market"] = market
|
1180
1722
|
body["orderId"] = orderId
|
1181
1723
|
body["operatorId"] = operatorId
|
1182
|
-
return self.
|
1724
|
+
return self.private_request("/order", "", body, "PUT") # type: ignore[return-value]
|
1183
1725
|
|
1726
|
+
@deprecated(version="2.2.0", reason="Use cancel_order instead")
|
1184
1727
|
def cancelOrder(
|
1185
1728
|
self,
|
1186
1729
|
market: str,
|
1187
1730
|
operatorId: int,
|
1188
1731
|
orderId: str | None = None,
|
1189
1732
|
clientOrderId: str | None = None,
|
1733
|
+
) -> strdict:
|
1734
|
+
return self.cancel_order(market, operatorId, orderId, clientOrderId)
|
1735
|
+
|
1736
|
+
def cancel_order(
|
1737
|
+
self,
|
1738
|
+
market: str,
|
1739
|
+
operatorId: int,
|
1740
|
+
orderId: str | None = None,
|
1741
|
+
clientOrderId: str | None = None,
|
1190
1742
|
) -> strdict:
|
1191
1743
|
"""Cancel an existing order for a specific market
|
1192
1744
|
|
@@ -1227,10 +1779,14 @@ class Bitvavo:
|
|
1227
1779
|
elif orderId is not None:
|
1228
1780
|
params["orderId"] = orderId
|
1229
1781
|
|
1230
|
-
postfix =
|
1231
|
-
return self.
|
1782
|
+
postfix = create_postfix(params)
|
1783
|
+
return self.private_request("/order", postfix, {}, "DELETE") # type: ignore[return-value]
|
1232
1784
|
|
1785
|
+
@deprecated(version="2.2.0", reason="Use get_order instead")
|
1233
1786
|
def getOrder(self, market: str, orderId: str) -> list[anydict] | errordict:
|
1787
|
+
return self.get_order(market, orderId)
|
1788
|
+
|
1789
|
+
def get_order(self, market: str, orderId: str) -> list[anydict] | errordict:
|
1234
1790
|
"""Get an existing order for a specific market
|
1235
1791
|
|
1236
1792
|
---
|
@@ -1292,10 +1848,14 @@ class Bitvavo:
|
|
1292
1848
|
}
|
1293
1849
|
```
|
1294
1850
|
"""
|
1295
|
-
postfix =
|
1296
|
-
return self.
|
1851
|
+
postfix = create_postfix({"market": market, "orderId": orderId})
|
1852
|
+
return self.private_request("/order", postfix, {}, "GET") # type: ignore[return-value]
|
1297
1853
|
|
1854
|
+
@deprecated(version="2.2.0", reason="Use get_orders instead")
|
1298
1855
|
def getOrders(self, market: str, options: anydict | None = None) -> list[anydict] | errordict:
|
1856
|
+
return self.get_orders(market, options)
|
1857
|
+
|
1858
|
+
def get_orders(self, market: str, options: anydict | None = None) -> list[anydict] | errordict:
|
1299
1859
|
"""Get multiple existing orders for a specific market
|
1300
1860
|
|
1301
1861
|
---
|
@@ -1368,10 +1928,14 @@ class Bitvavo:
|
|
1368
1928
|
""" # noqa: E501
|
1369
1929
|
options = _default(options, {})
|
1370
1930
|
options["market"] = market
|
1371
|
-
postfix =
|
1372
|
-
return self.
|
1931
|
+
postfix = create_postfix(options)
|
1932
|
+
return self.private_request("/orders", postfix, {}, "GET", 5) # type: ignore[return-value]
|
1373
1933
|
|
1934
|
+
@deprecated(version="2.2.0", reason="Use cancel_orders instead")
|
1374
1935
|
def cancelOrders(self, options: anydict | None = None) -> list[strdict] | errordict:
|
1936
|
+
return self.cancel_orders(options)
|
1937
|
+
|
1938
|
+
def cancel_orders(self, options: anydict | None = None) -> list[strdict] | errordict:
|
1375
1939
|
"""Cancel all existing orders for a specific market (or account)
|
1376
1940
|
|
1377
1941
|
---
|
@@ -1396,10 +1960,14 @@ class Bitvavo:
|
|
1396
1960
|
]
|
1397
1961
|
```
|
1398
1962
|
"""
|
1399
|
-
postfix =
|
1400
|
-
return self.
|
1963
|
+
postfix = create_postfix(options)
|
1964
|
+
return self.private_request("/orders", postfix, {}, "DELETE") # type: ignore[return-value]
|
1401
1965
|
|
1966
|
+
@deprecated(version="2.2.0", reason="Use orders_open instead")
|
1402
1967
|
def ordersOpen(self, options: anydict | None = None) -> list[anydict] | errordict:
|
1968
|
+
return self.orders_open(options)
|
1969
|
+
|
1970
|
+
def orders_open(self, options: anydict | None = None) -> list[anydict] | errordict:
|
1403
1971
|
"""Get all open orders, either for all markets, or a single market
|
1404
1972
|
|
1405
1973
|
---
|
@@ -1468,10 +2036,15 @@ class Bitvavo:
|
|
1468
2036
|
rateLimitingWeight = 25
|
1469
2037
|
if "market" in options:
|
1470
2038
|
rateLimitingWeight = 1
|
1471
|
-
postfix =
|
1472
|
-
return self.
|
2039
|
+
postfix = create_postfix(options)
|
2040
|
+
return self.private_request("/ordersOpen", postfix, {}, "GET", rateLimitingWeight) # type: ignore[return-value]
|
1473
2041
|
|
1474
|
-
def trades(
|
2042
|
+
def trades(
|
2043
|
+
self,
|
2044
|
+
market: str,
|
2045
|
+
options: anydict | None = None,
|
2046
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
2047
|
+
) -> list[anydict] | errordict | Any:
|
1475
2048
|
"""Get all historic trades from this account
|
1476
2049
|
|
1477
2050
|
---
|
@@ -1479,44 +2052,57 @@ class Bitvavo:
|
|
1479
2052
|
```python
|
1480
2053
|
market="BTC-EUR"
|
1481
2054
|
options={
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
2055
|
+
"limit": [ 1 .. 1000 ], default 500
|
2056
|
+
"start": int timestamp in ms >= 0
|
2057
|
+
"end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
|
2058
|
+
"tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
|
2059
|
+
"tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
|
1487
2060
|
}
|
1488
|
-
```
|
1489
2061
|
|
2062
|
+
# Output format selection:
|
2063
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
2064
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
2065
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
2066
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
2067
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
2068
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
2069
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
2070
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
2071
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
2072
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
2073
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
2074
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
2075
|
+
```
|
1490
2076
|
---
|
1491
2077
|
Rate Limit Weight:
|
1492
2078
|
```python
|
1493
2079
|
5
|
1494
2080
|
```
|
1495
|
-
|
1496
2081
|
---
|
1497
2082
|
Returns:
|
1498
2083
|
```python
|
1499
2084
|
[
|
1500
2085
|
{
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
2086
|
+
"id": "108c3633-0276-4480-a902-17a01829deae",
|
2087
|
+
"orderId": "1d671998-3d44-4df4-965f-0d48bd129a1b",
|
2088
|
+
"timestamp": 1542967486256,
|
2089
|
+
"market": "BTC-EUR",
|
2090
|
+
"side": "buy",
|
2091
|
+
"amount": "0.005",
|
2092
|
+
"price": "5000.1",
|
2093
|
+
"taker": true,
|
2094
|
+
"fee": "0.03",
|
2095
|
+
"feeCurrency": "EUR",
|
2096
|
+
"settled": true
|
1512
2097
|
}
|
1513
2098
|
]
|
1514
2099
|
```
|
1515
2100
|
""" # noqa: E501
|
1516
2101
|
options = _default(options, {})
|
1517
2102
|
options["market"] = market
|
1518
|
-
postfix =
|
1519
|
-
|
2103
|
+
postfix = create_postfix(options)
|
2104
|
+
result = self.private_request("/trades", postfix, {}, "GET", 5) # type: ignore[return-value]
|
2105
|
+
return convert_to_dataframe(result, output_format)
|
1520
2106
|
|
1521
2107
|
def account(self) -> dict[str, strdict]:
|
1522
2108
|
"""Get all fees for this account
|
@@ -1539,7 +2125,7 @@ class Bitvavo:
|
|
1539
2125
|
}
|
1540
2126
|
```
|
1541
2127
|
"""
|
1542
|
-
return self.
|
2128
|
+
return self.private_request("/account", "", {}, "GET") # type: ignore[return-value]
|
1543
2129
|
|
1544
2130
|
def fees(self, market: str | None = None, quote: str | None = None) -> list[strdict] | errordict:
|
1545
2131
|
"""Get market fees for a specific market or quote currency
|
@@ -1576,10 +2162,14 @@ class Bitvavo:
|
|
1576
2162
|
options["market"] = market
|
1577
2163
|
if quote is not None:
|
1578
2164
|
options["quote"] = quote
|
1579
|
-
postfix =
|
1580
|
-
return self.
|
2165
|
+
postfix = create_postfix(options)
|
2166
|
+
return self.private_request("/account/fees", postfix, {}, "GET") # type: ignore[return-value]
|
1581
2167
|
|
1582
|
-
def balance(
|
2168
|
+
def balance(
|
2169
|
+
self,
|
2170
|
+
options: strdict | None = None,
|
2171
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
2172
|
+
) -> list[strdict] | errordict | Any:
|
1583
2173
|
"""Get the balance for this account
|
1584
2174
|
|
1585
2175
|
---
|
@@ -1587,6 +2177,23 @@ class Bitvavo:
|
|
1587
2177
|
```python
|
1588
2178
|
options={} # return all balances
|
1589
2179
|
options={symbol="BTC"} # return a single balance
|
2180
|
+
|
2181
|
+
# Output format selection:
|
2182
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
2183
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
2184
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
2185
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
2186
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
2187
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
2188
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
2189
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
2190
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
2191
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
2192
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
2193
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
2194
|
+
|
2195
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
2196
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
1590
2197
|
```
|
1591
2198
|
|
1592
2199
|
---
|
@@ -1598,6 +2205,7 @@ class Bitvavo:
|
|
1598
2205
|
---
|
1599
2206
|
Returns:
|
1600
2207
|
```python
|
2208
|
+
# When output_format='dict' (default):
|
1601
2209
|
[
|
1602
2210
|
{
|
1603
2211
|
"symbol": "BTC",
|
@@ -1605,12 +2213,21 @@ class Bitvavo:
|
|
1605
2213
|
"inOrder": "0.74832374"
|
1606
2214
|
}
|
1607
2215
|
]
|
2216
|
+
|
2217
|
+
# When output_format is any DataFrame format:
|
2218
|
+
# Returns the above data as a DataFrame in the requested format (pandas, polars, etc.)
|
2219
|
+
# with columns: symbol, available, inOrder
|
1608
2220
|
```
|
1609
2221
|
"""
|
1610
|
-
postfix =
|
1611
|
-
|
2222
|
+
postfix = create_postfix(options)
|
2223
|
+
result = self.private_request("/balance", postfix, {}, "GET", 5) # type: ignore[return-value]
|
2224
|
+
return convert_to_dataframe(result, output_format)
|
1612
2225
|
|
2226
|
+
@deprecated(version="2.2.0", reason="Use account_history instead")
|
1613
2227
|
def accountHistory(self, options: strintdict | None = None) -> anydict | errordict:
|
2228
|
+
return self.account_history(options)
|
2229
|
+
|
2230
|
+
def account_history(self, options: strintdict | None = None) -> anydict | errordict:
|
1614
2231
|
"""Get all past transactions for your account
|
1615
2232
|
|
1616
2233
|
---
|
@@ -1654,10 +2271,14 @@ class Bitvavo:
|
|
1654
2271
|
}
|
1655
2272
|
```
|
1656
2273
|
"""
|
1657
|
-
postfix =
|
1658
|
-
return self.
|
2274
|
+
postfix = create_postfix(options)
|
2275
|
+
return self.private_request("/account/history", postfix, {}, "GET") # type: ignore[return-value]
|
1659
2276
|
|
2277
|
+
@deprecated(version="2.2.0", reason="Use deposit_assets instead")
|
1660
2278
|
def depositAssets(self, symbol: str) -> strdict:
|
2279
|
+
return self.deposit_assets(symbol)
|
2280
|
+
|
2281
|
+
def deposit_assets(self, symbol: str) -> strdict:
|
1661
2282
|
"""Get the deposit address (with paymentId for some assets) or bank account information to increase your balance
|
1662
2283
|
|
1663
2284
|
---
|
@@ -1689,10 +2310,14 @@ class Bitvavo:
|
|
1689
2310
|
}
|
1690
2311
|
```
|
1691
2312
|
"""
|
1692
|
-
postfix =
|
1693
|
-
return self.
|
2313
|
+
postfix = create_postfix({"symbol": symbol})
|
2314
|
+
return self.private_request("/deposit", postfix, {}, "GET") # type: ignore[return-value]
|
1694
2315
|
|
2316
|
+
@deprecated(version="2.2.0", reason="Use deposit_history instead")
|
1695
2317
|
def depositHistory(self, options: anydict | None = None) -> list[anydict] | errordict:
|
2318
|
+
return self.deposit_history(options)
|
2319
|
+
|
2320
|
+
def deposit_history(self, options: anydict | None = None) -> list[anydict] | errordict:
|
1696
2321
|
"""Get the deposit history of the account
|
1697
2322
|
|
1698
2323
|
Even when you want something from a single `symbol`, you'll still receive a list with multiple deposits.
|
@@ -1740,10 +2365,14 @@ class Bitvavo:
|
|
1740
2365
|
]
|
1741
2366
|
```
|
1742
2367
|
""" # noqa: E501
|
1743
|
-
postfix =
|
1744
|
-
return self.
|
2368
|
+
postfix = create_postfix(options)
|
2369
|
+
return self.private_request("/depositHistory", postfix, {}, "GET", 5) # type: ignore[return-value]
|
1745
2370
|
|
2371
|
+
@deprecated(version="2.2.0", reason="Use withdraw_assets instead")
|
1746
2372
|
def withdrawAssets(self, symbol: str, amount: str, address: str, body: anydict) -> anydict:
|
2373
|
+
return self.withdraw_assets(symbol, amount, address, body)
|
2374
|
+
|
2375
|
+
def withdraw_assets(self, symbol: str, amount: str, address: str, body: anydict) -> anydict:
|
1747
2376
|
"""Withdraw a coin/token to an external crypto address or bank account.
|
1748
2377
|
|
1749
2378
|
---
|
@@ -1778,9 +2407,21 @@ class Bitvavo:
|
|
1778
2407
|
body["symbol"] = symbol
|
1779
2408
|
body["amount"] = amount
|
1780
2409
|
body["address"] = address
|
1781
|
-
return self.
|
2410
|
+
return self.private_request("/withdrawal", "", body, "POST") # type: ignore[return-value]
|
2411
|
+
|
2412
|
+
@deprecated(version="2.2.0", reason="Use withdrawal_history instead")
|
2413
|
+
def withdrawalHistory(
|
2414
|
+
self,
|
2415
|
+
options: anydict | None = None,
|
2416
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
2417
|
+
) -> list[anydict] | errordict | Any:
|
2418
|
+
return self.withdrawal_history(options, output_format)
|
1782
2419
|
|
1783
|
-
def
|
2420
|
+
def withdrawal_history(
|
2421
|
+
self,
|
2422
|
+
options: anydict | None = None,
|
2423
|
+
output_format: OutputFormat = OutputFormat.DICT,
|
2424
|
+
) -> list[anydict] | errordict | Any:
|
1784
2425
|
"""Get the withdrawal history
|
1785
2426
|
|
1786
2427
|
---
|
@@ -1792,8 +2433,24 @@ class Bitvavo:
|
|
1792
2433
|
"start": int timestamp in ms >= 0
|
1793
2434
|
"end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
|
1794
2435
|
}
|
1795
|
-
```
|
1796
2436
|
|
2437
|
+
# Output format selection:
|
2438
|
+
output_format=OutputFormat.DICT # Default: returns standard Python dict/list
|
2439
|
+
output_format=OutputFormat.PANDAS # Returns pandas DataFrame
|
2440
|
+
output_format=OutputFormat.POLARS # Returns polars DataFrame
|
2441
|
+
output_format=OutputFormat.CUDF # Returns NVIDIA cuDF (GPU-accelerated)
|
2442
|
+
output_format=OutputFormat.MODIN # Returns modin (distributed pandas)
|
2443
|
+
output_format=OutputFormat.PYARROW # Returns Apache Arrow Table
|
2444
|
+
output_format=OutputFormat.DASK # Returns Dask DataFrame (distributed)
|
2445
|
+
output_format=OutputFormat.DUCKDB # Returns DuckDB relation
|
2446
|
+
output_format=OutputFormat.IBIS # Returns Ibis expression
|
2447
|
+
output_format=OutputFormat.PYSPARK # Returns PySpark DataFrame
|
2448
|
+
output_format=OutputFormat.PYSPARK_CONNECT # Returns PySpark Connect DataFrame
|
2449
|
+
output_format=OutputFormat.SQLFRAME # Returns SQLFrame DataFrame
|
2450
|
+
|
2451
|
+
# Note: DataFrame formats require narwhals and the respective library to be installed.
|
2452
|
+
# Install with: pip install 'bitvavo-api-upgraded[pandas]' or similar for other formats.
|
2453
|
+
```
|
1797
2454
|
---
|
1798
2455
|
Rate Limit Weight:
|
1799
2456
|
```python
|
@@ -1814,13 +2471,130 @@ class Bitvavo:
|
|
1814
2471
|
"fee": "0.00006",
|
1815
2472
|
"status": "awaiting_processing"
|
1816
2473
|
}
|
1817
|
-
|
2474
|
+
]
|
1818
2475
|
```
|
1819
2476
|
""" # noqa: E501
|
1820
|
-
postfix =
|
1821
|
-
|
2477
|
+
postfix = create_postfix(options)
|
2478
|
+
result = self.private_request("/withdrawalHistory", postfix, {}, "GET", 5) # type: ignore[return-value]
|
2479
|
+
return convert_to_dataframe(result, output_format)
|
2480
|
+
|
2481
|
+
# API Key Management Helper Methods
|
2482
|
+
|
2483
|
+
def add_api_key(self, api_key: str, api_secret: str) -> None:
|
2484
|
+
"""Add a new API key to the available keys.
|
2485
|
+
|
2486
|
+
Args:
|
2487
|
+
api_key: The API key to add
|
2488
|
+
api_secret: The corresponding API secret
|
2489
|
+
"""
|
2490
|
+
new_key = {"key": api_key, "secret": api_secret}
|
2491
|
+
self.api_keys.append(new_key)
|
2492
|
+
|
2493
|
+
# Initialize rate limit tracking for this key using settings default
|
2494
|
+
key_index = len(self.api_keys) - 1
|
2495
|
+
default_rate_limit = bitvavo_upgraded_settings.DEFAULT_RATE_LIMIT
|
2496
|
+
self.rate_limits[key_index] = {"remaining": default_rate_limit, "resetAt": ms(0)}
|
2497
|
+
|
2498
|
+
logger.info("api-key-added", key_index=key_index)
|
2499
|
+
|
2500
|
+
def remove_api_key(self, api_key: str) -> bool:
|
2501
|
+
"""Remove an API key from the available keys.
|
2502
|
+
|
2503
|
+
Args:
|
2504
|
+
api_key: The API key to remove
|
2505
|
+
|
2506
|
+
Returns:
|
2507
|
+
bool: True if the key was found and removed, False otherwise
|
2508
|
+
"""
|
2509
|
+
for i, key_data in enumerate(self.api_keys):
|
2510
|
+
if key_data["key"] == api_key:
|
2511
|
+
_ = self.api_keys.pop(i)
|
2512
|
+
# Remove rate limit tracking for this key
|
2513
|
+
if i in self.rate_limits:
|
2514
|
+
del self.rate_limits[i]
|
2515
|
+
# Update rate limit tracking indices (shift them down)
|
2516
|
+
new_rate_limits = {}
|
2517
|
+
for key_idx, limits in self.rate_limits.items():
|
2518
|
+
if key_idx == -1 or key_idx < i: # keyless
|
2519
|
+
new_rate_limits[key_idx] = limits
|
2520
|
+
elif key_idx > i:
|
2521
|
+
new_rate_limits[key_idx - 1] = limits
|
2522
|
+
self.rate_limits = new_rate_limits
|
2523
|
+
|
2524
|
+
# Update current index if needed
|
2525
|
+
if self.current_api_key_index >= i:
|
2526
|
+
self.current_api_key_index = max(0, self.current_api_key_index - 1)
|
2527
|
+
|
2528
|
+
logger.info("api-key-removed", key_index=i)
|
2529
|
+
return True
|
2530
|
+
return False
|
2531
|
+
|
2532
|
+
def get_api_key_status(self) -> dict[str, dict[str, int | str | bool]]:
|
2533
|
+
"""Get the current status of all API keys including rate limits.
|
1822
2534
|
|
2535
|
+
Returns:
|
2536
|
+
dict: Status information for keyless and all API keys
|
2537
|
+
"""
|
2538
|
+
status = {}
|
2539
|
+
|
2540
|
+
# Keyless status
|
2541
|
+
keyless_limits = self.rate_limits.get(-1, {"remaining": 0, "resetAt": ms(0)})
|
2542
|
+
status["keyless"] = {
|
2543
|
+
"remaining": int(keyless_limits["remaining"]),
|
2544
|
+
"resetAt": int(keyless_limits["resetAt"]),
|
2545
|
+
"available": self._has_rate_limit_available(-1, 1),
|
2546
|
+
}
|
2547
|
+
|
2548
|
+
# API key status
|
2549
|
+
for i, key_data in enumerate(self.api_keys):
|
2550
|
+
key_limits = self.rate_limits.get(i, {"remaining": 0, "resetAt": ms(0)})
|
2551
|
+
KEY_LENGTH = 12
|
2552
|
+
key_masked = (
|
2553
|
+
key_data["key"][:8] + "..." + key_data["key"][-4:]
|
2554
|
+
if len(key_data["key"]) > KEY_LENGTH
|
2555
|
+
else key_data["key"]
|
2556
|
+
)
|
2557
|
+
status[f"api_key_{i}"] = {
|
2558
|
+
"key": key_masked,
|
2559
|
+
"remaining": int(key_limits["remaining"]),
|
2560
|
+
"resetAt": int(key_limits["resetAt"]),
|
2561
|
+
"available": self._has_rate_limit_available(i, 1),
|
2562
|
+
}
|
2563
|
+
|
2564
|
+
return status
|
2565
|
+
|
2566
|
+
def set_keyless_preference(self, prefer_keyless: bool) -> None: # noqa: FBT001 (Boolean-typed positional argument in function definition)
|
2567
|
+
"""Set whether to prefer keyless requests.
|
2568
|
+
|
2569
|
+
Args:
|
2570
|
+
prefer_keyless: If True, use keyless requests first when available
|
2571
|
+
"""
|
2572
|
+
self.prefer_keyless = prefer_keyless
|
2573
|
+
logger.info("keyless-preference-changed", prefer_keyless=prefer_keyless)
|
2574
|
+
|
2575
|
+
def get_current_config(self) -> dict[str, str | bool | int]:
|
2576
|
+
"""Get the current configuration.
|
2577
|
+
|
2578
|
+
Returns:
|
2579
|
+
dict: Current configuration including key count and preferences
|
2580
|
+
"""
|
2581
|
+
KEY_LENGTH = 12
|
2582
|
+
return {
|
2583
|
+
"api_key_count": len(self.api_keys),
|
2584
|
+
"prefer_keyless": self.prefer_keyless,
|
2585
|
+
"current_api_key_index": self.current_api_key_index,
|
2586
|
+
"current_api_key": self._current_api_key[:8] + "..." + self._current_api_key[-4:]
|
2587
|
+
if len(self._current_api_key) > KEY_LENGTH
|
2588
|
+
else self._current_api_key,
|
2589
|
+
"rate_limit_remaining": self.rateLimitRemaining,
|
2590
|
+
"rate_limit_reset_at": int(self.rateLimitResetAt),
|
2591
|
+
}
|
2592
|
+
|
2593
|
+
@deprecated(version="2.2.0", reason="Use new_websocket instead")
|
1823
2594
|
def newWebsocket(self) -> Bitvavo.WebSocketAppFacade:
|
2595
|
+
return self.new_websocket()
|
2596
|
+
|
2597
|
+
def new_websocket(self) -> Bitvavo.WebSocketAppFacade:
|
1824
2598
|
return Bitvavo.WebSocketAppFacade(self.APIKEY, self.APISECRET, self.ACCESSWINDOW, self.wsUrl, self)
|
1825
2599
|
|
1826
2600
|
class WebSocketAppFacade:
|
@@ -1869,18 +2643,30 @@ class Bitvavo:
|
|
1869
2643
|
self.keepBookCopy = False
|
1870
2644
|
self.localBook: anydict = {}
|
1871
2645
|
|
2646
|
+
@deprecated(version="2.2.0", reason="Use close_socket instead")
|
1872
2647
|
def closeSocket(self) -> None:
|
2648
|
+
return self.close_socket()
|
2649
|
+
|
2650
|
+
def close_socket(self) -> None:
|
1873
2651
|
self.ws.close()
|
1874
2652
|
self.keepAlive = False
|
1875
2653
|
self.receiveThread.join()
|
1876
2654
|
|
1877
|
-
|
1878
|
-
|
2655
|
+
@deprecated(version="2.2.0", reason="Use wait_for_socket instead")
|
2656
|
+
def waitForSocket(self, ws: WebSocketApp, message: str, private: bool) -> None: # noqa: FBT001
|
2657
|
+
return self.wait_for_socket(ws, message, private)
|
2658
|
+
|
2659
|
+
def wait_for_socket(self, ws: WebSocketApp, message: str, private: bool) -> None: # noqa: ARG002, FBT001
|
2660
|
+
while self.keepAlive:
|
1879
2661
|
if (not private and self.open) or (private and self.authenticated and self.open):
|
1880
2662
|
return
|
1881
2663
|
time.sleep(0.1)
|
1882
2664
|
|
2665
|
+
@deprecated(version="2.2.0", reason="Use do_send instead")
|
1883
2666
|
def doSend(self, ws: WebSocketApp, message: str, private: bool = False) -> None: # noqa: FBT001, FBT002
|
2667
|
+
return self.do_send(ws, message, private)
|
2668
|
+
|
2669
|
+
def do_send(self, ws: WebSocketApp, message: str, private: bool = False) -> None: # noqa: FBT001, FBT002
|
1884
2670
|
# TODO(NostraDavid): add nap-time to the websocket, or do it here; I don't know yet.
|
1885
2671
|
if private and self.APIKEY == "":
|
1886
2672
|
logger.error(
|
@@ -1888,7 +2674,7 @@ class Bitvavo:
|
|
1888
2674
|
tip="set the API key to be able to make private API calls",
|
1889
2675
|
)
|
1890
2676
|
return
|
1891
|
-
self.
|
2677
|
+
self.wait_for_socket(ws, message, private)
|
1892
2678
|
ws.send(message)
|
1893
2679
|
if self.bitvavo.debugging:
|
1894
2680
|
logger.debug("message-sent", message=message)
|
@@ -1901,7 +2687,7 @@ class Bitvavo:
|
|
1901
2687
|
|
1902
2688
|
if "error" in msg_dict:
|
1903
2689
|
if msg_dict["errorCode"] == 105: # noqa: PLR2004
|
1904
|
-
self.bitvavo.
|
2690
|
+
self.bitvavo.update_rate_limit(msg_dict)
|
1905
2691
|
if "error" in callbacks:
|
1906
2692
|
callbacks["error"](msg_dict)
|
1907
2693
|
else:
|
@@ -1989,6 +2775,8 @@ class Bitvavo:
|
|
1989
2775
|
callbacks["subscriptionTrades"][market](msg_dict)
|
1990
2776
|
|
1991
2777
|
def on_error(self, ws: Any, error: Any) -> None: # noqa: ARG002
|
2778
|
+
# Stop the receive thread on error to prevent hanging
|
2779
|
+
self.receiveThread.stop()
|
1992
2780
|
if "error" in self.callbacks:
|
1993
2781
|
self.callbacks["error"](error)
|
1994
2782
|
else:
|
@@ -1999,47 +2787,51 @@ class Bitvavo:
|
|
1999
2787
|
if self.bitvavo.debugging:
|
2000
2788
|
logger.debug("websocket-closed")
|
2001
2789
|
|
2002
|
-
|
2790
|
+
@deprecated(version="2.2.0", reason="Use check_reconnect instead")
|
2791
|
+
def checkReconnect(self) -> None:
|
2792
|
+
return self.check_reconnect()
|
2793
|
+
|
2794
|
+
def check_reconnect(self) -> None: # noqa: C901, PLR0912 (too-complex)
|
2003
2795
|
if "subscriptionTicker" in self.callbacks:
|
2004
2796
|
for market in self.callbacks["subscriptionTicker"]:
|
2005
|
-
self.
|
2797
|
+
self.subscription_ticker(market, self.callbacks["subscriptionTicker"][market])
|
2006
2798
|
if "subscriptionTicker24h" in self.callbacks:
|
2007
2799
|
for market in self.callbacks["subscriptionTicker24h"]:
|
2008
|
-
self.
|
2800
|
+
self.subscription_ticker(market, self.callbacks["subscriptionTicker24h"][market])
|
2009
2801
|
if "subscriptionAccount" in self.callbacks:
|
2010
2802
|
for market in self.callbacks["subscriptionAccount"]:
|
2011
|
-
self.
|
2803
|
+
self.subscription_account(market, self.callbacks["subscriptionAccount"][market])
|
2012
2804
|
if "subscriptionCandles" in self.callbacks:
|
2013
2805
|
for market in self.callbacks["subscriptionCandles"]:
|
2014
2806
|
for interval in self.callbacks["subscriptionCandles"][market]:
|
2015
|
-
self.
|
2807
|
+
self.subscription_candles(
|
2016
2808
|
market,
|
2017
2809
|
interval,
|
2018
2810
|
self.callbacks["subscriptionCandles"][market][interval],
|
2019
2811
|
)
|
2020
2812
|
if "subscriptionTrades" in self.callbacks:
|
2021
2813
|
for market in self.callbacks["subscriptionTrades"]:
|
2022
|
-
self.
|
2814
|
+
self.subscription_trades(market, self.callbacks["subscriptionTrades"][market])
|
2023
2815
|
if "subscriptionBookUpdate" in self.callbacks:
|
2024
2816
|
for market in self.callbacks["subscriptionBookUpdate"]:
|
2025
|
-
self.
|
2817
|
+
self.subscription_book_update(market, self.callbacks["subscriptionBookUpdate"][market])
|
2026
2818
|
if "subscriptionBookUser" in self.callbacks:
|
2027
2819
|
for market in self.callbacks["subscriptionBookUser"]:
|
2028
|
-
self.
|
2820
|
+
self.subscription_book(market, self.callbacks["subscriptionBookUser"][market])
|
2029
2821
|
|
2030
2822
|
def on_open(self, ws: Any) -> None: # noqa: ARG002
|
2031
2823
|
now = time_ms() + bitvavo_upgraded_settings.LAG
|
2032
2824
|
self.open = True
|
2033
2825
|
self.reconnectTimer = 0.5
|
2034
2826
|
if self.APIKEY != "":
|
2035
|
-
self.
|
2827
|
+
self.do_send(
|
2036
2828
|
self.ws,
|
2037
2829
|
json.dumps(
|
2038
2830
|
{
|
2039
2831
|
"window": str(self.ACCESSWINDOW),
|
2040
2832
|
"action": "authenticate",
|
2041
2833
|
"key": self.APIKEY,
|
2042
|
-
"signature":
|
2834
|
+
"signature": create_signature(now, "GET", "/websocket", {}, self.APISECRET),
|
2043
2835
|
"timestamp": now,
|
2044
2836
|
},
|
2045
2837
|
),
|
@@ -2047,10 +2839,14 @@ class Bitvavo:
|
|
2047
2839
|
if self.reconnect:
|
2048
2840
|
if self.bitvavo.debugging:
|
2049
2841
|
logger.debug("reconnecting")
|
2050
|
-
thread = Thread(target=self.
|
2842
|
+
thread = Thread(target=self.check_reconnect)
|
2051
2843
|
thread.start()
|
2052
2844
|
|
2845
|
+
@deprecated(version="2.2.0", reason="Use set_error_callback instead")
|
2053
2846
|
def setErrorCallback(self, callback: Callable[[Any], None]) -> None:
|
2847
|
+
return self.set_error_callback(callback)
|
2848
|
+
|
2849
|
+
def set_error_callback(self, callback: Callable[[Any], None]) -> None:
|
2054
2850
|
self.callbacks["error"] = callback
|
2055
2851
|
|
2056
2852
|
def time(self, callback: Callable[[Any], None]) -> None:
|
@@ -2078,7 +2874,7 @@ class Bitvavo:
|
|
2078
2874
|
```
|
2079
2875
|
"""
|
2080
2876
|
self.callbacks["time"] = callback
|
2081
|
-
self.
|
2877
|
+
self.do_send(self.ws, json.dumps({"action": "getTime"}))
|
2082
2878
|
|
2083
2879
|
def markets(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2084
2880
|
"""Get all available markets with some meta-information, unless options is given a `market` key.
|
@@ -2132,7 +2928,7 @@ class Bitvavo:
|
|
2132
2928
|
"""
|
2133
2929
|
self.callbacks["markets"] = callback
|
2134
2930
|
options["action"] = "getMarkets"
|
2135
|
-
self.
|
2931
|
+
self.do_send(self.ws, json.dumps(options))
|
2136
2932
|
|
2137
2933
|
def assets(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2138
2934
|
"""Get all available assets, unless `options` is given a `symbol` key.
|
@@ -2183,7 +2979,7 @@ class Bitvavo:
|
|
2183
2979
|
"""
|
2184
2980
|
self.callbacks["assets"] = callback
|
2185
2981
|
options["action"] = "getAssets"
|
2186
|
-
self.
|
2982
|
+
self.do_send(self.ws, json.dumps(options))
|
2187
2983
|
|
2188
2984
|
def book(self, market: str, options: anydict, callback: Callable[[Any], None]) -> None:
|
2189
2985
|
"""Get a book (with two lists: asks and bids, as they're called)
|
@@ -2230,7 +3026,7 @@ class Bitvavo:
|
|
2230
3026
|
self.callbacks["book"] = callback
|
2231
3027
|
options["market"] = market
|
2232
3028
|
options["action"] = "getBook"
|
2233
|
-
self.
|
3029
|
+
self.do_send(self.ws, json.dumps(options))
|
2234
3030
|
|
2235
3031
|
def publicTrades(self, market: str, options: anydict, callback: Callable[[Any], None]) -> None:
|
2236
3032
|
"""Publically available trades
|
@@ -2282,7 +3078,7 @@ class Bitvavo:
|
|
2282
3078
|
self.callbacks["publicTrades"] = callback
|
2283
3079
|
options["market"] = market
|
2284
3080
|
options["action"] = "getTrades"
|
2285
|
-
self.
|
3081
|
+
self.do_send(self.ws, json.dumps(options))
|
2286
3082
|
|
2287
3083
|
def candles(
|
2288
3084
|
self,
|
@@ -2341,9 +3137,13 @@ class Bitvavo:
|
|
2341
3137
|
options["market"] = market
|
2342
3138
|
options["interval"] = interval
|
2343
3139
|
options["action"] = "getCandles"
|
2344
|
-
self.
|
3140
|
+
self.do_send(self.ws, json.dumps(options))
|
2345
3141
|
|
3142
|
+
@deprecated(version="2.2.0", reason="Use ticker_price instead")
|
2346
3143
|
def tickerPrice(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3144
|
+
return self.ticker_price(options, callback)
|
3145
|
+
|
3146
|
+
def ticker_price(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2347
3147
|
"""Get the current price for each market
|
2348
3148
|
|
2349
3149
|
---
|
@@ -2391,9 +3191,13 @@ class Bitvavo:
|
|
2391
3191
|
"""
|
2392
3192
|
self.callbacks["tickerPrice"] = callback
|
2393
3193
|
options["action"] = "getTickerPrice"
|
2394
|
-
self.
|
3194
|
+
self.do_send(self.ws, json.dumps(options))
|
2395
3195
|
|
3196
|
+
@deprecated(version="2.2.0", reason="Use ticker_book instead")
|
2396
3197
|
def tickerBook(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3198
|
+
return self.ticker_book(options, callback)
|
3199
|
+
|
3200
|
+
def ticker_book(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2397
3201
|
"""Get current bid/ask, bidsize/asksize per market
|
2398
3202
|
|
2399
3203
|
---
|
@@ -2434,7 +3238,7 @@ class Bitvavo:
|
|
2434
3238
|
""" # noqa: E501
|
2435
3239
|
self.callbacks["tickerBook"] = callback
|
2436
3240
|
options["action"] = "getTickerBook"
|
2437
|
-
self.
|
3241
|
+
self.do_send(self.ws, json.dumps(options))
|
2438
3242
|
|
2439
3243
|
def ticker24h(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2440
3244
|
"""Get current bid/ask, bidsize/asksize per market
|
@@ -2501,8 +3305,9 @@ class Bitvavo:
|
|
2501
3305
|
"""
|
2502
3306
|
self.callbacks["ticker24h"] = callback
|
2503
3307
|
options["action"] = "getTicker24h"
|
2504
|
-
self.
|
3308
|
+
self.do_send(self.ws, json.dumps(options))
|
2505
3309
|
|
3310
|
+
@deprecated(version="2.2.0", reason="Use place_order instead")
|
2506
3311
|
def placeOrder(
|
2507
3312
|
self,
|
2508
3313
|
market: str,
|
@@ -2511,6 +3316,17 @@ class Bitvavo:
|
|
2511
3316
|
operatorId: int,
|
2512
3317
|
body: anydict,
|
2513
3318
|
callback: Callable[[Any], None],
|
3319
|
+
) -> None:
|
3320
|
+
return self.place_order(market, side, orderType, operatorId, body, callback)
|
3321
|
+
|
3322
|
+
def place_order(
|
3323
|
+
self,
|
3324
|
+
market: str,
|
3325
|
+
side: str,
|
3326
|
+
orderType: str,
|
3327
|
+
operatorId: int,
|
3328
|
+
body: anydict,
|
3329
|
+
callback: Callable[[Any], None],
|
2514
3330
|
) -> None:
|
2515
3331
|
"""Place a new order on the exchange
|
2516
3332
|
|
@@ -2643,8 +3459,9 @@ class Bitvavo:
|
|
2643
3459
|
body["orderType"] = orderType
|
2644
3460
|
body["operatorId"] = operatorId
|
2645
3461
|
body["action"] = "privateCreateOrder"
|
2646
|
-
self.
|
3462
|
+
self.do_send(self.ws, json.dumps(body), True)
|
2647
3463
|
|
3464
|
+
@deprecated(version="2.2.0", reason="Use update_order instead")
|
2648
3465
|
def updateOrder(
|
2649
3466
|
self,
|
2650
3467
|
market: str,
|
@@ -2652,6 +3469,16 @@ class Bitvavo:
|
|
2652
3469
|
operatorId: int,
|
2653
3470
|
body: anydict,
|
2654
3471
|
callback: Callable[[Any], None],
|
3472
|
+
) -> None:
|
3473
|
+
return self.update_order(market, orderId, operatorId, body, callback)
|
3474
|
+
|
3475
|
+
def update_order(
|
3476
|
+
self,
|
3477
|
+
market: str,
|
3478
|
+
orderId: str,
|
3479
|
+
operatorId: int,
|
3480
|
+
body: anydict,
|
3481
|
+
callback: Callable[[Any], None],
|
2655
3482
|
) -> None:
|
2656
3483
|
"""
|
2657
3484
|
Update an existing order for a specific market. Make sure that at least one of the optional parameters
|
@@ -2741,8 +3568,9 @@ class Bitvavo:
|
|
2741
3568
|
body["orderId"] = orderId
|
2742
3569
|
body["operatorId"] = operatorId
|
2743
3570
|
body["action"] = "privateUpdateOrder"
|
2744
|
-
self.
|
3571
|
+
self.do_send(self.ws, json.dumps(body), True)
|
2745
3572
|
|
3573
|
+
@deprecated(version="2.2.0", reason="Use cancel_order instead")
|
2746
3574
|
def cancelOrder(
|
2747
3575
|
self,
|
2748
3576
|
market: str,
|
@@ -2750,6 +3578,16 @@ class Bitvavo:
|
|
2750
3578
|
callback: Callable[[Any], None],
|
2751
3579
|
orderId: str | None = None,
|
2752
3580
|
clientOrderId: str | None = None,
|
3581
|
+
) -> None:
|
3582
|
+
return self.cancel_order(market, operatorId, callback, orderId, clientOrderId)
|
3583
|
+
|
3584
|
+
def cancel_order(
|
3585
|
+
self,
|
3586
|
+
market: str,
|
3587
|
+
operatorId: int,
|
3588
|
+
callback: Callable[[Any], None],
|
3589
|
+
orderId: str | None = None,
|
3590
|
+
clientOrderId: str | None = None,
|
2753
3591
|
) -> None:
|
2754
3592
|
"""Cancel an existing order for a specific market
|
2755
3593
|
|
@@ -2793,9 +3631,13 @@ class Bitvavo:
|
|
2793
3631
|
elif orderId is not None:
|
2794
3632
|
options["orderId"] = orderId
|
2795
3633
|
|
2796
|
-
self.
|
3634
|
+
self.do_send(self.ws, json.dumps(options), True)
|
2797
3635
|
|
3636
|
+
@deprecated(version="2.2.0", reason="Use get_order instead")
|
2798
3637
|
def getOrder(self, market: str, orderId: str, callback: Callable[[Any], None]) -> None:
|
3638
|
+
return self.get_order(market, orderId, callback)
|
3639
|
+
|
3640
|
+
def get_order(self, market: str, orderId: str, callback: Callable[[Any], None]) -> None:
|
2799
3641
|
"""Get an existing order for a specific market
|
2800
3642
|
|
2801
3643
|
---
|
@@ -2864,9 +3706,13 @@ class Bitvavo:
|
|
2864
3706
|
"market": market,
|
2865
3707
|
"orderId": orderId,
|
2866
3708
|
}
|
2867
|
-
self.
|
3709
|
+
self.do_send(self.ws, json.dumps(options), True)
|
2868
3710
|
|
3711
|
+
@deprecated(version="2.2.0", reason="Use get_orders instead")
|
2869
3712
|
def getOrders(self, market: str, options: anydict, callback: Callable[[Any], None]) -> None:
|
3713
|
+
return self.get_orders(market, options, callback)
|
3714
|
+
|
3715
|
+
def get_orders(self, market: str, options: anydict, callback: Callable[[Any], None]) -> None:
|
2870
3716
|
"""Get multiple existing orders for a specific market
|
2871
3717
|
|
2872
3718
|
---
|
@@ -2944,9 +3790,13 @@ class Bitvavo:
|
|
2944
3790
|
self.callbacks["getOrders"] = callback
|
2945
3791
|
options["action"] = "privateGetOrders"
|
2946
3792
|
options["market"] = market
|
2947
|
-
self.
|
3793
|
+
self.do_send(self.ws, json.dumps(options), True)
|
2948
3794
|
|
3795
|
+
@deprecated(version="2.2.0", reason="Use cancel_orders instead")
|
2949
3796
|
def cancelOrders(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3797
|
+
return self.cancel_orders(options, callback)
|
3798
|
+
|
3799
|
+
def cancel_orders(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2950
3800
|
"""Cancel all existing orders for a specific market (or account)
|
2951
3801
|
|
2952
3802
|
---
|
@@ -2974,9 +3824,13 @@ class Bitvavo:
|
|
2974
3824
|
"""
|
2975
3825
|
self.callbacks["cancelOrders"] = callback
|
2976
3826
|
options["action"] = "privateCancelOrders"
|
2977
|
-
self.
|
3827
|
+
self.do_send(self.ws, json.dumps(options), True)
|
2978
3828
|
|
3829
|
+
@deprecated(version="2.2.0", reason="Use orders_open instead")
|
2979
3830
|
def ordersOpen(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3831
|
+
return self.orders_open(options, callback)
|
3832
|
+
|
3833
|
+
def orders_open(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
2980
3834
|
"""Get all open orders, either for all markets, or a single market
|
2981
3835
|
|
2982
3836
|
---
|
@@ -3044,7 +3898,7 @@ class Bitvavo:
|
|
3044
3898
|
"""
|
3045
3899
|
self.callbacks["ordersOpen"] = callback
|
3046
3900
|
options["action"] = "privateGetOrdersOpen"
|
3047
|
-
self.
|
3901
|
+
self.do_send(self.ws, json.dumps(options), True)
|
3048
3902
|
|
3049
3903
|
def trades(self, market: str, options: anydict, callback: Callable[[Any], None]) -> None:
|
3050
3904
|
"""Get all historic trades from this account
|
@@ -3093,7 +3947,7 @@ class Bitvavo:
|
|
3093
3947
|
self.callbacks["trades"] = callback
|
3094
3948
|
options["action"] = "privateGetTrades"
|
3095
3949
|
options["market"] = market
|
3096
|
-
self.
|
3950
|
+
self.do_send(self.ws, json.dumps(options), True)
|
3097
3951
|
|
3098
3952
|
def account(self, callback: Callable[[Any], None]) -> None:
|
3099
3953
|
"""Get all fees for this account
|
@@ -3123,7 +3977,7 @@ class Bitvavo:
|
|
3123
3977
|
```
|
3124
3978
|
"""
|
3125
3979
|
self.callbacks["account"] = callback
|
3126
|
-
self.
|
3980
|
+
self.do_send(self.ws, json.dumps({"action": "privateGetAccount"}), True)
|
3127
3981
|
|
3128
3982
|
def balance(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3129
3983
|
"""Get the balance for this account
|
@@ -3156,9 +4010,13 @@ class Bitvavo:
|
|
3156
4010
|
"""
|
3157
4011
|
options["action"] = "privateGetBalance"
|
3158
4012
|
self.callbacks["balance"] = callback
|
3159
|
-
self.
|
4013
|
+
self.do_send(self.ws, json.dumps(options), True)
|
3160
4014
|
|
4015
|
+
@deprecated(version="2.2.0", reason="Use deposit_assets instead")
|
3161
4016
|
def depositAssets(self, symbol: str, callback: Callable[[Any], None]) -> None:
|
4017
|
+
return self.deposit_assets(symbol, callback)
|
4018
|
+
|
4019
|
+
def deposit_assets(self, symbol: str, callback: Callable[[Any], None]) -> None:
|
3162
4020
|
"""
|
3163
4021
|
Get the deposit address (with paymentId for some assets) or bank account information to increase your
|
3164
4022
|
balance.
|
@@ -3194,13 +4052,17 @@ class Bitvavo:
|
|
3194
4052
|
```
|
3195
4053
|
"""
|
3196
4054
|
self.callbacks["depositAssets"] = callback
|
3197
|
-
self.
|
4055
|
+
self.do_send(
|
3198
4056
|
self.ws,
|
3199
4057
|
json.dumps({"action": "privateDepositAssets", "symbol": symbol}),
|
3200
4058
|
True,
|
3201
4059
|
)
|
3202
4060
|
|
4061
|
+
@deprecated(version="2.2.0", reason="Use deposit_history instead")
|
3203
4062
|
def depositHistory(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
4063
|
+
return self.deposit_history(options, callback)
|
4064
|
+
|
4065
|
+
def deposit_history(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3204
4066
|
"""Get the deposit history of the account
|
3205
4067
|
|
3206
4068
|
Even when you want something from a single `symbol`, you'll still receive a list with multiple deposits.
|
@@ -3252,8 +4114,9 @@ class Bitvavo:
|
|
3252
4114
|
"""
|
3253
4115
|
self.callbacks["depositHistory"] = callback
|
3254
4116
|
options["action"] = "privateGetDepositHistory"
|
3255
|
-
self.
|
4117
|
+
self.do_send(self.ws, json.dumps(options), True)
|
3256
4118
|
|
4119
|
+
@deprecated(version="2.2.0", reason="Use withdraw_assets instead")
|
3257
4120
|
def withdrawAssets(
|
3258
4121
|
self,
|
3259
4122
|
symbol: str,
|
@@ -3261,6 +4124,16 @@ class Bitvavo:
|
|
3261
4124
|
address: str,
|
3262
4125
|
body: anydict,
|
3263
4126
|
callback: Callable[[Any], None],
|
4127
|
+
) -> None:
|
4128
|
+
return self.withdraw_assets(symbol, amount, address, body, callback)
|
4129
|
+
|
4130
|
+
def withdraw_assets(
|
4131
|
+
self,
|
4132
|
+
symbol: str,
|
4133
|
+
amount: str,
|
4134
|
+
address: str,
|
4135
|
+
body: anydict,
|
4136
|
+
callback: Callable[[Any], None],
|
3264
4137
|
) -> None:
|
3265
4138
|
"""Withdraw a coin/token to an external crypto address or bank account.
|
3266
4139
|
|
@@ -3305,9 +4178,13 @@ class Bitvavo:
|
|
3305
4178
|
body["symbol"] = symbol
|
3306
4179
|
body["amount"] = amount
|
3307
4180
|
body["address"] = address
|
3308
|
-
self.
|
4181
|
+
self.do_send(self.ws, json.dumps(body), True)
|
3309
4182
|
|
4183
|
+
@deprecated(version="2.2.0", reason="Use withdrawal_history instead")
|
3310
4184
|
def withdrawalHistory(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
4185
|
+
return self.withdrawal_history(options, callback)
|
4186
|
+
|
4187
|
+
def withdrawal_history(self, options: anydict, callback: Callable[[Any], None]) -> None:
|
3311
4188
|
"""Get the withdrawal history
|
3312
4189
|
|
3313
4190
|
---
|
@@ -3348,9 +4225,13 @@ class Bitvavo:
|
|
3348
4225
|
"""
|
3349
4226
|
self.callbacks["withdrawalHistory"] = callback
|
3350
4227
|
options["action"] = "privateGetWithdrawalHistory"
|
3351
|
-
self.
|
4228
|
+
self.do_send(self.ws, json.dumps(options), True)
|
3352
4229
|
|
4230
|
+
@deprecated(version="2.2.0", reason="Use subscription_ticker instead")
|
3353
4231
|
def subscriptionTicker(self, market: str, callback: Callable[[Any], None]) -> None:
|
4232
|
+
return self.subscription_ticker(market, callback)
|
4233
|
+
|
4234
|
+
def subscription_ticker(self, market: str, callback: Callable[[Any], None]) -> None:
|
3354
4235
|
# TODO(NostraDavid): one possible improvement here is to turn `market` into a list of markets, so we can sub
|
3355
4236
|
# to all of them at once. Same goes for other `subscription*()`
|
3356
4237
|
"""
|
@@ -3392,7 +4273,7 @@ class Bitvavo:
|
|
3392
4273
|
if "subscriptionTicker" not in self.callbacks:
|
3393
4274
|
self.callbacks["subscriptionTicker"] = {}
|
3394
4275
|
self.callbacks["subscriptionTicker"][market] = callback
|
3395
|
-
self.
|
4276
|
+
self.do_send(
|
3396
4277
|
self.ws,
|
3397
4278
|
json.dumps(
|
3398
4279
|
{
|
@@ -3402,7 +4283,11 @@ class Bitvavo:
|
|
3402
4283
|
),
|
3403
4284
|
)
|
3404
4285
|
|
4286
|
+
@deprecated(version="2.2.0", reason="Use subscription_ticker24h instead")
|
3405
4287
|
def subscriptionTicker24h(self, market: str, callback: Callable[[Any], None]) -> None:
|
4288
|
+
return self.subscription_ticker24h(market, callback)
|
4289
|
+
|
4290
|
+
def subscription_ticker24h(self, market: str, callback: Callable[[Any], None]) -> None:
|
3406
4291
|
"""
|
3407
4292
|
Subscribe to the ticker-24-hour channel, which means `callback` gets passed the new object every second, if
|
3408
4293
|
values have changed.
|
@@ -3449,7 +4334,7 @@ class Bitvavo:
|
|
3449
4334
|
if "subscriptionTicker24h" not in self.callbacks:
|
3450
4335
|
self.callbacks["subscriptionTicker24h"] = {}
|
3451
4336
|
self.callbacks["subscriptionTicker24h"][market] = callback
|
3452
|
-
self.
|
4337
|
+
self.do_send(
|
3453
4338
|
self.ws,
|
3454
4339
|
json.dumps(
|
3455
4340
|
{
|
@@ -3459,7 +4344,11 @@ class Bitvavo:
|
|
3459
4344
|
),
|
3460
4345
|
)
|
3461
4346
|
|
4347
|
+
@deprecated(version="2.2.0", reason="Use subscription_account instead")
|
3462
4348
|
def subscriptionAccount(self, market: str, callback: Callable[[Any], None]) -> None:
|
4349
|
+
return self.subscription_account(market, callback)
|
4350
|
+
|
4351
|
+
def subscription_account(self, market: str, callback: Callable[[Any], None]) -> None:
|
3463
4352
|
"""
|
3464
4353
|
Subscribes to the account channel, which sends an update whenever an event happens which is related to
|
3465
4354
|
the account. These are 'order' events (create, update, cancel) or 'fill' events (a trade occurred).
|
@@ -3526,7 +4415,7 @@ class Bitvavo:
|
|
3526
4415
|
if "subscriptionAccount" not in self.callbacks:
|
3527
4416
|
self.callbacks["subscriptionAccount"] = {}
|
3528
4417
|
self.callbacks["subscriptionAccount"][market] = callback
|
3529
|
-
self.
|
4418
|
+
self.do_send(
|
3530
4419
|
self.ws,
|
3531
4420
|
json.dumps(
|
3532
4421
|
{
|
@@ -3537,7 +4426,11 @@ class Bitvavo:
|
|
3537
4426
|
True,
|
3538
4427
|
)
|
3539
4428
|
|
4429
|
+
@deprecated(version="2.2.0", reason="Use subscription_candles instead")
|
3540
4430
|
def subscriptionCandles(self, market: str, interval: str, callback: Callable[[Any], None]) -> None:
|
4431
|
+
return self.subscription_candles(market, interval, callback)
|
4432
|
+
|
4433
|
+
def subscription_candles(self, market: str, interval: str, callback: Callable[[Any], None]) -> None:
|
3541
4434
|
"""Subscribes to candles and returns a candle each time a new one is formed, depending on the interval
|
3542
4435
|
|
3543
4436
|
---
|
@@ -3585,7 +4478,7 @@ class Bitvavo:
|
|
3585
4478
|
if market not in self.callbacks["subscriptionCandles"]:
|
3586
4479
|
self.callbacks["subscriptionCandles"][market] = {}
|
3587
4480
|
self.callbacks["subscriptionCandles"][market][interval] = callback
|
3588
|
-
self.
|
4481
|
+
self.do_send(
|
3589
4482
|
self.ws,
|
3590
4483
|
json.dumps(
|
3591
4484
|
{
|
@@ -3601,7 +4494,11 @@ class Bitvavo:
|
|
3601
4494
|
),
|
3602
4495
|
)
|
3603
4496
|
|
4497
|
+
@deprecated(version="2.2.0", reason="Use subscription_trades instead")
|
3604
4498
|
def subscriptionTrades(self, market: str, callback: Callable[[Any], None]) -> None:
|
4499
|
+
return self.subscription_trades(market, callback)
|
4500
|
+
|
4501
|
+
def subscription_trades(self, market: str, callback: Callable[[Any], None]) -> None:
|
3605
4502
|
"""Subscribes to trades, which sends an object whenever a trade has occurred.
|
3606
4503
|
|
3607
4504
|
---
|
@@ -3638,7 +4535,7 @@ class Bitvavo:
|
|
3638
4535
|
if "subscriptionTrades" not in self.callbacks:
|
3639
4536
|
self.callbacks["subscriptionTrades"] = {}
|
3640
4537
|
self.callbacks["subscriptionTrades"][market] = callback
|
3641
|
-
self.
|
4538
|
+
self.do_send(
|
3642
4539
|
self.ws,
|
3643
4540
|
json.dumps(
|
3644
4541
|
{
|
@@ -3648,7 +4545,11 @@ class Bitvavo:
|
|
3648
4545
|
),
|
3649
4546
|
)
|
3650
4547
|
|
4548
|
+
@deprecated(version="2.2.0", reason="Use subscription_book_update instead")
|
3651
4549
|
def subscriptionBookUpdate(self, market: str, callback: Callable[[Any], None]) -> None:
|
4550
|
+
return self.subscription_book_update(market, callback)
|
4551
|
+
|
4552
|
+
def subscription_book_update(self, market: str, callback: Callable[[Any], None]) -> None:
|
3652
4553
|
"""Subscribes to the book and returns a delta on every change to the book.
|
3653
4554
|
|
3654
4555
|
---
|
@@ -3705,7 +4606,7 @@ class Bitvavo:
|
|
3705
4606
|
if "subscriptionBookUpdate" not in self.callbacks:
|
3706
4607
|
self.callbacks["subscriptionBookUpdate"] = {}
|
3707
4608
|
self.callbacks["subscriptionBookUpdate"][market] = callback
|
3708
|
-
self.
|
4609
|
+
self.do_send(
|
3709
4610
|
self.ws,
|
3710
4611
|
json.dumps(
|
3711
4612
|
{
|
@@ -3715,7 +4616,11 @@ class Bitvavo:
|
|
3715
4616
|
),
|
3716
4617
|
)
|
3717
4618
|
|
4619
|
+
@deprecated(version="2.2.0", reason="Use subscription_book instead")
|
3718
4620
|
def subscriptionBook(self, market: str, callback: Callable[[Any], None]) -> None:
|
4621
|
+
return self.subscription_book(market, callback)
|
4622
|
+
|
4623
|
+
def subscription_book(self, market: str, callback: Callable[[Any], None]) -> None:
|
3719
4624
|
"""Subscribes to the book and returns a delta on every change to the book.
|
3720
4625
|
|
3721
4626
|
---
|
@@ -3775,8 +4680,8 @@ class Bitvavo:
|
|
3775
4680
|
self.callbacks["subscriptionBookUser"][market] = callback
|
3776
4681
|
if "subscriptionBook" not in self.callbacks:
|
3777
4682
|
self.callbacks["subscriptionBook"] = {}
|
3778
|
-
self.callbacks["subscriptionBook"][market] =
|
3779
|
-
self.
|
4683
|
+
self.callbacks["subscriptionBook"][market] = process_local_book
|
4684
|
+
self.do_send(
|
3780
4685
|
self.ws,
|
3781
4686
|
json.dumps(
|
3782
4687
|
{
|
@@ -3787,4 +4692,4 @@ class Bitvavo:
|
|
3787
4692
|
)
|
3788
4693
|
|
3789
4694
|
self.localBook[market] = {}
|
3790
|
-
self.
|
4695
|
+
self.do_send(self.ws, json.dumps({"action": "getBook", "market": market}))
|