bitvavo-api-upgraded 1.16.0__py3-none-any.whl → 1.17.1__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 +8 -0
- bitvavo_api_upgraded/bitvavo.py +86 -40
- bitvavo_api_upgraded/helper_funcs.py +1 -1
- bitvavo_api_upgraded/settings.py +54 -31
- bitvavo_api_upgraded/type_aliases.py +3 -1
- bitvavo_api_upgraded-1.17.1.dist-info/METADATA +320 -0
- bitvavo_api_upgraded-1.17.1.dist-info/RECORD +10 -0
- {bitvavo_api_upgraded-1.16.0.dist-info → bitvavo_api_upgraded-1.17.1.dist-info}/WHEEL +1 -1
- {bitvavo_api_upgraded-1.16.0.dist-info → bitvavo_api_upgraded-1.17.1.dist-info}/licenses/LICENSE.txt +2 -2
- bitvavo_api_upgraded-1.16.0.dist-info/METADATA +0 -2429
- bitvavo_api_upgraded-1.16.0.dist-info/RECORD +0 -10
bitvavo_api_upgraded/__init__.py
CHANGED
bitvavo_api_upgraded/bitvavo.py
CHANGED
@@ -14,23 +14,23 @@ from structlog.stdlib import get_logger
|
|
14
14
|
from websocket import WebSocketApp # missing stubs for WebSocketApp
|
15
15
|
|
16
16
|
from bitvavo_api_upgraded.helper_funcs import configure_loggers, time_ms, time_to_wait
|
17
|
-
from bitvavo_api_upgraded.settings import
|
18
|
-
from bitvavo_api_upgraded.type_aliases import anydict, errordict, intdict, ms, s_f, strdict
|
17
|
+
from bitvavo_api_upgraded.settings import bitvavo_upgraded_settings
|
18
|
+
from bitvavo_api_upgraded.type_aliases import anydict, errordict, intdict, ms, s_f, strdict, strintdict
|
19
19
|
|
20
20
|
configure_loggers()
|
21
21
|
|
22
22
|
logger = get_logger(__name__)
|
23
23
|
|
24
24
|
|
25
|
-
def createSignature(timestamp: ms, method: str, url: str, body: anydict, api_secret: str) -> str:
|
25
|
+
def createSignature(timestamp: ms, method: str, url: str, body: anydict | None, api_secret: str) -> str:
|
26
26
|
string = f"{timestamp}{method}/v2{url}"
|
27
|
-
if len(body.keys())
|
27
|
+
if body is not None and len(body.keys()) > 0:
|
28
28
|
string += json.dumps(body, separators=(",", ":"))
|
29
29
|
signature = hmac.new(api_secret.encode("utf-8"), string.encode("utf-8"), hashlib.sha256).hexdigest()
|
30
30
|
return signature
|
31
31
|
|
32
32
|
|
33
|
-
def createPostfix(options: anydict) -> str:
|
33
|
+
def createPostfix(options: anydict | None) -> str:
|
34
34
|
"""Generate a URL postfix, based on the `options` dict.
|
35
35
|
|
36
36
|
---
|
@@ -41,11 +41,29 @@ def createPostfix(options: anydict) -> str:
|
|
41
41
|
Returns:
|
42
42
|
str: [description]
|
43
43
|
"""
|
44
|
+
options = _default(options, {})
|
44
45
|
params = [f"{key}={options[key]}" for key in options]
|
45
46
|
postfix = "&".join(params) # intersperse
|
46
47
|
return f"?{postfix}" if len(options) > 0 else postfix
|
47
48
|
|
48
49
|
|
50
|
+
def _default(value: anydict | None, fallback: anydict) -> anydict:
|
51
|
+
"""
|
52
|
+
Note that is close, but not actually equal to:
|
53
|
+
|
54
|
+
`return value or fallback`
|
55
|
+
|
56
|
+
I checked this with a temporary hypothesis test.
|
57
|
+
|
58
|
+
This note is all you will get out of me.
|
59
|
+
"""
|
60
|
+
return value if value is not None else fallback
|
61
|
+
|
62
|
+
|
63
|
+
def _epoch_millis(dt: dt.datetime) -> int:
|
64
|
+
return int(dt.timestamp() * 1000)
|
65
|
+
|
66
|
+
|
49
67
|
def asksCompare(a: float, b: float) -> bool:
|
50
68
|
return a < b
|
51
69
|
|
@@ -105,12 +123,15 @@ def processLocalBook(ws: Bitvavo.WebSocketAppFacade, message: anydict) -> None:
|
|
105
123
|
|
106
124
|
|
107
125
|
class ReceiveThread(Thread):
|
126
|
+
"""This used to be `class rateLimitThread`."""
|
127
|
+
|
108
128
|
def __init__(self, ws: WebSocketApp, ws_facade: Bitvavo.WebSocketAppFacade) -> None:
|
109
129
|
self.ws = ws
|
110
130
|
self.ws_facade = ws_facade
|
111
131
|
Thread.__init__(self)
|
112
132
|
|
113
133
|
def run(self) -> None:
|
134
|
+
"""This used to be `self.waitForReset`."""
|
114
135
|
try:
|
115
136
|
while self.ws_facade.keepAlive:
|
116
137
|
self.ws.run_forever()
|
@@ -283,10 +304,10 @@ class Bitvavo:
|
|
283
304
|
)
|
284
305
|
logger.info("napping-until-ban-lifted")
|
285
306
|
time.sleep(timeToWait + 1) # plus one second to ENSURE we're able to run again.
|
286
|
-
if "
|
287
|
-
self.rateLimitRemaining = int(response["
|
288
|
-
if "
|
289
|
-
self.rateLimitResetAt = int(response["
|
307
|
+
if "bitvavo-ratelimit-remaining" in response:
|
308
|
+
self.rateLimitRemaining = int(response["bitvavo-ratelimit-remaining"])
|
309
|
+
if "bitvavo-ratelimit-resetat" in response:
|
310
|
+
self.rateLimitResetAt = int(response["bitvavo-ratelimit-resetat"])
|
290
311
|
|
291
312
|
def publicRequest(
|
292
313
|
self,
|
@@ -311,7 +332,7 @@ class Bitvavo:
|
|
311
332
|
list[list[str]]
|
312
333
|
```
|
313
334
|
"""
|
314
|
-
if (self.rateLimitRemaining - rateLimitingWeight) <=
|
335
|
+
if (self.rateLimitRemaining - rateLimitingWeight) <= bitvavo_upgraded_settings.RATE_LIMITING_BUFFER:
|
315
336
|
self.sleep_until_can_continue()
|
316
337
|
if self.debugging:
|
317
338
|
logger.debug(
|
@@ -323,13 +344,13 @@ class Bitvavo:
|
|
323
344
|
},
|
324
345
|
)
|
325
346
|
if self.APIKEY != "":
|
326
|
-
now = time_ms() +
|
327
|
-
sig = createSignature(now, "GET", url.replace(self.base, ""),
|
347
|
+
now = time_ms() + bitvavo_upgraded_settings.LAG
|
348
|
+
sig = createSignature(now, "GET", url.replace(self.base, ""), None, self.APISECRET)
|
328
349
|
headers = {
|
329
|
-
"
|
330
|
-
"
|
331
|
-
"
|
332
|
-
"
|
350
|
+
"bitvavo-access-key": self.APIKEY,
|
351
|
+
"bitvavo-access-signature": sig,
|
352
|
+
"bitvavo-access-timestamp": str(now),
|
353
|
+
"bitvavo-access-window": str(self.ACCESSWINDOW),
|
333
354
|
}
|
334
355
|
r = get(url, headers=headers, timeout=(self.ACCESSWINDOW / 1000))
|
335
356
|
else:
|
@@ -344,7 +365,7 @@ class Bitvavo:
|
|
344
365
|
self,
|
345
366
|
endpoint: str,
|
346
367
|
postfix: str,
|
347
|
-
body: anydict,
|
368
|
+
body: anydict | None = None,
|
348
369
|
method: str = "GET",
|
349
370
|
rateLimitingWeight: int = 1,
|
350
371
|
) -> list[anydict] | list[list[str]] | intdict | strdict | anydict | Any | errordict:
|
@@ -370,17 +391,17 @@ class Bitvavo:
|
|
370
391
|
list[list[str]]
|
371
392
|
```
|
372
393
|
"""
|
373
|
-
if (self.rateLimitRemaining - rateLimitingWeight) <=
|
394
|
+
if (self.rateLimitRemaining - rateLimitingWeight) <= bitvavo_upgraded_settings.RATE_LIMITING_BUFFER:
|
374
395
|
self.sleep_until_can_continue()
|
375
396
|
# if this method breaks: add `= {}` after `body: dict`
|
376
|
-
now = time_ms() +
|
397
|
+
now = time_ms() + bitvavo_upgraded_settings.LAG
|
377
398
|
sig = createSignature(now, method, (endpoint + postfix), body, self.APISECRET)
|
378
399
|
url = self.base + endpoint + postfix
|
379
400
|
headers = {
|
380
|
-
"
|
381
|
-
"
|
382
|
-
"
|
383
|
-
"
|
401
|
+
"bitvavo-access-key": self.APIKEY,
|
402
|
+
"bitvavo-access-signature": sig,
|
403
|
+
"bitvavo-access-timestamp": str(now),
|
404
|
+
"bitvavo-access-window": str(self.ACCESSWINDOW),
|
384
405
|
}
|
385
406
|
if self.debugging:
|
386
407
|
logger.debug(
|
@@ -438,7 +459,7 @@ class Bitvavo:
|
|
438
459
|
"""
|
439
460
|
return self.publicRequest(f"{self.base}/time") # type: ignore[return-value]
|
440
461
|
|
441
|
-
def markets(self, options: strdict) -> list[anydict] | anydict | errordict:
|
462
|
+
def markets(self, options: strdict | None = None) -> list[anydict] | anydict | errordict:
|
442
463
|
"""Get all available markets with some meta-information, unless options is given a `market` key.
|
443
464
|
Then you will get a single market, instead of a list of markets.
|
444
465
|
|
@@ -490,7 +511,7 @@ class Bitvavo:
|
|
490
511
|
postfix = createPostfix(options)
|
491
512
|
return self.publicRequest(f"{self.base}/markets{postfix}") # type: ignore[return-value]
|
492
513
|
|
493
|
-
def assets(self, options: strdict) -> list[anydict] | anydict:
|
514
|
+
def assets(self, options: strdict | None = None) -> list[anydict] | anydict:
|
494
515
|
"""Get all available assets, unless `options` is given a `symbol` key.
|
495
516
|
Then you will get a single asset, instead of a list of assets.
|
496
517
|
|
@@ -539,7 +560,7 @@ class Bitvavo:
|
|
539
560
|
postfix = createPostfix(options)
|
540
561
|
return self.publicRequest(f"{self.base}/assets{postfix}") # type: ignore[return-value]
|
541
562
|
|
542
|
-
def book(self, market: str, options: intdict) -> dict[str, str | int | list[str]] | errordict:
|
563
|
+
def book(self, market: str, options: intdict | None = None) -> dict[str, str | int | list[str]] | errordict:
|
543
564
|
"""Get a book (with two lists: asks and bids, as they're called)
|
544
565
|
|
545
566
|
---
|
@@ -583,7 +604,7 @@ class Bitvavo:
|
|
583
604
|
postfix = createPostfix(options)
|
584
605
|
return self.publicRequest(f"{self.base}/{market}/book{postfix}") # type: ignore[return-value]
|
585
606
|
|
586
|
-
def publicTrades(self, market: str, options:
|
607
|
+
def publicTrades(self, market: str, options: strintdict | None = None) -> list[anydict] | errordict:
|
587
608
|
"""Publically available trades
|
588
609
|
|
589
610
|
---
|
@@ -633,11 +654,14 @@ class Bitvavo:
|
|
633
654
|
postfix = createPostfix(options)
|
634
655
|
return self.publicRequest(f"{self.base}/{market}/trades{postfix}", 5) # type: ignore[return-value]
|
635
656
|
|
636
|
-
def candles(
|
657
|
+
def candles( # noqa: PLR0913
|
637
658
|
self,
|
638
659
|
market: str,
|
639
660
|
interval: str,
|
640
|
-
options:
|
661
|
+
options: strintdict | None = None,
|
662
|
+
limit: int | None = None,
|
663
|
+
start: dt.datetime | None = None,
|
664
|
+
end: dt.datetime | None = None,
|
641
665
|
) -> list[list[str]] | errordict:
|
642
666
|
"""Get up to 1440 candles for a market, with a specific interval (candle size)
|
643
667
|
|
@@ -684,11 +708,18 @@ class Bitvavo:
|
|
684
708
|
]
|
685
709
|
```
|
686
710
|
"""
|
711
|
+
options = _default(options, {})
|
687
712
|
options["interval"] = interval
|
713
|
+
if limit is not None:
|
714
|
+
options["limit"] = limit
|
715
|
+
if start is not None:
|
716
|
+
options["start"] = _epoch_millis(start)
|
717
|
+
if end is not None:
|
718
|
+
options["end"] = _epoch_millis(end)
|
688
719
|
postfix = createPostfix(options)
|
689
720
|
return self.publicRequest(f"{self.base}/{market}/candles{postfix}") # type: ignore[return-value]
|
690
721
|
|
691
|
-
def tickerPrice(self, options: strdict) -> list[strdict] | strdict:
|
722
|
+
def tickerPrice(self, options: strdict | None = None) -> list[strdict] | strdict:
|
692
723
|
"""Get the current price for each market
|
693
724
|
|
694
725
|
---
|
@@ -736,7 +767,7 @@ class Bitvavo:
|
|
736
767
|
postfix = createPostfix(options)
|
737
768
|
return self.publicRequest(f"{self.base}/ticker/price{postfix}") # type: ignore[return-value]
|
738
769
|
|
739
|
-
def tickerBook(self, options: strdict) -> list[strdict] | strdict:
|
770
|
+
def tickerBook(self, options: strdict | None = None) -> list[strdict] | strdict:
|
740
771
|
"""Get current bid/ask, bidsize/asksize per market
|
741
772
|
|
742
773
|
---
|
@@ -777,7 +808,7 @@ class Bitvavo:
|
|
777
808
|
postfix = createPostfix(options)
|
778
809
|
return self.publicRequest(f"{self.base}/ticker/book{postfix}") # type: ignore[return-value]
|
779
810
|
|
780
|
-
def ticker24h(self, options: strdict) -> list[anydict] | anydict | errordict:
|
811
|
+
def ticker24h(self, options: strdict | None = None) -> list[anydict] | anydict | errordict:
|
781
812
|
"""Get current bid/ask, bidsize/asksize per market
|
782
813
|
|
783
814
|
---
|
@@ -839,6 +870,7 @@ class Bitvavo:
|
|
839
870
|
]
|
840
871
|
```
|
841
872
|
"""
|
873
|
+
options = _default(options, {})
|
842
874
|
rateLimitingWeight = 25
|
843
875
|
if "market" in options:
|
844
876
|
rateLimitingWeight = 1
|
@@ -1143,7 +1175,7 @@ class Bitvavo:
|
|
1143
1175
|
postfix = createPostfix({"market": market, "orderId": orderId})
|
1144
1176
|
return self.privateRequest("/order", postfix, {}, "GET") # type: ignore[return-value]
|
1145
1177
|
|
1146
|
-
def getOrders(self, market: str, options: anydict) -> list[anydict] | errordict:
|
1178
|
+
def getOrders(self, market: str, options: anydict | None = None) -> list[anydict] | errordict:
|
1147
1179
|
"""Get multiple existing orders for a specific market
|
1148
1180
|
|
1149
1181
|
---
|
@@ -1214,11 +1246,12 @@ class Bitvavo:
|
|
1214
1246
|
]
|
1215
1247
|
```
|
1216
1248
|
""" # noqa: E501
|
1249
|
+
options = _default(options, {})
|
1217
1250
|
options["market"] = market
|
1218
1251
|
postfix = createPostfix(options)
|
1219
1252
|
return self.privateRequest("/orders", postfix, {}, "GET", 5) # type: ignore[return-value]
|
1220
1253
|
|
1221
|
-
def cancelOrders(self, options: anydict) -> list[strdict] | errordict:
|
1254
|
+
def cancelOrders(self, options: anydict | None = None) -> list[strdict] | errordict:
|
1222
1255
|
"""Cancel all existing orders for a specific market (or account)
|
1223
1256
|
|
1224
1257
|
---
|
@@ -1246,7 +1279,7 @@ class Bitvavo:
|
|
1246
1279
|
postfix = createPostfix(options)
|
1247
1280
|
return self.privateRequest("/orders", postfix, {}, "DELETE") # type: ignore[return-value]
|
1248
1281
|
|
1249
|
-
def ordersOpen(self, options: anydict) -> list[anydict] | errordict:
|
1282
|
+
def ordersOpen(self, options: anydict | None = None) -> list[anydict] | errordict:
|
1250
1283
|
"""Get all open orders, either for all markets, or a single market
|
1251
1284
|
|
1252
1285
|
---
|
@@ -1311,13 +1344,14 @@ class Bitvavo:
|
|
1311
1344
|
]
|
1312
1345
|
```
|
1313
1346
|
"""
|
1347
|
+
options = _default(options, {})
|
1314
1348
|
rateLimitingWeight = 25
|
1315
1349
|
if "market" in options:
|
1316
1350
|
rateLimitingWeight = 1
|
1317
1351
|
postfix = createPostfix(options)
|
1318
1352
|
return self.privateRequest("/ordersOpen", postfix, {}, "GET", rateLimitingWeight) # type: ignore[return-value]
|
1319
1353
|
|
1320
|
-
def trades(self, market: str, options: anydict) -> list[anydict] | errordict:
|
1354
|
+
def trades(self, market: str, options: anydict | None = None) -> list[anydict] | errordict:
|
1321
1355
|
"""Get all historic trades from this account
|
1322
1356
|
|
1323
1357
|
---
|
@@ -1359,6 +1393,7 @@ class Bitvavo:
|
|
1359
1393
|
]
|
1360
1394
|
```
|
1361
1395
|
""" # noqa: E501
|
1396
|
+
options = _default(options, {})
|
1362
1397
|
options["market"] = market
|
1363
1398
|
postfix = createPostfix(options)
|
1364
1399
|
return self.privateRequest("/trades", postfix, {}, "GET", 5) # type: ignore[return-value]
|
@@ -1386,7 +1421,16 @@ class Bitvavo:
|
|
1386
1421
|
"""
|
1387
1422
|
return self.privateRequest("/account", "", {}, "GET") # type: ignore[return-value]
|
1388
1423
|
|
1389
|
-
def
|
1424
|
+
def fees(self, market: str | None = None, quote: str | None = None) -> list[strdict] | errordict:
|
1425
|
+
options = {}
|
1426
|
+
if market is not None:
|
1427
|
+
options["market"] = market
|
1428
|
+
if quote is not None:
|
1429
|
+
options["quote"] = quote
|
1430
|
+
postfix = createPostfix(options)
|
1431
|
+
return self.privateRequest("/account/fees", postfix, {}, "GET") # type: ignore[return-value]
|
1432
|
+
|
1433
|
+
def balance(self, options: strdict | None = None) -> list[strdict] | errordict:
|
1390
1434
|
"""Get the balance for this account
|
1391
1435
|
|
1392
1436
|
---
|
@@ -1452,7 +1496,7 @@ class Bitvavo:
|
|
1452
1496
|
postfix = createPostfix({"symbol": symbol})
|
1453
1497
|
return self.privateRequest("/deposit", postfix, {}, "GET") # type: ignore[return-value]
|
1454
1498
|
|
1455
|
-
def depositHistory(self, options: anydict) -> list[anydict] | errordict:
|
1499
|
+
def depositHistory(self, options: anydict | None = None) -> list[anydict] | errordict:
|
1456
1500
|
"""Get the deposit history of the account
|
1457
1501
|
|
1458
1502
|
Even when you want something from a single `symbol`, you'll still receive a list with multiple deposits.
|
@@ -1540,7 +1584,7 @@ class Bitvavo:
|
|
1540
1584
|
body["address"] = address
|
1541
1585
|
return self.privateRequest("/withdrawal", "", body, "POST") # type: ignore[return-value]
|
1542
1586
|
|
1543
|
-
def withdrawalHistory(self, options: anydict) -> list[anydict] | errordict:
|
1587
|
+
def withdrawalHistory(self, options: anydict | None = None) -> list[anydict] | errordict:
|
1544
1588
|
"""Get the withdrawal history
|
1545
1589
|
|
1546
1590
|
---
|
@@ -1700,6 +1744,8 @@ class Bitvavo:
|
|
1700
1744
|
callbacks["trades"](msg_dict["response"])
|
1701
1745
|
elif msg_dict["action"] == "privateGetAccount":
|
1702
1746
|
callbacks["account"](msg_dict["response"])
|
1747
|
+
elif msg_dict["action"] == "privateGetFees":
|
1748
|
+
callbacks["fees"](msg_dict["response"])
|
1703
1749
|
elif msg_dict["action"] == "privateGetBalance":
|
1704
1750
|
callbacks["balance"](msg_dict["response"])
|
1705
1751
|
elif msg_dict["action"] == "privateDepositAssets":
|
@@ -1786,7 +1832,7 @@ class Bitvavo:
|
|
1786
1832
|
self.subscriptionBook(market, self.callbacks["subscriptionBookUser"][market])
|
1787
1833
|
|
1788
1834
|
def on_open(self, ws: Any) -> None: # noqa: ARG002
|
1789
|
-
now = time_ms() +
|
1835
|
+
now = time_ms() + bitvavo_upgraded_settings.LAG
|
1790
1836
|
self.open = True
|
1791
1837
|
self.reconnectTimer = 0.5
|
1792
1838
|
if self.APIKEY != "":
|
@@ -34,7 +34,7 @@ def configure_loggers() -> None:
|
|
34
34
|
source: https://docs.python.org/3.9/library/logging.config.html#dictionary-schema-details
|
35
35
|
"""
|
36
36
|
shared_pre_chain: list[Callable[[WrappedLogger, str, EventDict], EventDict]] = [
|
37
|
-
structlog.threadlocal.merge_threadlocal,
|
37
|
+
# structlog.threadlocal.merge_threadlocal,
|
38
38
|
structlog.stdlib.add_logger_name, # show which named logger made the message!
|
39
39
|
structlog.processors.add_log_level, # info, warning, error, etc
|
40
40
|
structlog.processors.TimeStamper(fmt="%Y-%m-%dT%H:%M:%S", utc=False), # add an ISO formatted string
|
bitvavo_api_upgraded/settings.py
CHANGED
@@ -1,47 +1,70 @@
|
|
1
1
|
import logging
|
2
2
|
from pathlib import Path
|
3
3
|
|
4
|
-
from
|
4
|
+
from pydantic import Field, field_validator, model_validator
|
5
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
5
6
|
|
6
7
|
from bitvavo_api_upgraded.type_aliases import ms
|
7
8
|
|
8
|
-
# don't use/import python-decouple's `config`` variable, because the search_path isn't set,
|
9
|
-
# which means applications that use a .env file can't override these variables :(
|
10
|
-
config = AutoConfig(search_path=Path.cwd())
|
11
9
|
|
10
|
+
class BitvavoApiUpgradedSettings(BaseSettings):
|
11
|
+
"""
|
12
|
+
These settings provide extra functionality. Originally I wanted to combine
|
13
|
+
then, but I figured that would be a bad idea.
|
14
|
+
"""
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
"
|
22
|
-
|
23
|
-
|
16
|
+
LOG_LEVEL: str = Field("INFO")
|
17
|
+
LOG_EXTERNAL_LEVEL: str = Field("WARNING")
|
18
|
+
LAG: ms = Field(ms(50))
|
19
|
+
RATE_LIMITING_BUFFER: int = Field(25)
|
20
|
+
|
21
|
+
# Configuration for Pydantic Settings
|
22
|
+
model_config = SettingsConfigDict(
|
23
|
+
env_file=Path.cwd() / ".env",
|
24
|
+
env_file_encoding="utf-8",
|
25
|
+
env_prefix="BITVAVO_API_UPGRADED_",
|
26
|
+
extra="ignore",
|
24
27
|
)
|
25
|
-
LAG: ms = config("BITVAVO_API_UPGRADED_LAG", default=ms(50), cast=ms)
|
26
|
-
RATE_LIMITING_BUFFER: int = config("BITVAVO_API_UPGRADED_RATE_LIMITING_BUFFER", default=25, cast=int)
|
27
28
|
|
29
|
+
@classmethod
|
30
|
+
@field_validator("LOG_LEVEL", "LOG_EXTERNAL_LEVEL", mode="before")
|
31
|
+
def validate_log_level(cls, v: str) -> str:
|
32
|
+
if v not in logging._nameToLevel: # noqa: SLF001
|
33
|
+
msg = f"Invalid log level: {v}"
|
34
|
+
raise ValueError(msg)
|
35
|
+
return v
|
28
36
|
|
29
|
-
|
37
|
+
|
38
|
+
class BitvavoSettings(BaseSettings):
|
30
39
|
"""
|
31
|
-
|
32
|
-
Bitvavo's documentation and thus should not be able to be set outside of the application.
|
40
|
+
These are the base settings from the original library.
|
33
41
|
"""
|
34
42
|
|
35
|
-
ACCESSWINDOW: int =
|
36
|
-
API_RATING_LIMIT_PER_MINUTE: int = 1000
|
37
|
-
API_RATING_LIMIT_PER_SECOND:
|
38
|
-
APIKEY: str =
|
39
|
-
APISECRET: str =
|
40
|
-
DEBUGGING: bool =
|
41
|
-
RESTURL: str = "https://api.bitvavo.com/v2"
|
42
|
-
WSURL: str = "wss://ws.bitvavo.com/v2/"
|
43
|
+
ACCESSWINDOW: int = Field(10_000)
|
44
|
+
API_RATING_LIMIT_PER_MINUTE: int = Field(default=1000)
|
45
|
+
API_RATING_LIMIT_PER_SECOND: int = Field(default=1000)
|
46
|
+
APIKEY: str = Field(default="BITVAVO_APIKEY is missing")
|
47
|
+
APISECRET: str = Field(default="BITVAVO_APISECRET is missing")
|
48
|
+
DEBUGGING: bool = Field(default=False)
|
49
|
+
RESTURL: str = Field(default="https://api.bitvavo.com/v2")
|
50
|
+
WSURL: str = Field(default="wss://ws.bitvavo.com/v2/")
|
51
|
+
|
52
|
+
# Configuration for Pydantic Settings
|
53
|
+
model_config = SettingsConfigDict(
|
54
|
+
env_file=Path.cwd() / ".env",
|
55
|
+
env_file_encoding="utf-8",
|
56
|
+
env_prefix="BITVAVO_",
|
57
|
+
extra="ignore",
|
58
|
+
)
|
59
|
+
|
60
|
+
@model_validator(mode="after")
|
61
|
+
def set_api_rating_limit_per_second(self) -> "BitvavoSettings":
|
62
|
+
self.API_RATING_LIMIT_PER_SECOND = self.API_RATING_LIMIT_PER_SECOND // 60
|
63
|
+
return self
|
43
64
|
|
44
65
|
|
45
|
-
#
|
46
|
-
|
47
|
-
|
66
|
+
# Initialize the settings
|
67
|
+
bitvavo_upgraded_settings = BitvavoApiUpgradedSettings()
|
68
|
+
BITVAVO_API_UPGRADED = bitvavo_upgraded_settings
|
69
|
+
bitvavo_settings = BitvavoSettings()
|
70
|
+
BITVAVO = bitvavo_settings
|
@@ -3,12 +3,14 @@ This file contains all type aliases that I use within the lib,
|
|
3
3
|
to clearify the intention or semantics/meaning/unit of a variable
|
4
4
|
"""
|
5
5
|
|
6
|
-
from typing import Any
|
6
|
+
from typing import Any, Union
|
7
7
|
|
8
8
|
# type simplification
|
9
9
|
anydict = dict[str, Any]
|
10
10
|
strdict = dict[str, str]
|
11
11
|
intdict = dict[str, int]
|
12
|
+
# can't use | here, with __future__. Not sure why.
|
13
|
+
strintdict = dict[str, Union[str, int]]
|
12
14
|
errordict = dict[str, Any] # same type as anydict, but the semantics/meaning is different
|
13
15
|
|
14
16
|
# note: You can also use these for type conversion, so instead of int(some_float / 1000), you can just do ms(some_float
|