'use client'; import React, { useState, useMemo } from 'react'; import { useSandboxStore } from '@/lib/store'; import { calculateEventROC, calculateEventSurvival, runEventLMM } from '@/lib/math/statistics'; import { ResponsiveContainer, AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, LineChart, Line, ReferenceLine, Legend } from 'recharts'; import 'katex/dist/katex.min.css'; import { BlockMath, InlineMath } from 'react-katex'; import { Activity, BarChart4, Compass, GitMerge, Plus, Trash2, Calendar, Sparkles, AlertCircle, ChevronDown, ChevronUp, BookOpen, RefreshCw, Info, Check, TrendingUp, TrendingDown, Sliders, Database } from 'lucide-react'; // Predefined archetypes for Event Creation const ARCHETYPES: Record }> = { 'FED Zinsentscheid': { name: 'FED Zinsentscheid', defaultScores: { Apple: 1, NASDAQ: 2, Gold: -1, Bitcoin: 2 } }, 'US Wahlen (Präsidentschaft)': { name: 'US Wahlen', defaultScores: { Apple: 2, NASDAQ: 1, Gold: 3, Bitcoin: 2 } }, 'SpaceX IPO (Gerüchte)': { name: 'SpaceX IPO', defaultScores: { Apple: 0, NASDAQ: 2, Gold: -1, Bitcoin: 1 } }, 'CPI Inflationsdaten': { name: 'CPI Inflationsdaten', defaultScores: { Apple: 1, NASDAQ: 2, Gold: -2, Bitcoin: 1 } }, 'US Non-Farm Payrolls': { name: 'US Non-Farm Payrolls', defaultScores: { Apple: 0, NASDAQ: 1, Gold: -1, Bitcoin: 0 } }, 'EZB Pressekonferenz': { name: 'EZB Pressekonferenz', defaultScores: { Apple: -1, NASDAQ: -1, Gold: 2, Bitcoin: 1 } } }; const ASSETS = ['Apple', 'NASDAQ', 'Gold', 'Bitcoin']; export default function EventsDemo() { const { selectedModel, setSelectedModel, eventsMatrix, calendarProposals, lmmObservations, addEventToMatrix, updateMatrixCell, runEndogenousLMMCalibration } = useSandboxStore(); // Local State const [tauPre, setTauPre] = useState(7); const [tauPost, setTauPost] = useState(3); const [showMath, setShowMath] = useState(false); const [selectedSurvivalAsset, setSelectedSurvivalAsset] = useState('Apple'); // Custom Event Form State const [customName, setCustomName] = useState(''); const [customDate, setCustomDate] = useState('2026-06-15'); const [selectedArchetype, setSelectedArchetype] = useState('Custom'); // Calibration feedback states const [isCalibrating, setIsCalibrating] = useState(false); const [calibrationSuccess, setCalibrationSuccess] = useState(false); const [lastCalibrationTime, setLastCalibrationTime] = useState(null); // Current baseline date for relative time calculations const CURRENT_DATE_STR = '2026-06-06'; // Helper to calculate time kernel weight const getWeightAndDays = (eventDateStr: string) => { const eventDate = new Date(eventDateStr); const currentDate = new Date(CURRENT_DATE_STR); const diffTime = eventDate.getTime() - currentDate.getTime(); const d = diffTime / (1000 * 60 * 60 * 24); // days relative to today let weight = 0; if (d >= 0) { weight = Math.exp(-d / tauPre); } else { const daysSince = -d; weight = 1 / (1 + Math.log(1 + daysSince / tauPost)); } return { d: Math.round(d), weight: Math.round(weight * 100) / 100 }; }; // 1. Time Weighted Net Impact Scores & Final Action Signals const actionSignals = useMemo(() => { const totals: Record = { Apple: 0, NASDAQ: 0, Gold: 0, Bitcoin: 0 }; eventsMatrix.forEach((ev) => { const { weight } = getWeightAndDays(ev.date); ASSETS.forEach((asset) => { const score = ev.scores[asset] || 0; totals[asset] += score * weight; }); }); const signals: Record = {}; ASSETS.forEach((asset) => { const netScore = Math.round(totals[asset] * 100) / 100; let signal = 'NEUTRAL / HOLD'; let colorClass = 'bg-slate-800/40 border-slate-700/60 text-slate-400'; let textClass = 'text-slate-400'; let glowClass = 'shadow-slate-500/5'; if (netScore > 1.5) { signal = 'STRONG BUY'; colorClass = 'bg-emerald-950/40 border-emerald-800/80 text-emerald-400'; textClass = 'text-emerald-400 font-bold'; glowClass = 'shadow-emerald-500/10 shadow-[0_0_15px_rgba(16,185,129,0.15)]'; } else if (netScore > 0.4) { signal = 'ACCUMULATE'; colorClass = 'bg-teal-950/30 border-teal-800/50 text-teal-400'; textClass = 'text-teal-300 font-semibold'; glowClass = 'shadow-teal-500/5 shadow-[0_0_10px_rgba(20,184,166,0.1)]'; } else if (netScore < -1.5) { signal = 'STRONG SELL / RISK OFF'; colorClass = 'bg-rose-950/40 border-rose-800/80 text-rose-400'; textClass = 'text-rose-400 font-bold'; glowClass = 'shadow-rose-500/10 shadow-[0_0_15px_rgba(244,63,94,0.15)]'; } else if (netScore < -0.4) { signal = 'REDUCE / HEDGE'; colorClass = 'bg-amber-950/30 border-amber-800/50 text-amber-400'; textClass = 'text-amber-300 font-semibold'; glowClass = 'shadow-amber-500/5 shadow-[0_0_10px_rgba(245,158,11,0.1)]'; } signals[asset] = { netScore, signal, colorClass, textClass, glowClass }; }); return signals; }, [eventsMatrix, tauPre, tauPost]); // 2. Dynamic Decay Curve Chart Data const decayCurveData = useMemo(() => { const pts = []; // Generate weight for each day relative to event (d = E - T) for (let d = -30; d <= 30; d++) { let weight = 0; if (d >= 0) { weight = Math.exp(-d / tauPre); } else { const daysSince = -d; weight = 1 / (1 + Math.log(1 + daysSince / tauPost)); } pts.push({ days: d, weight: Math.round(weight * 1000) / 1000 }); } return pts; }, [tauPre, tauPost]); // 3. Dynamic ROC Data const { rocData, optimalThreshold, maxYouden, auc } = useMemo(() => { const predictions: number[] = []; const labels: number[] = []; lmmObservations.forEach((obs) => { // Find average event score of this asset in matrix to use as indicator bias const assetScores = eventsMatrix.map(ev => ev.scores[obs.asset] || 0); const avgScore = assetScores.reduce((sum, s) => sum + s, 0) / (assetScores.length || 1); // Construct a predictor between 0 and 1 let basePred = obs.eventType === 'BULLISH' ? 0.65 : 0.35; basePred += avgScore * 0.04 + obs.trend * 0.5 - obs.vix * 0.002; const finalPred = Math.min(0.99, Math.max(0.01, basePred)); predictions.push(finalPred); labels.push(obs.returnVal > 0.012 ? 1 : 0); // label 1 if return > 1.2% }); const res = calculateEventROC(predictions, labels); // Trapezoidal Area Under Curve (AUC) let computedAuc = 0; const sorted = [...res.points].sort((a, b) => a.fpr - b.fpr); for (let i = 1; i < sorted.length; i++) { const w = sorted[i].fpr - sorted[i - 1].fpr; const h = (sorted[i].tpr + sorted[i - 1].tpr) / 2; computedAuc += w * h; } return { rocData: res.points, optimalThreshold: res.optimalThreshold, maxYouden: res.maxYouden, auc: Math.round(Math.max(0.5, Math.min(0.99, computedAuc)) * 1000) / 1000 }; }, [eventsMatrix, lmmObservations]); // 4. Dynamic Survival Curve Data for selected asset const survivalData = useMemo(() => { const assetScores = eventsMatrix.map(ev => ev.scores[selectedSurvivalAsset] || 0); const sumScore = assetScores.reduce((sum, s) => sum + s, 0); const timesLong: number[] = []; const eventsLong: number[] = []; const timesShort: number[] = []; const eventsShort: number[] = []; // Simulate 15 historical events outcomes per direction for (let i = 0; i < 15; i++) { // LONG: Positive scores reduce time-to-event (gain target hit faster) let tLong = 35 - sumScore * 3.5 + (Math.sin(i * 1.5) * 12); let evLong = 1; if (tLong > 60 || sumScore < -1) { tLong = 60; evLong = 0; // right censored } timesLong.push(Math.round(Math.max(3, tLong))); eventsLong.push(evLong); // SHORT: Negative scores reduce time-to-event (loss target hit faster) let tShort = 35 + sumScore * 3.5 + (Math.cos(i * 1.9) * 12); let evShort = 1; if (tShort > 60 || sumScore > 1) { tShort = 60; evShort = 0; // right censored } timesShort.push(Math.round(Math.max(3, tShort))); eventsShort.push(evShort); } const curveLong = calculateEventSurvival(timesLong, eventsLong, 'LONG'); const curveShort = calculateEventSurvival(timesShort, eventsShort, 'SHORT'); // Merge for chart mapping const timeMap: Record = {}; for (let t = 0; t <= 60; t += 2) { timeMap[t] = { time: t }; } curveLong.forEach(pt => { const roundedT = Math.round(pt.time / 2) * 2; if (timeMap[roundedT]) timeMap[roundedT].longRate = pt.survivalRate; }); curveShort.forEach(pt => { const roundedT = Math.round(pt.time / 2) * 2; if (timeMap[roundedT]) timeMap[roundedT].shortRate = pt.survivalRate; }); const sortedMerged = Object.values(timeMap).sort((a, b) => a.time - b.time); let lastLong = 1.0; let lastShort = 1.0; return sortedMerged.map(pt => { if (pt.longRate !== undefined) lastLong = pt.longRate; else pt.longRate = lastLong; if (pt.shortRate !== undefined) lastShort = pt.shortRate; else pt.shortRate = lastShort; return pt; }); }, [eventsMatrix, selectedSurvivalAsset]); // 5. Dynamic LMM regression fitting const lmmResults = useMemo(() => { return runEventLMM(lmmObservations); }, [lmmObservations]); // Custom Event Handler const handleAddCustomEvent = (e: React.FormEvent) => { e.preventDefault(); let name = customName.trim(); let scores: Record = { Apple: 0, NASDAQ: 0, Gold: 0, Bitcoin: 0 }; if (selectedArchetype !== 'Custom') { const arch = ARCHETYPES[selectedArchetype]; name = name || arch.name; scores = { ...arch.defaultScores }; } else { name = name || 'Benutzerdefiniertes Ereignis'; } addEventToMatrix(name, customDate, scores); setCustomName(''); setSelectedArchetype('Custom'); }; // Calibration Action Trigger const handleTriggerCalibration = () => { setIsCalibrating(true); // Simulate complex econometric iterative calibration setTimeout(() => { runEndogenousLMMCalibration(); setIsCalibrating(false); setCalibrationSuccess(true); setLastCalibrationTime(new Date().toLocaleTimeString()); setTimeout(() => { setCalibrationSuccess(false); }, 4000); }, 1200); }; return (
{/* 1. Header with Model Selector */}
Element 5 Status: Calibrated & Active

