Files
investment-sandbox/components/modules/macro/MacroIndicatorsDemo.tsx
2026-06-13 15:16:57 +02:00

603 lines
30 KiB
TypeScript
Raw Permalink 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 MacroBlueprintModal from './MacroBlueprintModal';
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 [isBlueprintModalOpen, setIsBlueprintModalOpen] = 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('Error fetching macroeconomic indicators.');
}
} catch (err) {
console.error('Fetch macro indicators error:', err);
setError('Network error loading macroeconomic data.');
} 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: 'Central Banks & Liquidity',
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">Loading macroeconomic data archive...</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 || 'Error loading data.'}
</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">Previous: {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>
Real-time data retrieval (June 2026) is throttled due to rate limits (FMP HTTP 429). The system is operating in a secure, buffered historical archive mode.
</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" /> Macroeconomic Indicators & Credit Silo
</h2>
<p className="text-xs text-slate-400">
Analyzes cycles, liquidity flows, and yield curves over the past 24 months.
</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-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"
>
<BookOpen className="w-4 h-4" />
<span>📖 Quantitative Handbook</span>
</button>
<button
onClick={() => setIsBlueprintModalOpen(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 animate-pulse-slow"
>
<Activity className="w-4 h-4" />
<span> Operational Blueprint</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-555 uppercase font-mono">Last 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">Category 1</span>
<h4 className="text-sm font-bold text-slate-200">Inflation & Consumer 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">
Risk Status:{' '}
<span className={`font-bold ${
card1Status === 'RED' ? 'text-rose-400' : card1Status === 'AMBER' ? 'text-amber-400' : 'text-emerald-400'
}`}>
{card1Status === 'RED' ? '🚨 Critical Consumer Pressure' : card1Status === 'AMBER' ? '⚠️ Elevated Risk' : '✅ Stable'}
</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">Savings Rate</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">Delinquencies</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">Category 2</span>
<h4 className="text-sm font-bold text-slate-200">Valuation & Liquidity</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">
Risk Status:{' '}
<span className={`font-bold ${
card2Status === 'RED' ? 'text-rose-400' : card2Status === 'AMBER' ? 'text-amber-400' : 'text-emerald-400'
}`}>
{card2Status === 'RED' ? '🚨 Extreme Overvaluation' : card2Status === 'AMBER' ? '⚠️ Liquidity Squeeze' : '✅ Sufficient'}
</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">Category 3</span>
<h4 className="text-sm font-bold text-slate-200">Credit & Recession Risk</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">
Risk Status:{' '}
<span className={`font-bold ${
card3Status === 'RED' ? 'text-rose-400' : card3Status === 'AMBER' ? 'text-amber-400' : 'text-emerald-400'
}`}>
{card3Status === 'RED' ? '🚨 Recession Inversion' : card3Status === 'AMBER' ? '⚠️ Yield Curve Warning' : '✅ Stable'}
</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>🏛 Detailed Macroeconomic Indicators Ledger (21 Indicators)</span>
</div>
<div className="flex items-center gap-2">
<span className="text-xs text-slate-500 font-mono">
{isAccordionOpen ? 'Collapse' : 'Expand'}
</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 & Growth
</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" /> Central Banks & Liquidity
</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">Previous: {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" /> Credit & Bond Market
</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">Yield Curves & Spreads</span>
</div>
<div className="space-y-4">
{renderRow('us10y', us10y)}
{renderRow('yieldSpread', yieldSpread)}
{renderRow('hySpread', hySpread)}
{/* Sub-section named "Real Estate & Mortgage Credit" */}
<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" /> Real Estate & Mortgage Credit
</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">Systemic Macro & Credit Market Analysis</h3>
<p className="text-xs text-slate-400 leading-relaxed">
Yield curve inversions (e.g. when the <span className="text-indigo-400 font-semibold font-mono">2S10S Yield Spread</span> is negative) are historically reliable leading indicators of economic contractions. Currently, the spread is <span className="text-emerald-400 font-bold font-mono">{yieldSpread !== undefined ? (yieldSpread.current >= 0 ? '+' : '') + yieldSpread.current.toFixed(2) : '0.00'}%</span>. At the same time, the Buffett Indicator indicates an overvaluation of the US stock market relative to economic output. In the consumer sector, the combination of a low savings rate (<span className="text-amber-400 font-mono">{savingsRate !== undefined ? savingsRate.current.toFixed(1) : '0.0'}%</span>) and rising credit card delinquencies (<span className="text-rose-400 font-mono">{ccDelinquency !== undefined ? ccDelinquency.current.toFixed(1) : '0.0'}%</span>) suggests genuine distress, while the High-Yield credit spread (<span className="text-slate-300 font-bold font-mono">{hySpread !== undefined ? hySpread.current.toFixed(1) : '0.0'}%</span>) still indicates stability.
Monetary liquidity (<span className="font-bold text-indigo-300 font-mono">Net Fed Liquidity Proxy: {netLiquidityIndicator ? netLiquidityIndicator.current.toFixed(2) : '0.00'} T$</span>) acts as a central impulse: an increase in TGA volume or RRP usage drains liquidity from the banking system (headwind for equities/crypto), while a decrease in these items releases additional liquidity (tailwind for risk assets).
</p>
</div>
<MacroMathModal isOpen={isMathModalOpen} onClose={() => setIsMathModalOpen(false)} />
<MacroBlueprintModal isOpen={isBlueprintModalOpen} onClose={() => setIsBlueprintModalOpen(false)} />
</div>
);
}