Files
investment-sandbox/components/modules/macro/MacroIndicatorsDemo.tsx

592 lines
30 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import React, { useState, useEffect, useMemo } from 'react';
import { LineChart, Line, ResponsiveContainer } from 'recharts';
import 'katex/dist/katex.min.css';
import MacroMathModal from './MacroMathModal';
import {
TrendingUp, Landmark, AlertCircle, BookOpen, Percent,
ArrowDownRight, ArrowUpRight, Minus, Activity, ShieldAlert, Coins,
ChevronDown, ChevronUp
} from 'lucide-react';
interface IndicatorDataPoint {
date: string;
value: number;
}
interface MacroIndicator {
name: string;
unit: string;
category: string;
current: number;
previous: number;
trend: 'UP' | 'DOWN' | 'FLAT';
data: IndicatorDataPoint[];
}
interface MacroDataPayload {
dates: string[];
indicators: Record<string, MacroIndicator>;
liveDataAvailable: boolean;
timestamp: number;
}
export default function MacroIndicatorsDemo() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [payload, setPayload] = useState<MacroDataPayload | null>(null);
const [isMathModalOpen, setIsMathModalOpen] = useState(false);
const [isAccordionOpen, setIsAccordionOpen] = useState(false); // Collapsible accordion closed by default
useEffect(() => {
const fetchIndicators = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('/api/macro/indicators');
if (response.ok) {
const data = await response.json();
setPayload(data);
} else {
setError('Fehler beim Abruf der makroökonomischen Indikatoren.');
}
} catch (err) {
console.error('Fetch macro indicators error:', err);
setError('Netzwerkfehler beim Laden der Makroökonomischen Daten.');
} finally {
setLoading(false);
}
};
fetchIndicators();
}, []);
// Compute Fed Net Liquidity Proxy dynamically across the 24 months
// Formula: Net Liquidity = Fed Assets (T$) - TGA (B$/1000) - RRP (B$/1000)
const netLiquidityIndicator = useMemo(() => {
if (!payload?.indicators?.fedBalanceSheet || !payload?.indicators?.tga || !payload?.indicators?.rrp) {
return null;
}
const fedBalance = payload.indicators.fedBalanceSheet;
const tga = payload.indicators.tga;
const rrp = payload.indicators.rrp;
const netLiquidityData: IndicatorDataPoint[] = fedBalance.data.map((point, idx) => {
const assets = point.value;
const tgaVal = tga.data[idx]?.value || 0;
const rrpVal = rrp.data[idx]?.value || 0;
// Convert B$ to T$
const liq = assets - (tgaVal + rrpVal) / 1000;
return {
date: point.date,
value: parseFloat(liq.toFixed(3))
};
});
const len = netLiquidityData.length;
const current = netLiquidityData[len - 1].value;
const previous = netLiquidityData[len - 2].value;
let trend: 'UP' | 'DOWN' | 'FLAT' = 'FLAT';
if (current > previous) trend = 'UP';
if (current < previous) trend = 'DOWN';
return {
name: 'Federal Reserve Net Liquidity Proxy',
unit: 'T$',
category: 'Zentralbanken & Liquidität',
current,
previous,
trend,
data: netLiquidityData
} as MacroIndicator;
}, [payload]);
if (loading) {
return (
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-8 text-slate-100 shadow-xl min-h-[450px] flex flex-col items-center justify-center space-y-4">
<div className="w-10 h-10 rounded-full border-2 border-indigo-500 border-t-transparent animate-spin" />
<div className="text-slate-400 text-sm font-mono animate-pulse">Lade makroökonomisches Datenarchiv...</div>
</div>
);
}
if (error || !payload) {
return (
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl min-h-[400px] flex items-center justify-center">
<div className="text-rose-400 font-semibold flex items-center gap-2">
<AlertCircle className="w-5 h-5" /> {error || 'Fehler beim Laden.'}
</div>
</div>
);
}
const indicators = payload.indicators;
// Safe destructuring of all 21 indicators to guarantee no undefined layout shifts
const {
cpiYoY, coreCpi, ppi, savingsRate, ccDelinquency,
nfp, unemployment, joblessClaims,
fedFunds, ecbRefi, fedBalanceSheet, m2, rrp, tga, buffett,
us10y, yieldSpread, hySpread, housingStarts, mortgageApps, caseShiller
} = indicators;
// Compute Ampel (traffic light) health states
const getCard1Status = () => {
const cpiVal = cpiYoY?.current ?? 0;
const ccVal = ccDelinquency?.current ?? 0;
const saveVal = savingsRate?.current ?? 5;
if (cpiVal >= 3.0 || ccVal > 4.5 || saveVal < 3.0) return 'RED';
if (cpiVal >= 2.5 || ccVal > 3.5 || saveVal < 4.0) return 'AMBER';
return 'GREEN';
};
const getCard2Status = () => {
const bufVal = buffett?.current ?? 0;
const m2Trend = m2?.trend ?? 'FLAT';
if (bufVal > 150.0 || m2Trend === 'DOWN') return 'RED';
if (bufVal > 130.0 || (rrp && rrp.current < 400)) return 'AMBER';
return 'GREEN';
};
const getCard3Status = () => {
const yieldVal = yieldSpread?.current ?? 0;
const hyVal = hySpread?.current ?? 0;
if (yieldVal < 0.0 || hyVal > 5.0) return 'RED';
if (yieldVal < 0.1 || hyVal > 4.0) return 'AMBER';
return 'GREEN';
};
const card1Status = getCard1Status();
const card2Status = getCard2Status();
const card3Status = getCard3Status();
// Helper to color trends/values based on macroeconomic threshold rules
const getValHighlightClass = (key: string, val: number, trend: string) => {
if (key === 'hySpread') {
return val > 5.0 ? 'text-rose-400 font-bold animate-pulse' : 'text-slate-100';
}
if (key === 'yieldSpread') {
return val < 0.0 ? 'text-rose-500 font-bold' : 'text-emerald-400';
}
if (key === 'cpiYoY' || key === 'coreCpi') {
if (val <= 2.5) return 'text-emerald-400 font-semibold';
if (val >= 3.0) return 'text-rose-400';
}
if (key === 'm2' && trend === 'DOWN') {
return 'text-rose-400 font-bold animate-pulse';
}
if (key === 'rrp' && val < 400) {
return 'text-rose-400';
}
if (key === 'buffett' && val > 150.0) {
return 'text-rose-400 font-bold animate-pulse';
}
if (key === 'ccDelinquency' && val > 4.5) {
return 'text-rose-400 font-bold animate-pulse';
}
if (key === 'savingsRate' && val < 3.0) {
return 'text-rose-400 font-bold';
}
return 'text-slate-100';
};
// Helper for trend icons
const renderTrendIcon = (trend: 'UP' | 'DOWN' | 'FLAT', key: string) => {
const baseClass = "w-4 h-4 inline-block align-middle";
const inverseIndicators = ['cpiYoY', 'coreCpi', 'ppi', 'unemployment', 'joblessClaims', 'hySpread', 'ccDelinquency', 'buffett'];
const isInverse = inverseIndicators.includes(key);
if (trend === 'UP') {
return <ArrowUpRight className={`${baseClass} ${isInverse ? 'text-rose-400' : 'text-emerald-400'}`} />;
}
if (trend === 'DOWN') {
return <ArrowDownRight className={`${baseClass} ${isInverse ? 'text-emerald-400' : 'text-rose-400'}`} />;
}
return <Minus className={`${baseClass} text-slate-500`} />;
};
// Render individual row helper
const renderRow = (key: string, ind: MacroIndicator) => {
if (!ind) return null;
const inverseIndicators = ['cpiYoY', 'coreCpi', 'ppi', 'unemployment', 'joblessClaims', 'hySpread', 'ccDelinquency', 'buffett'];
const isInverse = inverseIndicators.includes(key);
const strokeColor = (ind.trend === 'UP' && isInverse) || (ind.trend === 'DOWN' && !isInverse) ? '#f43f5e' : '#10b981';
return (
<div key={key} className="bg-slate-950/40 border border-slate-850 rounded-xl p-3 flex justify-between items-center hover:bg-slate-950/60 transition-colors">
<div className="space-y-0.5 max-w-[130px]">
<div className="text-xs font-semibold text-slate-200 truncate" title={ind.name}>{ind.name}</div>
<div className="text-[9px] text-slate-500 font-mono">Vorherig: {ind.previous === null || ind.previous === undefined ? '' : ind.previous}{ind.unit}</div>
</div>
{/* Micro Recharts Sparkline */}
<div className="w-20 h-8">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={ind.data}>
<Line
type="monotone"
dataKey="value"
stroke={strokeColor}
strokeWidth={1.5}
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
<div className="text-right">
<div className={`font-mono text-sm font-bold ${getValHighlightClass(key, ind.current, ind.trend)}`}>
{ind.current}{ind.unit}
</div>
<div className="text-[9px] flex items-center justify-end gap-0.5">
{renderTrendIcon(ind.trend, key)}
<span className="text-slate-500 font-mono capitalize">{ind.trend.toLowerCase()}</span>
</div>
</div>
</div>
);
};
return (
<div className="space-y-6">
{/* ⚠️ Dynamic Rate-Limit Override Warning Banner */}
{!payload.liveDataAvailable && (
<div className="bg-rose-950/40 border border-rose-800/80 text-rose-400 text-xs rounded-xl p-4 flex items-center gap-3 shadow-[0_0_15px_rgba(244,63,94,0.15)]">
<AlertCircle className="w-5 h-5 text-rose-400 shrink-0" />
<div className="flex-1">
<span className="font-bold font-mono uppercase tracking-wider block mb-0.5">[ API Limit - Historical Archive Active]</span>
Der Echtzeit-Datenabruf (Juni 2026) ist aufgrund von Ratenbeschränkungen (FMP HTTP 429) gedrosselt. Das System operiert im sicheren, gepufferten historischen Archiv-Modus.
</div>
</div>
)}
{/* SECTION 1: Header & Control Bar */}
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-indigo-500/10 rounded-full blur-3xl -z-10" />
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div className="space-y-1">
<span className="text-indigo-400 text-xs font-semibold uppercase tracking-wider">Macroeconomics Silo</span>
<h2 className="text-2xl font-extrabold text-white flex items-center gap-2">
<Landmark className="text-indigo-400 w-6 h-6" /> Makroökonomische Indikatoren & Kredit-Silo
</h2>
<p className="text-xs text-slate-400">
Analysiert Zyklen, Liquiditätsflüsse und Zinskurven über die letzten 24 Monate.
</p>
</div>
<div className="flex items-center gap-3 w-full md:w-auto justify-end">
<button
onClick={() => setIsMathModalOpen(true)}
className="flex items-center gap-1.5 px-4 py-2.5 rounded-xl bg-slate-955/80 hover:bg-slate-900 border border-slate-800 hover:border-slate-700 transition-all font-semibold text-xs tracking-wider text-indigo-400 w-full md:w-auto justify-center h-11 cursor-pointer"
>
<BookOpen className="w-4 h-4" />
<span>📖 Modulerklärung</span>
</button>
<div className="bg-slate-955/80 border border-slate-800 rounded-xl px-4 py-2 text-right shrink-0 h-11 flex flex-col justify-center">
<div className="text-[9px] text-slate-555 uppercase font-mono">Letztes Update</div>
<div className="font-mono text-xs text-slate-300">
{new Date(payload.timestamp).toLocaleTimeString()}
</div>
</div>
</div>
</div>
</div>
{/* 🏛️ SECTION 1.5: 3 Large Glowing Neon-Ampel Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* CARD 1: Inflation & Konsum-Stress */}
<div className={`bg-slate-900/60 backdrop-blur-md border rounded-2xl p-5 text-slate-100 shadow-xl transition-all relative overflow-hidden ${
card1Status === 'RED' ? 'border-rose-500/30 shadow-[0_0_20px_rgba(244,63,94,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-rose-950/10' :
card1Status === 'AMBER' ? 'border-amber-500/30 shadow-[0_0_20px_rgba(245,158,11,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-amber-950/10' :
'border-emerald-500/30 shadow-[0_0_20px_rgba(16,185,129,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-emerald-950/10'
}`}>
<div className="absolute top-0 right-0 w-24 h-24 bg-rose-500/5 rounded-full blur-2xl -z-10" />
<div className="flex justify-between items-start">
<div className="space-y-1">
<span className="text-slate-400 text-[10px] font-mono uppercase tracking-wider">Kategorie 1</span>
<h4 className="text-sm font-bold text-slate-200">Inflation & Konsum-Stress</h4>
</div>
{/* Glowing Neon-Ampel Light */}
<div className={`w-3 h-3 rounded-full ${
card1Status === 'RED' ? 'bg-rose-500 shadow-[0_0_12px_rgba(244,63,94,0.85)] animate-pulse' :
card1Status === 'AMBER' ? 'bg-amber-500 shadow-[0_0_12px_rgba(245,158,11,0.85)] animate-pulse' :
'bg-emerald-500 shadow-[0_0_12px_rgba(16,185,129,0.85)] animate-pulse'
}`} />
</div>
<div className="mt-4 space-y-3">
<div className="text-xs text-slate-400">
Risiko-Status:{' '}
<span className={`font-bold ${
card1Status === 'RED' ? 'text-rose-400' : card1Status === 'AMBER' ? 'text-amber-400' : 'text-emerald-400'
}`}>
{card1Status === 'RED' ? '🚨 Kritischer Konsumdruck' : card1Status === 'AMBER' ? '⚠️ Erhöhtes Risiko' : '✅ Stabil'}
</span>
</div>
<div className="grid grid-cols-3 gap-2 pt-2 border-t border-slate-800/60 text-center font-mono">
<div>
<div className="text-[10px] text-slate-500">CPI</div>
<div className={`text-xs font-bold ${cpiYoY && cpiYoY.current >= 3.0 ? 'text-rose-400' : 'text-slate-200'}`}>
{cpiYoY ? cpiYoY.current.toFixed(1) : '0.0'}%
</div>
</div>
<div>
<div className="text-[10px] text-slate-500">Sparquote</div>
<div className={`text-xs font-bold ${savingsRate && savingsRate.current < 3.0 ? 'text-rose-400' : 'text-slate-200'}`}>
{savingsRate ? savingsRate.current.toFixed(1) : '0.0'}%
</div>
</div>
<div>
<div className="text-[10px] text-slate-500">Ausfälle</div>
<div className={`text-xs font-bold ${ccDelinquency && ccDelinquency.current > 4.5 ? 'text-rose-400' : 'text-slate-200'}`}>
{ccDelinquency ? ccDelinquency.current.toFixed(1) : '0.0'}%
</div>
</div>
</div>
</div>
</div>
{/* CARD 2: Bewertung & Liquidität */}
<div className={`bg-slate-900/60 backdrop-blur-md border rounded-2xl p-5 text-slate-100 shadow-xl transition-all relative overflow-hidden ${
card2Status === 'RED' ? 'border-rose-500/30 shadow-[0_0_20px_rgba(244,63,94,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-rose-950/10' :
card2Status === 'AMBER' ? 'border-amber-500/30 shadow-[0_0_20px_rgba(245,158,11,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-amber-950/10' :
'border-emerald-500/30 shadow-[0_0_20px_rgba(16,185,129,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-emerald-950/10'
}`}>
<div className="absolute top-0 right-0 w-24 h-24 bg-indigo-500/5 rounded-full blur-2xl -z-10" />
<div className="flex justify-between items-start">
<div className="space-y-1">
<span className="text-slate-400 text-[10px] font-mono uppercase tracking-wider">Kategorie 2</span>
<h4 className="text-sm font-bold text-slate-200">Bewertung & Liquidität</h4>
</div>
{/* Glowing Neon-Ampel Light */}
<div className={`w-3 h-3 rounded-full ${
card2Status === 'RED' ? 'bg-rose-500 shadow-[0_0_12px_rgba(244,63,94,0.85)] animate-pulse' :
card2Status === 'AMBER' ? 'bg-amber-500 shadow-[0_0_12px_rgba(245,158,11,0.85)] animate-pulse' :
'bg-emerald-500 shadow-[0_0_12px_rgba(16,185,129,0.85)] animate-pulse'
}`} />
</div>
<div className="mt-4 space-y-3">
<div className="text-xs text-slate-400">
Risiko-Status:{' '}
<span className={`font-bold ${
card2Status === 'RED' ? 'text-rose-400' : card2Status === 'AMBER' ? 'text-amber-400' : 'text-emerald-400'
}`}>
{card2Status === 'RED' ? '🚨 Extreme Überbewertung' : card2Status === 'AMBER' ? '⚠️ Liquiditäts-Engpass' : '✅ Ausreichend'}
</span>
</div>
<div className="grid grid-cols-2 gap-2 pt-2 border-t border-slate-800/60 text-center font-mono">
<div>
<div className="text-[10px] text-slate-500">Buffett Indicator</div>
<div className={`text-xs font-bold ${buffett && buffett.current > 150.0 ? 'text-rose-400' : 'text-slate-200'}`}>
{buffett ? buffett.current.toFixed(1) : '0.0'}%
</div>
</div>
<div>
<div className="text-[10px] text-slate-500">Net Fed Liquidity</div>
<div className="text-xs font-bold text-indigo-300">
{netLiquidityIndicator ? netLiquidityIndicator.current.toFixed(2) : '0.0'} T$
</div>
</div>
</div>
</div>
</div>
{/* CARD 3: Kredit- & Rezessionsrisiko */}
<div className={`bg-slate-900/60 backdrop-blur-md border rounded-2xl p-5 text-slate-100 shadow-xl transition-all relative overflow-hidden ${
card3Status === 'RED' ? 'border-rose-500/30 shadow-[0_0_20px_rgba(244,63,94,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-rose-950/10' :
card3Status === 'AMBER' ? 'border-amber-500/30 shadow-[0_0_20px_rgba(245,158,11,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-amber-950/10' :
'border-emerald-500/30 shadow-[0_0_20px_rgba(16,185,129,0.15)] bg-gradient-to-br from-slate-900 via-slate-900 to-emerald-950/10'
}`}>
<div className="absolute top-0 right-0 w-24 h-24 bg-emerald-500/5 rounded-full blur-2xl -z-10" />
<div className="flex justify-between items-start">
<div className="space-y-1">
<span className="text-slate-400 text-[10px] font-mono uppercase tracking-wider">Kategorie 3</span>
<h4 className="text-sm font-bold text-slate-200">Kredit- & Rezessionsrisiko</h4>
</div>
{/* Glowing Neon-Ampel Light */}
<div className={`w-3 h-3 rounded-full ${
card3Status === 'RED' ? 'bg-rose-500 shadow-[0_0_12px_rgba(244,63,94,0.85)] animate-pulse' :
card3Status === 'AMBER' ? 'bg-amber-500 shadow-[0_0_12px_rgba(245,158,11,0.85)] animate-pulse' :
'bg-emerald-500 shadow-[0_0_12px_rgba(16,185,129,0.85)] animate-pulse'
}`} />
</div>
<div className="mt-4 space-y-3">
<div className="text-xs text-slate-400">
Risiko-Status:{' '}
<span className={`font-bold ${
card3Status === 'RED' ? 'text-rose-400' : card3Status === 'AMBER' ? 'text-amber-400' : 'text-emerald-400'
}`}>
{card3Status === 'RED' ? '🚨 Rezessions-Inversion' : card3Status === 'AMBER' ? '⚠️ Zinskurven-Warnung' : '✅ Stabil'}
</span>
</div>
<div className="grid grid-cols-2 gap-2 pt-2 border-t border-slate-800/60 text-center font-mono">
<div>
<div className="text-[10px] text-slate-500">2S10S Spread</div>
<div className={`text-xs font-bold ${yieldSpread && yieldSpread.current < 0 ? 'text-rose-400' : 'text-emerald-400'}`}>
{yieldSpread ? (yieldSpread.current >= 0 ? '+' : '') + yieldSpread.current.toFixed(2) : '0.00'}%
</div>
</div>
<div>
<div className="text-[10px] text-slate-500">High-Yield Spread</div>
<div className={`text-xs font-bold ${hySpread && hySpread.current > 5.0 ? 'text-rose-400' : 'text-slate-200'}`}>
{hySpread ? hySpread.current.toFixed(1) : '0.0'}%
</div>
</div>
</div>
</div>
</div>
</div>
{/* SECTION 2: Accordion for Detailed Economic Data 3-Grid Panels */}
<div className="bg-slate-900/40 backdrop-blur-md border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
<button
onClick={() => setIsAccordionOpen(!isAccordionOpen)}
className="w-full px-6 py-4 flex items-center justify-between hover:bg-slate-900/60 transition-all font-bold text-sm text-slate-200 cursor-pointer"
>
<div className="flex items-center gap-2">
<Activity className="w-5 h-5 text-indigo-400" />
<span>🏛 Detailliertes makroökonomisches Indikatoren-Hauptbuch (21 Indikatoren)</span>
</div>
<div className="flex items-center gap-2">
<span className="text-xs text-slate-500 font-mono">
{isAccordionOpen ? 'Einklappen' : 'Ausklappen'}
</span>
<div className="text-slate-400">
{isAccordionOpen ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}
</div>
</div>
</button>
{isAccordionOpen && (
<div className="p-6 border-t border-slate-800/80 space-y-6 bg-slate-950/20">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* PANEL 1: Inflation & Wachstum */}
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-5 text-slate-100 shadow-xl space-y-4">
<div className="border-b border-slate-800 pb-3 flex items-center justify-between">
<h3 className="font-bold text-sm text-white flex items-center gap-2">
<Activity className="w-4 h-4 text-emerald-400" /> Inflation & Wachstum
</h3>
<span className="text-[10px] bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 px-2 py-0.5 rounded font-mono font-bold">Real-Data</span>
</div>
<div className="space-y-4">
{renderRow('cpiYoY', cpiYoY)}
{renderRow('coreCpi', coreCpi)}
{renderRow('ppi', ppi)}
{renderRow('savingsRate', savingsRate)}
{renderRow('ccDelinquency', ccDelinquency)}
</div>
</div>
{/* PANEL 2: Zentralbanken & Liquidität */}
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-5 text-slate-100 shadow-xl space-y-4">
<div className="border-b border-slate-800 pb-3 flex items-center justify-between">
<h3 className="font-bold text-sm text-white flex items-center gap-2">
<Coins className="w-4 h-4 text-indigo-400" /> Zentralbanken & Liquidität
</h3>
<span className="text-[10px] bg-indigo-500/10 text-indigo-400 border border-indigo-500/20 px-2 py-0.5 rounded font-mono font-bold">M2 / RRP / TGA</span>
</div>
<div className="space-y-4">
{renderRow('fedFunds', fedFunds)}
{renderRow('ecbRefi', ecbRefi)}
{renderRow('fedBalanceSheet', fedBalanceSheet)}
{renderRow('m2', m2)}
{renderRow('rrp', rrp)}
{renderRow('tga', tga)}
{renderRow('buffett', buffett)}
{/* Dynamic Calculated Net Liquidity Proxy Row */}
{netLiquidityIndicator && (
<div className="bg-indigo-950/20 border border-indigo-900/50 rounded-xl p-3 flex justify-between items-center hover:bg-indigo-950/30 transition-colors">
<div className="space-y-0.5 max-w-[130px]">
<div className="text-xs font-bold text-indigo-300 truncate" title={netLiquidityIndicator.name}>
Net Fed Liquidity
</div>
<div className="text-[9px] text-indigo-400/70 font-mono">Vorherig: {netLiquidityIndicator.previous}{netLiquidityIndicator.unit}</div>
</div>
{/* Micro Recharts Sparkline */}
<div className="w-20 h-8">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={netLiquidityIndicator.data}>
<Line
type="monotone"
dataKey="value"
stroke="#818cf8"
strokeWidth={2}
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
<div className="text-right">
<div className="font-mono text-sm font-extrabold text-indigo-300">
{netLiquidityIndicator.current}{netLiquidityIndicator.unit}
</div>
<div className="text-[9px] flex items-center justify-end gap-0.5">
{renderTrendIcon(netLiquidityIndicator.trend, 'netLiquidity')}
<span className="text-indigo-400 font-mono capitalize">{netLiquidityIndicator.trend.toLowerCase()}</span>
</div>
</div>
</div>
)}
</div>
</div>
{/* PANEL 3: Kredit- & Anleihemarkt */}
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-5 text-slate-100 shadow-xl space-y-4">
<div className="border-b border-slate-800 pb-3 flex items-center justify-between">
<h3 className="font-bold text-sm text-white flex items-center gap-2">
<ShieldAlert className="w-4 h-4 text-rose-400" /> Kredit- & Anleihemarkt
</h3>
<span className="text-[10px] bg-rose-500/10 text-rose-400 border border-rose-500/20 px-2 py-0.5 rounded font-mono font-bold">Zinskurven & Spreads</span>
</div>
<div className="space-y-4">
{renderRow('us10y', us10y)}
{renderRow('yieldSpread', yieldSpread)}
{renderRow('hySpread', hySpread)}
{/* Sub-section named "Immobilien- & Hypotheken-Kredite" */}
<div className="border-t border-slate-800/80 pt-4 mt-4">
<h4 className="text-xs font-bold text-indigo-400 uppercase tracking-wider font-mono mb-3 flex items-center gap-1.5">
<Landmark className="w-3.5 h-3.5" /> Immobilien- & Hypotheken-Kredite
</h4>
</div>
{renderRow('housingStarts', housingStarts)}
{renderRow('mortgageApps', mortgageApps)}
{renderRow('caseShiller', caseShiller)}
</div>
</div>
</div>
</div>
)}
</div>
{/* SECTION 3: Dynamic Macro analysis and explanation */}
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl space-y-4">
<h3 className="font-bold text-sm text-slate-200">Systemische Macro- & Kreditmarkt-Analyse</h3>
<p className="text-xs text-slate-400 leading-relaxed">
Zinskurveninversionen (z. B. wenn der <span className="text-indigo-400 font-semibold font-mono">2S10S-Yield-Spread</span> negativ ist) gelten historisch als zuverlässige Vorläufer ökonomischer Kontraktionen. Derzeit beträgt der Spread <span className="text-emerald-400 font-bold font-mono">{yieldSpread !== undefined ? (yieldSpread.current >= 0 ? '+' : '') + yieldSpread.current.toFixed(2) : '0.00'}%</span>. Gleichzeitig signalisiert der Buffett-Indikator mit <span className="text-rose-400 font-bold font-mono">{buffett !== undefined ? buffett.current.toFixed(1) : '0.0'}%</span> eine erhebliche Überbewertung des US-Aktienmarktes relativ zur Wirtschaftsleistung. Im Konsumsektor deutet die Kombination aus einer niedrigen Sparquote (<span className="text-amber-400 font-mono">{savingsRate !== undefined ? savingsRate.current.toFixed(1) : '0.0'}%</span>) und steigenden Kreditkartenausfällen (<span className="text-rose-400 font-mono">{ccDelinquency !== undefined ? ccDelinquency.current.toFixed(1) : '0.0'}%</span>) auf echten Stress hin, während der High-Yield Credit Spread (<span className="text-slate-300 font-bold font-mono">{hySpread !== undefined ? hySpread.current.toFixed(1) : '0.0'}%</span>) noch Stabilität anzeigt.
Monetäre Liquidität (<span className="font-bold text-indigo-300 font-mono">Net Fed Liquidity Proxy: {netLiquidityIndicator ? netLiquidityIndicator.current.toFixed(2) : '0.00'} T$</span>) wirkt als zentraler Impulsgeber: Ein Anstieg des TGA-Volumens oder der RRP-Nutzung zieht freie Liquidität aus dem Bankensystem ab (Bremswirkung für Aktien/Krypto), während ein Abbau dieser Posten zusätzliche Liquidität freisetzt (Rückenwind für Risk Assets).
</p>
</div>
<MacroMathModal isOpen={isMathModalOpen} onClose={() => setIsMathModalOpen(false)} />
</div>
);
}