bitvavo-api-upgraded 1.15.8__py3-none-any.whl → 1.16.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.
@@ -1,10 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime as dt
1
4
  import hashlib
2
5
  import hmac
3
6
  import json
4
7
  import time
5
- import datetime as dt
6
8
  from threading import Thread
7
- from typing import Any, Callable, Dict, List, Union
9
+ from typing import Any, Callable
8
10
 
9
11
  import websocket as ws_lib
10
12
  from requests import delete, get, post, put
@@ -20,11 +22,11 @@ configure_loggers()
20
22
  logger = get_logger(__name__)
21
23
 
22
24
 
23
- def createSignature(timestamp: ms, method: str, url: str, body: anydict, APISECRET: str) -> str:
25
+ def createSignature(timestamp: ms, method: str, url: str, body: anydict, api_secret: str) -> str:
24
26
  string = f"{timestamp}{method}/v2{url}"
25
27
  if len(body.keys()) != 0:
26
28
  string += json.dumps(body, separators=(",", ":"))
27
- signature = hmac.new(APISECRET.encode("utf-8"), string.encode("utf-8"), hashlib.sha256).hexdigest()
29
+ signature = hmac.new(api_secret.encode("utf-8"), string.encode("utf-8"), hashlib.sha256).hexdigest()
28
30
  return signature
29
31
 
30
32
 
@@ -53,10 +55,10 @@ def bidsCompare(a: float, b: float) -> bool:
53
55
 
54
56
 
55
57
  def sortAndInsert(
56
- asks_or_bids: List[List[str]],
57
- update: List[List[str]],
58
+ asks_or_bids: list[list[str]],
59
+ update: list[list[str]],
58
60
  compareFunc: Callable[[float, float], bool],
59
- ) -> Union[List[List[str]], errordict]:
61
+ ) -> list[list[str]] | errordict:
60
62
  for updateEntry in update:
61
63
  entrySet: bool = False
62
64
  for j in range(len(asks_or_bids)):
