import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { monthlyReturnsFromCandles, aggregateMonthSeasonality, buildSeasonalitySnapshot, electionCycleYear, upcomingSimpleEvents, } from '../seasonality.ts'; function mkYear(year: number, monthlyCloses: number[]) { // One candle per month end-ish return monthlyCloses.map((c, i) => ({ ts: new Date(Date.UTC(year, i, 28)).toISOString(), c, })); } test('monthlyReturnsFromCandles skips incomplete current month', () => { // Use fixed "now" via candles only in past years const candles = [ ...mkYear(2020, [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111]), ...mkYear(2021, [111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122]), ]; const monthly = monthlyReturnsFromCandles(candles, new Date(Date.UTC(2026, 0, 15))); assert.ok(monthly.length >= 20); assert.ok(monthly.every((m) => Number.isFinite(m.returnPct))); }); test('aggregateMonthSeasonality has 12 months', () => { const monthly = [ { year: 2020, month: 1, returnPct: 2 }, { year: 2021, month: 1, returnPct: -1 }, { year: 2020, month: 5, returnPct: -3 }, ]; const agg = aggregateMonthSeasonality(monthly); assert.equal(agg.length, 12); assert.equal(agg[0].sampleYears, 2); assert.ok(Math.abs(agg[0].avgReturnPct - 0.5) < 0.01); assert.equal(agg[0].winRate, 0.5); }); test('electionCycleYear labels 2026 as midterm year', () => { const c = electionCycleYear(2026); assert.equal(c.yearInCycle, 2); assert.ok(/midterm/i.test(c.label)); }); test('buildSeasonalitySnapshot marks current month', () => { const candles = [ ...mkYear(2019, Array.from({ length: 12 }, (_, i) => 100 + i)), ...mkYear(2020, Array.from({ length: 12 }, (_, i) => 110 + i)), ...mkYear(2021, Array.from({ length: 12 }, (_, i) => 120 + i)), ]; const snap = buildSeasonalitySnapshot('SPY', candles, new Date(Date.UTC(2026, 6, 15))); assert.equal(snap.currentMonth, 7); assert.equal(snap.symbol, 'SPY'); assert.ok(snap.months.length === 12); assert.ok(snap.electionCycle.label.length > 0); }); test('upcomingSimpleEvents uses plain language', () => { const events = upcomingSimpleEvents(); assert.ok(events.length >= 3); for (const e of events) { assert.ok(e.plainWhy.length > 20); assert.ok(!/you should buy|go long/i.test(e.plainWhy)); } });