bitvavo-api-upgraded 1.17.2__py3-none-any.whl → 2.0.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.
@@ -653,7 +653,7 @@ class Bitvavo:
653
653
  postfix = createPostfix(options)
654
654
  return self.publicRequest(f"{self.base}/{market}/trades{postfix}", 5) # type: ignore[return-value]
655
655
 
656
- def candles( # noqa: PLR0913
656
+ def candles(
657
657
  self,
658
658
  market: str,
659
659
  interval: str,
@@ -876,7 +876,95 @@ class Bitvavo:
876
876
  postfix = createPostfix(options)
877
877
  return self.publicRequest(f"{self.base}/ticker/24h{postfix}", rateLimitingWeight) # type: ignore[return-value]
878
878
 
879
- def placeOrder(self, market: str, side: str, orderType: str, body: anydict) -> anydict:
879
+ def reportTrades(self, market: str, options: strintdict | None = None) -> list[anydict] | errordict:
880
+ """Get MiCA-compliant trades report for a specific market
881
+
882
+ Returns trades from the specified market and time period made by all Bitvavo users.
883
+ The returned trades are sorted by timestamp in descending order (latest to earliest).
884
+ Includes data compliant with the European Markets in Crypto-Assets (MiCA) regulation.
885
+
886
+ ---
887
+ Examples:
888
+ * https://api.bitvavo.com/v2/report/BTC-EUR/trades
889
+ * https://api.bitvavo.com/v2/report/BTC-EUR/trades?limit=100&start=1640995200000
890
+
891
+ ---
892
+ Args:
893
+ ```python
894
+ market="BTC-EUR"
895
+ options={
896
+ "limit": [ 1 .. 1000 ], default 500
897
+ "start": int timestamp in ms >= 0
898
+ "end": int timestamp in ms <= 8_640_000_000_000_000 # Cannot be more than 24 hours after start
899
+ "tradeIdFrom": "" # if you get a list and want everything AFTER a certain id, put that id here
900
+ "tradeIdTo": "" # if you get a list and want everything BEFORE a certain id, put that id here
901
+ }
902
+ ```
903
+
904
+ ---
905
+ Rate Limit Weight:
906
+ ```python
907
+ 5
908
+ ```
909
+
910
+ ---
911
+ Returns:
912
+ ```python
913
+ [
914
+ {
915
+ "timestamp": 1542967486256,
916
+ "id": "57b1159b-6bf5-4cde-9e2c-6bd6a5678baf",
917
+ "amount": "0.1",
918
+ "price": "5012",
919
+ "side": "sell"
920
+ }
921
+ ]
922
+ ```
923
+ """
924
+ postfix = createPostfix(options)
925
+ return self.publicRequest(f"{self.base}/report/{market}/trades{postfix}", 5) # type: ignore[return-value]
926
+
927
+ def reportBook(self, market: str, options: intdict | None = None) -> dict[str, str | int | list[str]] | errordict:
928
+ """Get MiCA-compliant order book report for a specific market
929
+
930
+ Returns the list of all bids and asks for the specified market, sorted by price.
931
+ Includes data compliant with the European Markets in Crypto-Assets (MiCA) regulation.
932
+
933
+ ---
934
+ Examples:
935
+ * https://api.bitvavo.com/v2/report/BTC-EUR/book
936
+ * https://api.bitvavo.com/v2/report/BTC-EUR/book?depth=100
937
+
938
+ ---
939
+ Args:
940
+ ```python
941
+ market="BTC-EUR"
942
+ options={"depth": 100} # returns the best 100 asks and 100 bids, default 1000
943
+ options={} # returns up to 1000 bids and asks for that book
944
+ ```
945
+
946
+ ---
947
+ Rate Limit Weight:
948
+ ```python
949
+ 1
950
+ ```
951
+
952
+ ---
953
+ Returns:
954
+ ```python
955
+ {
956
+ "market": "BTC-EUR",
957
+ "nonce": 10378032,
958
+ "bids": [["41648", "0.12"], ["41647", "0.25"], ["41646", "0.33"]],
959
+ "asks": [["41649", "0.15"], ["41650", "0.28"], ["41651", "0.22"]],
960
+ "timestamp": 1700000000000,
961
+ }
962
+ ```
963
+ """
964
+ postfix = createPostfix(options)
965
+ return self.publicRequest(f"{self.base}/report/{market}/book{postfix}") # type: ignore[return-value]
966
+
967
+ def placeOrder(self, market: str, side: str, orderType: str, operatorId: int, body: anydict) -> anydict:
880
968
  """Place a new order on the exchange
881
969
 
882
970
  ---
@@ -886,9 +974,11 @@ class Bitvavo:
886
974
  side="buy" # Choose: buy, sell
887
975
  # For market orders either `amount` or `amountQuote` is required
888
976
  orderType="market" # Choose: market, limit, stopLoss, stopLossLimit, takeProfit, takeProfitLimit
977
+ operatorId=123 # Your identifier for the trader or bot that made the request
889
978
  body={
890
979
  "amount": "1.567",
891
980
  "amountQuote": "5000",
981
+ "clientOrderId": "2be7d0df-d8dc-7b93-a550-8876f3b393e9", # Optional: your identifier for the order
892
982
  # GTC orders will remain on the order book until they are filled or canceled.
893
983
  # IOC orders will fill against existing orders, but will cancel any remaining amount after that.
894
984
  # FOK orders will fill against existing orders in its entirety, or will be canceled (if the entire order cannot be filled).
@@ -904,6 +994,7 @@ class Bitvavo:
904
994
 
905
995
  # For limit orders `amount` and `price` are required.
906
996
  orderType="limit" # Choose: market, limit, stopLoss, stopLossLimit, takeProfit, takeProfitLimit
997
+ operatorId=123
907
998
  body={
908
999
  "amount": "1.567",
909
1000
  "price": "6000",
@@ -916,6 +1007,7 @@ class Bitvavo:
916
1007
  orderType="stopLoss"
917
1008
  # or
918
1009
  orderType="takeProfit"
1010
+ operatorId=123
919
1011
  body={
920
1012
  "amount": "1.567",
921
1013
  "amountQuote": "5000",
@@ -931,6 +1023,7 @@ class Bitvavo:
931
1023
  orderType="stopLossLimit"
932
1024
  # or
933
1025
  orderType="takeProfitLimit"
1026
+ operatorId=123
934
1027
  body={
935
1028
  "amount": "1.567",
936
1029
  "price": "6000",
@@ -999,9 +1092,10 @@ class Bitvavo:
999
1092
  body["market"] = market
1000
1093
  body["side"] = side
1001
1094
  body["orderType"] = orderType
1095
+ body["operatorId"] = operatorId
1002
1096
  return self.privateRequest("/order", "", body, "POST") # type: ignore[return-value]
1003
1097
 
1004
- def updateOrder(self, market: str, orderId: str, body: anydict) -> anydict:
1098
+ def updateOrder(self, market: str, orderId: str, operatorId: int, body: anydict) -> anydict:
1005
1099
  """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.
1006
1100
 
1007
1101
  ---
@@ -1009,11 +1103,13 @@ class Bitvavo:
1009
1103
  ```python
1010
1104
  market="BTC-EUR"
1011
1105
  orderId="95d92d6c-ecf0-4960-a608-9953ef71652e"
1106
+ operatorId=123 # Your identifier for the trader or bot that made the request
1012
1107
  body={
1013
1108
  "amount": "1.567",
1014
1109
  "amountRemaining": "1.567",
1015
1110
  "price": "6000",
1016
1111
  "triggerAmount": "4000", # only for stop orders
1112
+ "clientOrderId": "2be7d0df-d8dc-7b93-a550-8876f3b393e9", # Optional: your identifier for the order
1017
1113
  # GTC orders will remain on the order book until they are filled or canceled.
1018
1114
  # IOC orders will fill against existing orders, but will cancel any remaining amount after that.
1019
1115
  # FOK orders will fill against existing orders in its entirety, or will be canceled (if the entire order cannot be filled).
@@ -1082,22 +1178,32 @@ class Bitvavo:
1082
1178
  """ # noqa: E501
1083
1179
  body["market"] = market
1084
1180
  body["orderId"] = orderId
1181
+ body["operatorId"] = operatorId
1085
1182
  return self.privateRequest("/order", "", body, "PUT") # type: ignore[return-value]
1086
1183
 
1087
- def cancelOrder(self, market: str, orderId: str) -> strdict:
1184
+ def cancelOrder(
1185
+ self,
1186
+ market: str,
1187
+ operatorId: int,
1188
+ orderId: str | None = None,
1189
+ clientOrderId: str | None = None,
1190
+ ) -> strdict:
1088
1191
  """Cancel an existing order for a specific market
1089
1192
 
1090
1193
  ---
1091
1194
  Args:
1092
1195
  ```python
1093
1196
  market="BTC-EUR"
1094
- orderId="a4a5d310-687c-486e-a3eb-1df832405ccd"
1197
+ operatorId=123 # Your identifier for the trader or bot that made the request
1198
+ orderId="a4a5d310-687c-486e-a3eb-1df832405ccd" # Either orderId or clientOrderId required
1199
+ clientOrderId="2be7d0df-d8dc-7b93-a550-8876f3b393e9" # Either orderId or clientOrderId required
1200
+ # If both orderId and clientOrderId are provided, clientOrderId takes precedence
1095
1201
  ```
1096
1202
 
1097
1203
  ---
1098
1204
  Rate Limit Weight:
1099
1205
  ```python
1100
- 1
1206
+ N/A
1101
1207
  ```
1102
1208
 
1103
1209
  ---
@@ -1106,7 +1212,22 @@ class Bitvavo:
1106
1212
  {"orderId": "2e7ce7fc-44e2-4d80-a4a7-d079c4750b61"}
1107
1213
  ```
1108
1214
  """
1109
- postfix = createPostfix({"market": market, "orderId": orderId})
1215
+ if orderId is None and clientOrderId is None:
1216
+ msg = "Either orderId or clientOrderId must be provided"
1217
+ raise ValueError(msg)
1218
+
1219
+ params = {
1220
+ "market": market,
1221
+ "operatorId": operatorId,
1222
+ }
1223
+
1224
+ # clientOrderId takes precedence if both are provided
1225
+ if clientOrderId is not None:
1226
+ params["clientOrderId"] = clientOrderId
1227
+ elif orderId is not None:
1228
+ params["orderId"] = orderId
1229
+
1230
+ postfix = createPostfix(params)
1110
1231
  return self.privateRequest("/order", postfix, {}, "DELETE") # type: ignore[return-value]
1111
1232
 
1112
1233
  def getOrder(self, market: str, orderId: str) -> list[anydict] | errordict:
@@ -1421,6 +1542,35 @@ class Bitvavo:
1421
1542
  return self.privateRequest("/account", "", {}, "GET") # type: ignore[return-value]
1422
1543
 
1423
1544
  def fees(self, market: str | None = None, quote: str | None = None) -> list[strdict] | errordict:
1545
+ """Get market fees for a specific market or quote currency
1546
+
1547
+ ---
1548
+ Args:
1549
+ ```python
1550
+ market="BTC-EUR" # Optional: get fees for specific market
1551
+ quote="EUR" # Optional: get fees for all markets with EUR as quote currency
1552
+ # If both are provided, market takes precedence
1553
+ # If neither are provided, returns fees for all markets
1554
+ ```
1555
+
1556
+ ---
1557
+ Rate Limit Weight:
1558
+ ```python
1559
+ 1
1560
+ ```
1561
+
1562
+ ---
1563
+ Returns:
1564
+ ```python
1565
+ [
1566
+ {
1567
+ "market": "BTC-EUR",
1568
+ "maker": "0.0015",
1569
+ "taker": "0.0025"
1570
+ }
1571
+ ]
1572
+ ```
1573
+ """
1424
1574
  options = {}
1425
1575
  if market is not None:
1426
1576
  options["market"] = market
@@ -1460,6 +1610,53 @@ class Bitvavo:
1460
1610
  postfix = createPostfix(options)
1461
1611
  return self.privateRequest("/balance", postfix, {}, "GET", 5) # type: ignore[return-value]
1462
1612
 
1613
+ def accountHistory(self, options: strintdict | None = None) -> anydict | errordict:
1614
+ """Get all past transactions for your account
1615
+
1616
+ ---
1617
+ Args:
1618
+ ```python
1619
+ options={
1620
+ "fromDate": int timestamp in ms >= 0, # Starting timestamp to return transactions from
1621
+ "toDate": int timestamp in ms <= 8_640_000_000_000_000, # Timestamp up to which to return transactions
1622
+ "maxItems": [ 1 .. 100 ], default 100, # Maximum number of transactions per page
1623
+ "page": 1, # Page number to return (1-indexed)
1624
+ }
1625
+ ```
1626
+
1627
+ ---
1628
+ Rate Limit Weight:
1629
+ ```python
1630
+ 1
1631
+ ```
1632
+
1633
+ ---
1634
+ Returns:
1635
+ ```python
1636
+ {
1637
+ "items": [
1638
+ {
1639
+ "transactionId": "5f5e7b3b-4f5b-4b2d-8b2f-4f2b5b3f5e5f",
1640
+ "timestamp": 1542967486256,
1641
+ "type": "deposit",
1642
+ "symbol": "BTC",
1643
+ "amount": "0.99994",
1644
+ "description": "Deposit via bank transfer",
1645
+ "status": "completed",
1646
+ "feesCurrency": "EUR",
1647
+ "feesAmount": "0.01",
1648
+ "address": "BitcoinAddress"
1649
+ }
1650
+ ],
1651
+ "currentPage": 1,
1652
+ "totalPages": 1,
1653
+ "maxItems": 100
1654
+ }
1655
+ ```
1656
+ """
1657
+ postfix = createPostfix(options)
1658
+ return self.privateRequest("/account/history", postfix, {}, "GET") # type: ignore[return-value]
1659
+
1463
1660
  def depositAssets(self, symbol: str) -> strdict:
1464
1661
  """Get the deposit address (with paymentId for some assets) or bank account information to increase your balance
1465
1662
 
@@ -2311,6 +2508,7 @@ class Bitvavo:
2311
2508
  market: str,
2312
2509
  side: str,
2313
2510
  orderType: str,
2511
+ operatorId: int,
2314
2512
  body: anydict,
2315
2513
  callback: Callable[[Any], None],
2316
2514
  ) -> None:
@@ -2323,9 +2521,11 @@ class Bitvavo:
2323
2521
  side="buy" # Choose: buy, sell
2324
2522
  # For market orders either `amount` or `amountQuote` is required
2325
2523
  orderType="market" # Choose: market, limit, stopLoss, stopLossLimit, takeProfit, takeProfitLimit
2524
+ operatorId=123 # Your identifier for the trader or bot that made the request
2326
2525
  body={
2327
2526
  "amount": "1.567",
2328
2527
  "amountQuote": "5000",
2528
+ "clientOrderId": "2be7d0df-d8dc-7b93-a550-8876f3b393e9", # Optional: your identifier for the order
2329
2529
  # GTC orders will remain on the order book until they are filled or canceled.
2330
2530
  # IOC orders will fill against existing orders, but will cancel any remaining amount after that.
2331
2531
  # FOK orders will fill against existing orders in its entirety, or will be canceled (if the entire order cannot be filled).
@@ -2341,6 +2541,7 @@ class Bitvavo:
2341
2541
 
2342
2542
  # For limit orders `amount` and `price` are required.
2343
2543
  orderType="limit" # Choose: market, limit, stopLoss, stopLossLimit, takeProfit, takeProfitLimit
2544
+ operatorId=123
2344
2545
  body={
2345
2546
  "amount": "1.567",
2346
2547
  "price": "6000",
@@ -2353,6 +2554,7 @@ class Bitvavo:
2353
2554
  orderType="stopLoss"
2354
2555
  # or
2355
2556
  orderType="takeProfit"
2557
+ operatorId=123
2356
2558
  body={
2357
2559
  "amount": "1.567",
2358
2560
  "amountQuote": "5000",
@@ -2368,6 +2570,7 @@ class Bitvavo:
2368
2570
  orderType="stopLossLimit"
2369
2571
  # or
2370
2572
  orderType="takeProfitLimit"
2573
+ operatorId=123
2371
2574
  body={
2372
2575
  "amount": "1.567",
2373
2576
  "price": "6000",
@@ -2438,6 +2641,7 @@ class Bitvavo:
2438
2641
  body["market"] = market
2439
2642
  body["side"] = side
2440
2643
  body["orderType"] = orderType
2644
+ body["operatorId"] = operatorId
2441
2645
  body["action"] = "privateCreateOrder"
2442
2646
  self.doSend(self.ws, json.dumps(body), True)
2443
2647
 
@@ -2445,6 +2649,7 @@ class Bitvavo:
2445
2649
  self,
2446
2650
  market: str,
2447
2651
  orderId: str,
2652
+ operatorId: int,
2448
2653
  body: anydict,
2449
2654
  callback: Callable[[Any], None],
2450
2655
  ) -> None:
@@ -2457,11 +2662,13 @@ class Bitvavo:
2457
2662
  ```python
2458
2663
  market="BTC-EUR"
2459
2664
  orderId="95d92d6c-ecf0-4960-a608-9953ef71652e"
2665
+ operatorId=123 # Your identifier for the trader or bot that made the request
2460
2666
  body={
2461
2667
  "amount": "1.567",
2462
2668
  "amountRemaining": "1.567",
2463
2669
  "price": "6000",
2464
2670
  "triggerAmount": "4000", # only for stop orders
2671
+ "clientOrderId": "2be7d0df-d8dc-7b93-a550-8876f3b393e9", # Optional: your identifier for the order
2465
2672
  # GTC orders will remain on the order book until they are filled or canceled.
2466
2673
  # IOC orders will fill against existing orders, but will cancel any remaining amount after that.
2467
2674
  # FOK orders will fill against existing orders in its entirety, or will be canceled (if the entire order cannot be filled).
@@ -2532,24 +2739,35 @@ class Bitvavo:
2532
2739
  self.callbacks["updateOrder"] = callback
2533
2740
  body["market"] = market
2534
2741
  body["orderId"] = orderId
2742
+ body["operatorId"] = operatorId
2535
2743
  body["action"] = "privateUpdateOrder"
2536
2744
  self.doSend(self.ws, json.dumps(body), True)
2537
2745
 
2538
- def cancelOrder(self, market: str, orderId: str, callback: Callable[[Any], None]) -> None:
2746
+ def cancelOrder(
2747
+ self,
2748
+ market: str,
2749
+ operatorId: int,
2750
+ callback: Callable[[Any], None],
2751
+ orderId: str | None = None,
2752
+ clientOrderId: str | None = None,
2753
+ ) -> None:
2539
2754
  """Cancel an existing order for a specific market
2540
2755
 
2541
2756
  ---
2542
2757
  Args:
2543
2758
  ```python
2544
2759
  market="BTC-EUR"
2545
- orderId="a4a5d310-687c-486e-a3eb-1df832405ccd"
2760
+ operatorId=123 # Your identifier for the trader or bot that made the request
2546
2761
  callback=callback_example
2762
+ orderId="a4a5d310-687c-486e-a3eb-1df832405ccd" # Either orderId or clientOrderId required
2763
+ clientOrderId="2be7d0df-d8dc-7b93-a550-8876f3b393e9" # Either orderId or clientOrderId required
2764
+ # If both orderId and clientOrderId are provided, clientOrderId takes precedence
2547
2765
  ```
2548
2766
 
2549
2767
  ---
2550
2768
  Rate Limit Weight:
2551
2769
  ```python
2552
- 1
2770
+ N/A
2553
2771
  ```
2554
2772
 
2555
2773
  ---
@@ -2558,12 +2776,23 @@ class Bitvavo:
2558
2776
  {"orderId": "2e7ce7fc-44e2-4d80-a4a7-d079c4750b61"}
2559
2777
  ```
2560
2778
  """
2779
+ if orderId is None and clientOrderId is None:
2780
+ msg = "Either orderId or clientOrderId must be provided"
2781
+ raise ValueError(msg)
2782
+
2561
2783
  self.callbacks["cancelOrder"] = callback
2562
2784
  options = {
2563
2785
  "action": "privateCancelOrder",
2564
2786
  "market": market,
2565
- "orderId": orderId,
2787
+ "operatorId": operatorId,
2566
2788
  }
2789
+
2790
+ # clientOrderId takes precedence if both are provided
2791
+ if clientOrderId is not None:
2792
+ options["clientOrderId"] = clientOrderId
2793
+ elif orderId is not None:
2794
+ options["orderId"] = orderId
2795
+
2567
2796
  self.doSend(self.ws, json.dumps(options), True)
2568
2797
 
2569
2798
  def getOrder(self, market: str, orderId: str, callback: Callable[[Any], None]) -> None:
@@ -1,15 +1,10 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.3
2
2
  Name: bitvavo-api-upgraded
3
- Version: 1.17.2
3
+ Version: 2.0.0
4
4
  Summary: A unit-tested fork of the Bitvavo API
5
- Project-URL: homepage, https://github.com/Thaumatorium/bitvavo-api-upgraded
6
- Project-URL: repository, https://github.com/Thaumatorium/bitvavo-api-upgraded
7
- Project-URL: changelog, https://github.com/Thaumatorium/bitvavo-api-upgraded/blob/master/CHANGELOG.md
8
- Author: Bitvavo BV (original code)
5
+ Author: Bitvavo BV (original code), NostraDavid
9
6
  Author-email: NostraDavid <55331731+NostraDavid@users.noreply.github.com>
10
- Maintainer-email: NostraDavid <55331731+NostraDavid@users.noreply.github.com>
11
7
  License: ISC License
12
- License-File: LICENSE.txt
13
8
  Classifier: Development Status :: 5 - Production/Stable
14
9
  Classifier: Environment :: Console
15
10
  Classifier: Framework :: Pytest
@@ -20,93 +15,323 @@ Classifier: License :: OSI Approved :: ISC License (ISCL)
20
15
  Classifier: Operating System :: MacOS :: MacOS X
21
16
  Classifier: Operating System :: Microsoft :: Windows
22
17
  Classifier: Operating System :: POSIX
23
- Classifier: Programming Language :: Python
24
18
  Classifier: Programming Language :: Python :: 3.9
25
19
  Classifier: Programming Language :: Python :: 3.10
26
20
  Classifier: Programming Language :: Python :: 3.11
27
21
  Classifier: Programming Language :: Python :: 3.12
28
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python
29
24
  Classifier: Typing :: Typed
30
- Requires-Python: >=3.9
31
25
  Requires-Dist: pydantic-settings==2.*,>=2.6
32
26
  Requires-Dist: requests==2.*,>=2.26
33
- Requires-Dist: structlog==25.*,>=21.5
27
+ Requires-Dist: structlog>=21.5,==25.*
34
28
  Requires-Dist: websocket-client==1.*,>=1.2
29
+ Maintainer: NostraDavid
30
+ Maintainer-email: NostraDavid <55331731+NostraDavid@users.noreply.github.com>
31
+ Requires-Python: >=3.9
32
+ Project-URL: changelog, https://github.com/Thaumatorium/bitvavo-api-upgraded/blob/master/CHANGELOG.md
33
+ Project-URL: homepage, https://github.com/Thaumatorium/bitvavo-api-upgraded
34
+ Project-URL: repository, https://github.com/Thaumatorium/bitvavo-api-upgraded
35
35
  Description-Content-Type: text/markdown
36
36
 
37
37
  # Bitvavo API (upgraded)
38
38
 
39
- ## Userguide
39
+ A **typed, tested, and enhanced** Python wrapper for the Bitvavo cryptocurrency exchange API. This is an "upgraded" fork of the official Bitvavo SDK with comprehensive type hints, unit tests, and improved developer experience.
40
+
41
+ ## Quick Start
42
+
43
+ ```bash
44
+ pip install bitvavo_api_upgraded
45
+ ```
46
+
47
+ Scroll down for detailed usage examples and configuration instructions.
48
+
49
+ ## What Makes This "Upgraded"?
50
+
51
+ This wrapper improves upon the official Bitvavo SDK with:
52
+
53
+ - 🎯 **Complete type annotations** for all functions and classes
54
+ - 🧪 **Comprehensive test suite** (found and fixed multiple bugs in the original)
55
+ - 📋 **Detailed changelog** tracking all changes and improvements
56
+ - 🔄 **Up-to-date API compliance** including MiCA regulatory requirements
57
+ - 📚 **Enhanced documentation** with examples and clear usage patterns
58
+ - 🐍 **Modern Python support** (3.9+, dropped EOL versions)
59
+ - ⚡ **Better error handling** and rate limiting
60
+ - 🔧 **Developer-friendly tooling** (ruff, mypy, pre-commit hooks)
61
+
62
+ ## Features
40
63
 
41
- `pip install bitvavo_api_upgraded`
64
+ ### Full API Coverage
42
65
 
43
- Works the same as the official API lib, but I have:
66
+ - All REST endpoints (public and private)
67
+ - ✅ WebSocket support with reconnection logic
68
+ - ✅ Rate limiting with automatic throttling
69
+ - ✅ MiCA compliance reporting endpoints
44
70
 
45
- - typing for _all_ functions and classes
46
- - unit tests (I already found three bugs that I fixed, because the original code
47
- wasn't tested, at all)
48
- - a changelog, so you can track of the changes that I make
49
- - compatible with Python 3.7 and newer ([3.6 isn't supported as of
50
- 2021-12-23](https://endoflife.date/python))
71
+ ### Developer Experience
51
72
 
52
- ## Devguide
73
+ - ✅ Type hints for better IDE support
74
+ - ✅ Comprehensive error handling
75
+ - ✅ Detailed logging with `structlog`
76
+ - ✅ Configuration via `.env` files
77
+ - ✅ Extensive test coverage
78
+
79
+ ### Production Ready
80
+
81
+ - ✅ Automatic rate limit management
82
+ - ✅ Connection retry logic
83
+ - ✅ Proper error responses
84
+ - ✅ Memory efficient WebSocket handling
85
+
86
+ ## Configuration
87
+
88
+ Create a `.env` file in your project root:
89
+
90
+ ```env
91
+ BITVAVO_APIKEY=your-api-key-here
92
+ BITVAVO_APISECRET=your-api-secret-here
93
+ ```
94
+
95
+ Then use the settings:
96
+
97
+ ```python
98
+ from bitvavo_api_upgraded import Bitvavo, BitvavoSettings
99
+
100
+ # Option 1: Manual configuration
101
+ bitvavo = Bitvavo({
102
+ 'APIKEY': 'your-key',
103
+ 'APISECRET': 'your-secret'
104
+ })
105
+
106
+ # Option 2: Auto-load from .env
107
+ settings = BitvavoSettings()
108
+ bitvavo = Bitvavo(settings.model_dump())
109
+ ```
110
+
111
+ ## WebSocket Usage
112
+
113
+ ```python
114
+ from bitvavo_api_upgraded import Bitvavo
115
+
116
+ def handle_ticker(data):
117
+ print(f"Ticker update: {data}")
118
+
119
+ def handle_error(error):
120
+ print(f"Error: {error}")
121
+
122
+ # Initialize WebSocket
123
+ bitvavo = Bitvavo({'APIKEY': 'key', 'APISECRET': 'secret'})
124
+ ws = bitvavo.newWebsocket()
125
+ ws.setErrorCallback(handle_error)
126
+
127
+ # Subscribe to ticker updates
128
+ ws.subscriptionTicker("BTC-EUR", handle_ticker)
129
+
130
+ # Keep connection alive
131
+ try:
132
+ while True:
133
+ time.sleep(1)
134
+ except KeyboardInterrupt:
135
+ ws.closeSocket()
136
+ ```
137
+
138
+ ## API Examples
139
+
140
+ ### Public Endpoints (No Authentication)
141
+
142
+ ```python
143
+ # Get server time
144
+ time_resp = bitvavo.time()
145
+
146
+ # Get all markets
147
+ markets = bitvavo.markets({})
148
+
149
+ # Get specific market
150
+ btc_market = bitvavo.markets({'market': 'BTC-EUR'})
151
+
152
+ # Get order book
153
+ book = bitvavo.book('BTC-EUR', {})
154
+
155
+ # Get recent trades
156
+ trades = bitvavo.publicTrades('BTC-EUR', {})
157
+
158
+ # Get 24h ticker
159
+ ticker = bitvavo.ticker24h({'market': 'BTC-EUR'})
160
+ ```
161
+
162
+ ### Private Endpoints (Authentication Required)
163
+
164
+ ```python
165
+ # Get account info
166
+ account = bitvavo.account()
167
+
168
+ # Get balance
169
+ balance = bitvavo.balance({})
170
+
171
+ # Place order (requires operatorId for MiCA compliance)
172
+ order = bitvavo.placeOrder(
173
+ market="BTC-EUR",
174
+ side="buy",
175
+ orderType="limit",
176
+ body={"amount": "0.01", "price": "45000"},
177
+ operatorId=12345
178
+ )
179
+
180
+ # Get order history
181
+ orders = bitvavo.getOrders('BTC-EUR', {})
182
+
183
+ # Cancel order
184
+ cancel_result = bitvavo.cancelOrder(
185
+ market="BTC-EUR",
186
+ orderId="order-id-here",
187
+ operatorId=12345
188
+ )
189
+ ```
190
+
191
+ ### MiCA Compliance Features
192
+
193
+ ```python
194
+ # Generate trade report
195
+ trade_report = bitvavo.reportTrades(
196
+ market="BTC-EUR",
197
+ options={
198
+ "startDate": "2025-01-01T00:00:00.000Z",
199
+ "endDate": "2025-01-31T23:59:59.999Z"
200
+ }
201
+ )
202
+
203
+ # Generate order book report
204
+ book_report = bitvavo.reportBook(
205
+ market="BTC-EUR",
206
+ options={
207
+ "startDate": "2025-01-01T00:00:00.000Z",
208
+ "endDate": "2025-01-31T23:59:59.999Z"
209
+ }
210
+ )
211
+
212
+ # Get account history
213
+ history = bitvavo.accountHistory(options={})
214
+ ```
215
+
216
+ ## Error Handling
217
+
218
+ ```python
219
+ from bitvavo_api_upgraded import Bitvavo
220
+
221
+ bitvavo = Bitvavo({'APIKEY': 'key', 'APISECRET': 'secret'})
222
+
223
+ response = bitvavo.placeOrder(
224
+ market="BTC-EUR",
225
+ side="buy",
226
+ orderType="limit",
227
+ body={"amount": "0.01", "price": "45000"},
228
+ operatorId=12345
229
+ )
230
+
231
+ # Check for errors
232
+ if isinstance(response, dict) and 'errorCode' in response:
233
+ print(f"Error {response['errorCode']}: {response['error']}")
234
+ else:
235
+ print(f"Order placed successfully: {response['orderId']}")
236
+ ```
237
+
238
+ ## Rate Limiting
239
+
240
+ ```python
241
+ # Check remaining rate limit
242
+ remaining = bitvavo.getRemainingLimit()
243
+ print(f"Remaining API calls: {remaining}")
244
+
245
+ # The library automatically handles rate limiting
246
+ # But you can check limits before making calls
247
+ if remaining > 10:
248
+ # Safe to make API calls
249
+ response = bitvavo.balance({})
250
+ ```
251
+
252
+ ## Development & Contributing
53
253
 
54
254
  ```shell
55
255
  echo "install development requirements"
56
256
  uv sync
57
257
  echo "run tox, a program that creates separate environments for different python versions, for testing purposes (among other things)"
58
258
  uv run tox
59
- ```
60
-
61
- ### Semantic Versioning (SemVer)
259
+ ## Development & Contributing
62
260
 
63
- I'm using semantic versioning, which means that changes mean this:
261
+ ### Setup Development Environment
64
262
 
65
- 1. MAJOR version when you make incompatible API changes,
66
- 1. MINOR version when you add functionality in a backwards compatible manner,
67
- and
68
- 1. PATCH version when you make backwards compatible bug fixes.
263
+ ```shell
264
+ # Install uv (modern Python package manager)
265
+ curl -LsSf https://astral.sh/uv/install.sh | sh
69
266
 
70
- ### Versioning
267
+ # Clone and setup
268
+ git clone https://github.com/Thaumatorium/bitvavo-api-upgraded.git
269
+ cd bitvavo-api-upgraded
71
270
 
72
- Copy the following block to CHANGELOG.md and add all information since last
73
- version bump
271
+ # Install dependencies
272
+ uv sync
74
273
 
75
- ```markdown
76
- ## $UNRELEASED
274
+ # Run tests across Python versions
275
+ uv run tox
77
276
 
78
- ### Added
277
+ # Run tests for current Python version
278
+ uv run pytest
79
279
 
80
- ...
280
+ # Type checking
281
+ uv run mypy src/
81
282
 
82
- ### Changed
283
+ # Linting and formatting
284
+ uv run ruff check
285
+ uv run ruff format
286
+ ```
83
287
 
84
- ...
288
+ ### Project Structure
85
289
 
86
- ### Removed
290
+ ```text
291
+ src/bitvavo_api_upgraded/ # Source code
292
+ ├── __init__.py # Main exports
293
+ ├── bitvavo.py # Core API class
294
+ ├── settings.py # Pydantic settings
295
+ ├── helper_funcs.py # Utility functions
296
+ └── type_aliases.py # Type definitions
87
297
 
88
- ...
298
+ tests/ # Comprehensive test suite
299
+ docs/ # Documentation
89
300
  ```
90
301
 
91
- Commit those changes.
302
+ ### Semantic Versioning
303
+
304
+ This project follows [semantic versioning](https://semver.org/):
92
305
 
93
- After that, run `bump-my-version bump (major|minor|patch)` to automatically
94
- replace `$UNRELEASED` with the new version number, and also automatically tag
95
- and commit (with tag) to release a new version via the Github workflow.
306
+ 1. **MAJOR** version for incompatible API changes
307
+ 2. **MINOR** version for backwards-compatible functionality additions
308
+ 3. **PATCH** version for backwards-compatible bug fixes
96
309
 
97
- ## py.typed
310
+ ## Type Annotations
98
311
 
99
- Perhaps a curious file, but it simply exists to let `mypy` know that the code is
100
- typed: [Don't forget `py.typed` for your typed Python package
101
- ](https://blog.whtsky.me/tech/2021/dont-forget-py.typed-for-your-typed-python-package/)
312
+ This package includes a `py.typed` file to enable type checking. Reference: [Don't forget py.typed for your typed Python package](https://blog.whtsky.me/tech/2021/dont-forget-py.typed-for-your-typed-python-package/)
102
313
 
103
- ## Last note
314
+ ## Migration from Official SDK
104
315
 
105
- _below this line is the old README.md_
316
+ ### Key Differences
317
+
318
+ - Import: `from bitvavo_api_upgraded import Bitvavo` (instead of `from python_bitvavo_api.bitvavo import Bitvavo`)
319
+ - **Breaking Change**: Trading operations require `operatorId` parameter
320
+ - Enhanced error handling and type safety
321
+ - Better configuration management with `.env` support
322
+
323
+ ### Migration Steps
324
+
325
+ 1. Update import statements
326
+ 2. Add `operatorId` to trading method calls
327
+ 3. Optional: Migrate to `.env` configuration
328
+ 4. Enjoy improved type hints and error handling!
106
329
 
107
330
  ---
108
331
 
109
- # Bitvavo SDK for Python
332
+ ## Original Bitvavo SDK Documentation
333
+
334
+ The following is preserved from the original Bitvavo SDK for reference.
110
335
 
111
336
  Crypto starts with Bitvavo. You use Bitvavo SDK for Python to buy, sell, and
112
337
  store over 200 digital assets on Bitvavo from inside your app.
@@ -0,0 +1,9 @@
1
+ bitvavo_api_upgraded/__init__.py,sha256=0922b82678a737a9f1314aa84fe4bf565f5425ce0d3aac1f6db65ff909efa68e,222
2
+ bitvavo_api_upgraded/bitvavo.py,sha256=e4d986ae3529b082d329f1da51027e7400f7a342faa023e037181dceb439d481,134183
3
+ bitvavo_api_upgraded/helper_funcs.py,sha256=e2805d435c41f82d979104e637eade7f323359f3be214a300d25a139215d0915,3212
4
+ bitvavo_api_upgraded/py.typed,sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,0
5
+ bitvavo_api_upgraded/settings.py,sha256=647dac47d894c8e0bf9526aa993e74c04467814ba86efbbc7744cb8983799d38,2266
6
+ bitvavo_api_upgraded/type_aliases.py,sha256=3409cc4a4e67e9268422f1c579230a9d73b17cec276c5b849d129a01795c998c,932
7
+ bitvavo_api_upgraded-2.0.0.dist-info/WHEEL,sha256=2b400f346628f0064eb5bbf656b39df8dfcb092437ab08244409d295749b81a3,78
8
+ bitvavo_api_upgraded-2.0.0.dist-info/METADATA,sha256=e7e9d2261c0af757cfe323dc911012b2f834a030848684ac6d18585911eb6691,17102
9
+ bitvavo_api_upgraded-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.8.9
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -1,10 +0,0 @@
1
- bitvavo_api_upgraded/__init__.py,sha256=CSK4JninN6nxMUqoT-S_Vl9UJc4NOqwfbbZf-Qnvpo4,222
2
- bitvavo_api_upgraded/bitvavo.py,sha256=s7l-cAl0FteBrm5iF-gnM_U9_-Yds9lpn5pN6f0oaZU,125981
3
- bitvavo_api_upgraded/helper_funcs.py,sha256=4oBdQ1xB-C2XkQTmN-refzIzWfO-IUowDSWhOSFdCRU,3212
4
- bitvavo_api_upgraded/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- bitvavo_api_upgraded/settings.py,sha256=ZH2sR9iUyOC_lSaqmT50wERngUuobvu8d0TLiYN5nTg,2266
6
- bitvavo_api_upgraded/type_aliases.py,sha256=NAnMSk5n6SaEIvHFeSMKnXOxfOwnbFuEnRKaAXlcmYw,932
7
- bitvavo_api_upgraded-1.17.2.dist-info/METADATA,sha256=U_5w2WGTB4xykCwIgh3KtcfsVzsYDS3NZWlHwVRz1Tk,11547
8
- bitvavo_api_upgraded-1.17.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
- bitvavo_api_upgraded-1.17.2.dist-info/licenses/LICENSE.txt,sha256=hiFyor_njVlzVblnb-78mzx1Um3CGvuFxEH3YR735rc,744
10
- bitvavo_api_upgraded-1.17.2.dist-info/RECORD,,
@@ -1,4 +0,0 @@
1
- Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
3
- Root-Is-Purelib: true
4
- Tag: py3-none-any
@@ -1,15 +0,0 @@
1
- ISC License
2
-
3
- Copyright (c) 2024, Bitvavo B.V.
4
-
5
- Permission to use, copy, modify, and/or distribute this software for any
6
- purpose with or without fee is hereby granted, provided that the above
7
- copyright notice and this permission notice appear in all copies.
8
-
9
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.