fix: market fixed 6-slot order always; collapsed header shows P&L + alert count
This commit is contained in:
parent
ed84985237
commit
7cf1de885e
1 changed files with 38 additions and 9 deletions
|
|
@ -183,7 +183,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card collapsible" id="sec-pos" style="margin-bottom:16px">
|
||||
<div class="card-head" onclick="toggleCard('sec-pos')"><span class="card-title">Option Positions</span><div class="live-badge"><span class="ldot"></span>Live</div><span class="collapse-arrow">▼</span></div>
|
||||
<div class="card-head" onclick="toggleCard('sec-pos')"><span class="card-title">Option Positions</span><div style="display:flex;align-items:center;gap:10px"><div class="live-badge"><span class="ldot"></span>Live</div><span id="pos-hpnl" style="font-family:'Geist Mono',monospace;font-size:.75rem;font-weight:600;display:none"></span></div><span class="collapse-arrow">▼</span></div>
|
||||
<div class="collapse-body"><table>
|
||||
<thead><tr><th>Symbol</th><th>Qty</th><th>LTP</th><th>Avg Cost</th><th>Unrealised</th><th>Realised</th><th>Total P&L</th></tr></thead>
|
||||
<tbody id="pos-body"><tr><td colspan="7" style="text-align:center;padding:36px;color:var(--text3)">Loading…</td></tr></tbody>
|
||||
|
|
@ -191,7 +191,7 @@
|
|||
</table></div>
|
||||
</div>
|
||||
<div class="card collapsible" id="sec-alrt" style="margin-bottom:16px">
|
||||
<div class="card-head" onclick="toggleCard('sec-alrt')"><span class="card-title">Alert History</span><span class="collapse-arrow">▼</span></div>
|
||||
<div class="card-head" onclick="toggleCard('sec-alrt')"><span class="card-title">Alert History</span><div style="display:flex;align-items:center;gap:8px"><span id="alrt-hcount" style="font-size:.7rem;font-weight:600;padding:2px 9px;border-radius:9999px;background:rgba(245,166,35,.15);color:var(--amber);display:none"></span><span id="alrt-hmuted" style="font-size:.7rem;font-weight:600;padding:2px 9px;border-radius:9999px;background:rgba(231,76,60,.12);color:var(--red);display:none">MUTED</span></div><span class="collapse-arrow">▼</span></div>
|
||||
<div class="collapse-body"><table>
|
||||
<thead><tr><th>Symbol</th><th>P&L at Alert</th><th>Anchor</th><th>Move</th><th>LTP</th><th>Time (IST)</th></tr></thead>
|
||||
<tbody id="alrt-body"><tr><td colspan="6" style="text-align:center;padding:28px;color:var(--text3)">No alerts yet</td></tr></tbody>
|
||||
|
|
@ -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='<div class="card mcard" style="grid-column:1/-1;text-align:center;padding:14px;color:var(--text3)">Market data unavailable</div>';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'<div class="card mcard"><div class="mlbl">'+q.label+'</div><div class="mprice">'+price+' <span style="font-size:.62rem;color:var(--text2)">'+q.unit+'</span></div><div class="mchg '+cl+'">'+arrow+' '+Math.abs(q.changePct).toFixed(2)+'% <span style="opacity:.6">'+(q.change>=0?'+':'')+q.change.toFixed(0)+'</span></div>'+(q.stale?'<div class="mstale">prev close'+(q.cachedAt?' \u00b7 '+q.cachedAt.slice(11,16)+' UTC':'')+'</div>':'')+'</div>';}).join('');
|
||||
grid.innerHTML=SLOTS.map(slot=>{
|
||||
const q=map[slot.key];
|
||||
if(!q){
|
||||
// Placeholder — keep slot, show last known or dash
|
||||
return '<div class="card mcard"><div class="mlbl">'+slot.label+'</div><div class="mprice" style="color:var(--text3)">\u2014</div><div class="mchg" style="color:var(--text3)">no data</div></div>';
|
||||
}
|
||||
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 '<div class="card mcard"><div class="mlbl">'+slot.label+'</div>'+
|
||||
'<div class="mprice">'+price+' <span style="font-size:.62rem;color:var(--text2)">'+slot.unit+'</span></div>'+
|
||||
'<div class="mchg '+cl+'">'+arrow+' '+Math.abs(q.changePct).toFixed(2)+'% <span style="opacity:.6">'+(q.change>=0?'+':'')+q.change.toFixed(0)+'</span></div>'+
|
||||
(q.stale?'<div class="mstale">prev close'+(q.cachedAt?' \u00b7 '+q.cachedAt.slice(11,16)+' UTC':'')+'</div>':'')+'</div>';
|
||||
}).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='<tr><td colspan="7" style="text-align:center;padding:36px;color:var(--text3)">No open positions</td></tr>';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'<tr><td><div class="sym">'+p.tradingsymbol+'</div><div class="sym-meta">'+p.exchange+' \u00b7 '+p.producttype+'</div>'+(tte?'<div class="tte">'+tte+'</div>':'')+'</td><td class="'+(p.netqty<0?'qs':'ql')+'">'+(p.netqty>0?'+':'')+p.netqty+'</td><td class="mono">\u20b9'+(+p.ltp).toFixed(2)+'</td><td class="mono" style="color:var(--text2)">\u20b9'+(+p.avg_price).toFixed(2)+'</td><td class="mono '+cls(p.unrealised_pnl)+'">'+fmt(p.unrealised_pnl)+'</td><td class="mono '+cls(p.realised_pnl)+'">'+fmt(p.realised_pnl)+'</td><td class="mono '+cls(p.total_pnl)+'" style="font-weight:600">'+fmt(p.total_pnl)+'</td></tr>';}).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=>'<tr class="'+(a.direction==='up'?'alert-up':'alert-dn')+'"><td><div class="sym" style="font-size:.75rem">'+(a.direction==='up'?'\u25b2':'\u25bc')+' '+a.tradingsymbol+'</div></td><td class="mono '+cls(a.current_pnl)+'">'+fmt(a.current_pnl)+'</td><td class="mono" style="color:var(--text2)">'+fmt(a.anchor_pnl)+'</td><td class="mono '+cls(a.delta_abs)+'">'+fmt(a.delta_abs)+' <span style="opacity:.6;font-size:.72em">'+(a.delta_pct>0?'+':'')+parseFloat(a.delta_pct).toFixed(1)+'%</span></td><td class="mono">\u20b9'+parseFloat(a.ltp).toFixed(2)+'</td><td style="color:var(--text3);font-family:\'Geist Mono\',monospace;font-size:.72rem">'+toIST(a.alerted_at)+'</td></tr>').join('');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue