TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1/** Pearson correlation of daily log returns between two aligned series (§186). */2export function alignedLogReturns(a: Array<{ date: string; value: number }>, b: Array<{ date: string; value: number }>): Array<[number, number]> {3 const mb = new Map(b.map((p) => [p.date, p.value]));4 const pairs: Array<[number, number]> = [];5 let prevA: number | null = null;6 let prevB: number | null = null;7 for (const p of a) {8 const vb = mb.get(p.date);9 if (vb === undefined) continue;10 if (prevA !== null && prevB !== null && prevA > 0 && prevB > 0 && p.value > 0 && vb > 0) pairs.push([Math.log(p.value / prevA), Math.log(vb / prevB)]);11 prevA = p.value;12 prevB = vb;13 }14 return pairs;15}1617export function pearson(pairs: Array<[number, number]>): { r: number; n: number } | null {18 const n = pairs.length;19 if (n < 10) return null;20 const mx = pairs.reduce((s, p) => s + p[0], 0) / n;21 const my = pairs.reduce((s, p) => s + p[1], 0) / n;22 let sxy = 0;23 let sxx = 0;24 let syy = 0;25 for (const [x, y] of pairs) {26 sxy += (x - mx) * (y - my);27 sxx += (x - mx) ** 2;28 syy += (y - my) ** 2;29 }30 if (sxx === 0 || syy === 0) return { r: 0, n };31 return { r: Math.round((sxy / Math.sqrt(sxx * syy)) * 1000) / 1000, n };32}3334export function correlationMatrix(series: Record<string, Array<{ date: string; value: number }>>, sinceDate?: string): Array<{ a: string; b: string; r: number; n: number }> {35 const keys = Object.keys(series);36 const out: Array<{ a: string; b: string; r: number; n: number }> = [];37 for (let i = 0; i < keys.length; i++) {38 for (let j = i; j < keys.length; j++) {39 const A = sinceDate ? series[keys[i]!]!.filter((p) => p.date >= sinceDate) : series[keys[i]!]!;40 const B = sinceDate ? series[keys[j]!]!.filter((p) => p.date >= sinceDate) : series[keys[j]!]!;41 const p = pearson(alignedLogReturns(A, B));42 if (p) out.push({ a: keys[i]!, b: keys[j]!, r: i === j ? 1 : p.r, n: p.n });43 }44 }45 return out;46}47