feat: forcePoll at startup + POST /api/refresh (bypasses market hours)

This commit is contained in:
Manohar 2026-05-08 16:44:11 +00:00
parent a0aede9294
commit 60e5c7647d
3 changed files with 44 additions and 2 deletions

View file

@ -78,6 +78,14 @@ export function createServer(): express.Application {
});
// ── GET /api/health ───────────────────────────────────────────────────────
// ── POST /api/refresh ─────────────────────────────────────────────────────
// Force-fetch positions right now (works outside market hours)
app.post('/api/refresh', async (_req, res) => {
const { forcePoll } = await import('../tracker/poll.js');
await forcePoll();
res.json({ ok: true, message: 'Refresh complete' });
});
app.get('/api/health', (_req, res) => {
const lastError = db.prepare(
`SELECT error, occurred_at FROM poll_errors ORDER BY occurred_at DESC LIMIT 1`

View file

@ -2,7 +2,7 @@
import cron from 'node-cron';
import { initDb } from './db/client.js';
import { login } from './angel/auth.js';
import { pollTick } from './tracker/poll.js';
import { pollTick, forcePoll } from './tracker/poll.js';
import { createServer } from './api/server.js';
import { sendServiceNotification } from './notify/telegram.js';
@ -25,7 +25,7 @@ async function main() {
});
// 4. Run first poll immediately so dashboard shows data on startup
await pollTick();
await forcePoll();
// 5. Schedule recurring poll
// node-cron doesn't support sub-minute; for 60s we use a cron expression.

View file

@ -169,3 +169,37 @@ async function evaluateAlerts(positions: Position[], today: string): Promise<voi
}
}
}
/**
* Force a poll regardless of market hours.
* Used at startup and for manual refresh via API.
*/
export async function forcePoll(): Promise<void> {
const today = todayIST();
try {
const positions = await fetchAllPositions();
const upsertPos = db.prepare(`
INSERT INTO positions (key, exchange, tradingsymbol, instrumenttype, producttype,
netqty, ltp, avg_price, unrealised_pnl, realised_pnl, total_pnl, source, updated_at)
VALUES (@key, @exchange, @tradingsymbol, @instrumenttype, @producttype,
@netqty, @ltp, @avg_price, @unrealised_pnl, @realised_pnl, @total_pnl, @source, datetime('now'))
ON CONFLICT(key) DO UPDATE SET
netqty = excluded.netqty, ltp = excluded.ltp, avg_price = excluded.avg_price,
unrealised_pnl = excluded.unrealised_pnl, realised_pnl = excluded.realised_pnl,
total_pnl = excluded.total_pnl, updated_at = excluded.updated_at
`);
for (const pos of positions) {
upsertPos.run({ key: pos.key, exchange: pos.exchange, tradingsymbol: pos.tradingsymbol,
instrumenttype: pos.instrumenttype, producttype: pos.producttype, netqty: pos.netqty,
ltp: pos.ltp, avg_price: pos.avgPrice, unrealised_pnl: pos.unrealisedPnl,
realised_pnl: pos.realisedPnl, total_pnl: pos.totalPnl, source: pos.source });
}
const activeKeys = positions.map(p => p.key);
if (activeKeys.length > 0) {
db.prepare(`UPDATE positions SET is_closed = 1 WHERE key NOT IN (${activeKeys.map(() => "?").join(",")})`).run(...activeKeys);
}
console.log(`[poll] Force fetch: ${positions.length} positions`);
} catch (err) {
console.error(`[poll] Force fetch error: ${err instanceof Error ? err.message : err}`);
}
}