RELAY โ ๐ MIS session: off-sheets quote data is LIVE (Fable infra session, Jul 1 2026 ~6 PM ET)
UPDATE ~7 PM ET (Sam's corrections applied): primary source is now YOUR Schwab data โ
ops-api parsesfn=watchlist&k=MIS_SETUP_KEY(the page runsmisV2SchwabQuotes_over the full
MIS_UNIVERSE_server-side) โ 237/237 covered in ONE pass, real-time, no rate limits, zero
MIS code touched. Universe = a synced 237 mirror โช whatever the live page serves (your
MIS_UNIVERSE_edits flow through automatically). Finnhub is now only the gap-filler (BRK.B โ
Schwab share-class format โ and BTCUSD) + the fallback ladder.src:"schwab-v2"on quotes.
The Finnhub-chunking discussion below is retained for the fallback path only.
What shipped (ops-api, deployed + verified): the ยง4 data foundation from docs/MIS_OPSAPI_CONTRACTS.md, WITHOUT touching any MIS code (your lane untouched โ engine, v2/Code.gs, scoring all yours).
Endpoints (auth: x-ops-key header, or ?token=<MIS_RUN_TOKEN>, or portal referer)
GET /mis/quotes?syms=META,SPY,...โ up to 40 symbols per call; any stale ones are live-freshened at request time. Callers page the universe in 40s.GET /mis/quotes(no params) โ the whole KV map as-is, per-symbol{price, prevClose, pct, asof, source, stale}(market-hours-aware staleness: 30 min in-hours / 18h off / 26h hard, per your contract).GET /mis/quotes?refresh=1โ advances a refresh chunk (~35 syms). This is the external-pinger hook.GET /mis/history?sym=META&days=90โ daily closes from D1price_history(one row per sym per NY trading day; snapshots at 17:00 + 22:00 ET, INSERT OR REPLACE so late refreshes refine the same day). First rows land tonight (Jul 1).
Universe
Pulled LIVE from your v2 web app fn=watchlist (KV-cached 24h; parsed 181 tradeable symbols on Jul 1), fallback = the 183 list in docs/MIS_UNIVERSE.md. You grow MIS_UNIVERSE_, the quote store follows automatically.
Source + the Schwab slot-in
- Live source today: Finnhub (BTCUSD via Binance feed), chunk-walked ~35/pass under the free-plan 50-subrequest cap, throttled in waves of 8 (a flat 35-parallel burst was getting rate-limited โ observed + fixed live).
- Schwab-in-Worker is CODED and inert: OAuth refresh-token handler + one-call batch quotes for the whole universe. It self-activates the moment the three secrets exist:
SCHWAB_CLIENT_ID,SCHWAB_SECRET,SCHWAB_REFRESH_TOKEN(wrangler secret putfrom ops-api/)./healthshowsschwab_setso you can see when it's armed. If you want it armed: seed those three from your Script Properties (your call โ it also needs your engine-freeze window discipline for anything scoring-related; this is data plumbing only). Note Schwab refresh tokens still expire ~7 days โ the weekly re-auth isn't killed until we add the full auth dance, this just centralizes the consumption side.
Refresh cadence honesty (the one design constraint)
The Worker's 5-cron cap is FULL, so there is no dedicated intraday quotes cron. Coverage advances by: (a) any /mis/quotes read self-warms a chunk when the store is >5 min old, (b) the 06:30/17:00/22:00 ET crons each advance a chunk, (c) anything you point at ?refresh=1 (e.g. a 5-min Apps Script time trigger during market hours would keep the whole 181 under ~30 min old โ YOUR side, your lane, if you want it).
Verified live (Jul 1 ~5:45 PM ET)
META 612.91 +8.81% ยท SPY 745.76 โ0.14% ยท AAPL 294.38 +1.73% ยท BTCUSD 60,718 โ all stale:false, coverage walking toward full universe. /health โ secrets_ok:true, quotes_ready:true, schwab_set:false, build bundle-2026-07-01-quotes.