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,
};
}
|