"use client"; import { useEffect, useMemo, useState } from "react"; import { api } from "@/lib/api"; import { ChartReport, PricePoint } from "@/lib/types"; import { PlotPanel } from "./PlotPanel"; const TIMEFRAMES = ["1D", "5D", "1M", "3M", "6M", "YTD", "1Y", "5Y"]; export function ChartAnalystPanel({ ticker, prices = [] }: { ticker: string; prices?: PricePoint[] }) { const [timeframe, setTimeframe] = useState("6M"); const [report, setReport] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const loadReport = async (tf = timeframe) => { setError(""); try { setReport(await api.chartTechnicalReport(ticker, tf)); } catch (err) { setError((err as Error).message); } }; useEffect(() => { loadReport(timeframe); }, [ticker, timeframe]); const analyze = async () => { setBusy(true); setError(""); try { setReport(await api.chartAnalyzeTicker(ticker, timeframe, periodFor(timeframe), false)); } catch (err) { setError((err as Error).message); } finally { setBusy(false); } }; const chartPrices = useMemo(() => trimPrices(report?.price_series?.length ? report.price_series : prices, timeframe), [report, prices, timeframe]); const hybrid = (report?.hybrid_analysis ?? {}) as ChartReport["hybrid_analysis"]; const deterministic = (report?.deterministic_analysis ?? {}) as Record; const levels = hybrid.key_levels ?? {}; const visual = report?.visual_analysis ?? {}; const plot = chartPlot(chartPrices, levels); return (
Chart Vision Technical Analyst

{ticker} technical chart intelligence.

{error &&
Chart API warning: {error}
}
); } function TechnicalSnapshot({ report }: { report: ChartReport | null }) { const d = report?.deterministic_analysis ?? {}; const h = (report?.hybrid_analysis ?? {}) as ChartReport["hybrid_analysis"]; const momentum = d.momentum ?? {}; const volatility = d.volatility ?? {}; const volume = d.volume ?? {}; const pattern = d.patterns?.[0]?.pattern ?? "pending"; return (
Technical Snapshot {Number(h.confidence_score ?? report?.confidence ?? 0).toFixed(1)} confidence
{String(h.timeframe_relevance ?? "timeframe pending").replaceAll("_", " ")} {h.trend_summary ?? "Technical summary will appear when stored OHLCV evidence is sufficient for the autonomous chart analyst."}

{(report?.warnings ?? h.warnings)?.join(" ") ?? "Vision model status and deterministic analysis warnings will appear here."}

); } function KeyLevels({ levels }: { levels: Record }) { return (
Key Levelszones, not guarantees
); } function SignalEvidence({ hybrid }: { hybrid: any }) { return (
Signal Evidence{hybrid.technical_signals?.length ?? 0} signals
); } function AnalystReport({ hybrid, visual, deterministic }: { hybrid: any; visual: Record; deterministic: Record }) { return (
Analyst Report {String(visual.mode ?? "deterministic").replaceAll("_", " ")}

{hybrid.analyst_report ?? deterministic.technical_summary ?? "The autonomous chart analyst is waiting for sufficient deterministic technical evidence."}

{(hybrid.possible_scenarios ?? []).map((scenario: any) => (
{scenario.name} {scenario.condition} {scenario.impact}
))}
{(hybrid.what_to_watch_next ?? []).slice(0, 6).map((item: string) => {item})}
); } function HistoricalSimilarity({ similarity }: { similarity: Record }) { return (
Historical Similarity {similarity.reliability_score ? `${Number(similarity.reliability_score).toFixed(1)} reliability` : "maturing"}

{similarity.explanation ?? "Chart pattern memory will mature as future OHLCV outcomes are observed."}

); } function SnapshotMetric({ label, value }: { label: string; value: string | number }) { return
{label}{value}
; } function Level({ label, value }: { label: string; value: any }) { return
{label}{formatLevel(value)}
; } function EvidenceList({ title, rows }: { title: string; rows: string[] }) { return (

{title}

{rows.length ? rows.slice(0, 6).map((item) => {item}) : No confirmed evidence yet.}
); } function chartPlot(prices: PricePoint[], levels: Record) { const x = prices.map((point) => point.date); const shapes = levelShapes(levels, x); return { data: [ { type: "candlestick", x, open: prices.map((point) => point.open ?? point.close), high: prices.map((point) => point.high ?? point.close), low: prices.map((point) => point.low ?? point.close), close: prices.map((point) => point.close), increasing: { line: { color: "#20e070" } }, decreasing: { line: { color: "#ff4d5e" } }, name: "OHLC" }, { type: "bar", x, y: prices.map((point) => point.volume ?? 0), yaxis: "y2", marker: { color: "rgba(77,216,255,.24)" }, name: "Volume" } ], layout: { shapes, xaxis: { rangeslider: { visible: false }, gridcolor: "rgba(255,255,255,.06)" }, yaxis: { domain: [0.24, 1], gridcolor: "rgba(255,255,255,.06)" }, yaxis2: { domain: [0, 0.17], gridcolor: "rgba(255,255,255,.04)" }, showlegend: false } }; } function levelShapes(levels: Record, x: string[]) { if (!x.length) return []; const x0 = x[0]; const x1 = x[x.length - 1]; const candidates = [ { value: levels.support_1, color: "#20e070" }, { value: levels.support_2, color: "#20e070" }, { value: levels.resistance_1, color: "#ff4d5e" }, { value: levels.resistance_2, color: "#ff4d5e" }, { value: levels.invalidation_level, color: "#ffb000" } ]; return candidates.filter((item) => item.value).map((item) => ({ type: "line", xref: "x", yref: "y", x0, x1, y0: Number(item.value), y1: Number(item.value), line: { color: item.color, width: 1, dash: "dot" } })); } function trimPrices(prices: PricePoint[], timeframe: string) { const limits: Record = { "1D": 96, "5D": 120, "1M": 42, "3M": 84, "6M": 150, "YTD": 260, "1Y": 260, "5Y": 1260 }; return prices.slice(-(limits[timeframe] ?? 150)); } function periodFor(timeframe: string) { const periods: Record = { "1D": "5d", "5D": "1mo", "1M": "3mo", "3M": "6mo", "6M": "1y", "YTD": "1y", "1Y": "2y", "5Y": "5y" }; return periods[timeframe] ?? "1y"; } function formatLevel(value: any) { if (value === null || value === undefined || Number.isNaN(Number(value))) return "n/a"; return Number(value).toFixed(2); } function formatPercent(value: any) { if (value === null || value === undefined || Number.isNaN(Number(value))) return "Pending"; return `${Number(value).toFixed(2)}%`; } function ratio(value: any) { if (value === null || value === undefined || Number.isNaN(Number(value))) return "Pending"; return `${Math.round(Number(value) * 100)}%`; }