Advanced Econometric Event-Analysis Matrix

Analyzes multi-asset cross-impact networks under logarithmic decay timelines. Evaluates predictive efficiency via ROC, models target boundaries with directional survival, and performs endogenous regressions via LMM feedback.

{/* 2. Main Dashboard: Clean View Matrix & Action Signals */}
{/* Left/Middle Matrix & Settings (2/3 width) */}
{/* A. Event-Asset Matrix Table */}

Event Impact Matrix

- Bearish (-3) + Bullish (+3)
{ASSETS.map(asset => ( ))} {eventsMatrix.map((ev) => { const { d, weight } = getWeightAndDays(ev.date); return ( {ASSETS.map((asset) => { const score = ev.scores[asset] || 0; // Determine color style based on score value let badgeStyle = 'text-slate-400 bg-slate-950 border-slate-800/60'; if (score > 1.5) badgeStyle = 'text-emerald-400 bg-emerald-950/30 border-emerald-800/50'; else if (score > 0) badgeStyle = 'text-teal-400 bg-teal-950/20 border-teal-900/30'; else if (score < -1.5) badgeStyle = 'text-rose-400 bg-rose-950/30 border-rose-800/50'; else if (score < 0) badgeStyle = 'text-orange-400 bg-orange-950/20 border-orange-900/30'; return ( ); })} ); })}
Makro-Ereignis Datum{asset}Kernel-Gewicht
{ev.name} {ev.date} {d === 0 ? 'Heute' : d > 0 ? `In ${d} Tagen` : `Vor ${-d} Tagen`}
{score > 0 ? `+${score}` : score}
{Math.round(weight * 100)}% ({weight.toFixed(2)})
{/* B. Add Event Form & Time Kernel Weights config (split) */}
{/* Form to Add Event */}

Event hinzufügen

setCustomName(e.target.value)} placeholder="z.B. OPEC Treffen" className="w-full bg-slate-950 border border-slate-800 rounded-lg p-2 text-slate-200 focus:outline-none focus:border-rose-500/50 font-sans" />
setCustomDate(e.target.value)} className="w-full bg-slate-950 border border-slate-800 rounded-lg p-2 text-slate-200 focus:outline-none focus:border-rose-500/50 font-mono" />
{/* Time Decay Kernel Sliders & Live Decay Curve Chart */}

Time-Kernel Decay Parameters

Pre-Event Slope () {tauPre} Tage
setTauPre(Number(e.target.value))} className="w-full accent-purple-500 bg-slate-950 h-1 rounded-lg appearance-none cursor-pointer" /> Schnellerer Anstieg (kleinerer Wert) nähert sich dem Ereignis.
Post-Event Half-Life () {tauPost} Tage
setTauPost(Number(e.target.value))} className="w-full accent-purple-500 bg-slate-950 h-1 rounded-lg appearance-none cursor-pointer" /> Langsameres Abklingen logarithmisch nach dem Stichtag.
{/* Mini Kernel Chart showing bell curve of relevance */}
`Relative Tage: ${label}`} />
{/* Right Sidebar: Suggestions & Final Action Signals (1/3 width) */}
{/* Calendar Inbox Panel */}

Wirtschaftskalender Vorschläge

{calendarProposals.length === 0 ? (
Keine ausstehenden Vorschläge im Posteingang.
) : (
{calendarProposals.map((cp) => (
{cp.name}
{cp.date} {cp.archetype}
))}
)}
{/* Action Signals Dashboard */}

Aggregated Trade Signals

{ASSETS.map((asset) => { const { netScore, signal, colorClass, textClass, glowClass } = actionSignals[asset]; return (
{asset} Weighted Score: {netScore > 0 ? `+${netScore}` : netScore}
{signal}
{netScore > 0.4 ? ( ) : netScore < -0.4 ? ( ) : ( )} {netScore > 0 ? 'Bullish Drift' : netScore < 0 ? 'Bearish Risk' : 'Stationary'}
); })}
{/* LMM Feedback Trigger */}

Endogenous Calibration

Updates manual matrix scores with optimal significant coefficients estimated from historical Linear Mixed Models, calibrating feedback parameters dynamically.

{calibrationSuccess && (
Kalibrierung erfolgreich abgeschlossen!
)} {lastCalibrationTime && (
Letzter Durchlauf: {lastCalibrationTime} ({lmmObservations.length} Obs.)
)}
{/* 3. Bottom: Econometric Charts & Show Math Panel */}
{/* Model Tabs Header */}

{selectedModel === 'ROC' && 'ROC Model Diagnostics'} {selectedModel === 'SURVIVAL' && 'Survival Analysis (Time-to-Event)'} {selectedModel === 'LMM' && 'LMM Panel Regression Summary'}

{/* Collapsible LaTeX equations */} {showMath && (

ROC Model Diagnostics

Sensitivity (TPR) maps positive asset breakouts, while Specificity (1-FPR) maps false alerts.

Kaplan-Meier Survival

Calculates probability of NOT hitting target thresholds over 60 days. Events beyond 60 days are mathematically censored.

Linear Mixed Model (LMM)

Estimates pure event returns controlling for systemic covariates. Assets are modeled as random effect intercept adjustments.

)} {/* Tab Content */}
{/* Left Panel: Description & Stats Cards (1/3 width) */}
{selectedModel === 'ROC' && (

The Receiver Operating Characteristic (ROC) curve evaluates classifier strength on binary events (e.g. Return > +3.0% within 14 days). An AUC of 0.5 denotes a random baseline, while 1.0 represents a perfect oracle.

Area Under Curve (AUC) {auc}
Max Youden Index (J) {maxYouden}
Optimal Youden Threshold Score ≥ {optimalThreshold}
)} {selectedModel === 'SURVIVAL' && (

Kaplan-Meier survival curves map time-to-rebound (Long target: +5%) and time-to-drawdown (Short target: -5%). Separation of long and short tracks prevents arithmetic zero-sum cancellation.

Right Censoring 60 Tage
Observation Count 30 Event Runs
)} {selectedModel === 'LMM' && (

Linear Mixed Model estimates the true impact of events on returns, isolating asset-level intercepts as random deviations. Standard Errors, t-stats, and p-values determine significance.

AIC {lmmResults.aic}
BIC {lmmResults.bic}
Adj. R² {(lmmResults.rSquared * 100).toFixed(1)}%
)}
{/* Right Panel: Charts / Regression Table (2/3 width) */}
{selectedModel === 'ROC' && (
Modell-Klassifikationstrennung (FPR vs TPR)
v.toFixed(1)} /> [parseFloat(value).toFixed(2), 'True Positive Rate']} /> {/* Diagonal baseline */}
)} {selectedModel === 'SURVIVAL' && (
LONG Rebound (+5%) SHORT Drawdown (-5%)
`${(v * 100).toFixed(0)}%`} /> `${(parseFloat(v) * 100).toFixed(1)}%`} />
)} {selectedModel === 'LMM' && (
Fixed Effects Coefficients Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05
{lmmResults.fixedEffects.map((coeff) => ( ))}
Parameter Estimate Std. Error p-value Sig. 95% Conf. Interval
{coeff.name} {coeff.estimate.toFixed(4)} {coeff.se.toFixed(4)} {coeff.pVal.toFixed(5)} {coeff.sig} [{coeff.ciLower.toFixed(4)}, {coeff.ciUpper.toFixed(4)}]
Random Effects Asset (intercepts): {lmmResults.randomEffects.map((re) => ( {re.asset}: {re.intercept > 0 ? `+${re.intercept.toFixed(4)}` : re.intercept.toFixed(4)} ))}
)}
); }