@@ -70,16 +72,15 @@ def sortAndInsert(
70
72
  asks_or_bids[j] = updateEntry
71
73
  entrySet = True
72
74
  break
73
- else:
74
- asks_or_bids.pop(j)
75
- entrySet = True
76
- break
75
+ asks_or_bids.pop(j)
76
+ entrySet = True
77
+ break
77
78
  if not entrySet:
78
79
  asks_or_bids.append(updateEntry)
79
80
  return asks_or_bids
80
81
 
81
82
 
82
- def processLocalBook(ws: "Bitvavo.WebSocketAppFacade", message: anydict) -> None:
83
+ def processLocalBook(ws: Bitvavo.WebSocketAppFacade, message: anydict) -> None:
83
84
  market: str = ""
84
85
  if "action" in message:
85
86
  if message["action"] == "getBook":
@@ -88,24 +89,23 @@ def processLocalBook(ws: "Bitvavo.WebSocketAppFacade", message: anydict) -> None
88
89
  ws.localBook[market]["asks"] = message["response"]["asks"]
89
90
  ws.localBook[market]["nonce"] = message["response"]["nonce"]
90
91
  ws.localBook[market]["market"] = market
91
- elif "event" in message:
92
- if message["event"] == "book":
93
- market = message["market"]
92
+ elif "event" in message and message["event"] == "book":
93
+ market = message["market"]
94
94
 
95
- if message["nonce"] != ws.localBook[market]["nonce"] + 1:
96
- # I think I've fixed this, by looking at the other Bitvavo repos (search for 'nonce' or '!=' 😆)
97
- ws.subscriptionBook(market, ws.callbacks[market])
98
- return
99
- ws.localBook[market]["bids"] = sortAndInsert(ws.localBook[market]["bids"], message["bids"], bidsCompare)
100
- ws.localBook[market]["asks"] = sortAndInsert(ws.localBook[market]["asks"], message["asks"], asksCompare)
101
- ws.localBook[market]["nonce"] = message["nonce"]
95
+ if message["nonce"] != ws.localBook[market]["nonce"] + 1:
96
+ # I think I've fixed this, by looking at the other Bitvavo repos (search for 'nonce' or '!=' 😆)
97
+ ws.subscriptionBook(market, ws.callbacks[market])
98
+ return
99
+ ws.localBook[market]["bids"] = sortAndInsert(ws.localBook[market]["bids"], message["bids"], bidsCompare)
100
+ ws.localBook[market]["asks"] = sortAndInsert(ws.localBook[market]["asks"], message["asks"], asksCompare)
101
+ ws.localBook[market]["nonce"] = message["nonce"]
102
102
 
103
103
  if market != "":
104
104
  ws.callbacks["subscriptionBookUser"][market](ws.localBook[market])
105
105
 
106
106
 
107
107
  class ReceiveThread(Thread):
108
- def __init__(self, ws: WebSocketApp, ws_facade: "Bitvavo.WebSocketAppFacade"):
108
+ def __init__(self, ws: WebSocketApp, ws_facade: Bitvavo.WebSocketAppFacade) -> None:
109
109
  self.ws = ws
110
110
  self.ws_facade = ws_facade
111
111
  Thread.__init__(self)
@@ -118,9 +118,8 @@ class ReceiveThread(Thread):
118
118
  self.ws_facade.authenticated = False
119
119
  time.sleep(self.ws_facade.reconnectTimer)
120
120
  if self.ws_facade.bitvavo.debugging:
121
- logger.debug(
122
- f"we have just set reconnect to true and have waited for {self.ws_facade.reconnectTimer}",
123
- )
121
+ msg = f"we have just set reconnect to true and have waited for {self.ws_facade.reconnectTimer}"
122
+ logger.debug(msg)
124
123
  self.ws_facade.reconnectTimer = self.ws_facade.reconnectTimer * 2
125
124
  except KeyboardInterrupt:
126
125
  if self.ws_facade.bitvavo.debugging:
@@ -136,17 +135,18 @@ def callback_example(response: Any) -> None:
136
135
 
137
136
  I made this so you can see what kind of function you'll need to stick into the websocket functions.
138
137
  """
139
- if isinstance(response, Dict):
138
+ if isinstance(response, dict):
140
139
  # instead of printing, you could save the object to a file:
141
140
  import json
142
141
  from pathlib import Path
143
142
 
144
143
  HERE = Path.cwd() # root of your project folder
145
144
  filepath = HERE / "your_output.json"
146
- # a = append; figure out yourself to create multiple callback functions, probably one for each type of call that you want to make
145
+ # a = append; figure out yourself to create multiple callback functions, probably one for each type of call that
146
+ # you want to make
147
147
  with filepath.open("a") as file:
148
148
  file.write(json.dumps(response))
149
- elif isinstance(response, List):
149
+ elif isinstance(response, list):
150
150
  # Whether `item` is a list or a dict doesn't matter to print
151
151
  for item in response:
152
152
  print(item)
@@ -187,9 +187,9 @@ def error_callback_example(msg: errordict) -> None:
187
187
  content=f"{msg}",
188
188
  ).execute()
189
189
  ```
190
- """
190
+ """ # noqa: E501
191
191
  # easiest thing is to use the logger, but there's a good chance this message gets silently eaten.
192
- logger.error(msg)
192
+ logger.error("error", msg=msg)
193
193
 
194
194
 
195
195
  class Bitvavo:
@@ -211,7 +211,9 @@ class Bitvavo:
211
211
  ```
212
212
  """
213
213
 
214
- def __init__(self, options: Dict[str, Union[str, int]] = {}):
214
+ def __init__(self, options: dict[str, str | int] | None = None) -> None:
215
+ if options is None:
216
+ options = {}
215
217
  _options = {k.upper(): v for k, v in options.items()}
216
218
  self.base: str = str(_options.get("RESTURL", "https://api.bitvavo.com/v2"))
217
219
  self.wsUrl: str = str(_options.get("WSURL", "wss://ws.bitvavo.com/v2/"))
@@ -220,7 +222,7 @@ class Bitvavo:
220
222
  self.APISECRET = str(_options.get("APISECRET", ""))
221
223
  self.rateLimitRemaining: int = 1000
222
224
  self.rateLimitResetAt: ms = 0
223
- # TODO(NostraDavid) for v2: remove this functionality - logger.debug is a level that can be set
225
+ # TODO(NostraDavid): for v2: remove this functionality - logger.debug is a level that can be set
224
226
  self.debugging = bool(_options.get("DEBUGGING", False))
225
227
 
226
228
  def calcLag(self) -> ms:
@@ -256,7 +258,7 @@ class Bitvavo:
256
258
  """
257
259
  return self.rateLimitRemaining
258
260
 
259
- def updateRateLimit(self, response: Union[anydict, errordict]) -> None:
261
+ def updateRateLimit(self, response: anydict | errordict) -> None:
260
262
  """
261
263
  Update the rate limited
262
264
 
@@ -264,24 +266,23 @@ class Bitvavo:
264
266
 
265
267
  If you're not banned, then use the received headers to update the variables.
266
268
  """
267
- if "errorCode" in response:
268
- if response["errorCode"] == 105:
269
- self.rateLimitRemaining = 0
270
- # rateLimitResetAt is a value that's stripped from a string.
271
- # Kind of a terrible way to pass that information, but eh, whatever, I guess...
272
- # Anyway, here is the string that's being pulled apart:
273
- # "Your IP or API key has been banned for not respecting the rate limit. The ban expires at ${expiryInMs}""
274
- self.rateLimitResetAt = ms(response["error"].split(" at ")[1].split(".")[0])
275
- timeToWait = time_to_wait(self.rateLimitResetAt)
276
- logger.warning(
277
- "banned",
278
- info={
279
- "wait_time_seconds": timeToWait + 1,
280
- "until": (dt.datetime.now() + dt.timedelta(seconds=timeToWait + 1)).isoformat(),
281
- },
282
- )
283
- logger.info("napping-until-ban-lifted")
284
- time.sleep(timeToWait + 1) # plus one second to ENSURE we're able to run again.
269
+ if "errorCode" in response and response["errorCode"] == 105: # noqa: PLR2004
270
+ self.rateLimitRemaining = 0
271
+ # rateLimitResetAt is a value that's stripped from a string.
272
+ # Kind of a terrible way to pass that information, but eh, whatever, I guess...
273
+ # Anyway, here is the string that's being pulled apart:
274
+ # "Your IP or API key has been banned for not respecting the rate limit. The ban expires at ${expiryInMs}""
275
+ self.rateLimitResetAt = ms(response["error"].split(" at ")[1].split(".")[0])
276
+ timeToWait = time_to_wait(self.rateLimitResetAt)
277
+ logger.warning(
278
+ "banned",
279
+ info={
280
+ "wait_time_seconds": timeToWait + 1,
281
+ "until": (dt.datetime.now(tz=dt.timezone.utc) + dt.timedelta(seconds=timeToWait + 1)).isoformat(),
282
+ },
283
+ )
284
+ logger.info("napping-until-ban-lifted")
285
+ time.sleep(timeToWait + 1) # plus one second to ENSURE we're able to run again.
285
286
  if "Bitvavo-Ratelimit-Remaining" in response:
286
287
  self.rateLimitRemaining = int(response["Bitvavo-Ratelimit-Remaining"])
287
288
  if "Bitvavo-Ratelimit-ResetAt" in response:
@@ -291,7 +292,7 @@ class Bitvavo:
291
292
  self,
292
293
  url: str,
293
294
  rateLimitingWeight: int = 1,
294
- ) -> Union[List[anydict], List[List[str]], intdict, strdict, anydict, errordict]:
295
+ ) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | errordict:
295
296
  """Execute a request to the public part of the API; no API key and/or SECRET necessary.
296
297
  Will return the reponse as one of three types.
297
298
 
@@ -305,9 +306,9 @@ class Bitvavo:
305
306
  Returns:
306
307
  ```python
307
308
  # either of one:
308
- Dict[str, Any]
309
- List[Dict[str, Any]]
310
- List[List[str]]
309
+ dict[str, Any]
310
+ list[dict[str, Any]]
311
+ list[list[str]]
311
312
  ```
312
313
  """
313
314
  if (self.rateLimitRemaining - rateLimitingWeight) <= BITVAVO_API_UPGRADED.RATE_LIMITING_BUFFER:
@@ -337,7 +338,7 @@ class Bitvavo:
337
338
  self.updateRateLimit(r.json())
338
339
  else:
339
340
  self.updateRateLimit(dict(r.headers))
340
- return r.json() # type:ignore
341
+ return r.json() # type:ignore[no-any-return]
341
342
 
342
343
  def privateRequest(
343
344
  self,
@@ -346,7 +347,7 @@ class Bitvavo:
346
347
  body: anydict,
347
348
  method: str = "GET",
348
349
  rateLimitingWeight: int = 1,
349
- ) -> Union[List[anydict], List[List[str]], intdict, strdict, anydict, Any, errordict]:
350
+ ) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | Any | errordict:
350
351
  """Execute a request to the private part of the API. API key and SECRET are required.
351
352
  Will return the reponse as one of three types.
352
353
 
@@ -364,14 +365,14 @@ class Bitvavo:
364
365
  Returns:
365
366
  ```python
366
367
  # either of one:
367
- Dict[str, Any]
368
- List[Dict[str, Any]]
369
- List[List[str]]
368
+ dict[str, Any]
369
+ list[dict[str, Any]]
370
+ list[list[str]]
370
371
  ```
371
372
  """
372
373
  if (self.rateLimitRemaining - rateLimitingWeight) <= BITVAVO_API_UPGRADED.RATE_LIMITING_BUFFER:
373
374
  self.sleep_until_can_continue()
374
- # if this method breaks: add `= {}` after `body:Dict``
375
+ # if this method breaks: add `= {}` after `body: dict`
375
376
  now = time_ms() + BITVAVO_API_UPGRADED.LAG
376
377
  sig = createSignature(now, method, (endpoint + postfix), body, self.APISECRET)
377
378
  url = self.base + endpoint + postfix
@@ -405,12 +406,15 @@ class Bitvavo:
405
406
  self.updateRateLimit(dict(r.headers))
406
407
  return r.json()
407
408
 
408
- def sleep_until_can_continue(self):
409
+ def sleep_until_can_continue(self) -> None:
409
410
  napTime = time_to_wait(self.rateLimitResetAt)
410
411
  logger.warning("rate-limit-reached", rateLimitRemaining=self.rateLimitRemaining)
411
- logger.info("napping-until-reset", napTime=napTime,
412
- currentTime=dt.datetime.now().isoformat(),
413
- targetDatetime=dt.datetime.fromtimestamp(self.rateLimitResetAt / 1000.0).isoformat())
412
+ logger.info(
413
+ "napping-until-reset",
414
+ napTime=napTime,
415
+ currentTime=dt.datetime.now(tz=dt.timezone.utc).isoformat(),
416
+ targetDatetime=dt.datetime.fromtimestamp(self.rateLimitResetAt / 1000.0, tz=dt.timezone.utc).isoformat(),
417
+ )
414
418
  time.sleep(napTime + 1) # +1 to add a tiny bit of buffer time
415
419
 
416
420
  def time(self) -> intdict:
@@ -432,9 +436,9 @@ class Bitvavo:
432
436
  {"time": 1539180275424 }
433
437
  ```
434
438
  """
435
- return self.publicRequest(f"{self.base}/time")
439
+ return self.publicRequest(f"{self.base}/time") # type: ignore[return-value]
436
440
 
437
- def markets(self, options: strdict) -> Union[List[anydict], anydict, errordict]:
441
+ def markets(self, options: strdict) -> list[anydict] | anydict | errordict:
438
442
  """Get all available markets with some meta-information, unless options is given a `market` key.
439
443
  Then you will get a single market, instead of a list of markets.
440
444
 
@@ -484,9 +488,9 @@ class Bitvavo:
484
488
  ```
485
489
  """
486
490
  postfix = createPostfix(options)
487
- return self.publicRequest(f"{self.base}/markets{postfix}")
491
+ return self.publicRequest(f"{self.base}/markets{postfix}") # type: ignore[return-value]
488
492
 
489
- def assets(self, options: strdict) -> Union[List[anydict], anydict]:
493
+ def assets(self, options: strdict) -> list[anydict] | anydict:
490
494
  """Get all available assets, unless `options` is given a `symbol` key.
491
495
  Then you will get a single asset, instead of a list of assets.
492
496
 
@@ -533,9 +537,9 @@ class Bitvavo:
533
537
  ```
534
538
  """
535
539
  postfix = createPostfix(options)
536
- return self.publicRequest(f"{self.base}/assets{postfix}")
540
+ return self.publicRequest(f"{self.base}/assets{postfix}") # type: ignore[return-value]
537
541
 
538
- def book(self, market: str, options: intdict) -> Union[Dict[str, Union[str, int, List[str]]], errordict]:
542
+ def book(self, market: str, options: intdict) -> dict[str, str | int | list[str]] | errordict:
539
543
  """Get a book (with two lists: asks and bids, as they're called)
540
544
 
541
545
  ---
@@ -577,9 +581,9 @@ class Bitvavo:
577
581
  ```
578
582
  """
579
583
  postfix = createPostfix(options)
580
- return self.publicRequest(f"{self.base}/{market}/book{postfix}")
584
+ return self.publicRequest(f"{self.base}/{market}/book{postfix}") # type: ignore[return-value]
581
585
 
582
- def publicTrades(self, market: str, options: Dict[str, Union[str, int]]) -> Union[List[anydict], errordict]:
586
+ def publicTrades(self, market: str, options: dict[str, str | int]) -> list[anydict] | errordict:
583
587
  """Publically available trades
584
588
 
585
589
  ---
@@ -599,7 +603,8 @@ class Bitvavo:
599
603
  options={
600
604
  "limit": [ 1 .. 1000 ], default 500
601
605
  "start": int timestamp in ms >= 0
602
- "end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
606
+ # (that's somewhere in the year 2243, or near the number 2^52)
607
+ "end": int timestamp in ms <= 8_640_000_000_000_000
603
608
  "tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
604
609
  "tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
605
610
  }
@@ -626,14 +631,14 @@ class Bitvavo:
626
631
  ```
627
632
  """
628
633
  postfix = createPostfix(options)
629
- return self.publicRequest(f"{self.base}/{market}/trades{postfix}", 5)
634
+ return self.publicRequest(f"{self.base}/{market}/trades{postfix}", 5) # type: ignore[return-value]
630
635
 
631
636
  def candles(
632
637
  self,
633
638
  market: str,
634
639
  interval: str,
635
- options: Dict[str, Union[str, int]],
636
- ) -> Union[List[List[str]], errordict]:
640
+ options: dict[str, str | int],
641
+ ) -> list[list[str]] | errordict:
637
642
  """Get up to 1440 candles for a market, with a specific interval (candle size)
638
643
 
639
644
  Extra reading material: https://en.wikipedia.org/wiki/Candlestick_chart
@@ -681,9 +686,9 @@ class Bitvavo:
681
686
  """
