function addTradingDays(date, days) { const value = new Date(`${date}T00:00:00Z`); let remaining = Math.max(0, Number(days) || 0); while (remaining > 0) { value.setUTCDate(value.getUTCDate() + 1); const weekday = value.getUTCDay(); if (weekday !== 0 && weekday !== 6) remaining -= 1; } return value.toISOString().slice(0, 10); } function nearestOnOrAfter(history, date) { return history.find((row) => row.date >= date)?.close ?? null; } function calculateOutcome(prediction, instrumentHistory, benchmarkHistory) { const eventDate = String(prediction.information_cutoff).slice(0, 10); const horizonDate = addTradingDays(eventDate, prediction.horizon_days); const price0 = nearestOnOrAfter(instrumentHistory, eventDate); const priceHorizon = nearestOnOrAfter(instrumentHistory, horizonDate); const benchmark0 = nearestOnOrAfter(benchmarkHistory, eventDate); const benchmarkHorizon = nearestOnOrAfter(benchmarkHistory, horizonDate); if (![price0, priceHorizon, benchmark0, benchmarkHorizon].every(Number.isFinite)) return null; const instrumentReturn = (priceHorizon - price0) / price0; const benchmarkReturn = (benchmarkHorizon - benchmark0) / benchmark0; const excessReturn = instrumentReturn - benchmarkReturn; const directionCorrect = prediction.direction === 'positive' ? excessReturn > 0 : excessReturn < 0; return { price0, priceHorizon, benchmark0, benchmarkHorizon, excessReturn, directionCorrect: directionCorrect ? 1 : 0, eventDate, horizonDate, }; } module.exports = { addTradingDays, nearestOnOrAfter, calculateOutcome };