zfin/TODO.md

3.7 KiB

Future Work

Covered call portfolio valuation

Portfolio value should account for sold call options. Shares covered by in-the-money calls should be valued at the strike price, not the market price.

Example: 500 shares of AMZN at $225, with 3 sold calls at $220 strike. 300 shares should be valued at $220 (covered), 200 shares at $225 (uncovered).

Human review of analytics modules

AI review complete; human review still needed for:

  • src/analytics/performance.zig — Morningstar-style trailing returns
  • src/analytics/risk.zig — Sharpe, volatility, max drawdown, portfolio summary
  • src/analytics/indicators.zig — SMA, Bollinger Bands, RSI
  • src/models/classification.zig — Sector/geo/asset-class metadata parsing

Known issues from AI review:

  • risk.zig uses population variance (divides by n) instead of sample variance (n-1). Negligible with 252+ data points but technically wrong.

Risk-free rate maintenance

risk.zig default_risk_free_rate is currently 4.5% (T-bill proxy as of early 2026). This is now a parameter to computeRisk with the default exported as a public constant. Callers currently pass the default.

Action needed: When the Fed moves rates significantly, update default_risk_free_rate in src/analytics/risk.zig. Eventually consider making this a config value (env var or .env) so it doesn't require a rebuild.

CLI/TUI code review (lower priority)

No review has been done on these files. They are presentation-layer code and not part of the reusable library API.

TUI:

  • src/tui.zig
  • src/tui/chart.zig
  • src/tui/keybinds.zig
  • src/tui/theme.zig

Commands:

  • src/commands/common.zig
  • src/commands/analysis.zig
  • src/commands/cache.zig
  • src/commands/divs.zig
  • src/commands/earnings.zig
  • src/commands/enrich.zig
  • src/commands/etf.zig
  • src/commands/history.zig
  • src/commands/lookup.zig
  • src/commands/options.zig
  • src/commands/perf.zig
  • src/commands/portfolio.zig
  • src/commands/quote.zig
  • src/commands/splits.zig

Market-aware cache TTL for daily candles

Daily candle TTL is currently 24 hours, but candle data only becomes meaningful after the market close. Investigate keying the cache freshness to ~4:30 PM Eastern (or whenever TwelveData actually publishes the daily candle) rather than a rolling 24-hour window. This would avoid unnecessary refetches during the trading day and ensure a fetch shortly after close gets fresh data. I think that issue has been alleviated by the 23hr 45min plus cron job.

On-demand server-side fetch for new symbols

Currently the server's SRF endpoints (/candles, /dividends, etc.) are pure cache reads — they 404 if the data isn't already on disk. New symbols only get populated when added to the portfolio and picked up by the next cron refresh.

Consider: on a cache miss, instead of blocking the HTTP response with a multi-second provider fetch, kick off an async background fetch (or just auto-add the symbol to the portfolio) and return 404 as usual. The next request — or the next cron run — would then have the data. This gives "instant-ish gratification" for new symbols without the downsides of synchronous fetch-on-miss (latency, rate limit contention, unbounded cache growth from arbitrary tickers).

Note that this process doesn't do anything to eliminate all the API keys that are necessary for a fully functioning system. A more aggressive view would be to treat ZFIN_SERVER has a 100% record of reference, but that would introduce some opacity to the process as we wait for candles (for example) to populate. This could be solved on the server by spawning a thread to fetch the data, then returning 202 Accepted, which could then be polled client side. Maybe this is a better long term approach?