From 8cd09d8a0eba5f8cbd2479d406ee1a0705a708fb Mon Sep 17 00:00:00 2001 From: Manohar Date: Wed, 24 Jun 2026 10:07:20 +0530 Subject: [PATCH] =?UTF-8?q?fix:=20three=20issues=20=E2=80=94=20symbol=20pa?= =?UTF-8?q?rser,=20booked=20PnL=20window,=20token=20reauth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. parseOptionSymbol (greeks.ts) Added Format 3 for SENSEX weekly DD+MON+STRIKE symbols (no year). SENSEX26JUN77300PE was returning null — parser read year=26, day=77 giving invalid date 2026-06-77. Now correctly parses as day=26, month=JUN, year=2026 (inferred), strike=77300. 2. Booked PnL window (server.ts) Changed all three booked-PnL queries from 'today IST only' to 'last 7 days'. With a stale Angel One token, positions are not updated today so date(updated_at)=today returns 0 even when real closed-position PnL exists from yesterday. 3. Angel One token expiry UX (server.ts + index.html) - Added POST /api/reauth endpoint — forces a fresh Angel One login without restarting the container - Nav now shows red error text + ⚡ Reauth button whenever lastError is set; clicking reauth calls /api/reauth then re-runs refresh + analysis - Fixed loadHealth() to show/hide the error span (was always hidden due to missing display toggle) - Label changed from 'Booked P&L Today' → 'Booked PnL (7d)' Co-Authored-By: Claude Sonnet 4.6 --- public/index.html | 30 +++++++++++++++++++++++++----- src/api/server.ts | 28 ++++++++++++++++++---------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/public/index.html b/public/index.html index 5557550..7e205fe 100644 --- a/public/index.html +++ b/public/index.html @@ -157,7 +157,8 @@
Open Positions
-
Booked P&L Today
Closed legs realised
+
Booked PnL (7d)
Closed legs realised
Alerts Today
@@ -469,11 +470,30 @@ async function loadHealth(){ const pill=document.getElementById('mkt-pill'); document.getElementById('mkt-lbl').textContent=d.marketOpen?'Market Open':'Market Closed'; pill.className='pill'+(d.marketOpen?' open':''); - const e=document.getElementById('s-err'); - if(d.lastError){e.textContent=d.lastError.error.slice(0,30)+'\u2026';e.style.color='var(--red)';} - else{e.textContent='Healthy';e.style.color='var(--green)';} + const errEl=document.getElementById('s-err'); + const reauthBtn=document.getElementById('reauth-btn'); + if(d.lastError){ + errEl.textContent=d.lastError.error.slice(0,40)+'\u2026'; + errEl.style.display='inline'; + if(reauthBtn)reauthBtn.style.display='inline-block'; + } else { + errEl.textContent='';errEl.style.display='none'; + if(reauthBtn)reauthBtn.style.display='none'; + } document.getElementById('ts').textContent=new Date().toLocaleTimeString('en-IN',{timeZone:'Asia/Kolkata',hour12:false})+' IST'; } + +async function forceReauth(){ + const btn=document.getElementById('reauth-btn'); + if(btn){btn.textContent='Authing\u2026';btn.disabled=true;} + try{ + const r=await fetch('/api/reauth',{method:'POST'}).then(function(x){return x.json();}); + if(r.ok){await refresh();await loadAnalysis();} + else{alert('Reauth failed: '+r.error);} + }catch(e){alert('Reauth error: '+String(e));} + if(btn){btn.textContent='\u26a1 Reauth';btn.disabled=false;} + loadHealth(); +} async function loadConfig(){ const {global:g}=await fetch('/api/config').then(r=>r.json()).catch(()=>({})); if(!g)return; diff --git a/src/api/server.ts b/src/api/server.ts index 3348d45..9901a76 100644 --- a/src/api/server.ts +++ b/src/api/server.ts @@ -86,6 +86,18 @@ export function createServer(): express.Application { res.json({ ok: true, message: 'Refresh complete' }); }); + // ── POST /api/reauth ────────────────────────────────────────────────────── + // Force a fresh Angel One login (use when token has expired) + app.post('/api/reauth', async (_req, res) => { + try { + const { login } = await import('../angel/auth.js'); + await login(); + res.json({ ok: true, message: 'Re-authenticated with Angel One' }); + } catch (err) { + res.status(500).json({ ok: false, error: String(err) }); + } + }); + // ── GET /api/pnl-history ────────────────────────────────────────────────── // Returns P&L snapshots for charting. ?hours=N (default 8, max 72) app.get('/api/pnl-history', (_req, res) => { @@ -105,15 +117,14 @@ export function createServer(): express.Application { FROM positions WHERE is_closed = 0 AND netqty != 0 `).get() as any; - // Booked P&L: fully closed positions TODAY only (netqty=0, updated today IST) + // Booked P&L: fully closed positions in the last 7 days (covers a full trading week) // Separate from open-position realised (partial fills on live legs) const bookedToday = db.prepare(` SELECT COALESCE(SUM(realised_pnl),0) as r FROM positions WHERE netqty = 0 AND realised_pnl != 0 - AND date(updated_at, '+5 hours', '+30 minutes') - = date('now', '+5 hours', '+30 minutes') + AND updated_at >= datetime('now', '-7 days') `).get() as any; // Open-position realised = partial fills on still-open legs (shown separately) @@ -149,8 +160,7 @@ export function createServer(): express.Application { const bookedRealised = (db.prepare(` SELECT COALESCE(SUM(realised_pnl),0) as r FROM positions WHERE netqty = 0 AND realised_pnl != 0 - AND date(updated_at, '+5 hours', '+30 minutes') - = date('now', '+5 hours', '+30 minutes') + AND updated_at >= datetime('now', '-7 days') `).get() as { r: number }).r; res.json({ @@ -166,18 +176,16 @@ export function createServer(): express.Application { // ── GET /api/closed-positions ───────────────────────────────────────────── - // Positions closed TODAY (IST). Groups by expiry so UI can show per-expiry view. + // Positions closed in last 7 days with realised P&L. app.get('/api/closed-positions', (_req, res) => { - const todayIST = "date('now', '+5 hours', '+30 minutes')"; - - // Today's closed positions only — filtered by IST date + // Last 7 days of closed positions (covers a full trading week + weekend buffer) const closed = db.prepare(` SELECT tradingsymbol, exchange, producttype, instrumenttype, netqty, avg_price, ltp, realised_pnl, updated_at FROM positions WHERE realised_pnl != 0 AND netqty = 0 - AND date(updated_at, '+5 hours', '+30 minutes') = date('now', '+5 hours', '+30 minutes') + AND updated_at >= datetime('now', '-7 days') ORDER BY ABS(realised_pnl) DESC `).all();