36 lines
1.6 KiB
JavaScript
36 lines
1.6 KiB
JavaScript
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 };
|