Files
worldmonitor/api
Elie Habib d3836ba49b feat(sentiment): add AAII investor sentiment survey (#2930)
* feat(sentiment): add AAII investor sentiment survey

Weekly bull/bear/neutral sentiment from AAII (1987-present). Shows
current reading, bull-bear spread, and 52-week historical chart.
Seeder fetches from AAII CSV, stores last 52 weeks in Redis.

* fix(aaii): wire panel loading + mark fallback data explicitly

* fix(aaii): keep panel live across refreshes + surface in health monitoring

- fetchData now falls back to /api/bootstrap?keys=aaiiSentiment on
  refresh (getHydratedData is one-shot and returns undefined after
  the first read, causing a permanent spinner on hourly refresh)
- Shows an error state with auto-retry when both hydrated and
  bootstrap-fetch miss, matching the WsbTickerScannerPanel pattern
- Registered aaiiSentiment in api/health.js BOOTSTRAP_KEYS and
  api/seed-health.js SEED_DOMAINS so rollout failures and
  fallback-only operation are observable in the monitoring dashboards

* fix(sentiment): handle BIFF8 SST trailing bytes and use UTC for AAII Thursday calc

Two P2 greptile fixes from PR #2930 review:

1. BIFF8 SST parser was reading the rich-text run count (cRun, flags & 0x08)
   and extended-string size (cbExtRst, flags & 0x04) to advance past those
   header fields, but never skipped the trailing bytes AFTER the char data:
   4 * cRun formatting-run bytes and cbExtRst ext-rst bytes. If any string
   before the column header was rich-text formatted, every subsequent SST
   entry parsed from the wrong offset, silently breaking XLS extraction and
   falling back to HTML scraping.

2. parseHtmlSentiment() computed last-Thursday via today.getDay() +
   setDate(today.getDate() - daysToThursday), both local-TZ-dependent. On
   Railway (non-UTC TZ) the inferred Thursday could drift by a day, causing
   the HTML-derived row to mismatch the XLS historical rows. Switched to
   getUTCDay() + Date.UTC() for TZ-stable arithmetic.
2026-04-11 17:05:39 +04:00
..
2026-03-20 12:37:24 +04:00