From 2e6009b612f05c15c2d20dec3c7977f01c0dab75 Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 31 Aug 2026 21:44:21 +0200 Subject: [PATCH] docs: record the measured reach of the rate lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ran the real lookup against both live APIs rather than the stubs. Three findings worth being written down instead of rediscovered: - Kraken returns ~721 daily candles, so ~2 years for any pair it quotes. EUR and GBP both resolve from it directly; the fallback never fires for them. - CoinGecko's free tier answers within 365 days and returns 401 Unauthorized beyond it — a plan limit wearing an auth error's clothes, not a transient failure. The practical ceiling is therefore ~2 years for major pairs and 1 year for anything else, which matters given the ask was "historical data for up to even a year". - The two sources disagree by ~2.5% on the same date (2026-05-15: Kraken 68,047.50 EUR/BTC, CoinGecko 69,743.26) — one exchange's daily close versus a cross-exchange average. Neither is wrong, which is the reason the ledger records rate_source beside rate rather than presenting a bare figure as canonical. Refusal behaviour confirmed live: an unknown pair and a 2019 date both come back None rather than falling through to some other number. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018jy52j9GRZ6XKa1Zt21LLj --- docs/operations.md | 17 +++++++++++++++++ rates.py | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/docs/operations.md b/docs/operations.md index 813e753..b3a7869 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -67,6 +67,23 @@ exchange providers: those are spot tickers with no date parameter, and the rate history behind the admin chart is RAM-only, single-currency and wiped on restart. +How far back this actually reaches, measured against both live APIs: + +| payday age | major pair (EUR, GBP, USD…) | currency Kraken doesn't quote | +|---|---|---| +| under 1 year | Kraken | CoinGecko | +| 1–2 years | Kraken | **unavailable** | +| over 2 years | **unavailable** | **unavailable** | + +CoinGecko's free tier returns `401 Unauthorized` past 365 days — a plan +limit wearing an auth error's clothes. Unavailable means the period fails +and says so; it never guesses. + +The two sources disagree by a couple of percent on the same date (for +2026-05-15: Kraken 68,047.50 EUR/BTC, CoinGecko 69,743.26 — one exchange's +close versus a cross-exchange average). That is why the ledger records +`rate_source` beside `rate` instead of presenting a bare figure as canonical. + Where payroll converts, it hands `create_invoice` a **sat** amount, because `create_invoice` always prices fiat itself and cannot be told a rate. The rate used and its source are recorded on the payout row either way — in diff --git a/rates.py b/rates.py index a0a9910..795c7fc 100644 --- a/rates.py +++ b/rates.py @@ -18,6 +18,20 @@ LNbits itself trusts. addressed by date rather than by series, so it costs one request per date — fine as a fallback, bad as a default against free-tier rate limits. +Verified coverage (measured against both live APIs, 2026-08-31): + +* Kraken returns ~721 daily candles, so **roughly two years** back for any + pair it quotes. EUR and GBP both resolve from it directly. +* CoinGecko's free tier answers within **365 days** and returns `401 + Unauthorized` beyond that — a plan limit dressed as an auth error, not a + transient failure. So dates older than ~2 years are unreachable for + everything, and older than a year for currencies Kraken does not quote. +* The two disagree: for 2026-05-15 Kraken gave 68,047.50 EUR/BTC and + CoinGecko 69,743.26, about 2.5% apart — one exchange's daily close versus + a cross-exchange volume-weighted average. Neither is wrong, which is + exactly why the payout records `rate_source` next to `rate` rather than + presenting a bare number as though it were canonical. + Both return None rather than raising. A rate that cannot be established is a payout that must not happen: the caller records a failed period and lets a human decide, which is the same thing an underfunded wallet does. The one