"use client"; import { Box, Flex, Heading, Text, SimpleGrid, Card, VStack, HStack, Badge, Spinner, Table, } from "@chakra-ui/react"; import { NativeSelectRoot, NativeSelectField, } from "@/components/ui/forms/native-select"; import { useColorModeValue } from "@/components/ui/color-mode"; import { AnimatedCounter } from "@/components/motion"; import { useModelPerformance } from "@/lib/api/admin/use-hooks"; import type { ModelPerformanceMarketDto } from "@/lib/api/admin/types"; import { LuTarget, LuTrendingUp, LuCircleCheck, LuLayers } from "react-icons/lu"; import { useState } from "react"; const MARKET_LABELS: Record = { MS: "Maç Sonucu", DC: "Çifte Şans", OU15: "Üst/Alt 1.5", OU25: "Üst/Alt 2.5", OU35: "Üst/Alt 3.5", BTTS: "Karşılıklı Gol", HT: "İlk Yarı Sonucu", HT_OU05: "İY Üst/Alt 0.5", HT_OU15: "İY Üst/Alt 1.5", HTFT: "İlk Yarı / Maç Sonu", OE: "Tek / Çift", CARDS: "Kart Üst/Alt", HCAP: "Handikap", }; const calibrationLabel = (c: string): { text: string; color: string } => { if (c === "good") return { text: "Dürüst", color: "green" }; if (c === "overconfident") return { text: "Fazla iyimser", color: "red" }; return { text: "Düşük tahmin", color: "orange" }; }; const roiColor = (roi: number): string => roi > 3 ? "green" : roi < -3 ? "red" : "gray"; export default function ModelPerformanceContent() { const [days, setDays] = useState(90); const { data, isLoading } = useModelPerformance(days); const cardBg = useColorModeValue("white", "gray.800"); const borderColor = useColorModeValue("gray.200", "gray.700"); const headBg = useColorModeValue("gray.50", "whiteAlpha.50"); const perf = data?.data; const markets = perf?.markets ?? []; return ( Model Performansı (İleri Test) Her market için "model %X dedi → gerçekte %Y oldu". Sonuçlanmış gerçek maçlardan otomatik hesaplanır (lookahead yok). setDays(Number(e.target.value))} > {isLoading ? ( ) : !perf || markets.length === 0 ? ( Henüz yeterli sonuçlanmış veri yok Tahminler maçlar oynandıkça sonuçlanır. Güvenilir kalibrasyon için market başına ~100 sonuçlanmış tahmin gerekir; bu birkaç hafta içinde birikir. ) : ( <> {/* Summary cards */} } label="Sonuçlanan tahmin" value={perf.settled_runs} bg={cardBg} border={borderColor} /> } label="Sonuçlanan market" value={perf.settled_markets} bg={cardBg} border={borderColor} /> } label="Dürüst market" value={markets.filter((m) => m.calibration === "good").length} bg={cardBg} border={borderColor} /> } label="Kârlı market (BET)" value={markets.filter((m) => m.bet_roi_pct > 3).length} bg={cardBg} border={borderColor} /> {/* Calibration + ROI table */} Market N Model % Gerçek % Fark Kalibrasyon Bahis İsabet % ROI % {markets.map((m) => { const cal = calibrationLabel(m.calibration); return ( {m.market} {MARKET_LABELS[m.market] ?? m.market} {m.samples} {m.shown_pct.toFixed(1)}% {m.actual_pct.toFixed(1)}% {m.gap > 0 ? "+" : ""} {m.gap.toFixed(1)} {cal.text} {m.bet_count} {m.bet_count > 0 ? `${m.bet_hit_pct.toFixed(0)}%` : "—"} {m.bet_count > 0 ? ( {m.bet_roi_pct > 0 ? "+" : ""} {m.bet_roi_pct.toFixed(1)}% ) : ( "—" )} ); })} {/* Decision rationale (why picks come out) */} Öneri gerekçesi (karar dağılımı) Her market için betting brain'in ne karar verdiği: BET (oyna), WATCH (izle), REJECT (ele). {markets.map((m) => ( {m.market} {MARKET_LABELS[m.market] ?? ""} {Object.entries(m.actions) .sort((a, b) => b[1] - a[1]) .map(([action, count]) => ( {action}: {count} ))} ))} Son güncelleme:{" "} {perf.generated_at ? new Date(perf.generated_at).toLocaleString("tr-TR") : "—"}{" "} · Pencere: {perf.window_days} gün · ECE düşük = daha dürüst )} ); } function StatCard({ icon, label, value, bg, border, }: { icon: React.ReactNode; label: string; value: number; bg: string; border: string; }) { return ( {icon} {label} ); }