# Added symbols — `/ES`, `/NQ`, `/GC`, `/CL` **Status: placeholders in, switcher not.** Agreed 2026-08-31. Profile and `symbol` stamps are live; still one stream and no UI switcher. The process is one chart of one contract. Yahoo and Schwab already know the other roots. Almost everything after the stream is *this* instrument. The likely set is four: **ES, NQ, gold, oil**. Design for N, not a boolean ES/gold switch. --- ## Decision **Persist `symbol` now. Do not split `Runtime` now.** Same move as `user_id: "shared"` in `docs/multi_user.md`: cheap while there is one value, expensive after two files exist. ### Do first (placeholders, still one live series) 1. **Instrument profile** the settings and the browser both see: ``` id: "es" | "nq" | "gc" | "cl" yahoo_symbol: "ES=F" | "NQ=F" | "GC=F" | "CL=F" schwab_symbol: "/ES" | "/NQ" | "/GC" | "/CL" tick: 0.25 | 0.25 | 0.10 | 0.01 decimals: 2 session: globex_18_17 rth: spy_rth | spy_rth | none | nymex_day ``` Snap, nudge, alert inputs, and the status label read `tick` / names from here. Kill `ConfluenceChart.TICK = 0.25`. Session code stays shared. RTH follows the profile (`none` hides SPY marks on gold). 2. **Stamp persistence.** Drawings, alert-state rows, and events get `symbol` (Schwab root, e.g. `/ES`). Missing field means `/ES`. New writes always stamp the current profile. Do not wait for a second chart. 3. **Keep one store, one stream, one seed.** `Bar.symbol` already exists. `InMemoryBarStore` stays `tf → bars` until something actually switches. Today’s env still selects the one live profile (`YAHOO_SYMBOL` / `SCHWAB_SYMBOL` or an `INSTRUMENT=es` key). Default remains ES. ### Then (after placeholders have been live) 4. Prove a second root as a **replace**: point env at NQ or GC, restart, confirm Yahoo history + Schwab stream + tick snaps. NQ is the cheap proof (same tick and RTH as ES). GC or CL is the proof that tick/RTH actually split. 5. **Switcher** last: UI picks the profile; snapshot replace (`setBars`), not a tick; filter drawings/alerts by `symbol`; lazy-seed the other series; do not Schwab-sub the hidden root. ### Not in this plan Two live streams (ES and gold on screen together). That is a second tick path and a second 5k-bar store inside Stay cheap, plus an unverified double `CHART_FUTURES` sub on one token. Not until the switcher has been used. --- ## Instrument table | | `/ES` | `/NQ` | `/GC` | `/CL` | |---|---|---|---|---| | Tick | 0.25 | 0.25 | 0.10 | 0.01 | | Display | 2 dp | 2 dp | 2 dp | 2 dp | | Yahoo | `ES=F` | `NQ=F` | `GC=F` | `CL=F` | | Schwab | `/ES` | `/NQ` | `/GC` | `/CL` | | Globex 18:00–17:00 | yes | yes | yes | yes | | SPY RTH overlay | yes | yes | no | no (NYMEX day 9:00–14:30 ET) | | Options UI | keep | hide or later | hide | hide | NQ is the cheapest second chart. Gold and oil are why tick cannot stay a chart constant. `toFixed(2)` covers all four. --- ## Why the placeholders The current process is one `Runtime`, one Schwab socket, one bar store, one `manual_lines.json`, one alert-state file, one ntfy topic. | File | Today | After step 2 | |---|---|---| | `data/manual_lines.json` | no symbol | each row `symbol: "/ES"` | | `data/alert_state.json` | zones by price | zone + symbol | | `data/events.json` | one log | tagged or filtered by symbol | | `data/user_prefs.json` | global | leave global until the switcher | | `localStorage` | layers, theme | leave until the switcher | Without `symbol` on drawings, a switcher would mix ES lines onto NQ. Without it on alert state, gold 2650 would be silenced by an old ES 2650. Adding the field later is a migration of production JSON. `ConfluenceChart.TICK = 0.25` feeds every snap, keyboard nudge, and the price-alert `step`. Gold cannot ship with that literal. Pulling it into the profile is not scaffolding — it is deleting a lie. Schwab continuous roots (`/ES`, `/NQ`, `/GC`, `/CL`) should auto-resolve the front month the same way `/ES` → `/ESU26`. Verify each with `scripts/check_stream.py` before trusting it. Singular `get_quote("/GC")` is still the equity slash trap; always `get_quotes`. --- ## What stays generic Aggregator, VWAP, daily MAs, prior-day H/L/C (session is shared Globex), WebSocket snapshot shape, drawing tools, Fibonacci, comments, confluence *math*, ntfy, auth. They work if the bars and the profile are the instrument’s. What does not: RTH marks, tick grid, options panel, confluence *score* (28 and the 4h cooldown were calibrated on ES — re-run `scripts/calibrate_alerts.py` per tape before turning confluence on). Yahoo daily bars stay unused. 1h → session 1d, same as ES. --- ## Stay cheap The tick budget is one series: forming tick = candle + price label. A symbol switch is `setBars`. Do not subscribe Schwab to a hidden root. Do not 2× the ~82s Yahoo seed; lazy-load the next instrument on first view. Two symbols on one Schwab socket is unverified. Two *processes* both opening a stream still kick each other off. --- ## What not to do - Do not split `Runtime` or the bar store until the switcher exists. - Do not add a disabled switcher, a second seed, or a second Schwab sub in the placeholder change. - Do not add a second `package.json`, Vue app, or process “for gold.” - Do not leave `TICK = 0.25` and “just chart gold.” - Do not reuse ES confluence calibration. - Do not show SPY RTH on metals or oil. - Do not build gold/oil/NQ options in the same change as the chart. - Do not put two symbols in one `manual_lines.json` without a symbol key. - Do not start two live streams. --- ## Open, before the switcher (not before placeholders) 1. First extra root to prove as a replace: NQ (easy) or GC (forces tick)? 2. Does `/NQ` `/GC` `/CL` on this account stream `delayed: false`? 3. Does each Yahoo `*F` 1h series go back far enough for a daily 200 SMA?