Skip to content

Commit e59dd7b

Browse files
Resolve "Add fee_schedule/schedules support to Spot User.get_trade_volume" (#454)
## Summary - Adds a `fee_schedule` parameter to `User.get_trade_volume`, which requests the `schedules` field from Kraken's `GetTradeVolume` endpoint (added 2026-06-16). This is the replacement for the `fees`/`fees_maker` fields on `AssetPairs`, deprecated on 2026-09-08. - Kraken validates `fee_schedule` strictly against the JSON boolean literals `"true"`/`"false"` and rejects Python's default `str(bool)` capitalization (`"True"`/`"False"`) with `EGeneral:Invalid arguments`. Verified live against the real API; the value is now serialized correctly. Closes #452
1 parent 3e97f5e commit e59dd7b

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

‎src/kraken/spot/user.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ def get_trade_volume(
926926
pair: str | list[str] | None = None,
927927
*,
928928
fee_info: bool = True,
929+
fee_schedule: bool | None = None,
929930
extra_params: dict | None = None,
930931
) -> dict:
931932
"""
@@ -940,6 +941,12 @@ def get_trade_volume(
940941
:type pair: str | list[str], optional
941942
:param fee_info: Include fee information or not (default: ``True``)
942943
:type fee_info: bool, optional
944+
:param fee_schedule: Include the full fee schedule per trading pair in
945+
the response's ``schedules`` field (default: ``None``, i.e. not
946+
requested). This is the replacement for the deprecated
947+
``fees``/``fees_maker`` fields on
948+
:func:`kraken.spot.Market.get_asset_pairs`.
949+
:type fee_schedule: bool, optional
943950
944951
.. code-block:: python
945952
:linenos:
@@ -979,11 +986,40 @@ def get_trade_volume(
979986
}
980987
}
981988
}
989+
>>> u.get_trade_volume(pair="DOTUSD", fee_schedule=True)
990+
{
991+
'currency': 'ZUSD',
992+
'volume': '212243.1210',
993+
'fees': {...},
994+
'fees_maker': {...},
995+
'schedules': [
996+
{
997+
'pair': 'DOTUSD',
998+
'class': 'forex',
999+
'tiers': [
1000+
{'maker_fee': '0.4000', 'taker_fee': '0.8000', 'active': True},
1001+
{
1002+
'maker_fee': '0.3000',
1003+
'taker_fee': '0.6000',
1004+
'min_futures_volume': '5000000.0000',
1005+
'min_spot_volume': '2500.0000'
1006+
},
1007+
...
1008+
]
1009+
}
1010+
]
1011+
}
9821012
9831013
"""
9841014
params: dict = {"fee-info": fee_info}
9851015
if defined(pair):
9861016
params["pair"] = pair
1017+
if defined(fee_schedule):
1018+
# Kraken validates this parameter strictly against the JSON
1019+
# boolean literals "true"/"false" and rejects the default
1020+
# str(bool) capitalization ("True"/"False") with
1021+
# EGeneral:Invalid arguments.
1022+
params["fee_schedule"] = "true" if fee_schedule else "false"
9871023
return self.request( # type: ignore[return-value]
9881024
method="POST",
9891025
uri="/0/private/TradeVolume",

‎tests/spot/test_spot_user.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ def test_get_balance(self: Self) -> None:
5353
"available_balance": 1.96307091,
5454
}
5555

56+
@pytest.mark.parametrize(
57+
("fee_schedule", "expected"),
58+
[(True, "true"), (False, "false")],
59+
)
60+
def test_get_trade_volume_fee_schedule_serialization(
61+
self: Self,
62+
fee_schedule: bool,
63+
expected: str,
64+
) -> None:
65+
"""
66+
Checks that ``fee_schedule`` is sent as the lowercase JSON boolean
67+
literal Kraken expects, not Python's ``str(bool)`` capitalization
68+
(which Kraken rejects with ``EGeneral:Invalid arguments``).
69+
"""
70+
with mock.patch.object(User, "request", return_value={}) as request:
71+
User().get_trade_volume(fee_schedule=fee_schedule)
72+
assert request.call_args.kwargs["params"]["fee_schedule"] == expected
73+
5674

5775
@pytest.mark.integration
5876
@pytest.mark.spot
@@ -290,6 +308,17 @@ def test_get_trade_volume(self: Self, spot_auth_user: User) -> None:
290308
spot_auth_user.get_trade_volume(pair="DOT/EUR", fee_info=False),
291309
)
292310

311+
def test_get_trade_volume_fee_schedule(self: Self, spot_auth_user: User) -> None:
312+
"""
313+
Checks that ``fee_schedule=True`` includes the ``schedules`` field
314+
in the response, replacing the deprecated ``fees``/``fees_maker``
315+
fields on ``Market.get_asset_pairs``.
316+
"""
317+
response = spot_auth_user.get_trade_volume(pair="XBTUSD", fee_schedule=True)
318+
assert is_not_error(response)
319+
assert "schedules" in response
320+
assert response["schedules"][0]["tiers"][0]["maker_fee"]
321+
293322
@pytest.mark.parametrize("report", ["trades", "ledgers"])
294323
def test_request_save_export_report(
295324
self: Self,

0 commit comments

Comments
 (0)