682
687
  options["interval"] = interval
683
688
  postfix = createPostfix(options)
684
- return self.publicRequest(f"{self.base}/{market}/candles{postfix}")
689
+ return self.publicRequest(f"{self.base}/{market}/candles{postfix}") # type: ignore[return-value]
685
690
 
686
- def tickerPrice(self, options: strdict) -> Union[List[strdict], strdict]:
691
+ def tickerPrice(self, options: strdict) -> list[strdict] | strdict:
687
692
  """Get the current price for each market
688
693
 
689
694
  ---
@@ -729,9 +734,9 @@ class Bitvavo:
729
734
  ```
730
735
  """
731
736
  postfix = createPostfix(options)
732
- return self.publicRequest(f"{self.base}/ticker/price{postfix}")
737
+ return self.publicRequest(f"{self.base}/ticker/price{postfix}") # type: ignore[return-value]
733
738
 
734
- def tickerBook(self, options: strdict) -> Union[List[strdict], strdict]:
739
+ def tickerBook(self, options: strdict) -> list[strdict] | strdict:
735
740
  """Get current bid/ask, bidsize/asksize per market
736
741
 
737
742
  ---
@@ -770,9 +775,9 @@ class Bitvavo:
770
775
  ```
771
776
  """
772
777
  postfix = createPostfix(options)
