import { useState } from 'react' import { usePatternReliability, useCalibration } from '../hooks/useApi' import clsx from 'clsx' import { BarChart2, Target, TrendingUp, AlertTriangle } from 'lucide-react' function ReliabilityTable({ data }: { data: any[] }) { if (!data || data.length === 0) { return (
No reliability data
Data appears after 3+ mature trades (≥35% of horizon elapsed) per pattern
) } return (
{data.map((r: any) => { const wr = r.win_rate_pct const wrColor = wr >= 60 ? 'text-emerald-400' : wr >= 40 ? 'text-amber-400' : 'text-red-400' const rel = r.reliability_score const relColor = rel >= 1.5 ? 'text-emerald-400' : rel >= 0.8 ? 'text-amber-400' : 'text-red-400' return ( ) })}
Pattern Trades Win Rate Avg PnL Max gain Max loss Reliability score
{r.pattern_name}
{r.pattern_id}
{r.trade_count} {wr}% = 0 ? 'text-emerald-400' : 'text-red-400')}> {r.avg_pnl_pct > 0 ? '+' : ''}{r.avg_pnl_pct}% +{r.max_pnl_pct}% {r.max_loss_pct}% {rel.toFixed(2)}
WR × log(n+1)
) } function CalibrationSection({ data }: { data: any }) { if (!data) return null const { brier_score, interpretation, buckets, sample_size } = data if (!brier_score && sample_size === 0) { return (
No calibration data yet
Requires mature trades with stored probability vs realized outcome
) } const brierColor = brier_score < 0.15 ? 'text-emerald-400' : brier_score < 0.25 ? 'text-amber-400' : 'text-red-400' return (
{/* Score summary */}
{brier_score?.toFixed(3) ?? '—'}
Brier Score
(0 = perfect, 1 = null)
{sample_size}
Trades analyzed
{interpretation ?? '—'}
Interpretation
{/* Calibration buckets */} {buckets && buckets.length > 0 && (
Calibration by decile (predicted vs realized)
{buckets.map((b: any) => { const bias = b.bias const biasColor = Math.abs(bias) < 0.05 ? 'text-emerald-400' : Math.abs(bias) < 0.15 ? 'text-amber-400' : 'text-red-400' const actualPct = Math.round(b.actual_rate * 100) const predPct = Math.round(b.predicted_mid * 100) return ( ) })}
Predicted prob. Actual rate Bias Trades Bar
{b.predicted_range} {actualPct}% {bias > 0 ? '+' : ''}{(bias * 100).toFixed(1)}% {b.count}
{/* Predicted (grey) vs actual (colored) */}
= predPct ? 'bg-emerald-500' : 'bg-red-500')} style={{ width: `${actualPct}%` }} />
Grey bar = predicted prob. · Colored bar = realized rate · Green if actual ≥ predicted, red otherwise
)}
) } export default function Analytics() { const { data: reliabilityData, isLoading: loadingR } = usePatternReliability() const [calDays, setCalDays] = useState(365) const { data: calData, isLoading: loadingC } = useCalibration(calDays) const reliability: any[] = (reliabilityData as any)?.reliability ?? [] const topPatterns = reliability.slice(0, 5) const bottomPatterns = reliability.filter((r: any) => r.trade_count >= 3).slice(-3) return (

Analytics & Calibration

Historical pattern reliability · Probabilistic calibration · Brier score

{/* Summary KPIs */} {reliability.length > 0 && (
{reliability.length}
Patterns with history
{reliability.reduce((s: number, r: any) => s + r.trade_count, 0)}
Mature trades analyzed
{topPatterns[0] && (
Best pattern
{topPatterns[0].pattern_name}
{topPatterns[0].win_rate_pct}% WR
)} {bottomPatterns[0] && (
To avoid
{bottomPatterns[0].pattern_name}
{bottomPatterns[0].win_rate_pct}% WR
)}
)} {/* Reliability table */}
Reliability by pattern (trades ≥35% of horizon only)
{loadingR ? (
{[1,2,3].map(i =>
)}
) : ( )}
{/* Calibration */}
Probabilistic calibration
{loadingC ? (
{[1,2,3].map(i =>
)}
) : ( )}
) }