From 0a5f2af3ae622d5cd447c61e97ea30c848eafc86 Mon Sep 17 00:00:00 2001 From: multipleof4 Date: Sun, 15 Mar 2026 14:40:32 -0700 Subject: [PATCH] Fix: heartbeat state + startup write --- worker.js | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/worker.js b/worker.js index 6e616d2..ebed9c5 100644 --- a/worker.js +++ b/worker.js @@ -8,6 +8,7 @@ import fs from 'fs'; // Shared state file for the Next.js frontend to read const STATE_FILE = '/tmp/kalbot-state.json'; +const HEARTBEAT_MS = 2000; async function main() { console.log('=== Kalbot Worker Starting ==='); @@ -25,17 +26,24 @@ async function main() { new ThresholdStrategy({ triggerPct: 65, betSize: 1 }) ]; - console.log(`[Worker] Loaded ${strategies.length} strategies: ${strategies.map(s => s.name).join(', ')}`); + console.log(`[Worker] Loaded ${strategies.length} strategies: ${strategies.map((s) => s.name).join(', ')}`); // Initialize market tracker const tracker = new MarketTracker(); + let latestMarketState = null; + let heartbeatTimer = null; + + // Write initial heartbeat immediately so dashboard never shows "Updated: never" + writeState(latestMarketState, paper, strategies); // On every market update, run strategies tracker.on('update', async (state) => { - if (!state) return; + latestMarketState = state || null; // Write state to file for frontend - writeState(state, paper, strategies); + writeState(latestMarketState, paper, strategies); + + if (!state) return; // Run each strategy for (const strategy of strategies) { @@ -53,7 +61,7 @@ async function main() { } // Update state file after potential trades - writeState(state, paper, strategies); + writeState(latestMarketState, paper, strategies); }); // On market settlement, settle paper positions and notify strategies @@ -64,10 +72,9 @@ async function main() { // Notify strategies about settlement for (const strategy of strategies) { - if (settledPositions) { - for (const trade of settledPositions) { - strategy.onSettlement(result, trade); - } + if (!settledPositions) continue; + for (const trade of settledPositions) { + strategy.onSettlement(result, trade); } } @@ -77,33 +84,39 @@ async function main() { 'default', 'chart_with_upwards_trend' ); + + writeState(latestMarketState, paper, strategies); }); // Start tracking await tracker.start(); await notify('🤖 Kalbot Worker started!', 'Kalbot Online', 'low', 'robot,green_circle'); + // Keep state fresh even if market API is temporarily quiet + heartbeatTimer = setInterval(() => { + writeState(latestMarketState, paper, strategies); + }, HEARTBEAT_MS); + console.log('[Worker] Running. Press Ctrl+C to stop.'); - // Graceful shutdown - process.on('SIGINT', async () => { - console.log('\n[Worker] Shutting down...'); + const shutdown = async (signal) => { + console.log(`\n[Worker] ${signal} received. Shutting down...`); + clearInterval(heartbeatTimer); tracker.stop(); await notify('🔴 Kalbot Worker stopped', 'Kalbot Offline', 'high', 'robot,red_circle'); process.exit(0); - }); + }; - process.on('SIGTERM', async () => { - tracker.stop(); - process.exit(0); - }); + // Graceful shutdown + process.on('SIGINT', () => shutdown('SIGINT')); + process.on('SIGTERM', () => shutdown('SIGTERM')); } function writeState(marketState, paper, strategies) { const data = { market: marketState, paper: paper.getStats(), - strategies: strategies.map(s => s.toJSON()), + strategies: strategies.map((s) => s.toJSON()), workerUptime: process.uptime(), lastUpdate: Date.now() };