Closes #ISSUE-008 - Overreaction Scanner Overhaul: GJR-GARCH rebound gauge, catalyst drawers, and Category C small-caps
This commit is contained in:
@@ -6,7 +6,8 @@ import 'katex/dist/katex.min.css';
|
||||
import MacroMathModal from './MacroMathModal';
|
||||
import {
|
||||
TrendingUp, Landmark, AlertCircle, BookOpen, Percent,
|
||||
ArrowDownRight, ArrowUpRight, Minus, Activity, ShieldAlert, Coins
|
||||
ArrowDownRight, ArrowUpRight, Minus, Activity, ShieldAlert, Coins,
|
||||
ChevronDown, ChevronUp
|
||||
} from 'lucide-react';
|
||||
|
||||
interface IndicatorDataPoint {
|
||||
@@ -36,6 +37,7 @@ export default function MacroIndicatorsDemo() {
|
||||
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 () => {
|
||||
@@ -122,6 +124,44 @@ export default function MacroIndicatorsDemo() {
|
||||
|
||||
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') {
|
||||
@@ -132,31 +172,83 @@ export default function MacroIndicatorsDemo() {
|
||||
}
|
||||
if (key === 'cpiYoY' || key === 'coreCpi') {
|
||||
if (val <= 2.5) return 'text-emerald-400 font-semibold';
|
||||
if (val >= 3.0) return 'text-amber-400';
|
||||
if (val >= 3.0) return 'text-rose-400';
|
||||
}
|
||||
if (key === 'm2' && trend === 'DOWN') {
|
||||
return 'text-rose-400';
|
||||
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') {
|
||||
const isBad = key === 'cpiYoY' || key === 'coreCpi' || key === 'ppi' || key === 'unemployment' || key === 'joblessClaims' || key === 'hySpread';
|
||||
return <ArrowUpRight className={`${baseClass} ${isBad ? 'text-rose-400' : 'text-emerald-400'}`} />;
|
||||
return <ArrowUpRight className={`${baseClass} ${isInverse ? 'text-rose-400' : 'text-emerald-400'}`} />;
|
||||
}
|
||||
if (trend === 'DOWN') {
|
||||
const isBad = key === 'nfp' || key === 'fedFunds' || key === 'fedBalanceSheet' || key === 'm2' || key === 'rrp' || key === 'tga';
|
||||
return <ArrowDownRight className={`${baseClass} ${isBad ? 'text-rose-400' : 'text-emerald-400'}`} />;
|
||||
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">
|
||||
|
||||
@@ -189,14 +281,14 @@ export default function MacroIndicatorsDemo() {
|
||||
<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-950/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"
|
||||
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-950/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-500 uppercase font-mono">Letztes Update</div>
|
||||
<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>
|
||||
@@ -205,199 +297,291 @@ export default function MacroIndicatorsDemo() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SECTION 2: Economic Data 3-Grid Panels */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* 🏛️ SECTION 1.5: 3 Large Glowing Neon-Ampel Cards */}
|
||||
<div className="grid grid-cols-1 md: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>
|
||||
{/* 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="space-y-4">
|
||||
{Object.entries(indicators)
|
||||
.filter(([_, ind]) => ind.category === 'Inflation & Wachstum')
|
||||
.map(([key, ind]) => (
|
||||
<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}{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={ind.trend === 'UP' ? '#f43f5e' : '#10b981'}
|
||||
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>
|
||||
))}
|
||||
</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">
|
||||
{/* Fed Funds, ECB Refi, Fed Assets, M2, RRP, TGA */}
|
||||
{Object.entries(indicators)
|
||||
.filter(([_, ind]) => ind.category === 'Zentralbanken & Liquidität')
|
||||
.map(([key, ind]) => (
|
||||
<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}{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={key === 'rrp' || key === 'fedBalanceSheet' ? '#f43f5e' : '#10b981'}
|
||||
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>
|
||||
))}
|
||||
|
||||
{/* 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 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>
|
||||
|
||||
{/* 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">
|
||||
{Object.entries(indicators)
|
||||
.filter(([_, ind]) => ind.category === 'Kredit- & Anleihemarkt')
|
||||
.map(([key, ind]) => (
|
||||
<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}{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={key === 'yieldSpread' && ind.current < 0 ? '#f43f5e' : '#10b981'}
|
||||
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>
|
||||
<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 un-invertiert die Kurve (<span className="text-emerald-400 font-bold font-mono">+{indicators.yieldSpread?.current.toFixed(2)}%</span>), was oft kurz vor oder während einer konjunkturellen Anpassungsphase auftritt. Gleichzeitig zeigt der Kreditmarkt mit einem High-Yield Credit Spread von <span className="text-slate-300 font-bold font-mono">{indicators.hySpread?.current}%</span> ein ruhiges, risikoarmes Bild.
|
||||
Monetäre Liquidität (<span className="font-bold text-indigo-300 font-mono">Net Fed Liquidity Proxy: {netLiquidityIndicator?.current} 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).
|
||||
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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user