TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { mad, median } from '@rareindex/shared';23export interface PricePoint {4 id: string;5 priceUsd: number;6 date: Date;7 quantity?: number;8 isBundle?: boolean;9}1011export interface OutlierFlag {12 id: string;13 reason: string;14 score: number;15}1617/**18 * Robust outlier detection on log prices within a variant (§116): modified z-score19 * 0.6745·(x − median)/MAD > threshold. Needs ≥ 5 points; with a MAD of 0 (identical prices) any20 * different price beyond 3× is flagged. Never deletes — returns flags for audit.21 */22export function detectOutliers(points: PricePoint[], threshold = 3.5): OutlierFlag[] {23 const valid = points.filter((p) => p.priceUsd > 0 && !p.isBundle && (p.quantity ?? 1) === 1);24 if (valid.length < 5) return [];25 const logs = valid.map((p) => Math.log(p.priceUsd));26 const med = median(logs)!;27 const m = mad(logs)!;28 const out: OutlierFlag[] = [];29 for (let i = 0; i < valid.length; i++) {30 const p = valid[i]!;31 const dev = logs[i]! - med;32 let score: number;33 if (m > 0) score = (0.6745 * dev) / m;34 else score = Math.abs(dev) > Math.log(3) ? Math.sign(dev) * threshold * 2 : 0;35 if (Math.abs(score) > threshold) out.push({ id: p.id, reason: score > 0 ? 'abnormally_high_price' : 'abnormally_low_price', score: Math.round(score * 100) / 100 });36 }37 return out;38}39