Files
KalBot/worker.js

122 lines
3.4 KiB
JavaScript

import { MarketTracker } from './lib/market/tracker.js';
import { PaperEngine } from './lib/paper/engine.js';
import { MartingaleStrategy } from './lib/strategies/martingale.js';
import { ThresholdStrategy } from './lib/strategies/threshold.js';
import { db } from './lib/db.js';
import { notify } from './lib/notify.js';
import fs from 'fs';
// Shared state file for the Next.js frontend to read
const STATE_FILE = '/tmp/kalbot-state.json';
async function main() {
console.log('=== Kalbot Worker Starting ===');
// Connect to SurrealDB
await db.connect();
// Initialize paper engine
const paper = new PaperEngine(1000);
await paper.init();
// Initialize strategies
const strategies = [
new MartingaleStrategy({ threshold: 70, baseBet: 1, maxDoublings: 5 }),
new ThresholdStrategy({ triggerPct: 65, betSize: 1 })
];
console.log(`[Worker] Loaded ${strategies.length} strategies: ${strategies.map(s => s.name).join(', ')}`);
// Initialize market tracker
const tracker = new MarketTracker();
// On every market update, run strategies
tracker.on('update', async (state) => {
if (!state) return;
// Write state to file for frontend
writeState(state, paper, strategies);
// Run each strategy
for (const strategy of strategies) {
if (!strategy.enabled) continue;
const signal = strategy.evaluate(state);
if (signal) {
console.log(`[Worker] Signal from ${strategy.name}: ${signal.side} @ ${signal.price}¢ — ${signal.reason}`);
if (strategy.mode === 'paper') {
await paper.executeTrade(signal, state);
}
// TODO: Live mode — use placeOrder() from rest.js
}
}
// Update state file after potential trades
writeState(state, paper, strategies);
});
// On market settlement, settle paper positions and notify strategies
tracker.on('settled', async ({ ticker, result }) => {
console.log(`[Worker] Market ${ticker} settled: ${result}`);
const settledPositions = await paper.settle(ticker, result);
// Notify strategies about settlement
for (const strategy of strategies) {
if (settledPositions) {
for (const trade of settledPositions) {
strategy.onSettlement(result, trade);
}
}
}
await notify(
`Market ${ticker} settled: ${result?.toUpperCase() || 'unknown'}`,
'Market Settled',
'default',
'chart_with_upwards_trend'
);
});
// Start tracking
await tracker.start();
await notify('🤖 Kalbot Worker started!', 'Kalbot Online', 'low', 'robot,green_circle');
console.log('[Worker] Running. Press Ctrl+C to stop.');
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\n[Worker] Shutting down...');
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);
});
}
function writeState(marketState, paper, strategies) {
const data = {
market: marketState,
paper: paper.getStats(),
strategies: strategies.map(s => s.toJSON()),
workerUptime: process.uptime(),
lastUpdate: Date.now()
};
try {
fs.writeFileSync(STATE_FILE, JSON.stringify(data));
} catch (e) {
// Non-critical
}
}
main().catch((err) => {
console.error('[Worker] Fatal error:', err);
process.exit(1);
});