mirror of
https://github.com/multipleof4/KalBot.git
synced 2026-03-17 05:51:02 +00:00
Fix: reconcile delayed live settlements after rotation
This commit is contained in:
57
worker.js
57
worker.js
@@ -16,10 +16,12 @@ const BALANCE_POLL_MS = 30000;
|
|||||||
|
|
||||||
let isSettling = false;
|
let isSettling = false;
|
||||||
async function lockSettling() {
|
async function lockSettling() {
|
||||||
while (isSettling) await new Promise(r => setTimeout(r, 50));
|
while (isSettling) await new Promise((r) => setTimeout(r, 50));
|
||||||
isSettling = true;
|
isSettling = true;
|
||||||
}
|
}
|
||||||
function unlockSettling() { isSettling = false; }
|
function unlockSettling() {
|
||||||
|
isSettling = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('=== Kalbot Worker Starting ===');
|
console.log('=== Kalbot Worker Starting ===');
|
||||||
@@ -35,7 +37,7 @@ async function main() {
|
|||||||
// Dynamically load all strategies
|
// Dynamically load all strategies
|
||||||
const strategies = [];
|
const strategies = [];
|
||||||
const strategiesDir = path.join(__dirname, 'lib', 'strategies');
|
const strategiesDir = path.join(__dirname, 'lib', 'strategies');
|
||||||
const stratFiles = fs.readdirSync(strategiesDir).filter(f => f.endsWith('.js') && f !== 'base.js');
|
const stratFiles = fs.readdirSync(strategiesDir).filter((f) => f.endsWith('.js') && f !== 'base.js');
|
||||||
|
|
||||||
for (const file of stratFiles) {
|
for (const file of stratFiles) {
|
||||||
try {
|
try {
|
||||||
@@ -56,13 +58,17 @@ async function main() {
|
|||||||
paper._getAccount(s.name);
|
paper._getAccount(s.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
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(', ')}`);
|
||||||
|
|
||||||
let latestMarketState = null;
|
let latestMarketState = null;
|
||||||
|
|
||||||
async function pollBalance() {
|
async function pollBalance() {
|
||||||
try { await live.fetchBalance(); } catch {}
|
try {
|
||||||
try { await live.fetchPositions(); } catch {}
|
await live.fetchBalance();
|
||||||
|
} catch {}
|
||||||
|
try {
|
||||||
|
await live.fetchPositions();
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
await pollBalance();
|
await pollBalance();
|
||||||
setInterval(pollBalance, BALANCE_POLL_MS);
|
setInterval(pollBalance, BALANCE_POLL_MS);
|
||||||
@@ -72,7 +78,10 @@ async function main() {
|
|||||||
await lockSettling();
|
await lockSettling();
|
||||||
try {
|
try {
|
||||||
const { settled, expired } = await paper.checkOrphans(getMarket);
|
const { settled, expired } = await paper.checkOrphans(getMarket);
|
||||||
const allResolved = [...settled, ...expired];
|
const settledLive = await live.checkOrphans(getMarket);
|
||||||
|
|
||||||
|
const allResolved = [...settled, ...expired, ...settledLive];
|
||||||
|
|
||||||
if (allResolved.length > 0) {
|
if (allResolved.length > 0) {
|
||||||
for (const strategy of strategies) {
|
for (const strategy of strategies) {
|
||||||
for (const trade of allResolved) {
|
for (const trade of allResolved) {
|
||||||
@@ -91,7 +100,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await processOrphans();
|
await processOrphans();
|
||||||
setInterval(processOrphans, 60000);
|
setInterval(processOrphans, 10000);
|
||||||
|
|
||||||
const tracker = new MarketTracker();
|
const tracker = new MarketTracker();
|
||||||
let heartbeatTimer = null;
|
let heartbeatTimer = null;
|
||||||
@@ -121,7 +130,9 @@ async function main() {
|
|||||||
if (live.isStrategyEnabled(strategy.name) && !live.hasOpenPositionForStrategy(strategy.name)) {
|
if (live.isStrategyEnabled(strategy.name) && !live.hasOpenPositionForStrategy(strategy.name)) {
|
||||||
const liveSignal = strategy.evaluate(state, 'live');
|
const liveSignal = strategy.evaluate(state, 'live');
|
||||||
if (liveSignal) {
|
if (liveSignal) {
|
||||||
console.log(`[Worker] LIVE signal from ${strategy.name}: ${liveSignal.side} @ ${liveSignal.price}¢ (max: ${liveSignal.maxPrice}¢)`);
|
console.log(
|
||||||
|
`[Worker] LIVE signal from ${strategy.name}: ${liveSignal.side} @ ${liveSignal.price}¢ (max: ${liveSignal.maxPrice}¢)`
|
||||||
|
);
|
||||||
await live.executeTrade(liveSignal, state);
|
await live.executeTrade(liveSignal, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,16 +159,30 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const settledLive = await live.settle(ticker, result);
|
const settledLive = await live.settle(ticker, result);
|
||||||
|
if (settledLive) {
|
||||||
|
for (const strategy of strategies) {
|
||||||
|
for (const trade of settledLive) {
|
||||||
|
strategy.onSettlement(trade.result, trade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (settledPaper || settledLive) {
|
if (settledPaper || settledLive) {
|
||||||
await notify(
|
await notify(
|
||||||
`Market ${ticker} settled: ${result.toUpperCase()}`,
|
`Market ${ticker} settled: ${result.toUpperCase()}`,
|
||||||
'Market Settled', 'default', 'chart_with_upwards_trend'
|
'Market Settled',
|
||||||
|
'default',
|
||||||
|
'chart_with_upwards_trend'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
unlockSettling();
|
unlockSettling();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Market rotated but result not posted yet. Recover via orphan reconciliation shortly.
|
||||||
|
setTimeout(() => {
|
||||||
|
processOrphans().catch((e) => console.error('[Worker] delayed orphan reconcile failed:', e.message));
|
||||||
|
}, 4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeState(latestMarketState, paper, live, strategies);
|
writeState(latestMarketState, paper, live, strategies);
|
||||||
@@ -255,7 +280,7 @@ function writeState(marketState, paper, live, strategies) {
|
|||||||
market: marketState ? { ...marketState, orderbook: undefined } : null,
|
market: marketState ? { ...marketState, orderbook: undefined } : null,
|
||||||
paper: paper.getStats(),
|
paper: paper.getStats(),
|
||||||
paperByStrategy: paper.getPerStrategyStats(),
|
paperByStrategy: paper.getPerStrategyStats(),
|
||||||
strategies: strategies.map(s => s.toJSON()),
|
strategies: strategies.map((s) => s.toJSON()),
|
||||||
workerUptime: process.uptime(),
|
workerUptime: process.uptime(),
|
||||||
lastUpdate: Date.now()
|
lastUpdate: Date.now()
|
||||||
};
|
};
|
||||||
@@ -263,13 +288,17 @@ function writeState(marketState, paper, live, strategies) {
|
|||||||
const liveData = {
|
const liveData = {
|
||||||
market: marketState ? { ...marketState, orderbook: undefined } : null,
|
market: marketState ? { ...marketState, orderbook: undefined } : null,
|
||||||
live: live.getStats(),
|
live: live.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()
|
||||||
};
|
};
|
||||||
|
|
||||||
try { fs.writeFileSync(STATE_FILE, JSON.stringify(paperData)); } catch {}
|
try {
|
||||||
try { fs.writeFileSync(LIVE_STATE_FILE, JSON.stringify(liveData)); } catch {}
|
fs.writeFileSync(STATE_FILE, JSON.stringify(paperData));
|
||||||
|
} catch {}
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(LIVE_STATE_FILE, JSON.stringify(liveData));
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user