DocumentationUnder the hood

Gamma cache persistence

Status: Implemented and live since v1.0.0; migrated to the daemon.db authority on 2026-07-20. Created: 2026-05-22 07:30 CEST Last update: 2026-07-20 Owner: osauer Related: internal/daemon/gamma_zero_cache.go, internal/daemon/gamma_zero_store.go, Concepts

Why this exists

The gamma-zero compute is intentionally heavy. It fans out across SPY and SPX option chains, filters out post-settlement 0DTE expiries, fits sticky-moneyness skew curves, and produces per-index plus combined regime diagnostics. After the slot-anchored expiry picker landed, a cold combined run could cross the five-minute mark.

Restarting the daemon during a trading day should not force every dashboard or CLI caller through that full compute again. The result's natural TTL is one New York trading session, so the cache can persist safely as long as stale sessions, stale methodology, and wrong-scope data are rejected on load.

Design

Gamma persistence is per-domain. It keeps gamma's lifecycle and validation local while using the daemon's sole live SQLite authority:

The live location is $XDG_STATE_HOME/ibkr/daemon.db, falling back to $HOME/.local/state/ibkr/daemon.db. Former $XDG_CACHE_HOME/ibkr/gamma-zero/*.json files are one-time cutover inputs and isolated codec-test seams only.

Scope isolation

The daemon supports three cache scopes:

Each scope has its own stable authority key:

market/gamma/zero/spy+spx
market/gamma/zero/spy
market/gamma/zero/spx

Scope-keying is load-bearing. Without it, an SPY-only diagnostic call could poison the combined dashboard state, and persistence would make that wrong-scope value survive a daemon restart.

Persisted shape

{
  "version": 1,
  "session_key": "2026-05-22",
  "scope": "spy+spx",
  "method": "bs-gamma-profile-v3-stickymoneyness-0dte-split",
  "result": { "...": "rpc.GammaZeroComputed" }
}

Four independent gates turn persisted state into a cold cache:

The store never migrates or coerces old results. A methodology bump should recompute from IBKR data, not reinterpret prior cache contents.

Closed-session stale fallback and failed-refresh fallback are serve paths, not rankability paths. The payload remains inspectable, but quality.rankability must be context_only, blocked, or unavailable until freshness and coverage gates recover.

Non-goals

Test coverage

The store tests cover daemon.db round-trip persistence, current-state plus observation writes, scope isolation, missing state, version/session/scope/method mismatches, and constructor seeding from valid persisted scopes. Legacy file codec tests remain only to prove deterministic one-time import.