773
- return self.publicRequest(f"{self.base}/ticker/book{postfix}")
778
+ return self.publicRequest(f"{self.base}/ticker/book{postfix}") # type: ignore[return-value]
774
779
 
775
- def ticker24h(self, options: strdict) -> Union[List[anydict], anydict, errordict]:
780
+ def ticker24h(self, options: strdict) -> list[anydict] | anydict | errordict:
776
781
  """Get current bid/ask, bidsize/asksize per market
777
782
 
778
783
  ---
@@ -838,7 +843,7 @@ class Bitvavo:
838
843
  if "market" in options:
839
844
  rateLimitingWeight = 1
840
845
  postfix = createPostfix(options)
841
- return self.publicRequest(f"{self.base}/ticker/24h{postfix}", rateLimitingWeight)
846
+ return self.publicRequest(f"{self.base}/ticker/24h{postfix}", rateLimitingWeight) # type: ignore[return-value]
842
847
 
843
848
  def placeOrder(self, market: str, side: str, orderType: str, body: anydict) -> anydict:
844
849
  """Place a new order on the exchange
@@ -959,15 +964,14 @@ class Bitvavo:
959
964
  "disableMarketProtection": true
960
965
  }
961
966
  ```
962
- """
967
+ """ # noqa: E501
963
968
  body["market"] = market
964
969
  body["side"] = side
965
970
  body["orderType"] = orderType
966
- return self.privateRequest("/order", "", body, "POST")
971
+ return self.privateRequest("/order", "", body, "POST") # type: ignore[return-value]
967
972
 
968
973
  def updateOrder(self, market: str, orderId: str, body: anydict) -> anydict:
969
- """Update an existing order for a specific market. Make sure that at least one of the optional parameters is set,
970
- otherwise nothing will be updated.
974
+ """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.
971
975
 
972
976
  ---
973
977
  Args:
@@ -1044,10 +1048,10 @@ class Bitvavo:
1044
1048
  "disableMarketProtection": true
1045
1049
  }
1046
1050
  ```
1047
- """
1051
+ """ # noqa: E501
1048
1052
  body["market"] = market
1049
1053
  body["orderId"] = orderId
1050
- return self.privateRequest("/order", "", body, "PUT")
1054
+ return self.privateRequest("/order", "", body, "PUT") # type: ignore[return-value]
1051
1055
 
1052
1056
  def cancelOrder(self, market: str, orderId: str) -> strdict:
1053
1057
  """Cancel an existing order for a specific market
@@ -1072,9 +1076,9 @@ class Bitvavo:
1072
1076
  ```
1073
1077
  """
1074
1078
  postfix = createPostfix({"market": market, "orderId": orderId})
1075
- return self.privateRequest("/order", postfix, {}, "DELETE")
1079
+ return self.privateRequest("/order", postfix, {}, "DELETE") # type: ignore[return-value]
1076
1080
 
1077
- def getOrder(self, market: str, orderId: str) -> Union[List[anydict], errordict]:
1081
+ def getOrder(self, market: str, orderId: str) -> list[anydict] | errordict:
1078
1082
  """Get an existing order for a specific market
1079
1083
 
1080
1084
  ---
@@ -1137,9 +1141,9 @@ class Bitvavo:
1137
1141
  ```
1138
1142
  """
1139
1143
  postfix = createPostfix({"market": market, "orderId": orderId})
1140
- return self.privateRequest("/order", postfix, {}, "GET")
1144
+ return self.privateRequest("/order", postfix, {}, "GET") # type: ignore[return-value]
1141
1145
 
