[object Object]

← back to Stock Vshape Viewer

Include ETFs in scan (48 major ETFs: SPY/QQQ/sector SPDRs/ARKK/etc, high scan priority) + Reset watchlist/Reset all buttons + fix cached-universe filter dropping price-0 entries

27672c829fddca7af0b99ab797a9921dd3618ed8 · 2026-08-27 11:31:45 -0700 · Steve

Files touched

Diff

commit 27672c829fddca7af0b99ab797a9921dd3618ed8
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 27 11:31:45 2026 -0700

    Include ETFs in scan (48 major ETFs: SPY/QQQ/sector SPDRs/ARKK/etc, high scan priority) + Reset watchlist/Reset all buttons + fix cached-universe filter dropping price-0 entries
---
 public/app.js     | 18 +++++++++++++++++-
 public/index.html |  4 ++++
 server.js         |  6 +++++-
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/public/app.js b/public/app.js
index e225fdd..c393a25 100644
--- a/public/app.js
+++ b/public/app.js
@@ -507,7 +507,9 @@ function wireFilters() {
     el.onchange = () => { state.filters[key] = el.checked; save(LS.filters, state.filters); applyFilters(); renderWatchlist(); };
   }
   document.getElementById('reset-filters').onclick = () => {
-    state.filters = { ...window.DEFAULT_FILTERS }; save(LS.filters, state.filters); syncFilterUI(); process();
+    state.filters = { ...window.DEFAULT_FILTERS }; save(LS.filters, state.filters);
+    document.querySelectorAll('#presets .p').forEach(el => el.classList.remove('on'));
+    syncFilterUI(); syncOptUI(); updateCriteriaChips(); process();
   };
 }
 function updateCriteriaChips() {
@@ -668,6 +670,20 @@ function wireScanAndOptions() {
   });
   // lazy-load options when the detail toggle is clicked
   document.getElementById('d-opttoggle').onclick = () => { if (state.selected) loadOptions(state.selected); };
+  // reset buttons
+  document.getElementById('reset-watchlist').onclick = () => {
+    state.tickers = window.SEED.map(s => s.ticker); save(LS.tickers, state.tickers);
+    if (state.selected && !state.tickers.includes(state.selected)) clearSelect();
+    loadAll();
+  };
+  document.getElementById('reset-all').onclick = () => {
+    state.filters = { ...window.DEFAULT_FILTERS }; state.tfN = 126; state.tickers = window.SEED.map(s => s.ticker);
+    save(LS.filters, state.filters); save(LS.tf, state.tfN); save(LS.tickers, state.tickers);
+    document.querySelectorAll('#presets .p').forEach(el => el.classList.remove('on'));
+    document.getElementById('scan-results').innerHTML = ''; document.getElementById('scan-prog').innerHTML = '';
+    clearSelect(); syncFilterUI(); syncOptUI(); updateCriteriaChips();
+    loadAll();
+  };
 }
 function syncOptUI() {
   document.getElementById('f-optMin').value = state.filters.optMin;
diff --git a/public/index.html b/public/index.html
index e5e5b06..7d5aa29 100644
--- a/public/index.html
+++ b/public/index.html
@@ -188,6 +188,10 @@
         <button class="btn" id="add-btn">+ Add</button>
       </div>
       <div class="wl" id="wl"></div>
+      <div style="display:flex;gap:6px;margin-top:8px">
+        <button class="btn" id="reset-watchlist" style="flex:1">↺ Watchlist</button>
+        <button class="btn" id="reset-all" style="flex:1">↺ Reset all</button>
+      </div>
     </div>
 
     <div class="sec">
diff --git a/server.js b/server.js
index b8c19cf..042ce53 100644
--- a/server.js
+++ b/server.js
@@ -21,6 +21,7 @@ const JOBS = new Map();
 const UA = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', 'Accept': 'application/json' };
 
 const DJIA = ['AAPL','AMGN','AXP','BA','CAT','CRM','CSCO','CVX','DIS','DOW','GS','HD','HON','IBM','JNJ','JPM','KO','MCD','MMM','MRK','MSFT','NKE','NVDA','PG','SHW','TRV','UNH','V','VZ','WMT'];
+const MAJOR_ETFS = new Set(['SPY','QQQ','IWM','DIA','VOO','VTI','VEA','VWO','IVV','VUG','VTV','VIG','SCHD','SMH','SOXX','XLK','XLF','XLE','XLV','XLY','XLP','XLI','XLU','XLB','XLRE','XLC','ARKK','ARKG','XBI','IBB','GLD','SLV','TLT','HYG','LQD','GDX','KRE','ITB','XHB','VNQ','EEM','EFA','IEMG','QUAL','MTUM','TQQQ','SQQQ','SOXL']);
 
 const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json; charset=utf-8' };
 function unauthorized(res) { res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="stock-vshape"' }); res.end('Auth required'); }
@@ -56,7 +57,7 @@ async function getHistory(t) {
 
 // ---------------- universe (NASDAQ screener + DJIA) ----------------
 async function getUniverse(minPrice) {
-  if (UNIV && Date.now() - UNIV_TS < UTTL) return UNIV.filter(u => u.price >= minPrice);
+  if (UNIV && Date.now() - UNIV_TS < UTTL) return UNIV.filter(u => u.price >= minPrice || u.cap === Infinity);
   const r = await fetch('https://api.nasdaq.com/api/screener/stocks?tableonly=true&limit=6000&offset=0&exchange=NASDAQ', { headers: { ...UA, 'Accept-Language': 'en-US,en;q=0.9' } });
   let rows = [];
   if (r.ok) { const j = await r.json(); rows = j?.data?.table?.rows || j?.data?.rows || []; }
@@ -69,6 +70,9 @@ async function getUniverse(minPrice) {
     seen.add(sym); list.push({ ticker: sym, price, cap, name: row.name || sym });
   }
   for (const d of DJIA) if (!seen.has(d)) { seen.add(d); list.push({ ticker: d, price: 0, cap: Infinity, name: d + ' (DJIA)' }); }
+  // major ETFs — added explicitly (the ETF screener ignores limit + omits SPY/QQQ);
+  // price 0 here, real price comes from history during the scan.
+  for (const e of MAJOR_ETFS) if (!seen.has(e)) { seen.add(e); list.push({ ticker: e, price: 0, cap: Infinity, name: e + ' (ETF)', isEtf: true }); }
   list.sort((a, b) => b.cap - a.cap);
   UNIV = list; UNIV_TS = Date.now();
   return list.filter(u => u.price >= minPrice || u.cap === Infinity);

← d409d10 10 preloaded strategy presets (one-click screens: Deep V-Rec  ·  back to Stock Vshape Viewer  ·  5x report: six-way core PASS all 3 browsers, 0 console error fdcc20e →