TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { median } from '@rareindex/shared';23export interface GradedSale {4 assetId: string;5 grader: string | null;6 grade: string | null;7 priceUsd: number;8}910export interface GradePremium {11 grader: string;12 grade: string;13 marketMultiplier: number;14 sampleSize: number;15}1617/**18 * Empirical grade premiums (§118): for each (grader, grade), the median ratio of a graded sale to19 * the median raw/ungraded (or reference-grade) price of the SAME asset. Computed per category from20 * paired evidence only — never assumed across graders.21 */22export function computeGradePremiums(sales: GradedSale[], opts: { referenceKey?: string; minPairs?: number } = {}): GradePremium[] {23 const minPairs = opts.minPairs ?? 8;24 const byAsset = new Map<string, GradedSale[]>();25 for (const s of sales) if (s.priceUsd > 0) byAsset.set(s.assetId, [...(byAsset.get(s.assetId) ?? []), s]);26 const ratios = new Map<string, number[]>();27 for (const list of byAsset.values()) {28 const key = (s: GradedSale) => (s.grader && s.grade ? `${s.grader}|${s.grade}` : 'raw');29 const groups = new Map<string, number[]>();30 for (const s of list) groups.set(key(s), [...(groups.get(key(s)) ?? []), s.priceUsd]);31 const refKey = opts.referenceKey ?? 'raw';32 let ref = median(groups.get(refKey) ?? []);33 if (ref === null) {34 // fall back to the most common graded key for this asset as reference35 const best = [...groups.entries()].filter(([k]) => k !== refKey).sort((a, b) => b[1].length - a[1].length)[0];36 if (!best || best[1].length < 2) continue;37 ref = median(best[1]);38 // express others relative to that grade, then to raw is impossible → skip unless raw exists39 continue;40 }41 for (const [k, prices] of groups) {42 if (k === refKey) continue;43 const m = median(prices)!;44 ratios.set(k, [...(ratios.get(k) ?? []), m / ref]);45 }46 }47 const out: GradePremium[] = [];48 for (const [k, rs] of ratios) {49 if (rs.length < minPairs) continue;50 const [grader, grade] = k.split('|') as [string, string];51 out.push({ grader, grade, marketMultiplier: Math.round(median(rs)! * 1000) / 1000, sampleSize: rs.length });52 }53 return out.sort((a, b) => a.grader.localeCompare(b.grader) || Number(a.grade) - Number(b.grade));54}5556/** Adjustment factor target/source from premium tables (both relative to raw). null when either is unknown. */57export function adjustmentFactor(premiums: GradePremium[], source: { grader: string | null; grade: string | null }, target: { grader: string | null; grade: string | null }): number | null {58 const mult = (g: { grader: string | null; grade: string | null }): number | null => {59 if (!g.grader || !g.grade) return 1; // raw60 const p = premiums.find((x) => x.grader === g.grader && x.grade === g.grade);61 return p ? p.marketMultiplier : null;62 };63 const s = mult(source);64 const t = mult(target);65 if (s === null || t === null || s <= 0) return null;66 return t / s;67}68