1142
- def getOrders(self, market: str, options: anydict) -> Union[List[anydict], errordict]:
1146
+ def getOrders(self, market: str, options: anydict) -> list[anydict] | errordict:
1143
1147
  """Get multiple existing orders for a specific market
1144
1148
 
1145
1149
  ---
@@ -1209,12 +1213,12 @@ class Bitvavo:
1209
1213
  }
1210
1214
  ]
1211
1215
  ```
1212
- """
1216
+ """ # noqa: E501
1213
1217
  options["market"] = market
1214
1218
  postfix = createPostfix(options)
1215
- return self.privateRequest("/orders", postfix, {}, "GET", 5)
1219
+ return self.privateRequest("/orders", postfix, {}, "GET", 5) # type: ignore[return-value]
1216
1220
 
1217
- def cancelOrders(self, options: anydict) -> Union[List[strdict], errordict]:
1221
+ def cancelOrders(self, options: anydict) -> list[strdict] | errordict:
1218
1222
  """Cancel all existing orders for a specific market (or account)
1219
1223
 
1220
1224
  ---
@@ -1240,9 +1244,9 @@ class Bitvavo:
1240
1244
  ```
1241
1245
  """
1242
1246
  postfix = createPostfix(options)
1243
- return self.privateRequest("/orders", postfix, {}, "DELETE")
1247
+ return self.privateRequest("/orders", postfix, {}, "DELETE") # type: ignore[return-value]
1244
1248
 
1245
- def ordersOpen(self, options: anydict) -> Union[List[anydict], errordict]:
1249
+ def ordersOpen(self, options: anydict) -> list[anydict] | errordict:
1246
1250
  """Get all open orders, either for all markets, or a single market
1247
1251
 
1248
1252
  ---
@@ -1311,9 +1315,9 @@ class Bitvavo:
1311
1315
  if "market" in options:
1312
1316
  rateLimitingWeight = 1
1313
1317
  postfix = createPostfix(options)
1314
- return self.privateRequest("/ordersOpen", postfix, {}, "GET", rateLimitingWeight)
1318
+ return self.privateRequest("/ordersOpen", postfix, {}, "GET", rateLimitingWeight) # type: ignore[return-value]
1315
1319
 
1316
- def trades(self, market: str, options: anydict) -> Union[List[anydict], errordict]:
1320
+ def trades(self, market: str, options: anydict) -> list[anydict] | errordict:
1317
1321
  """Get all historic trades from this account
1318
1322
 
1319
1323
  ---
@@ -1354,12 +1358,12 @@ class Bitvavo:
1354
1358
  }
1355
1359
  ]
1356
1360
  ```
1357
- """
1361
+ """ # noqa: E501
1358
1362
  options["market"] = market
1359
1363
  postfix = createPostfix(options)
1360
- return self.privateRequest("/trades", postfix, {}, "GET", 5)
1364
+ return self.privateRequest("/trades", postfix, {}, "GET", 5) # type: ignore[return-value]
1361
1365
 
1362
- def account(self) -> Dict[str, strdict]:
1366
+ def account(self) -> dict[str, strdict]:
1363
1367
  """Get all fees for this account
1364
1368
 
1365
1369
  ---
@@ -1380,9 +1384,9 @@ class Bitvavo:
1380
1384
  }
1381
1385
  ```
1382
1386
  """
1383
- return self.privateRequest("/account", "", {}, "GET")
1387
+ return self.privateRequest("/account", "", {}, "GET") # type: ignore[return-value]
1384
1388
 
1385
- def balance(self, options: strdict) -> Union[List[strdict], errordict]:
1389
+ def balance(self, options: strdict) -> list[strdict] | errordict:
1386
1390
  """Get the balance for this account
1387
1391
 
1388
1392
  ---
@@ -1411,7 +1415,7 @@ class Bitvavo:
1411
1415
  ```
1412
1416
  """
1413
1417
  postfix = createPostfix(options)
1414
- return self.privateRequest("/balance", postfix, {}, "GET", 5)
1418
+ return self.privateRequest("/balance", postfix, {}, "GET", 5) # type: ignore[return-value]
1415
1419
 
1416
1420
  def depositAssets(self, symbol: str) -> strdict:
1417
1421
  """Get the deposit address (with paymentId for some assets) or bank account information to increase your balance
@@ -1446,9 +1450,9 @@ class Bitvavo:
1446
1450
  ```
1447
1451
  """
1448
1452
  postfix = createPostfix({"symbol": symbol})
1449
- return self.privateRequest("/deposit", postfix, {}, "GET")
1453
+ return self.privateRequest("/deposit", postfix, {}, "GET") # type: ignore[return-value]
1450
1454
 
1451
- def depositHistory(self, options: anydict) -> Union[List[anydict], errordict]:
1455
+ def depositHistory(self, options: anydict) -> list[anydict] | errordict:
1452
1456
  """Get the deposit history of the account
1453
1457
 
1454
1458
  Even when you want something from a single `symbol`, you'll still receive a list with multiple deposits.
@@ -1495,9 +1499,9 @@ class Bitvavo:
1495
1499
  }
1496
1500
  ]
1497
1501
  ```
1498
- """
1502
+ """ # noqa: E501
1499
1503
  postfix = createPostfix(options)
1500
- return self.privateRequest("/depositHistory", postfix, {}, "GET", 5)
1504
+ return self.privateRequest("/depositHistory", postfix, {}, "GET", 5) # type: ignore[return-value]
1501
1505
 
1502
1506
  def withdrawAssets(self, symbol: str, amount: str, address: str, body: anydict) -> anydict:
1503
1507
  """Withdraw a coin/token to an external crypto address or bank account.
@@ -1530,13 +1534,13 @@ class Bitvavo:
1530
1534
  "amount": "1.5"
1531
1535
  }
