Fix: heartbeat state + startup write

This commit is contained in:
2026-03-15 14:40:32 -07:00
parent 6921fb1cdd
commit 0a5f2af3ae

View File

@@ -8,6 +8,7 @@ import fs from 'fs';
// Shared state file for the Next.js frontend to read // Shared state file for the Next.js frontend to read
const STATE_FILE = '/tmp/kalbot-state.json'; const STATE_FILE = '/tmp/kalbot-state.json';
const HEARTBEAT_MS = 2000;
async function main() { async function main() {
console.log('=== Kalbot Worker Starting ==='); console.log('=== Kalbot Worker Starting ===');
@@ -25,17 +26,24 @@ async function main() {
new ThresholdStrategy({ triggerPct: 65, betSize: 1 }) 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 // Initialize market tracker
const tracker = new MarketTracker(); 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 // On every market update, run strategies
tracker.on('update', async (state) => { tracker.on('update', async (state) => {
if (!state) return; latestMarketState = state || null;
// Write state to file for frontend // Write state to file for frontend
writeState(state, paper, strategies); writeState(latestMarketState, paper, strategies);
if (!state) return;
// Run each strategy // Run each strategy
for (const strategy of strategies) { for (const strategy of strategies) {
@@ -53,7 +61,7 @@ async function main() {
} }
// Update state file after potential trades // Update state file after potential trades
writeState(state, paper, strategies); writeState(latestMarketState, paper, strategies);
}); });
// On market settlement, settle paper positions and notify strategies // On market settlement, settle paper positions and notify strategies
@@ -64,10 +72,9 @@ async function main() {
// Notify strategies about settlement // Notify strategies about settlement
for (const strategy of strategies) { for (const strategy of strategies) {
if (settledPositions) { if (!settledPositions) continue;
for (const trade of settledPositions) { for (const trade of settledPositions) {
strategy.onSettlement(result, trade); strategy.onSettlement(result, trade);
}
} }
} }
@@ -77,33 +84,39 @@ async function main() {
'default', 'default',
'chart_with_upwards_trend' 'chart_with_upwards_trend'
); );
writeState(latestMarketState, paper, strategies);
}); });
// Start tracking // Start tracking
await tracker.start(); await tracker.start();
await notify('🤖 Kalbot Worker started!', 'Kalbot Online', 'low', 'robot,green_circle'); 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.'); console.log('[Worker] Running. Press Ctrl+C to stop.');
// Graceful shutdown const shutdown = async (signal) => {
process.on('SIGINT', async () => { console.log(`\n[Worker] ${signal} received. Shutting down...`);
console.log('\n[Worker] Shutting down...'); clearInterval(heartbeatTimer);
tracker.stop(); tracker.stop();
await notify('🔴 Kalbot Worker stopped', 'Kalbot Offline', 'high', 'robot,red_circle'); await notify('🔴 Kalbot Worker stopped', 'Kalbot Offline', 'high', 'robot,red_circle');
process.exit(0); process.exit(0);
}); };
process.on('SIGTERM', async () => { // Graceful shutdown
tracker.stop(); process.on('SIGINT', () => shutdown('SIGINT'));
process.exit(0); process.on('SIGTERM', () => shutdown('SIGTERM'));
});
} }
function writeState(marketState, paper, strategies) { function writeState(marketState, paper, strategies) {
const data = { const data = {
market: marketState, market: marketState,
paper: paper.getStats(), paper: paper.getStats(),
strategies: strategies.map(s => s.toJSON()), strategies: strategies.map((s) => s.toJSON()),
workerUptime: process.uptime(), workerUptime: process.uptime(),
lastUpdate: Date.now() lastUpdate: Date.now()
}; };