diff --git a/docs/explanation/returns-and-performance.md b/docs/explanation/returns-and-performance.md index 937af9e..8ff996f 100644 --- a/docs/explanation/returns-and-performance.md +++ b/docs/explanation/returns-and-performance.md @@ -19,10 +19,25 @@ Two columns show up throughout zfin: 3-Year Return: 20.75% 22.22% ann. ``` -Total return needs dividend history, which comes from Polygon -- so it -requires `POLYGON_API_KEY`. Without it, you still get price-only -returns. For a dividend payer like SCHD the gap between the two columns -is large; for a non-payer it's near zero. +Total return is computed two ways, and zfin reports whichever comes out +higher per period: + +- **Dividend reinvestment** -- walk the dividend history, buy more shares + at each ex-date's close, compound. Exact, but only as complete as the + dividend records, which come from Polygon -- so this path wants + `POLYGON_API_KEY`. +- **Adjusted close** -- providers publish an `adj_close` that already + bakes dividends in. No dividend history needed, but it is only as + current as the last full price-history download (see + [Incremental candle updates](caching.md#incremental-candle-updates)). + +Both failure modes -- a missing dividend record, a stale adjustment -- +understate the return, never overstate it, which is why taking the higher +of the two is safe rather than arbitrary. So without `POLYGON_API_KEY` +you still get a genuine total return, just via the second path; the gap +you'd see is at most a distribution or two, not the whole yield. For a +dividend payer like SCHD the gap between the price-only and total-return +columns is large; for a non-payer it's near zero. ## Annualized (CAGR) diff --git a/docs/guides/offline-and-refresh.md b/docs/guides/offline-and-refresh.md index 1572029..fae808f 100644 --- a/docs/guides/offline-and-refresh.md +++ b/docs/guides/offline-and-refresh.md @@ -17,11 +17,29 @@ zfin --refresh-data=force perf VTI zfin --refresh-data=never analysis ``` -| Value | Behavior | -|------------------|---------------------------------------------------------------------------------------------------------------| -| `auto` (default) | Respect each data type's cache TTL; fetch only what's stale. | -| `force` | Re-fetch every symbol regardless of freshness. Use after a market close, or when you suspect bad cached data. | -| `never` | Serve cache contents only; make no network calls. True offline mode. | +| Value | Behavior | +|------------------|----------------------------------------------------------------------------------------------------------------| +| `auto` (default) | Respect each data type's cache TTL; fetch only what's stale. | +| `force` | Re-ask providers regardless of freshness. Use after a market close, or when a fetch seems to have been missed. | +| `never` | Serve cache contents only; make no network calls. True offline mode. | + +### `force` tops up price history, it does not rebuild it + +Worth knowing before you reach for it: for daily candles, `force` skips +the TTL and asks the provider for anything newer than the last cached +bar. It does **not** re-download the series. So if you suspect the +*existing* bars are wrong rather than merely incomplete, `force` will +not help -- it appends and moves on. + +That case is handled automatically now. When a dividend or split goes ex, +the bars behind it need their adjusted closes marked down, and zfin +re-downloads that symbol's full history to pick up the corrected values +on its own -- see +[Incremental candle updates](../explanation/caching.md#incremental-candle-updates). +`zfin diagnose SYMBOL` says whether one is pending. + +`zfin cache clear` remains the blunt instrument if you want to discard +everything and start over. ## Working offline @@ -66,7 +84,8 @@ zfin cache clear # delete all cached data `cache clear` is safe -- everything re-fetches on the next run (subject to provider rate limits). Reach for it only when you suspect corrupt -cached data; normal staleness is handled by `auto`. See +cached data; normal staleness is handled by `auto`, and a stale +adjustment basis repairs itself. See [`zfin cache`](../reference/cli/cache.md). ## See also diff --git a/src/commands/diagnose.zig b/src/commands/diagnose.zig index 88c59a3..118b484 100644 --- a/src/commands/diagnose.zig +++ b/src/commands/diagnose.zig @@ -50,6 +50,8 @@ pub const meta: framework.Meta = .{ \\ adj basis how current the cached adj_close values are, against the \\ newest dividend or split that has already gone ex \\ peers newest bar held by other symbols of the same kind + \\ tracked whether a normal run fetches this symbol at all, or it is + \\ only pulled on demand (a projections benchmark, say) \\ server what ZFIN_SERVER offers, whether it is ahead or behind, and \\ whether it refreshes this symbol at all \\ provider what the upstream provider actually has right now