| Symbol | P&L at Alert | Anchor | Move | LTP | Time (IST) |
| No alerts yet |
@@ -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='| No open positions |
';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''+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)+' |
';}).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=>''+(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)+' |
').join('');
}