TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { describe, expect, it } from 'vitest';2import { chainLinkedIndex, horizonReturns } from './chain.js';3import { repeatSalesIndex } from './repeat-sales.js';4import { correlationMatrix } from './correlation.js';56describe('chain-linked index', () => {7 it('chains equal-weight log returns and survives composition changes', () => {8 const series = [9 { id: 'a', points: [{ date: '2024-01-01', value: 100 }, { date: '2024-01-02', value: 110 }, { date: '2024-01-03', value: 121 }] },10 { id: 'b', points: [{ date: '2024-01-01', value: 50 }, { date: '2024-01-02', value: 50 }, { date: '2024-01-03', value: 55 }] },11 { id: 'c', points: [{ date: '2024-01-03', value: 10 }, { date: '2024-01-04', value: 20 }] }, // joins later, doubles12 ];13 const idx = chainLinkedIndex(series, { baseDate: '2024-01-01', baseValue: 1000, minConstituents: 2 });14 expect(idx[0]).toMatchObject({ date: '2024-01-01', value: 1000 });15 // day 2: a +10% (log .0953), b 0 → mean .0477 → ×1.048816 expect(idx[1]!.value).toBeCloseTo(1000 * Math.exp((Math.log(1.1) + 0) / 2), 2);17 // day 4: only c has a value → below minConstituents → not published18 expect(idx.at(-1)!.date).toBe('2024-01-03');19 expect(idx.at(-1)!.constituents).toBe(3);20 });21 it('publishes nothing below the constituent threshold', () => {22 const idx = chainLinkedIndex([{ id: 'a', points: [{ date: '2024-01-01', value: 1 }, { date: '2024-01-02', value: 2 }] }], { baseDate: '2024-01-01', baseValue: 1000, minConstituents: 5 });23 expect(idx).toEqual([]);24 });25 it('computes horizon returns', () => {26 const pts = Array.from({ length: 400 }, (_, i) => ({ date: new Date(Date.UTC(2025, 0, 1) + i * 86_400_000).toISOString().slice(0, 10), value: 1000 * (1 + i / 400) }));27 const h = horizonReturns(pts);28 expect(h['1d']!).toBeGreaterThan(0);29 expect(h['1y']!).toBeGreaterThan(h['30d']!);30 expect(h['10y']).toBeNull();31 expect(h.all!).toBeCloseTo(399 / 400, 5);32 });33});3435describe('repeat-sales', () => {36 it('recovers a known monthly path', () => {37 // true levels: Jan 1.0, Feb 1.1, Mar 1.21, Apr 1.138 const levels: Record<string, number> = { '2024-01': 1, '2024-02': 1.1, '2024-03': 1.21, '2024-04': 1.1 };39 const sales: Parameters<typeof repeatSalesIndex>[0] = [];40 const months = Object.keys(levels);41 for (let v = 0; v < 15; v++) {42 const base = 100 + v * 10;43 const m1 = months[v % 3]!;44 const m2 = months[(v % 3) + 1]!;45 sales.push({ variantId: `v${v}`, date: `${m1}-10`, priceUsd: base * levels[m1]! });46 sales.push({ variantId: `v${v}`, date: `${m2}-12`, priceUsd: base * levels[m2]! });47 }48 const r = repeatSalesIndex(sales, { minPairs: 5 })!;49 expect(r.periods).toEqual(months);50 expect(r.levels[1]!).toBeCloseTo(1.1, 3);51 expect(r.levels[2]!).toBeCloseTo(1.21, 3);52 expect(r.levels[3]!).toBeCloseTo(1.1, 3);53 });54 it('returns null with too few pairs', () => {55 expect(repeatSalesIndex([{ variantId: 'a', date: '2024-01-01', priceUsd: 1 }, { variantId: 'a', date: '2024-02-01', priceUsd: 2 }])).toBeNull();56 });57});5859describe('correlation', () => {60 it('finds perfect correlation of identical series', () => {61 const s = Array.from({ length: 30 }, (_, i) => ({ date: `2024-01-${String(i + 1).padStart(2, '0')}`, value: 100 + Math.sin(i) * 10 }));62 const m = correlationMatrix({ A: s, B: s.map((p) => ({ ...p, value: p.value * 2 })) });63 expect(m.find((x) => x.a === 'A' && x.b === 'B')!.r).toBe(1);64 });65});66