1532
1536
  ```
1533
- """
1537
+ """ # noqa: E501
1534
1538
  body["symbol"] = symbol
1535
1539
  body["amount"] = amount
1536
1540
  body["address"] = address
1537
- return self.privateRequest("/withdrawal", "", body, "POST")
1541
+ return self.privateRequest("/withdrawal", "", body, "POST") # type: ignore[return-value]
1538
1542
 
1539
- def withdrawalHistory(self, options: anydict) -> Union[List[anydict], anydict]:
1543
+ def withdrawalHistory(self, options: anydict) -> list[anydict] | errordict:
1540
1544
  """Get the withdrawal history
1541
1545
 
1542
1546
  ---
@@ -1572,11 +1576,11 @@ class Bitvavo:
1572
1576
  }
1573
1577
  }
1574
1578
  ```
1575
- """
1579
+ """ # noqa: E501
1576
1580
  postfix = createPostfix(options)
1577
- return self.privateRequest("/withdrawalHistory", postfix, {}, "GET", 5)
1581
+ return self.privateRequest("/withdrawalHistory", postfix, {}, "GET", 5) # type: ignore[return-value]
1578
1582
 
1579
- def newWebsocket(self) -> "Bitvavo.WebSocketAppFacade":
1583
+ def newWebsocket(self) -> Bitvavo.WebSocketAppFacade:
1580
1584
  return Bitvavo.WebSocketAppFacade(self.APIKEY, self.APISECRET, self.ACCESSWINDOW, self.wsUrl, self)
1581
1585
 
1582
1586
  class WebSocketAppFacade:
@@ -1586,7 +1590,14 @@ class Bitvavo:
1586
1590
  It's a facade for the WebSocketApp class, with its own implementation for the on_* methods
1587
1591
  """
1588
1592
 
1589
- def __init__(self, APIKEY: str, APISECRET: str, ACCESSWINDOW: int, WSURL: str, bitvavo: "Bitvavo"):
1593
+ def __init__(
1594
+ self,
1595
+ APIKEY: str,
1596
+ APISECRET: str,
1597
+ ACCESSWINDOW: int,
1598
+ WSURL: str,
1599
+ bitvavo: Bitvavo,
1600
+ ) -> None:
1590
1601
  self.APIKEY = APIKEY
1591
1602
  self.APISECRET = APISECRET
1592
1603
  self.ACCESSWINDOW = ACCESSWINDOW
@@ -1623,35 +1634,38 @@ class Bitvavo:
1623
1634
  self.keepAlive = False
1624
1635
  self.receiveThread.join()
1625
1636
 
1626
- def waitForSocket(self, ws: WebSocketApp, message: str, private: bool) -> None:
1637
+ def waitForSocket(self, ws: WebSocketApp, message: str, private: bool) -> None: # noqa: ARG002, FBT001
1627
1638
  while True:
1628
1639
  if (not private and self.open) or (private and self.authenticated and self.open):
1629
1640
  return
1630
1641
  time.sleep(0.1)
1631
1642
 
1632
- def doSend(self, ws: WebSocketApp, message: str, private: bool = False) -> None:
1633
- # TODO(NostraDavid) add nap-time to the websocket, or do it here; I don't know yet.
1643
+ def doSend(self, ws: WebSocketApp, message: str, private: bool = False) -> None: # noqa: FBT001, FBT002
1644
+ # TODO(NostraDavid): add nap-time to the websocket, or do it here; I don't know yet.
1634
1645
  if private and self.APIKEY == "":
1635
- logger.error("no-apikey", tip="set the API key to be able to make private API calls")
1646
+ logger.error(
1647
+ "no-apikey",
1648
+ tip="set the API key to be able to make private API calls",
1649
+ )
1636
1650
  return
1637
1651
  self.waitForSocket(ws, message, private)
1638
1652
  ws.send(message)
1639
1653
  if self.bitvavo.debugging:
1640
1654
  logger.debug("message-sent", message=message)
1641
1655
 
1642
- def on_message(self, ws, msg: str) -> None: # noqa: C901 (too-complex)
1656
+ def on_message(self, ws: Any, msg: str) -> None: # noqa: C901, PLR0912, PLR0915, ARG002 (too-complex)
1643
1657
  if self.bitvavo.debugging:
1644
1658
  logger.debug("message-received", message=msg)
1645
1659
  msg_dict: anydict = json.loads(msg)
1646
1660
  callbacks = self.callbacks
1647
1661
 
1648
1662
  if "error" in msg_dict:
1649
- if msg_dict["errorCode"] == 105:
1663
+ if msg_dict["errorCode"] == 105: # noqa: PLR2004
1650
1664
  self.bitvavo.updateRateLimit(msg_dict)
1651
1665
  if "error" in callbacks:
1652
1666
  callbacks["error"](msg_dict)
1653
1667
  else:
1654
- logger.error(msg_dict)
1668
+ logger.error("error", msg_dict=msg_dict)
1655
1669
 
1656
1670
  if "action" in msg_dict:
1657
1671
  if msg_dict["action"] == "getTime":
@@ -1702,17 +1716,13 @@ class Bitvavo:
1702
1716
  market = msg_dict["response"]["market"]
1703
1717
  if "book" in callbacks:
1704
1718
  callbacks["book"](msg_dict["response"])
1705
- if self.keepBookCopy:
1706
- if market in callbacks["subscriptionBook"]:
1707
- callbacks["subscriptionBook"][market](self, msg_dict)
1719
+ if self.keepBookCopy and market in callbacks["subscriptionBook"]:
1720
+ callbacks["subscriptionBook"][market](self, msg_dict)
1708
1721
 
1709
1722
  elif "event" in msg_dict:
1710
1723
  if msg_dict["event"] == "authenticate":
1711
1724
  self.authenticated = True
1712
- elif msg_dict["event"] == "fill":
1713
- market = msg_dict["market"]
1714
- callbacks["subscriptionAccount"][market](msg_dict)
1715
- elif msg_dict["event"] == "order":
1725
+ elif msg_dict["event"] == "fill" or msg_dict["event"] == "order":
1716
1726
  market = msg_dict["market"]
1717
1727
  callbacks["subscriptionAccount"][market](msg_dict)
1718
1728
  elif msg_dict["event"] == "ticker":
@@ -1727,29 +1737,27 @@ class Bitvavo:
1727
1737
  callbacks["subscriptionCandles"][market][interval](msg_dict)
1728
1738
  elif msg_dict["event"] == "book":
1729
1739
  market = msg_dict["market"]
1730
- if "subscriptionBookUpdate" in callbacks:
1731
- if market in callbacks["subscriptionBookUpdate"]:
1732
- callbacks["subscriptionBookUpdate"][market](msg_dict)
1733
- if self.keepBookCopy:
1734
- if market in callbacks["subscriptionBook"]:
1735
- callbacks["subscriptionBook"][market](self, msg_dict)
1740
+ if "subscriptionBookUpdate" in callbacks and market in callbacks["subscriptionBookUpdate"]:
1741
+ callbacks["subscriptionBookUpdate"][market](msg_dict)
1742
+ if self.keepBookCopy and market in callbacks["subscriptionBook"]:
1743
+ callbacks["subscriptionBook"][market](self, msg_dict)
1736
1744
  elif msg_dict["event"] == "trade":
1737
1745
  market = msg_dict["market"]
1738
1746
  if "subscriptionTrades" in callbacks:
1739
1747
  callbacks["subscriptionTrades"][market](msg_dict)
1740
1748
 
1741
- def on_error(self, ws, error: Any) -> None:
1749
+ def on_error(self, ws: Any, error: Any) -> None: # noqa: ARG002
1742
1750
  if "error" in self.callbacks:
1743
1751
  self.callbacks["error"](error)
1744
1752
  else:
1745
1753
  logger.error(error)
1746
1754
 
1747
- def on_close(self, ws) -> None:
1755
+ def on_close(self, ws: Any) -> None: # noqa: ARG002
1748
1756
  self.receiveThread.stop()
1749
1757
  if self.bitvavo.debugging:
1750
1758
  logger.debug("websocket-closed")
1751
1759
 
1752
- def checkReconnect(self) -> None: # noqa: C901 (too-complex)
1760
+ def checkReconnect(self) -> None: # noqa: C901, PLR0912 (too-complex)
1753
1761
  if "subscriptionTicker" in self.callbacks:
1754
1762
  for market in self.callbacks["subscriptionTicker"]:
1755
1763
  self.subscriptionTicker(market, self.callbacks["subscriptionTicker"][market])
@@ -1777,7 +1785,7 @@ class Bitvavo:
1777
1785
  for market in self.callbacks["subscriptionBookUser"]:
1778
1786
  self.subscriptionBook(market, self.callbacks["subscriptionBookUser"][market])
1779
1787
 
1780
- def on_open(self, ws) -> None:
1788
+ def on_open(self, ws: Any) -> None: # noqa: ARG002
1781
1789
  now = time_ms() + BITVAVO_API_UPGRADED.LAG
1782
1790
  self.open = True
1783
1791
  self.reconnectTimer = 0.5
@@ -2028,13 +2036,19 @@ class Bitvavo:
2028
2036
  }
2029
2037
  ]
2030
2038
  ```
2031
- """
2039
+ """ # noqa: E501
2032
2040
  self.callbacks["publicTrades"] = callback
2033
2041
  options["market"] = market
2034
2042
  options["action"] = "getTrades"
2035
2043
  self.doSend(self.ws, json.dumps(options))
2036
2044
 
2037
- def candles(self, market: str, interval: str, options: anydict, callback: Callable[[Any], None]) -> None:
2045
+ def candles(
2046
+ self,
2047
+ market: str,
2048
+ interval: str,
2049
+ options: anydict,
2050
+ callback: Callable[[Any], None],
2051
+ ) -> None:
2038
2052
  """Get up to 1440 candles for a market, with a specific interval (candle size)
