fix: login mutex, zero-qty realised PnL, market cache-on-error, UI preserve cards

This commit is contained in:
Manohar 2026-05-12 04:23:50 +00:00
parent 882d55adad
commit 6f06755e11
4 changed files with 18 additions and 10 deletions

View file

@ -6,6 +6,7 @@ import { AngelAuthResponse } from './types.js';
let jwtToken: string | null = null;
let refreshToken: string | null = null;
let tokenExpiry: Date | null = null;
let loginMutex: Promise<void> | null = null; // prevents concurrent logins
const BASE_URL = 'https://apiconnect.angelbroking.com';
@ -87,9 +88,12 @@ export async function login(): Promise<void> {
export async function getToken(): Promise<string> {
const now = new Date();
// No token yet, or past expiry
if (!jwtToken || !tokenExpiry || now >= tokenExpiry) {
await login();
// Mutex: wait for in-progress login instead of starting a second one
if (!loginMutex) {
loginMutex = login().finally(() => { loginMutex = null; });
}
await loginMutex;
}
return jwtToken!;

View file

@ -54,8 +54,9 @@ export async function fetchHoldings(): Promise<AngelHolding[]> {
*/
function normalisePosition(p: AngelPosition): Position | null {
const netqty = parseFloat(p.netqty);
// Skip positions with zero net qty (fully closed intraday)
if (netqty === 0) return null;
const realisedPnl = parseFloat(p.realised) || 0;
// Only skip if qty=0 AND no realised PnL (truly empty row)
if (netqty === 0 && realisedPnl === 0) return null;
const ltp = parseFloat(p.ltp) || 0;
const unrealised = parseFloat(p.unrealised) || 0;

View file

@ -93,13 +93,14 @@ export async function fetchMarketData(): Promise<MarketQuote[]> {
try {
const live = await fetchFromAngel();
if (live.length > 0) {
saveToCache(live); // update cache whenever we get fresh data
saveToCache(live);
return live;
}
// Empty from Angel (market closed) — serve cache
return loadFromCache();
} catch (err) {
console.error('[market] Angel fetch error:', err instanceof Error ? err.message : err);
// Auth/network error — always serve last known cache so UI never goes blank
console.error('[market] fetch error, serving cache:', err instanceof Error ? err.message : err);
return loadFromCache();
}
// Market closed or error — serve cached data
const cached = loadFromCache();
return cached; // empty array if never cached (first run before market open)
}

View file

@ -109,7 +109,9 @@ async function evaluateAlerts(positions: Position[], today: string): Promise<voi
@delta_abs, @delta_pct, @direction, @ltp, @netqty, datetime('now'))
`);
for (const pos of positions) {
// Only evaluate alerts for positions that are still open (netqty != 0)
const openPositions = positions.filter(p => p.netqty !== 0);
for (const pos of openPositions) {
let state = getBandState.get(pos.key) as BandState | undefined;
// First time seeing this position, or new trading day → initialise anchor