diff --git a/kalshi/perps/klear/resources/margin.py b/kalshi/perps/klear/resources/margin.py index 40d19a5..6df8147 100644 --- a/kalshi/perps/klear/resources/margin.py +++ b/kalshi/perps/klear/resources/margin.py @@ -63,6 +63,19 @@ def _validate_date_range(start_date: str, end_date: str) -> None: f"start_date / end_date must be YYYY-MM-DD strings (got " f"start_date={start_date!r}, end_date={end_date!r}): {exc}" ) from exc + # ``date.fromisoformat`` (3.11+) also accepts non-canonical forms like + # "20260605" or ISO week dates ("2026-W23-5"), which the raw string would + # then forward to a server expecting strict YYYY-MM-DD. Require the canonical + # form so the SDK rejects them here rather than relying on a server 400. + for label, raw, parsed in ( + ("start_date", start_date, start), + ("end_date", end_date, end), + ): + if parsed.isoformat() != raw: + raise ValueError( + f"{label} must be a canonical YYYY-MM-DD date, got {raw!r} " + f"(parsed as {parsed.isoformat()})" + ) if end < start: raise ValueError( f"end_date ({end_date}) must be on or after start_date ({start_date})" diff --git a/tests/perps/klear/test_margin.py b/tests/perps/klear/test_margin.py index 4c65082..a32bab7 100644 --- a/tests/perps/klear/test_margin.py +++ b/tests/perps/klear/test_margin.py @@ -202,6 +202,12 @@ def test_rejects_malformed_or_inverted_dates_client_side( auth_klear_client.margin.margin_reports(start_date="nope", end_date="2026-06-01") with pytest.raises(ValueError, match="on or after"): auth_klear_client.margin.margin_reports(start_date="2026-06-02", end_date="2026-06-01") + # #409: fromisoformat is lenient — reject non-canonical forms that would + # otherwise be forwarded raw to a server expecting strict YYYY-MM-DD. + with pytest.raises(ValueError, match="canonical YYYY-MM-DD"): + auth_klear_client.margin.margin_reports(start_date="20260501", end_date="2026-06-01") + with pytest.raises(ValueError, match="canonical YYYY-MM-DD"): + auth_klear_client.margin.margin_reports(start_date="2026-05-01", end_date="2026-W22-1") assert not route.called auth_klear_client.close()