fix stale provider doc comments

This commit is contained in:
Emil Lerch 2026-08-17 19:43:11 -07:00
parent 6674969001
commit 337d346421
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 40 additions and 18 deletions

48
src/cache/store.zig vendored
View file

@ -1408,13 +1408,28 @@ pub const Store = struct {
/// (SRF returns FieldNotFoundOnFieldWithoutDefaultValue).
/// `readCandleMeta` swallows the error and returns null,
/// making the symbol look like a cache miss - `getCandles`
/// then triggers a fresh fetch via `populateAllFromTiingo`,
/// then triggers a fresh fetch via `refetchFullHistory`,
/// which writes a new meta file with the provider explicit.
/// The wipe happens naturally on first use post-upgrade.
///
/// This is also why no *other* field on this struct may be
/// default-less: that cold-start path is the destructive one.
/// It writes a negative-cache marker over `candles_daily.srf`
/// when no provider carries the symbol, so routing a
/// populated cache through it risks discarding real history.
/// Fields added since (`tiingo_retry_after_s`, `adj_basis`)
/// are defaulted so legacy caches keep parsing.
provider: CandleProvider,
/// Consecutive transient failure count for the primary provider (Tiingo).
/// Incremented on ServerError; reset to 0 on success. When >= 3, the
/// symbol is degraded to a fallback provider until Tiingo recovers.
/// Consecutive transient-failure count for a candle fetch
/// (ServerError / connection failure). Reset to 0 on any
/// successful fetch. At >= 3, `getCandles` serves the stale
/// cached series rather than returning an error, so a provider
/// outage degrades to "slightly old prices" instead of "no
/// prices".
///
/// It does **not** switch providers. Provider selection is
/// `tiingo_retry_after_s`'s job, and only a genuine 404 moves
/// it - a transient outage says nothing about coverage.
fail_count: u8 = 0,
/// Unix-seconds instant before which Tiingo should not be
/// consulted for this symbol. `0` (the default) means "no
@ -1492,17 +1507,22 @@ pub const Store = struct {
/// writes produce this value (TwelveData was demoted in an
/// earlier change because its `adj_close` was unreliable).
/// Cache reads still recognize the value for backwards
/// compatibility.
/// compatibility, and `getCandles` treats such a cache as
/// unusable so the symbol gets refetched.
twelvedata,
/// Legacy: candles were sourced from Yahoo Finance. No new
/// writes produce this value (Yahoo was removed from the
/// candle pipeline in the 2026-05 audit; Yahoo is still used
/// for `getQuote` real-time prices but not for historical
/// candles). Cache reads still recognize the value for
/// backwards compatibility.
/// Candles were sourced from Yahoo Finance.
///
/// Actively written. `fetchCandlesFromProviders` falls back to
/// Yahoo whenever Tiingo cannot serve a symbol, and
/// `refetchFullHistory` does the same for a full restatement.
/// Yahoo's `adj_close` is split- and dividend-adjusted like
/// Tiingo's, but its parser yields 0 for a JSON `null` element,
/// which the analytics layer then has to discard - so Tiingo is
/// preferred where both will answer.
yahoo,
/// Active: candles sourced from Tiingo. The only value
/// produced by current writes.
/// Candles were sourced from Tiingo. Preferred: it serves
/// dividends and splits from the same response, and its
/// `adj_close` is what the analytics layer is written against.
tiingo,
pub fn fromString(s: []const u8) CandleProvider {
@ -3684,7 +3704,7 @@ test "deserializeCandleMeta fails on old cache that elided provider field" {
// (model has no default for provider). The graceful handling is
// upstream: `readCandleMeta` swallows the deserialization error
// and returns null, which makes `getCandles` treat it as a cache
// miss and trigger a fresh fetch via `populateAllFromTiingo`.
// miss and trigger a fresh fetch via `refetchFullHistory`.
// The new fetch writes a meta file with the provider explicit.
//
// This test documents the failure mode and confirms it's not a

View file

@ -83,10 +83,12 @@ pub fn run(ctx: *framework.RunCtx, parsed: ParsedArgs) !void {
try out.print(")\nLatest close: {f}\n", .{Money.from(c[c.len - 1].close)});
// `dividends != null` indicates we got explicit dividend records
// from the provider. When false we still display total return
// (synthesized from adj_close, which most providers bake dividends
// into), but we surface a hint that explicit dividend data is
// missing.
// from the provider. When false we still display total return -
// `performance.totalReturns` degrades to the provider's `adj_close`,
// which is dividend-adjusted - but we surface a hint, because that
// series is only as current as the last full candle fetch (see
// `CandleMeta.adj_basis`) and understates by the missed yield when a
// distribution has gone ex since.
const has_explicit_divs = result.dividends != null;
// -- As-of-date returns --