2039
2053
 
2040
2054
  Extra reading material: https://en.wikipedia.org/wiki/Candlestick_chart
@@ -2175,7 +2189,7 @@ class Bitvavo:
2175
2189
  # and another 215 markets below this point
2176
2190
  ]
2177
2191
  ```
2178
- """
2192
+ """ # noqa: E501
2179
2193
  self.callbacks["tickerBook"] = callback
2180
2194
  options["action"] = "getTickerBook"
2181
2195
  self.doSend(self.ws, json.dumps(options))
@@ -2374,7 +2388,7 @@ class Bitvavo:
2374
2388
  "disableMarketProtection": true
2375
2389
  }
2376
2390
  ```
2377
- """
2391
+ """ # noqa: E501
2378
2392
  self.callbacks["placeOrder"] = callback
2379
2393
  body["market"] = market
2380
2394
  body["side"] = side
@@ -2382,9 +2396,16 @@ class Bitvavo:
2382
2396
  body["action"] = "privateCreateOrder"
2383
2397
  self.doSend(self.ws, json.dumps(body), True)
2384
2398
 
2385
- def updateOrder(self, market: str, orderId: str, body: anydict, callback: Callable[[Any], None]) -> None:
2386
- """Update an existing order for a specific market. Make sure that at least one of the optional parameters is set,
2387
- otherwise nothing will be updated.
2399
+ def updateOrder(
2400
+ self,
2401
+ market: str,
2402
+ orderId: str,
2403
+ body: anydict,
2404
+ callback: Callable[[Any], None],
2405
+ ) -> None:
2406
+ """
2407
+ Update an existing order for a specific market. Make sure that at least one of the optional parameters
2408
+ is set, otherwise nothing will be updated.
2388
2409
 
2389
2410
  ---
2390
2411
  Args:
@@ -2462,7 +2483,7 @@ class Bitvavo:
2462
2483
  "disableMarketProtection": true
2463
2484
  }
