Spaces:
Runtime error
Runtime error
| import test from 'node:test'; | |
| import assert from 'node:assert/strict'; | |
| import { runSignalBacktest, runTechnicalSignalBacktest, BacktestInput } from '../lib/backtest'; | |
| import { WeeklyQuote } from '../lib/signal-engine'; | |
| function weeklyQuote(week: number, close: number, overrides: Partial<WeeklyQuote> = {}): WeeklyQuote { | |
| return { | |
| date: new Date(Date.UTC(2020, 0, 3 + week * 7)), | |
| open: close - 1, | |
| high: close + 1, | |
| low: close - 1, | |
| close, | |
| ...overrides, | |
| }; | |
| } | |
| function weekIso(week: number): string { | |
| const date = weeklyQuote(week, 0).date; | |
| assert.ok(date instanceof Date); | |
| return date.toISOString(); | |
| } | |
| function inputWithFundamentals(asOf: string): BacktestInput { | |
| const bars = Array.from({ length: 210 }, (_, index) => weeklyQuote(index, 100)); | |
| bars[204] = weeklyQuote(204, 101, { open: 98, low: 99, high: 102 }); | |
| bars[205] = weeklyQuote(205, 103); | |
| bars[209] = weeklyQuote(209, 110); | |
| return { | |
| symbols: [ | |
| { | |
| symbol: 'TEST', | |
| quotes: bars, | |
| fundamentals: [ | |
| { asOf: new Date('2023-01-01T00:00:00.000Z'), roe: 0.05, debtToEquity: 10, marketCap: 25_000_000_000 }, | |
| { asOf: new Date(asOf), roe: 0.25, debtToEquity: 10, marketCap: 25_000_000_000 }, | |
| ], | |
| }, | |
| ], | |
| horizonsWeeks: [4], | |
| }; | |
| } | |
| test('runSignalBacktest does not use fundamentals published after the signal date', () => { | |
| const result = runSignalBacktest(inputWithFundamentals('2024-01-01T00:00:00.000Z')); | |
| assert.equal(result.events.length, 0); | |
| }); | |
| test('runSignalBacktest labels 4-week outcomes using only bars after the signal date', () => { | |
| const result = runSignalBacktest(inputWithFundamentals('2023-11-01T00:00:00.000Z')); | |
| assert.equal(result.events.length, 1); | |
| const event = result.events[0]; | |
| assert.ok(event); | |
| assert.equal(event.symbol, 'TEST'); | |
| assert.equal(event.signalDate.toISOString(), weekIso(204)); | |
| assert.equal(event.entryDate.toISOString(), weekIso(205)); | |
| assert.equal(event.horizonWeeks, 4); | |
| assert.equal(event.exitDate.toISOString(), weekIso(209)); | |
| assert.equal(Number(event.forwardReturn.toFixed(4)), Number(((110 - 103) / 103).toFixed(4))); | |
| assert.equal(result.summary.eventCount, 1); | |
| assert.equal(result.summary.winRate, 1); | |
| }); | |
| test('runTechnicalSignalBacktest reports quality metrics for 4 and 6 week horizons without fundamentals', () => { | |
| const bars = Array.from({ length: 214 }, (_, index) => weeklyQuote(index, 100)); | |
| bars[204] = weeklyQuote(204, 101, { open: 98, low: 99, high: 102 }); | |
| bars[205] = weeklyQuote(205, 103); | |
| bars[209] = weeklyQuote(209, 110); | |
| bars[211] = weeklyQuote(211, 99); | |
| const result = runTechnicalSignalBacktest({ | |
| symbols: [{ symbol: 'TECH', quotes: bars }], | |
| horizonsWeeks: [4, 6], | |
| }); | |
| assert.equal(result.summary.eventCount, 2); | |
| assert.equal(result.summary.byHorizon[4].eventCount, 1); | |
| assert.equal(result.summary.byHorizon[4].winRate, 1); | |
| assert.equal(Number(result.summary.byHorizon[4].meanReturn.toFixed(4)), Number(((110 - 103) / 103).toFixed(4))); | |
| assert.equal(result.summary.byHorizon[6].eventCount, 1); | |
| assert.equal(result.summary.byHorizon[6].winRate, 0); | |
| assert.equal(Number(result.summary.byHorizon[6].meanReturn.toFixed(4)), Number(((99 - 103) / 103).toFixed(4))); | |
| }); | |