From 7cf1de885ebd0991392518a4bf7faf2b6678143d Mon Sep 17 00:00:00 2001 From: Manohar Date: Mon, 11 May 2026 04:54:16 +0000 Subject: [PATCH] fix: market fixed 6-slot order always; collapsed header shows P&L + alert count --- public/index.html | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/public/index.html b/public/index.html index 72b1682..b820303 100644 --- a/public/index.html +++ b/public/index.html @@ -183,7 +183,7 @@
-
Option Positions
Live
+
Option Positions
Live
@@ -191,7 +191,7 @@
SymbolQtyLTPAvg CostUnrealisedRealisedTotal P&L
Loading…
-
Alert History
+
Alert History
@@ -253,13 +253,29 @@ async function loadChart(h=8){ async function loadMarket(){ const {ok,data=[]}=await fetch('/api/market').then(r=>r.json()).catch(()=>({ok:false,data:[]})); const grid=document.getElementById('market-grid'); - if(!ok||!data.length){grid.innerHTML='
Market data unavailable
';return;} - const ORDER=['SENSEX','NIFTY50','BANKNIFTY','INDIAVIX','CRUDEOIL','USDINR']; + // Fixed labels for all 6 slots — always render in this order even if data missing + const SLOTS=[ + {key:'SENSEX', label:'SENSEX', unit:'pts'}, + {key:'NIFTY50', label:'NIFTY 50', unit:'pts'}, + {key:'BANKNIFTY',label:'BANK NIFTY', unit:'pts'}, + {key:'INDIAVIX', label:'INDIA VIX', unit:'%'}, + {key:'CRUDEOIL', label:'Crude Oil', unit:'\u20b9/bbl'}, + {key:'USDINR', label:'INR/USD', unit:'\u20b9'}, + ]; const map=Object.fromEntries(data.map(q=>[q.key,q])); - const sorted=ORDER.map(k=>map[k]).filter(Boolean); - // append any keys not in ORDER - data.forEach(q=>{if(!ORDER.includes(q.key))sorted.push(q);}); - grid.innerHTML=sorted.map(q=>{const up=q.changePct>=0,cl=up?'up':'dn',arrow=up?'\u25b2':'\u25bc';const price=q.price>10000?q.price.toLocaleString('en-IN',{maximumFractionDigits:0}):q.price.toFixed(2);return'
'+q.label+'
'+price+' '+q.unit+'
'+arrow+' '+Math.abs(q.changePct).toFixed(2)+'% '+(q.change>=0?'+':'')+q.change.toFixed(0)+'
'+(q.stale?'
prev close'+(q.cachedAt?' \u00b7 '+q.cachedAt.slice(11,16)+' UTC':'')+'
':'')+'
';}).join(''); + grid.innerHTML=SLOTS.map(slot=>{ + const q=map[slot.key]; + if(!q){ + // Placeholder — keep slot, show last known or dash + return '
'+slot.label+'
\u2014
no data
'; + } + const up=q.changePct>=0,cl=up?'up':'dn',arrow=up?'\u25b2':'\u25bc'; + const price=q.price>10000?q.price.toLocaleString('en-IN',{maximumFractionDigits:0}):q.price.toFixed(2); + return '
'+slot.label+'
'+ + '
'+price+' '+slot.unit+'
'+ + '
'+arrow+' '+Math.abs(q.changePct).toFixed(2)+'% '+(q.change>=0?'+':'')+q.change.toFixed(0)+'
'+ + (q.stale?'
prev close'+(q.cachedAt?' \u00b7 '+q.cachedAt.slice(11,16)+' UTC':'')+'
':'')+'
'; + }).join(''); } async function loadPositions(){ const {data}=await fetch('/api/positions').then(r=>r.json()); @@ -267,6 +283,9 @@ async function loadPositions(){ const tbody=document.getElementById('pos-body'),tfoot=document.getElementById('pos-foot'); if(!open.length){tbody.innerHTML='';tfoot.style.display='none';return;} const sU=open.reduce((s,p)=>s+p.unrealised_pnl,0),sR=open.reduce((s,p)=>s+p.realised_pnl,0),sT=open.reduce((s,p)=>s+p.total_pnl,0); + // Update collapsed header badge + const phEl=document.getElementById('pos-hpnl'); + if(phEl){phEl.textContent=fmt(sT);phEl.className='';phEl.style.cssText='font-family:Geist Mono,monospace;font-size:.75rem;font-weight:600;display:inline;color:'+(sT>50?'var(--green)':sT<-50?'var(--red)':'var(--text2)');} tbody.innerHTML=open.map(p=>{const tte=parseTTE(p.tradingsymbol);return'';}).join(''); tfoot.style.display=''; const sf=(id,v)=>{const e=document.getElementById(id);e.textContent=fmt(v);e.className='mono '+cls(v)};sf('f-u',sU);sf('f-r',sR);sf('f-t',sT); @@ -275,7 +294,17 @@ async function loadPositions(){ async function loadAlerts(){ const {data}=await fetch('/api/alerts?limit=20').then(r=>r.json()); const today=new Date().toISOString().slice(0,10); - document.getElementById('s-alrt').textContent=data.filter(a=>a.alerted_at.startsWith(today)).length; + const todayCnt=data.filter(a=>a.alerted_at.startsWith(today)).length; + document.getElementById('s-alrt').textContent=todayCnt; + // Update collapsed header badge + const ahEl=document.getElementById('alrt-hcount'); + if(ahEl){if(todayCnt>0){ahEl.textContent=todayCnt+' today';ahEl.style.display='inline';}else{ahEl.style.display='none';}} + // Check if any position is muted + fetch('/api/config').then(r=>r.json()).then(cfg=>{ + const muted=(cfg.overrides||[]).some(o=>o.muted_until&&new Date(o.muted_until)>new Date()); + const mEl=document.getElementById('alrt-hmuted'); + if(mEl)mEl.style.display=muted?'inline':'none'; + }).catch(()=>{}); if(!data.length)return; document.getElementById('alrt-body').innerHTML=data.map(a=>'').join(''); }
SymbolP&L at AlertAnchorMoveLTPTime (IST)
No alerts yet
No open positions
'+p.tradingsymbol+'
'+p.exchange+' \u00b7 '+p.producttype+'
'+(tte?'
'+tte+'
':'')+'
'+(p.netqty>0?'+':'')+p.netqty+'\u20b9'+(+p.ltp).toFixed(2)+'\u20b9'+(+p.avg_price).toFixed(2)+''+fmt(p.unrealised_pnl)+''+fmt(p.realised_pnl)+''+fmt(p.total_pnl)+'
'+(a.direction==='up'?'\u25b2':'\u25bc')+' '+a.tradingsymbol+'
'+fmt(a.current_pnl)+''+fmt(a.anchor_pnl)+''+fmt(a.delta_abs)+' '+(a.delta_pct>0?'+':'')+parseFloat(a.delta_pct).toFixed(1)+'%\u20b9'+parseFloat(a.ltp).toFixed(2)+''+toIST(a.alerted_at)+'