Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import { ChartDataPoint } from '../components/charts/types'; import { predictNextTrend } from './predict-next'; import { detectStabilityZones } from './stability-detector'; import { detectConfidenceSurges } from './confidence-surge'; export type InsightBadge = 'STABILITY' | 'SURGE' | 'PREDICTION' | 'BASELINE'; export interface Insight { point: ChartDataPoint | (ChartDataPoint & { isPrediction?: boolean }); prev?: ChartDataPoint; next?: ChartDataPoint; rolling: { window: number; avgChangeRate: number; avgConfidence: number; }; badges: InsightBadge[]; stabilityWindow?: { start: number; end: number }; surgeDelta?: number; predictedNext?: { timestamp: number; trendValue: number; confidence: number } | null; } export function computeInsight(series: ChartDataPoint[], idx: number, windowSize = 5): Insight { const clamp = (n: number, a = 0, b = 1) => Math.max(a, Math.min(b, n)); const point = series[idx]; const prev = series[idx - 1]; const next = series[idx + 1]; // rolling stats const from = Math.max(0, idx - (windowSize - 1)); const slice = series.slice(from, idx + 1); const avgChangeRate = slice.reduce((s, d) => s + (d.changeRate ?? 0), 0) / slice.length; const avgConfidence = slice.reduce((s, d) => s + clamp(d.confidence ?? 0.5), 0) / slice.length; // zones & surges const zones = detectStabilityZones(series, windowSize, 5); const inZone = zones.find((z) => point.timestamp >= z.start && point.timestamp <= z.end); const surges = detectConfidenceSurges(series, 5, 2); const surge = surges.find((s) => s.timestamp === point.timestamp); // prediction (from full series) const predicted = predictNextTrend(series); const badges: InsightBadge[] = []; if (inZone) badges.push('STABILITY'); if (surge) badges.push('SURGE'); if ((point as any).isPrediction) badges.push('PREDICTION'); if (!badges.length) badges.push('BASELINE'); return { point, prev, next, rolling: { window: windowSize, avgChangeRate, avgConfidence }, badges, stabilityWindow: inZone ? { start: inZone.start, end: inZone.end } : undefined, surgeDelta: surge?.delta, predictedNext: predicted ? { timestamp: predicted.timestamp, trendValue: predicted.trendValue, confidence: predicted.confidence, } : null, }; } |