diff --git a/frontend/tracker.html b/frontend/tracker.html
index 9e859ee..0143f91 100644
--- a/frontend/tracker.html
+++ b/frontend/tracker.html
@@ -278,15 +278,41 @@
-
-
-
Watchlist:
-
-
-
- ✕
-
-
+
+
+
+
Watchlist
+
+ Refresh
+
+
+
+
+
+
+
+
+ ✕
+
+
+
+
+
Spot
+
ATM IV
+
+ RR25
+
+
+
+
loading…
+
+
+
+
+
@@ -297,7 +323,11 @@
-
No snapshot data. Enter a symbol and click Load History.
+
+ No snapshot data yet. Click a watchlist card above, or enter a symbol and click Load History .
+
+ Tip: add symbols to the watchlist from the Strategy page's Save to Tracker button.
+
@@ -647,6 +677,8 @@
error: '',
watchlist: [],
+ watchlistData: {},
+ watchlistLoading: false,
_charts: { atmIv: null, rr25: null, fly25: null },
@@ -661,7 +693,13 @@
if (this.snapshots.length) this.$nextTick(() => this.renderCharts());
}
this._loadWatchlist();
- window.addEventListener('storage', (e) => { if (e.key === 'optionsPricer:watchlist') this._loadWatchlist(); });
+ window.addEventListener('storage', (e) => { if (e.key === 'optionsPricer:watchlist') { this._loadWatchlist(); this._loadWatchlistData(); } });
+ // populate the watchlist cards
+ if (this.watchlist.length) this._loadWatchlistData();
+ // if we have nothing loaded yet but a watchlist exists, auto-open the first
+ if (this.snapshots.length === 0 && this.watchlist.length > 0) {
+ this.loadSymbol(this.watchlist[0]);
+ }
},
_loadWatchlist() {
@@ -669,6 +707,38 @@
catch { this.watchlist = []; }
},
+ async _loadWatchlistData() {
+ if (this.watchlist.length === 0) return;
+ this.watchlistLoading = true;
+ try {
+ const data = { ...this.watchlistData };
+ // fetch each symbol's nearest analytics in parallel
+ await Promise.all(this.watchlist.map(async (sym) => {
+ try {
+ const r = await fetch('/api/analytics?symbol=' + encodeURIComponent(sym));
+ if (!r.ok) return;
+ const e = await r.json();
+ const d = e.data ?? e;
+ // skewMetrics is keyed by expiry — pick the nearest
+ const expiries = (d.volSurface?.expiries || Object.keys(d.skewMetrics || {})).sort();
+ const front = expiries[0];
+ const m = (d.skewMetrics || {})[front] || {};
+ data[sym] = {
+ spot: d.spot ?? null,
+ atmIv: m.atmIv ?? d.atmIv ?? null,
+ rr25: m.rr25 ?? null,
+ fly25: m.fly25 ?? null,
+ expiry: front || null,
+ ts: new Date().toISOString(),
+ };
+ } catch {}
+ }));
+ this.watchlistData = data;
+ } finally {
+ this.watchlistLoading = false;
+ }
+ },
+
loadSymbol(s) {
this.symbol = s;
this.fetchExpirations();
@@ -678,6 +748,9 @@
removeWatch(s) {
this.watchlist = this.watchlist.filter(x => x !== s);
try { localStorage.setItem('optionsPricer:watchlist', JSON.stringify(this.watchlist)); } catch {}
+ delete this.watchlistData[s];
+ // keep reactivity by reassigning
+ this.watchlistData = { ...this.watchlistData };
},
_persist() {