2464
2485
  ```
2465
- """
2486
+ """ # noqa: E501
2466
2487
  self.callbacks["updateOrder"] = callback
2467
2488
  body["market"] = market
2468
2489
  body["orderId"] = orderId
@@ -2581,9 +2602,12 @@ class Bitvavo:
2581
2602
  options={
2582
2603
  "limit": [ 1 .. 1000 ], default 500
2583
2604
  "start": int timestamp in ms >= 0
2584
- "end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
2585
- "tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
2586
- "tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
2605
+ # (that's somewhere in the year 2243, or near the number 2^52)
2606
+ "end": int timestamp in ms <= 8_640_000_000_000_000
2607
+ # if you get a list and want everything AFTER a certain id, put that id here
2608
+ "tradeIdFrom": ""
2609
+ # if you get a list and want everything BEFORE a certain id, put that id here
2610
+ "tradeIdTo": ""
2587
2611
  }
2588
2612
  callback=callback_example
2589
2613
  ```
@@ -2758,7 +2782,8 @@ class Bitvavo:
2758
2782
  options={
2759
2783
  "limit": [ 1 .. 1000 ], default 500
2760
2784
  "start": int timestamp in ms >= 0
2761
- "end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
2785
+ # (that's somewhere in the year 2243, or near the number 2^52)
2786
+ "end": int timestamp in ms <= 8_640_000_000_000_000
2762
2787
  "tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
2763
2788
  "tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
2764
2789
  }
@@ -2860,7 +2885,9 @@ class Bitvavo:
2860
2885
  self.doSend(self.ws, json.dumps(options), True)
2861
2886
 
2862
2887
  def depositAssets(self, symbol: str, callback: Callable[[Any], None]) -> None:
2863
- """Get the deposit address (with paymentId for some assets) or bank account information to increase your balance
2888
+ """
2889
+ Get the deposit address (with paymentId for some assets) or bank account information to increase your
2890
+ balance.
2864
2891
 
2865
2892
  ---
2866
2893
  Args:
@@ -2911,7 +2938,8 @@ class Bitvavo:
2911
2938
  "symbol":"EUR"
2912
2939
  "limit": [ 1 .. 1000 ], default 500
2913
2940
  "start": int timestamp in ms >= 0
2914
- "end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
2941
+ # (that's somewhere in the year 2243, or near the number 2^52)
2942
+ "end": int timestamp in ms <= 8_640_000_000_000_000
2915
2943
  }
2916
2944
  callback=callback_example
2917
2945
  ```
@@ -2969,9 +2997,15 @@ class Bitvavo:
2969
2997
  amount=10
2970
2998
  address="BitcoinAddress", # Wallet address or IBAN
2971
2999
  options={
2972
- "paymentId": "10002653", # For digital assets only. Should be set when withdrawing straight to another exchange or merchants that require payment id's.
2973
- "internal": false, # For digital assets only. Should be set to true if the withdrawal must be sent to another Bitvavo user internally
2974
- "addWithdrawalFee": false # If set to true, the fee will be added on top of the requested amount, otherwise the fee is part of the requested amount and subtracted from the withdrawal.
3000
+ # For digital assets only. Should be set when withdrawing straight to another exchange or merchants that
3001
+ # require payment id's.
3002
+ "paymentId": "10002653",
3003
+ # For digital assets only. Should be set to true if the withdrawal must be sent to another Bitvavo user
3004
+ # internally
3005
+ "internal": false,
3006
+ # If set to true, the fee will be added on top of the requested amount, otherwise the fee is part of the
3007
+ # requested amount and subtracted from the withdrawal.
3008
+ "addWithdrawalFee": false
2975
3009
  }
2976
3010
  callback=callback_example
2977
3011
  ```
@@ -3009,7 +3043,8 @@ class Bitvavo:
3009
3043
  "symbol":"SHIB"
3010
3044
  "limit": [ 1 .. 1000 ], default 500
3011
3045
  "start": int timestamp in ms >= 0
3012
- "end": int timestamp in ms <= 8_640_000_000_000_000 # (that's somewhere in the year 2243, or near the number 2^52)
3046
+ # (that's somewhere in the year 2243, or near the number 2^52)
3047
+ "end": int timestamp in ms <= 8_640_000_000_000_000
3013
3048
  }
3014
3049
  callback=callback_example
3015
3050
  ```
@@ -3042,8 +3077,11 @@ class Bitvavo:
3042
3077
  self.doSend(self.ws, json.dumps(options), True)
3043
3078
 
3044
3079
  def subscriptionTicker(self, market: str, callback: Callable[[Any], None]) -> None:
3045
- # TODO(NostraDavid) one possible improvement here is to turn `market` into a list of markets, so we can sub to all of them at once. Same goes for other `subscription*()`
3046
- """Subscribe to the ticker channel, which means `callback` gets passed the new best bid or ask whenever they change (server-side).
3080
+ # TODO(NostraDavid): one possible improvement here is to turn `market` into a list of markets, so we can sub
3081
+ # to all of them at once. Same goes for other `subscription*()`
3082
+ """
3083
+ Subscribe to the ticker channel, which means `callback` gets passed the new best bid or ask whenever they
3084
+ change (server-side).
3047
3085
 
3048
3086
 
3049
3087
  ---
@@ -3091,7 +3129,9 @@ class Bitvavo:
3091
3129
  )
3092
3130
 
3093
3131
  def subscriptionTicker24h(self, market: str, callback: Callable[[Any], None]) -> None:
3094
- """Subscribe to the ticker-24-hour channel, which means `callback` gets passed the new object every second, if values have changed.
3132
+ """
3133
+ Subscribe to the ticker-24-hour channel, which means `callback` gets passed the new object every second, if
3134
+ values have changed.
3095
3135
 
3096
3136
  ---
3097
3137
  Args:
@@ -3146,8 +3186,9 @@ class Bitvavo:
3146
3186
  )
3147
3187
 
3148
3188
  def subscriptionAccount(self, market: str, callback: Callable[[Any], None]) -> None:
3149
- """Subscribes to the account channel, which sends an update whenever an event happens which is related to the account.
3150
- These are 'order' events (create, update, cancel) or 'fill' events (a trade occurred).
3189
+ """
3190
+ Subscribes to the account channel, which sends an update whenever an event happens which is related to
3191
+ the account. These are 'order' events (create, update, cancel) or 'fill' events (a trade occurred).
3151
3192
 
3152
3193
  ---
3153
3194
  Args: