feat: 30s polling, market open/close Telegram alerts, mobile responsive UI
This commit is contained in:
parent
ea6af0ea82
commit
ed84985237
3 changed files with 87 additions and 1 deletions
|
|
@ -95,6 +95,43 @@
|
||||||
.collapse-body { overflow:hidden; transition:max-height .35s cubic-bezier(.4,0,.2,1); max-height:5000px; }
|
.collapse-body { overflow:hidden; transition:max-height .35s cubic-bezier(.4,0,.2,1); max-height:5000px; }
|
||||||
.collapsible.collapsed .collapse-body { max-height:0 !important; }
|
.collapsible.collapsed .collapse-body { max-height:0 !important; }
|
||||||
|
|
||||||
|
|
||||||
|
/* ── Mobile responsive ── */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.wrap { padding: 12px; }
|
||||||
|
nav { padding: 10px 14px; flex-wrap: wrap; gap: 8px; }
|
||||||
|
.nav-sub { display: none; }
|
||||||
|
.ts { display: none; }
|
||||||
|
.g3, .g6 { grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 10px; }
|
||||||
|
.g6 { grid-template-columns: repeat(3, 1fr); }
|
||||||
|
.pcard { padding: 16px 14px 12px; }
|
||||||
|
.pval { font-size: 1.6rem; }
|
||||||
|
.pcard.pt .pval { font-size: 2rem; }
|
||||||
|
.mcard { padding: 10px 12px; }
|
||||||
|
.mprice { font-size: 1.1rem; }
|
||||||
|
.scard { padding: 10px 12px; }
|
||||||
|
.card-head { padding: 12px 14px; flex-wrap: wrap; gap: 6px; }
|
||||||
|
.chart-wrap { height: 160px; padding: 8px 10px 12px; }
|
||||||
|
.range-btns { gap: 2px; }
|
||||||
|
.rbtn { padding: 2px 7px; font-size: .65rem; }
|
||||||
|
/* Tables: horizontal scroll */
|
||||||
|
.card > .collapse-body { overflow-x: auto; }
|
||||||
|
table { min-width: 520px; }
|
||||||
|
td, thead th { padding: 9px 10px; font-size: .75rem; }
|
||||||
|
.sym { font-size: .72rem; }
|
||||||
|
.sym-meta, .tte { font-size: .6rem; }
|
||||||
|
.mono { font-size: .72rem; }
|
||||||
|
/* Settings */
|
||||||
|
.sg { grid-template-columns: 1fr; gap: 10px; }
|
||||||
|
tfoot td { padding: 8px 10px; font-size: .72rem; }
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.g3 { grid-template-columns: 1fr; }
|
||||||
|
.g6 { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.pval { font-size: 1.4rem; }
|
||||||
|
.pcard.pt .pval { font-size: 1.7rem; }
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import cron from 'node-cron';
|
import cron from 'node-cron';
|
||||||
import { initDb } from './db/client.js';
|
import { initDb } from './db/client.js';
|
||||||
import { login } from './angel/auth.js';
|
import { login } from './angel/auth.js';
|
||||||
import { pollTick, forcePoll } from './tracker/poll.js';
|
import { pollTick, forcePoll, sendMarketOpenAlert, sendPreCloseAlert } from './tracker/poll.js';
|
||||||
import { createServer } from './api/server.js';
|
import { createServer } from './api/server.js';
|
||||||
import { sendServiceNotification } from './notify/telegram.js';
|
import { sendServiceNotification } from './notify/telegram.js';
|
||||||
|
|
||||||
|
|
@ -39,6 +39,12 @@ async function main() {
|
||||||
console.log(`[main] Polling every ${POLL_SECONDS}s`);
|
console.log(`[main] Polling every ${POLL_SECONDS}s`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Market open alert — 9:15 IST = 3:45 UTC
|
||||||
|
cron.schedule("45 3 * * 1-5", sendMarketOpenAlert, { timezone: "UTC" });
|
||||||
|
// Pre-close alert — 3:25 IST = 9:55 UTC
|
||||||
|
cron.schedule("55 9 * * 1-5", sendPreCloseAlert, { timezone: "UTC" });
|
||||||
|
console.log("[main] Market open/close alerts scheduled");
|
||||||
// 6. Notify Telegram that service started
|
// 6. Notify Telegram that service started
|
||||||
await sendServiceNotification('start');
|
await sendServiceNotification('start');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -218,3 +218,46 @@ function recordSnapshot(positions: Position[]): void {
|
||||||
VALUES (?, ?, ?, ?, datetime('now'))
|
VALUES (?, ?, ?, ?, datetime('now'))
|
||||||
`).run(totalUnrealised, totalRealised, totalPnl, positions.length);
|
`).run(totalUnrealised, totalRealised, totalPnl, positions.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Market open alert (9:15 IST) — summary of all positions at open
|
||||||
|
*/
|
||||||
|
export async function sendMarketOpenAlert(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const positions = await fetchAllPositions();
|
||||||
|
if (!positions.length) {
|
||||||
|
await sendTelegram("🔔 *Market Open* — No open positions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const totalPnl = positions.reduce((s, p) => s + p.totalPnl, 0);
|
||||||
|
const lines = positions.map(p =>
|
||||||
|
` • ${p.tradingsymbol}: ₹${p.totalPnl >= 0 ? "+" : ""}${p.totalPnl.toFixed(0)} (qty ${p.netqty})`
|
||||||
|
).join("\n");
|
||||||
|
await sendTelegram(
|
||||||
|
`🔔 *Market Open — Position Summary*\n\n${lines}\n\n*Total P&L: ₹${totalPnl >= 0 ? "+" : ""}${totalPnl.toFixed(0)}*`
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[poll] Market open alert error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Market close warning (3:25 IST) — 5 min before close
|
||||||
|
*/
|
||||||
|
export async function sendPreCloseAlert(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const positions = await fetchAllPositions();
|
||||||
|
const totalUnrealised = positions.reduce((s, p) => s + p.unrealisedPnl, 0);
|
||||||
|
const totalRealised = positions.reduce((s, p) => s + p.realisedPnl, 0);
|
||||||
|
const totalPnl = positions.reduce((s, p) => s + p.totalPnl, 0);
|
||||||
|
await sendTelegram(
|
||||||
|
`⚠️ *5 Min to Close — Review Positions*\n\n` +
|
||||||
|
`Unrealised: ₹${totalUnrealised >= 0 ? "+" : ""}${totalUnrealised.toFixed(0)}\n` +
|
||||||
|
`Realised: ₹${totalRealised >= 0 ? "+" : ""}${totalRealised.toFixed(0)}\n` +
|
||||||
|
`*Total: ₹${totalPnl >= 0 ? "+" : ""}${totalPnl.toFixed(0)}*\n\n` +
|
||||||
|
`${positions.length} open positions — consider closing before 3:30`
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[poll] Pre-close alert error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue