Closes #014 - System-wide handbook sweep, English consolidation & Crypto state persistence
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
import React from 'react';
|
||||
import { BookOpen } from 'lucide-react';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import { BlockMath, InlineMath } from 'react-katex';
|
||||
|
||||
interface PortfolioMathModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function PortfolioMathModal({ isOpen, onClose }: PortfolioMathModalProps) {
|
||||
React.useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
if (isOpen) {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/85 backdrop-blur-md p-4 sm:p-6 md:p-8">
|
||||
<div className="bg-slate-900 border border-slate-800/80 rounded-3xl w-full max-w-4xl h-[80vh] flex flex-col overflow-hidden shadow-2xl relative text-slate-300">
|
||||
|
||||
{/* Modal Header */}
|
||||
<div className="flex justify-between items-center px-6 py-4 bg-slate-950/40 border-b border-slate-800/60">
|
||||
<div>
|
||||
<h2 className="text-base font-bold bg-gradient-to-r from-teal-400 to-emerald-400 bg-clip-text text-transparent flex items-center gap-2">
|
||||
<BookOpen className="w-5 h-5 text-teal-400" /> Portfolio Sandbox - Math & Logic Specification
|
||||
</h2>
|
||||
<p className="text-[10px] text-slate-500 font-mono">Institutional Specification Manual</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-slate-400 hover:text-slate-200 bg-slate-950/50 border border-slate-800 hover:border-slate-700 px-3 py-1.5 rounded-lg text-xs font-semibold font-mono transition-all cursor-pointer"
|
||||
>
|
||||
Schließen (ESC)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Modal Body */}
|
||||
<div className="flex-1 overflow-y-auto p-6 sm:p-8 space-y-6 text-slate-300 scrollbar-thin">
|
||||
<div className="space-y-6">
|
||||
<div className="border-b border-slate-800/80 pb-3">
|
||||
<h3 className="text-base font-bold text-slate-200">5. Portfolio Sandbox & Rebalancing Engine</h3>
|
||||
<p className="text-xs text-slate-400 mt-1">Estimates aggregate portfolio drawdowns and controls covariance drift boundaries.</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono">A. Synthetic Portfolio Model & Asset Weightings</h4>
|
||||
<p className="text-xs leading-relaxed text-slate-400">
|
||||
Constructs a continuous synthetic asset representing your active weight allocations and its daily return track:
|
||||
</p>
|
||||
<div className="bg-slate-950/40 p-4 rounded-xl border border-slate-800/60 my-2 space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1">Active Percentage Weighting (<InlineMath math="w_i" />) Calculation:</p>
|
||||
<BlockMath math="w_i = \\frac{\\text{Shares}_i \\times P_{\\text{current}, i}}{\\sum_{j} \\text{Shares}_j \\times P_{\\text{current}, j}}" />
|
||||
</div>
|
||||
<div className="border-t border-slate-850 pt-3">
|
||||
<p className="text-xs text-slate-400 mb-1">Synthetic Portfolio Log Return (<InlineMath math="R_{pt}" />):</p>
|
||||
<BlockMath math="R_{pt} = \\sum_{i} w_i \\times \\ln\\left(\\frac{P_{t, i}}{P_{t-1, i}}\\right)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono">B. Linear Mixed Effects Panel Regression (LMM)</h4>
|
||||
<p className="text-xs leading-relaxed text-slate-400">
|
||||
Solves the system-wide macro response model across all historical event instances <InlineMath math="j" /> using a Swamy-Arora GLS estimator:
|
||||
</p>
|
||||
<div className="bg-slate-950/40 p-4 rounded-xl border border-slate-800/60 my-2 space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1">Panel Model Specification with VIX Controls:</p>
|
||||
<BlockMath math="R_{ptj} = \\beta_0 + \\beta_{\\text{drift}} \\text{Pre}_t + \\beta_{\\text{impact}} \\text{Post}_t + \\beta_{\\text{VIX}} VIX_{tj} + u_j + e_{ptj}" />
|
||||
<p className="text-[10px] text-slate-500 mt-2 font-mono leading-relaxed">
|
||||
where:
|
||||
<br />
|
||||
- <InlineMath math="t \\in [-30, +30]" /> is the relative day offset from event date <InlineMath math="T_j" />.
|
||||
<br />
|
||||
- <InlineMath math="\\text{Pre}_t = \\mathbb{I}(t < 0)" /> and <InlineMath math="\\text{Post}_t = \\mathbb{I}(t > 0)" /> are relative phase indicators.
|
||||
<br />
|
||||
- <InlineMath math="VIX_{tj}" /> is the background market-wide volatility covariate.
|
||||
<br />
|
||||
- <InlineMath math="u_j \\sim N(0, \\sigma_u^2)" /> is the random group intercept (event instance shock).
|
||||
<br />
|
||||
- <InlineMath math="e_{ptj} \\sim N(0, \\sigma_e^2)" /> is the residual error.
|
||||
</p>
|
||||
</div>
|
||||
<div className="border-t border-slate-850 pt-3">
|
||||
<p className="text-xs text-slate-400 mb-1">Optimal Kelly Criterion Position Sizing:</p>
|
||||
<BlockMath math="f^* = \\frac{p \\times b - (1 - p)}{b}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono">C. Reinvestment & Optimization Generation</h4>
|
||||
<p className="text-xs leading-relaxed text-slate-400">
|
||||
Integrates signals across three engines: Scanner (underpriced value), Econometrics (macro event post-event betas), and Insiders (corporate buying).
|
||||
Ranks candidates and suggests target reallocations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { calculateEWMA, calculateKellyFraction, calculateAssetCovariance } from
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, ReferenceLine, AreaChart, Area } from 'recharts';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import { BlockMath, InlineMath } from 'react-katex';
|
||||
import PortfolioMathModal from './PortfolioMathModal';
|
||||
import SandboxMathModal from './SandboxMathModal';
|
||||
import {
|
||||
TrendingUp, Wallet, ArrowDownRight, ArrowUpRight, Percent, Plus, FolderSync,
|
||||
HelpCircle, Settings, Calendar, DollarSign, Tag, Check, AlertCircle, ChevronDown, ChevronUp, Sparkles,
|
||||
@@ -53,7 +53,7 @@ export default function SandboxDemo() {
|
||||
const [simulateFees, setSimulateFees] = useState(true);
|
||||
const [isBackfill, setIsBackfill] = useState(false);
|
||||
const [backfillDate, setBackfillDate] = useState('2026-05-20');
|
||||
const [hypothesisTag, setHypothesisTag] = useState('Fokus auf KI-Infrastruktur');
|
||||
const [hypothesisTag, setHypothesisTag] = useState('Focus on AI Infrastructure');
|
||||
const [orderError, setOrderError] = useState<string | null>(null);
|
||||
const [orderSuccess, setOrderSuccess] = useState(false);
|
||||
|
||||
@@ -89,11 +89,11 @@ export default function SandboxDemo() {
|
||||
const data = await response.json();
|
||||
setStressData(data);
|
||||
} else {
|
||||
setStressError("Fehler beim Laden der Stresstest-Daten.");
|
||||
setStressError("Error loading stress test data.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Stress test fetch error:", err);
|
||||
setStressError("Netzwerkfehler beim Laden des Stresstests.");
|
||||
setStressError("Network error loading stress test.");
|
||||
} finally {
|
||||
setStressLoading(false);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ export default function SandboxDemo() {
|
||||
const shares = Number(newAssetShares);
|
||||
const price = Number(newAssetPrice);
|
||||
if (isNaN(shares) || shares <= 0 || isNaN(price) || price <= 0) {
|
||||
alert("Bitte geben Sie eine gültige Stückzahl und einen Einstandskurs an.");
|
||||
alert("Please enter a valid number of shares and entry price.");
|
||||
return;
|
||||
}
|
||||
updatePortfolioAsset(ticker, shares, price);
|
||||
@@ -307,7 +307,7 @@ export default function SandboxDemo() {
|
||||
if (!mounted) {
|
||||
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-slate-400 text-sm font-mono animate-pulse">Lade Sandbox-Modul...</div>
|
||||
<div className="text-slate-400 text-sm font-mono animate-pulse">Loading Sandbox Module...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -331,7 +331,7 @@ export default function SandboxDemo() {
|
||||
setOrderSuccess(false);
|
||||
|
||||
if (tradeShares <= 0 || tradePrice <= 0) {
|
||||
setOrderError('Bitte geben Sie eine gültige Stückzahl und einen Kurs an.');
|
||||
setOrderError('Please enter a valid number of shares and price.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -354,8 +354,8 @@ export default function SandboxDemo() {
|
||||
} else {
|
||||
setOrderError(
|
||||
tradeType === 'BUY'
|
||||
? 'Unzureichendes Barguthaben (inklusive allfälliger Transaktionsgebühren).'
|
||||
: 'Unzureichende Anteile im Depot für den Verkauf.'
|
||||
? 'Insufficient cash balance (including transaction fees).'
|
||||
: 'Insufficient shares in portfolio for sale.'
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -367,7 +367,7 @@ export default function SandboxDemo() {
|
||||
<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)] animate-pulse">
|
||||
<AlertCircle className="w-5 h-5 text-rose-400 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<span className="font-bold">Kritische Klumpenrisiken (Kovarianz RED):</span> {activePortfolio.riskProfile.message}
|
||||
<span className="font-bold">Critical Concentration Risks (Covariance RED):</span> {activePortfolio.riskProfile.message}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -395,7 +395,7 @@ export default function SandboxDemo() {
|
||||
<button
|
||||
onClick={() => setShowNewPortfolioModal(true)}
|
||||
className="p-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-emerald-400 hover:text-emerald-300 transition-colors border border-slate-700"
|
||||
title="Neues Sandbox-Portfolio erstellen"
|
||||
title="Create new Sandbox Portfolio"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
</button>
|
||||
@@ -408,12 +408,12 @@ export default function SandboxDemo() {
|
||||
className="flex items-center gap-1.5 px-4 py-3 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-emerald-400 justify-center h-[58px] shrink-0"
|
||||
>
|
||||
<BookOpen className="w-3.5 h-3.5" />
|
||||
<span>📖 Modulerklärung</span>
|
||||
<span>📖 Quantitative Handbook</span>
|
||||
</button>
|
||||
|
||||
{/* Net Worth Card */}
|
||||
<div className="flex-1 md:flex-initial bg-slate-950/80 border border-slate-800 rounded-xl p-3 px-5 min-w-[140px]">
|
||||
<div className="text-[10px] text-slate-400 uppercase font-semibold">Gesamtwert</div>
|
||||
<div className="text-[10px] text-slate-400 uppercase font-semibold">Total Value</div>
|
||||
<div className="font-mono text-xl font-bold text-slate-100 mt-1">
|
||||
${netWorth.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||
</div>
|
||||
@@ -421,7 +421,7 @@ export default function SandboxDemo() {
|
||||
|
||||
{/* Performance Card */}
|
||||
<div className="flex-1 md:flex-initial bg-slate-950/80 border border-slate-800 rounded-xl p-3 px-5 min-w-[140px]">
|
||||
<div className="text-[10px] text-slate-400 uppercase font-semibold">GuV (Gesamt)</div>
|
||||
<div className="text-[10px] text-slate-400 uppercase font-semibold">PnL (Total)</div>
|
||||
<div className={`font-mono text-xl font-bold mt-1 flex items-center gap-1 ${isPositiveOverall ? 'text-emerald-400' : 'text-rose-400'}`}>
|
||||
{isPositiveOverall ? <ArrowUpRight className="w-5 h-5" /> : <ArrowDownRight className="w-5 h-5" />}
|
||||
<span>{isPositiveOverall ? '+' : ''}{totalGainLossPct.toFixed(2)}%</span>
|
||||
@@ -431,8 +431,8 @@ export default function SandboxDemo() {
|
||||
{/* Live EWMA Vol Card */}
|
||||
<div className="flex-1 md:flex-initial bg-slate-950/80 border border-slate-800 rounded-xl p-3 px-5 min-w-[140px] relative group">
|
||||
<div className="text-[10px] text-slate-400 uppercase font-semibold flex items-center gap-1">
|
||||
<span>EWMA Volatilität</span>
|
||||
<span className="cursor-help flex items-center" title="Annualisierte Schwankungsbreite basierend auf historischen Renditen.">
|
||||
<span>EWMA Volatility</span>
|
||||
<span className="cursor-help flex items-center" title="Annualized volatility based on historical returns.">
|
||||
<HelpCircle className="w-3.5 h-3.5 text-slate-500 group-hover:text-emerald-400 transition-colors" />
|
||||
</span>
|
||||
</div>
|
||||
@@ -444,8 +444,8 @@ export default function SandboxDemo() {
|
||||
{/* Covariance Risk Traffic Light Card */}
|
||||
<div className="flex-1 md:flex-initial bg-slate-950/80 border border-slate-800 rounded-xl p-3 px-5 min-w-[150px] relative group">
|
||||
<div className="text-[10px] text-slate-400 uppercase font-semibold flex items-center gap-1">
|
||||
<span>Kovarianz-Ampel</span>
|
||||
<span className="cursor-help flex items-center" title="Systemische Portfolio-Klumpenrisiken basierend auf historischen Asset-Kovarianzen.">
|
||||
<span>Covariance Traffic Light</span>
|
||||
<span className="cursor-help flex items-center" title="Systemic portfolio concentration risks based on historical asset covariances.">
|
||||
<HelpCircle className="w-3.5 h-3.5 text-slate-500 group-hover:text-rose-400 transition-colors" />
|
||||
</span>
|
||||
</div>
|
||||
@@ -471,21 +471,21 @@ export default function SandboxDemo() {
|
||||
{showNewPortfolioModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 max-w-sm w-full space-y-4 shadow-2xl">
|
||||
<h3 className="text-lg font-bold text-white">Neues Sandbox-Portfolio</h3>
|
||||
<h3 className="text-lg font-bold text-white">New Sandbox Portfolio</h3>
|
||||
<form onSubmit={handleCreatePortfolio} className="space-y-4">
|
||||
<div>
|
||||
<label className="text-xs text-slate-400 block mb-1">Portfolio Name</label>
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="z.B. Biotech Risk High Yield"
|
||||
placeholder="e.g. Biotech Risk High Yield"
|
||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg p-2.5 text-slate-100 focus:outline-none focus:border-emerald-500"
|
||||
value={newPortfolioName}
|
||||
onChange={(e) => setNewPortfolioName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-400 block mb-1">Startkapital ($)</label>
|
||||
<label className="text-xs text-slate-400 block mb-1">Starting Capital ($)</label>
|
||||
<input
|
||||
type="number"
|
||||
required
|
||||
@@ -500,13 +500,13 @@ export default function SandboxDemo() {
|
||||
onClick={() => setShowNewPortfolioModal(false)}
|
||||
className="flex-1 bg-slate-850 hover:bg-slate-800 text-slate-300 font-semibold py-2 rounded-lg transition-colors border border-slate-700"
|
||||
>
|
||||
Abbrechen
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex-1 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-slate-950 font-bold py-2 rounded-lg transition-all shadow-lg shadow-emerald-500/20"
|
||||
>
|
||||
Erstellen
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -521,53 +521,53 @@ export default function SandboxDemo() {
|
||||
className="flex items-center gap-1.5 text-xs text-slate-400 hover:text-emerald-400 transition-colors focus:outline-none"
|
||||
>
|
||||
<span>{showMathAccordion ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}</span>
|
||||
<span className="font-semibold uppercase tracking-wider">Mathematische Spezifikation & EWMA-Volatilitätsmodell</span>
|
||||
<span className="font-semibold uppercase tracking-wider">Mathematical Specification & EWMA Volatility Model</span>
|
||||
</button>
|
||||
|
||||
{showMathAccordion && (
|
||||
<div className="mt-4 p-4 rounded-xl border border-slate-850 bg-slate-950/40 text-xs text-slate-300 space-y-4">
|
||||
<div>
|
||||
<h4 className="font-semibold text-emerald-400 mb-1.5">1. EWMA Volatilitätsmodell</h4>
|
||||
<h4 className="font-semibold text-emerald-400 mb-1.5">1. EWMA Volatility Model</h4>
|
||||
<p className="mb-2">
|
||||
Die Volatilität wird mittels des <strong>Exponentially Weighted Moving Average (EWMA)</strong>-Modells ermittelt. Jüngere Renditen erhalten hierbei ein höheres Gewicht als weiter in der Vergangenheit liegende Renditen, gesteuert durch den Zerfallsparameter <InlineMath math="\lambda" /> (Lambda).
|
||||
Volatility is calculated using the <strong>Exponentially Weighted Moving Average (EWMA)</strong> model. Recent returns receive a higher weighting than returns further in the past, controlled by the decay parameter <InlineMath math="\lambda" /> (Lambda).
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto">
|
||||
<BlockMath math="\sigma_t^2 = \lambda \sigma_{t-1}^2 + (1 - \lambda) r_{t-1}^2" />
|
||||
</div>
|
||||
<p className="mb-2">
|
||||
Die tägliche Volatilität <InlineMath math="\sigma_t" /> wird auf ein ganzes Jahr hochgerechnet (Annualisierung) unter der Annahme von 252 Handelstagen:
|
||||
The daily volatility <InlineMath math="\sigma_t" /> is extrapolated to an entire year (annualization) assuming 252 trading days:
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto">
|
||||
<BlockMath math="\sigma_{\text{ann}} = \sqrt{\sigma_t^2 \times 252}" />
|
||||
</div>
|
||||
<p className="text-slate-400">
|
||||
RiskMetrics empfiehlt für tägliche Finanzdaten einen Lambda-Wert von <InlineMath math="\lambda = 0.94" />.
|
||||
RiskMetrics recommends a Lambda value of <InlineMath math="\lambda = 0.94" /> for daily financial data.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-slate-800 pt-3">
|
||||
<h4 className="font-semibold text-emerald-400 mb-1.5">2. Kelly-Kriterium zur Positionsgrößenbestimmung</h4>
|
||||
<h4 className="font-semibold text-emerald-400 mb-1.5">2. Kelly Criterion for Position Sizing</h4>
|
||||
<p className="mb-2">
|
||||
Die Kelly-Formel bestimmt den optimalen Anteil des Kapitals (<InlineMath math="f^*" />), der in ein Geschäft investiert werden soll, um das exponentielle Wachstum des Kapitals zu maximieren:
|
||||
The Kelly formula determines the optimal fraction of capital (<InlineMath math="f^*" />) to invest in a trade to maximize the exponential growth of wealth:
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto">
|
||||
<BlockMath math="f^* = \frac{p \cdot b - q}{b} = \frac{p \cdot b - (1 - p)}{b}" />
|
||||
</div>
|
||||
<p className="mb-2">
|
||||
Um Risiken durch ungenaue Schätzungen zu verringern, wenden wir das konservative <strong>Half-Kelly</strong>-Sizing an und begrenzen das Ergebnis auf <InlineMath math="0.5 \times f^*" /> (zusätzlich begrenzt auf <InlineMath math="\ge 0" />).
|
||||
To mitigate risks from inaccurate estimations, we apply the conservative <strong>Half-Kelly</strong> sizing and limit the result to <InlineMath math="0.5 \times f^*" /> (additionally constrained to <InlineMath math="\ge 0" />).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-slate-800 pt-3">
|
||||
<h4 className="font-semibold text-rose-400 mb-1.5">3. Covariance & Cluster Risk (Kovarianz-Ampel)</h4>
|
||||
<h4 className="font-semibold text-rose-400 mb-1.5">3. Covariance & Cluster Risk (Covariance Traffic Light)</h4>
|
||||
<p className="mb-2">
|
||||
Die Kovarianz zwischen Assets wird durch Multiplikation ihrer paarweisen Korrelation mit ihren jeweiligen Standardabweichungen (Volatilitäten) bestimmt:
|
||||
The covariance between assets is determined by multiplying their pairwise correlation by their respective standard deviations (volatilities):
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto">
|
||||
<BlockMath math="\text{Cov}(A, B) = \text{Corr}(A, B) \times \sigma_A \times \sigma_B" />
|
||||
</div>
|
||||
<p className="text-slate-400">
|
||||
Ein <strong>Klumpenrisiko (Risk RED)</strong> wird ausgelöst, wenn ein Asset eine Korrelation <InlineMath math="\text{Corr}(A, B) > 0.70" /> zu bestehenden Positionen aufweist und diese Positionen jeweils mehr als 15% des Portfolios ausmachen.
|
||||
A <strong>concentration risk (Risk RED)</strong> is triggered when an asset exhibits a correlation <InlineMath math="\text{Corr}(A, B) > 0.70" /> to existing positions and these positions each exceed 15% of the portfolio.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -575,14 +575,14 @@ export default function SandboxDemo() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SECTION: Mein Portfolio Ingestion Cockpit */}
|
||||
{/* SECTION: My Portfolio Ingestion Cockpit */}
|
||||
<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">
|
||||
<div className="flex justify-between items-center border-b border-slate-800 pb-3">
|
||||
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
||||
<Wallet className="text-emerald-400 w-5 h-5" /> Mein Portfolio Cockpit
|
||||
<Wallet className="text-emerald-400 w-5 h-5" /> My Portfolio Cockpit
|
||||
</h3>
|
||||
<span className="text-xs text-slate-400 font-mono">
|
||||
Gesamt-Inventarwert: <span className="text-emerald-400 font-bold font-mono">${portfolioCalculated.totalValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
|
||||
Total Inventory Value: <span className="text-emerald-400 font-bold font-mono">${portfolioCalculated.totalValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -591,20 +591,20 @@ export default function SandboxDemo() {
|
||||
<thead>
|
||||
<tr className="border-b border-slate-800 text-slate-400 font-semibold bg-slate-900/40">
|
||||
<th className="p-3">Asset / Ticker</th>
|
||||
<th className="p-3 text-center">Stücke (Shares)</th>
|
||||
<th className="p-3 text-center">Einstandspreis</th>
|
||||
<th className="p-3 text-center">Aktueller Kurs</th>
|
||||
<th className="p-3 text-right">Positionswert</th>
|
||||
<th className="p-3 text-center">Shares</th>
|
||||
<th className="p-3 text-center">Entry Price</th>
|
||||
<th className="p-3 text-center">Current Price</th>
|
||||
<th className="p-3 text-right">Position Value</th>
|
||||
<th className="p-3 text-right">Performance (PnL)</th>
|
||||
<th className="p-3">Gewichtung (w_i)</th>
|
||||
<th className="p-3 text-center">Aktionen</th>
|
||||
<th className="p-3">Weighting (w_i)</th>
|
||||
<th className="p-3 text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{portfolioCalculated.items.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={8} className="p-8 text-center text-slate-500 italic">
|
||||
Bislang keine Assets im Ingestion-Cockpit. Fügen Sie unten ein Asset hinzu.
|
||||
No assets in the Ingestion Cockpit yet. Add an asset below.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
@@ -687,7 +687,7 @@ export default function SandboxDemo() {
|
||||
<button
|
||||
onClick={() => removePortfolioAsset(item.ticker)}
|
||||
className="p-1.5 rounded-lg bg-slate-950 hover:bg-rose-950/40 text-slate-500 hover:text-rose-400 transition-colors border border-slate-850 hover:border-rose-900/30 cursor-pointer"
|
||||
title="Asset löschen"
|
||||
title="Delete asset"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
@@ -703,7 +703,7 @@ export default function SandboxDemo() {
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="Ticker (z.B. AAPL)"
|
||||
placeholder="Ticker (e.g. AAPL)"
|
||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg p-2 text-slate-100 font-mono text-xs uppercase focus:border-emerald-500 focus:outline-none"
|
||||
value={newAssetTicker}
|
||||
onChange={(e) => setNewAssetTicker(e.target.value)}
|
||||
@@ -713,7 +713,7 @@ export default function SandboxDemo() {
|
||||
<input
|
||||
type="number"
|
||||
required
|
||||
placeholder="Stücke"
|
||||
placeholder="Shares"
|
||||
className="w-24 bg-slate-950 border border-slate-800 rounded-lg p-2 text-slate-100 font-mono text-xs text-center focus:border-emerald-500 focus:outline-none"
|
||||
value={newAssetShares === '' ? '' : newAssetShares}
|
||||
onChange={(e) => setNewAssetShares(e.target.value === '' ? '' : Number(e.target.value))}
|
||||
@@ -723,7 +723,7 @@ export default function SandboxDemo() {
|
||||
<input
|
||||
type="number"
|
||||
required
|
||||
placeholder="Einstand ($)"
|
||||
placeholder="Entry ($)"
|
||||
className="w-28 bg-slate-950 border border-slate-800 rounded-lg p-2 text-slate-100 font-mono text-xs text-center focus:border-emerald-500 focus:outline-none"
|
||||
value={newAssetPrice === '' ? '' : newAssetPrice}
|
||||
onChange={(e) => setNewAssetPrice(e.target.value === '' ? '' : Number(e.target.value))}
|
||||
@@ -736,9 +736,9 @@ export default function SandboxDemo() {
|
||||
<td className="p-3 text-center">
|
||||
<button
|
||||
onClick={handleAddNewAsset}
|
||||
className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-bold py-1.5 px-3 rounded-lg text-xs shadow-md shadow-emerald-500/10 flex items-center justify-center gap-1 mx-auto transition-all active:scale-[0.96] cursor-pointer animate-pulse hover:animate-none"
|
||||
className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-bold py-1.5 px-3 rounded-lg text-xs shadow-md shadow-emerald-500/10 flex items-center justify-center gap-1 mx-auto transition-all active:scale-[0.96] cursor-pointer"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" /> Hinzufügen
|
||||
<Plus className="w-3.5 h-3.5" /> Add
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -747,15 +747,15 @@ export default function SandboxDemo() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SECTION: Systemischer Makro-Stresstest (Portfolio-LMM) */}
|
||||
{/* SECTION: Systemic Macro Stress Test (Portfolio LMM) */}
|
||||
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl space-y-6">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 border-b border-slate-800 pb-3">
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
||||
<TrendingUp className="text-purple-400 w-5 h-5" /> Systemischer Makro-Stresstest (Portfolio-LMM)
|
||||
<TrendingUp className="text-purple-400 w-5 h-5" /> Systemic Macro Stress Test (Portfolio LMM)
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400">
|
||||
Analysiert die historische Sensitivität des Portfolios gegenüber Kern-Makro-Ereignissen über die letzten 36 Monate.
|
||||
Analyzes the historical sensitivity of the portfolio to core macro events over the last 36 months.
|
||||
</p>
|
||||
</div>
|
||||
{/* Event type tabs */}
|
||||
@@ -779,18 +779,18 @@ export default function SandboxDemo() {
|
||||
{stressLoading ? (
|
||||
<div className="h-80 flex flex-col items-center justify-center space-y-3">
|
||||
<div className="w-8 h-8 rounded-full border-2 border-purple-500 border-t-transparent animate-spin" />
|
||||
<span className="text-xs text-slate-400 font-mono animate-pulse">Kalkuliere Swamy-Arora GLS-Schätzer...</span>
|
||||
<span className="text-xs text-slate-400 font-mono animate-pulse">Calculating Swamy-Arora GLS estimators...</span>
|
||||
</div>
|
||||
) : stressError || !stressData ? (
|
||||
<div className="h-80 flex items-center justify-center text-slate-500 italic">
|
||||
{stressError || 'Keine Daten geladen.'}
|
||||
{stressError || 'No data loaded.'}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* LMM Summary Statistics */}
|
||||
<div className="bg-slate-950/40 rounded-xl p-4 border border-slate-850 flex flex-col justify-between space-y-4">
|
||||
<div>
|
||||
<span className="text-[10px] uppercase tracking-wider text-purple-400 font-bold block mb-2">Regressions-Koeffizienten (GLS)</span>
|
||||
<span className="text-[10px] uppercase tracking-wider text-purple-400 font-bold block mb-2">Regression Coefficients (GLS)</span>
|
||||
|
||||
{/* Fixed Effects list */}
|
||||
<div className="space-y-3">
|
||||
@@ -803,13 +803,13 @@ export default function SandboxDemo() {
|
||||
}`}>
|
||||
<div>
|
||||
<div className={`text-xs font-semibold ${isImpact ? 'text-purple-300 font-bold' : 'text-slate-350'}`}>
|
||||
{fe.name === 'Intercept' ? 'Basisschnittstelle (Intercept)' :
|
||||
{fe.name === 'Intercept' ? 'Baseline Intercept' :
|
||||
fe.name === 'Pre-Event Drift' ? 'Pre-Event Trend (Drift)' :
|
||||
fe.name === 'Post-Event Impact' ? 'Systemisches Portfolio Beta' :
|
||||
'VIX-Volatilitäts-Sensitivität'}
|
||||
fe.name === 'Post-Event Impact' ? 'Systemic Portfolio Beta' :
|
||||
'VIX Volatility Sensitivity'}
|
||||
</div>
|
||||
<div className="text-[9px] text-slate-500">
|
||||
SE: {fe.se.toFixed(4)} | p-Wert: {fe.pVal.toFixed(4)}
|
||||
SE: {fe.se.toFixed(4)} | p-value: {fe.pVal.toFixed(4)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
@@ -830,7 +830,7 @@ export default function SandboxDemo() {
|
||||
{/* Model Fit metrics */}
|
||||
<div className="border-t border-slate-850 pt-3 space-y-2">
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-slate-400">R-Quadrat (Bestimmtheitsmaß):</span>
|
||||
<span className="text-slate-400">R-Squared:</span>
|
||||
<span className="font-mono font-bold text-slate-200">{(stressData.regressionResults?.rSquared * 100).toFixed(1)}%</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-[11px] text-slate-500 font-mono">
|
||||
@@ -844,8 +844,8 @@ export default function SandboxDemo() {
|
||||
{/* Recharts Area/Line Chart (2/3 width) */}
|
||||
<div className="lg:col-span-2 bg-slate-950/30 rounded-xl p-4 border border-slate-850 space-y-3">
|
||||
<div className="flex justify-between items-center text-xs">
|
||||
<span className="text-slate-400 font-mono">Durchschnittlicher kumulierter Ertrag im Zeitfenster [-30, +30] Tage</span>
|
||||
<span className="text-[9px] text-slate-500 font-mono">Akkumulierte Log-Renditen</span>
|
||||
<span className="text-slate-400 font-mono">Average cumulative return in the [-30, +30] days window</span>
|
||||
<span className="text-[9px] text-slate-500 font-mono">Accumulated log returns</span>
|
||||
</div>
|
||||
|
||||
<div className="h-64 w-full">
|
||||
@@ -875,7 +875,7 @@ export default function SandboxDemo() {
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{ backgroundColor: '#090d16', borderColor: '#1e293b', borderRadius: '8px', fontSize: '11px' }}
|
||||
labelFormatter={(label) => `Relativer Tag: T${label >= 0 ? '+' : ''}${label}`}
|
||||
labelFormatter={(label) => `Relative Day: T${label >= 0 ? '+' : ''}${label}`}
|
||||
/>
|
||||
<Legend verticalAlign="top" height={36} iconType="circle" />
|
||||
|
||||
@@ -911,7 +911,7 @@ export default function SandboxDemo() {
|
||||
stroke="#ef4444"
|
||||
strokeWidth={1.5}
|
||||
strokeDasharray="3 3"
|
||||
label={{ value: 'Stichtag (T0)', fill: '#ef4444', fontSize: 9, position: 'top' }}
|
||||
label={{ value: 'Event Date (T0)', fill: '#ef4444', fontSize: 9, position: 'top' }}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
@@ -925,7 +925,7 @@ export default function SandboxDemo() {
|
||||
<div className="bg-purple-950/20 border border-purple-900/40 rounded-xl p-4 flex gap-3 items-start text-xs text-purple-300">
|
||||
<Sparkles className="w-5 h-5 text-purple-400 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<span className="font-bold uppercase tracking-wider block mb-1">Quantitative Analyse-Auswertung</span>
|
||||
<span className="font-bold uppercase tracking-wider block mb-1">Quantitative Analysis Evaluation</span>
|
||||
<p className="leading-relaxed">
|
||||
{(() => {
|
||||
const impactBeta = stressData.regressionResults?.fixedEffects.find((f: any) => f.name === 'Post-Event Impact')?.estimate || 0;
|
||||
@@ -934,17 +934,17 @@ export default function SandboxDemo() {
|
||||
const pVal = stressData.regressionResults?.fixedEffects.find((f: any) => f.name === 'Post-Event Impact')?.pVal || 0;
|
||||
const isSignificant = pVal < 0.05;
|
||||
|
||||
let eventNameText = activeStressTab === 'FOMC Rates' ? 'FOMC-Zinsentscheiden' :
|
||||
activeStressTab === 'CPI Inflation' ? 'CPI-Inflationsdaten' : 'Arbeitsmarktdaten';
|
||||
let eventNameText = activeStressTab === 'FOMC Rates' ? 'FOMC rate decisions' :
|
||||
activeStressTab === 'CPI Inflation' ? 'CPI inflation releases' : 'labor market updates';
|
||||
|
||||
let significanceText = isSignificant
|
||||
? `Dieser Effekt ist mit einem p-Wert von ${pVal.toFixed(4)} statistisch signifikant.`
|
||||
: `Dieser Effekt ist mit einem p-Wert von ${pVal.toFixed(4)} statistisch nicht hochgradig signifikant (Rauscheinfluss möglich).`;
|
||||
? `This effect is statistically significant with a p-value of ${pVal.toFixed(4)}.`
|
||||
: `This effect is not highly statistically significant (potential noise) with a p-value of ${pVal.toFixed(4)}.`;
|
||||
|
||||
if (isNegative) {
|
||||
return `Historische Reaktivität: Bei ${eventNameText} zeigt dein Depot ein negatives Beta von -${absBeta}. ${significanceText} Eine Absicherung über defensive Sektoren (z.B. Erhöhung der Bargeldquote oder defensive Consumer-Titel) senkt das Volatilitätsrisiko in dieser Post-Event-Phase um ca. ${Math.round(Math.abs(impactBeta) * 35)}%.`;
|
||||
return `Historical reactivity: During ${eventNameText}, your portfolio exhibits a negative Beta of -${absBeta}. ${significanceText} Hedging via defensive sectors (e.g., increasing cash ratio or defensive consumer stocks) reduces volatility risk in this post-event phase by approx. ${Math.round(Math.abs(impactBeta) * 35)}%.`;
|
||||
} else {
|
||||
return `Historische Reaktivität: Bei ${eventNameText} zeigt dein Depot ein positives Beta von +${absBeta}. ${significanceText} Dein Portfolio profitiert tendenziell von der anschließenden Marktdynamik. Sie können erwägen, die Hebelwirkung durch Zukäufe in Momentum-Aktien zu erhöhen.`;
|
||||
return `Historical reactivity: During ${eventNameText}, your portfolio exhibits a positive Beta of +${absBeta}. ${significanceText} Your portfolio tends to benefit from the subsequent market momentum. You might consider increasing leverage via momentum additions.`;
|
||||
}
|
||||
})()}
|
||||
</p>
|
||||
@@ -960,7 +960,7 @@ export default function SandboxDemo() {
|
||||
<div className="xl:col-span-2 bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl space-y-6">
|
||||
<div className="flex justify-between items-center border-b border-slate-800 pb-3">
|
||||
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
||||
<TrendingUp className="text-emerald-400 w-5 h-5" /> Portfolio Wertentwicklung & Benchmark
|
||||
<TrendingUp className="text-emerald-400 w-5 h-5" /> Portfolio Performance & Benchmark
|
||||
</h3>
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="flex items-center gap-2 text-xs text-slate-400 cursor-pointer">
|
||||
@@ -970,7 +970,7 @@ export default function SandboxDemo() {
|
||||
onChange={(e) => setShowMsciBenchmark(e.target.checked)}
|
||||
className="rounded border-slate-800 text-emerald-500 focus:ring-0 accent-emerald-500 w-4 h-4"
|
||||
/>
|
||||
<span>MSCI World (Benchmark) anzeigen</span>
|
||||
<span>Show MSCI World (Benchmark)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -985,7 +985,7 @@ export default function SandboxDemo() {
|
||||
<Legend verticalAlign="top" height={36} />
|
||||
<Line type="monotone" dataKey="Portfolio" name={activePortfolio.name} stroke="#10b981" strokeWidth={3} dot={false} activeDot={{ r: 6 }} />
|
||||
{showMsciBenchmark && (
|
||||
<Line type="monotone" dataKey="MSCI World (Benchmark)" name="MSCI World Index (Normiert)" stroke="#3b82f6" strokeWidth={2} strokeDasharray="4 4" dot={false} />
|
||||
<Line type="monotone" dataKey="MSCI World (Benchmark)" name="MSCI World Index (Normalized)" stroke="#3b82f6" strokeWidth={2} strokeDasharray="4 4" dot={false} />
|
||||
)}
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
@@ -994,8 +994,8 @@ export default function SandboxDemo() {
|
||||
{/* EWMA parameter tuner slider */}
|
||||
<div className="p-4 rounded-xl border border-slate-850 bg-slate-950/20 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-sm font-semibold text-slate-200">Parameteranpassung EWMA Lambda (λ)</h4>
|
||||
<p className="text-xs text-slate-400">Zerfallsfaktor steuert die Schock-Sensitivität des Volatilitätsmodells.</p>
|
||||
<h4 className="text-sm font-semibold text-slate-200">Parameter Tuning EWMA Lambda (λ)</h4>
|
||||
<p className="text-xs text-slate-400">Decay factor controls the shock sensitivity of the volatility model.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 w-full sm:w-auto">
|
||||
<input
|
||||
@@ -1017,7 +1017,7 @@ export default function SandboxDemo() {
|
||||
<div className="space-y-6">
|
||||
<div className="border-b border-slate-800 pb-3">
|
||||
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
||||
<Settings className="text-emerald-400 w-5 h-5" /> Order-Maske (Simuliert)
|
||||
<Settings className="text-emerald-400 w-5 h-5" /> Order Mask (Simulated)
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -1045,7 +1045,7 @@ export default function SandboxDemo() {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-400 block mb-1">Stücke</label>
|
||||
<label className="text-xs text-slate-400 block mb-1">Shares</label>
|
||||
<input
|
||||
type="number"
|
||||
required
|
||||
@@ -1055,7 +1055,7 @@ export default function SandboxDemo() {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-400 block mb-1">Kurs ($)</label>
|
||||
<label className="text-xs text-slate-400 block mb-1">Price ($)</label>
|
||||
<input
|
||||
type="number"
|
||||
required
|
||||
@@ -1073,14 +1073,14 @@ export default function SandboxDemo() {
|
||||
onClick={() => setTradeType('BUY')}
|
||||
className={`flex-1 py-1.5 text-xs font-bold rounded-lg transition-all ${tradeType === 'BUY' ? 'bg-emerald-500 text-slate-950 shadow-md shadow-emerald-500/10' : 'text-slate-400 hover:text-slate-200'}`}
|
||||
>
|
||||
Kauf (Long)
|
||||
Buy (Long)
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTradeType('SELL')}
|
||||
className={`flex-1 py-1.5 text-xs font-bold rounded-lg transition-all ${tradeType === 'SELL' ? 'bg-rose-500 text-white shadow-md shadow-rose-500/10' : 'text-slate-400 hover:text-slate-200'}`}
|
||||
>
|
||||
Verkauf (Short)
|
||||
Sell (Short)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1102,15 +1102,15 @@ export default function SandboxDemo() {
|
||||
onChange={(e) => setKellySource(e.target.value as any)}
|
||||
className="w-full bg-slate-900 border border-slate-800 rounded px-1.5 py-1 text-[10px] text-slate-200 focus:outline-none"
|
||||
>
|
||||
<option value="custom">Manueller Regler</option>
|
||||
<option value="scanner">El. 2 Scanner-Score</option>
|
||||
<option value="crypto">El. 4 Bayes Posterior</option>
|
||||
<option value="econometric">El. 5 ROC Breakout</option>
|
||||
<option value="custom">Manual Slider</option>
|
||||
<option value="scanner">Level 2 Scanner Score</option>
|
||||
<option value="crypto">Level 4 Bayes Posterior</option>
|
||||
<option value="econometric">Level 5 ROC Breakout</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-[9px] text-slate-500 block mb-0.5 font-semibold uppercase">Odds-Verhältnis (b)</label>
|
||||
<label className="text-[9px] text-slate-500 block mb-0.5 font-semibold uppercase">Odds Ratio (b)</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.1"
|
||||
@@ -1125,7 +1125,7 @@ export default function SandboxDemo() {
|
||||
{kellySource === 'custom' && (
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between text-[9px] text-slate-500">
|
||||
<span>Erfolgswahrscheinlichkeit:</span>
|
||||
<span>Probability of Success:</span>
|
||||
<span className="font-mono text-emerald-400 font-bold">{(kellyProbability * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<input
|
||||
@@ -1142,7 +1142,7 @@ export default function SandboxDemo() {
|
||||
|
||||
{kellySource !== 'custom' && (
|
||||
<div className="text-[9px] text-slate-400 flex justify-between bg-slate-900/40 p-1 px-1.5 rounded border border-slate-900">
|
||||
<span>System-Wahrscheinlichkeit:</span>
|
||||
<span>System Probability:</span>
|
||||
<span className="font-mono text-emerald-400 font-bold">{(kellyProbability * 100).toFixed(1)}%</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -1151,18 +1151,18 @@ export default function SandboxDemo() {
|
||||
<div className="p-2 bg-rose-500/10 text-rose-400 border border-rose-500/20 text-[9px] rounded flex items-start gap-1">
|
||||
<AlertCircle className="w-3.5 h-3.5 shrink-0 text-rose-400" />
|
||||
<div>
|
||||
<strong>Klumpenrisiko!</strong> Korrelation > 0.70 zu bestehenden Positionen. Kelly-Empfehlung wurde um 50% halbiert.
|
||||
<strong>Concentration Risk!</strong> Correlation > 0.70 to existing positions. Kelly recommendation halved by 50%.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-slate-900/80 p-2 rounded-lg border border-slate-850 flex justify-between items-center text-xs">
|
||||
<div>
|
||||
<span className="block text-[9px] text-slate-500 uppercase font-semibold">Kelly-Anteil:</span>
|
||||
<span className="font-mono font-bold text-slate-200">{(kellyFraction * 100).toFixed(1)}% des Cashs</span>
|
||||
<span className="block text-[9px] text-slate-500 uppercase font-semibold">Kelly Fraction:</span>
|
||||
<span className="font-mono font-bold text-slate-200">{(kellyFraction * 100).toFixed(1)}% of Cash</span>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<span className="block text-[9px] text-slate-500 uppercase font-semibold">Kaufvolumen:</span>
|
||||
<span className="block text-[9px] text-slate-500 uppercase font-semibold">Buy Volume:</span>
|
||||
<span className="font-mono font-bold text-emerald-400">${Math.round(recommendedKellyCash).toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1173,11 +1173,11 @@ export default function SandboxDemo() {
|
||||
<div>
|
||||
<label className="text-xs text-slate-400 block mb-1 flex items-center gap-1">
|
||||
<Tag className="w-3 h-3 text-emerald-400" />
|
||||
<span>Hypothese / What-if Notiz</span>
|
||||
<span>Hypothesis / What-if Note</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="z.B. Ferrari E-Auto Skepsis"
|
||||
placeholder="e.g., Ferrari EV Skepticism"
|
||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg p-2 text-slate-100 text-xs focus:outline-none focus:border-emerald-500"
|
||||
value={hypothesisTag}
|
||||
onChange={(e) => setHypothesisTag(e.target.value)}
|
||||
@@ -1187,7 +1187,7 @@ export default function SandboxDemo() {
|
||||
{/* Fees Toggle */}
|
||||
<div className="flex items-center justify-between border-t border-slate-850 pt-3 text-xs">
|
||||
<span className="text-slate-400 flex items-center gap-1">
|
||||
<DollarSign className="w-3.5 h-3.5 text-slate-500" /> Ordergebühren simulieren
|
||||
<DollarSign className="w-3.5 h-3.5 text-slate-500" /> Simulate transaction fees
|
||||
</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -1201,7 +1201,7 @@ export default function SandboxDemo() {
|
||||
<div className="space-y-2 border-t border-slate-850 pt-3 text-xs">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-slate-400 flex items-center gap-1">
|
||||
<Calendar className="w-3.5 h-3.5 text-slate-500" /> Historischer Backfill
|
||||
<Calendar className="w-3.5 h-3.5 text-slate-500" /> Historical Backfill
|
||||
</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -1231,7 +1231,7 @@ export default function SandboxDemo() {
|
||||
{orderSuccess && (
|
||||
<div className="p-3 rounded-lg bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 text-xs flex items-center gap-2">
|
||||
<Check className="w-4 h-4 shrink-0" />
|
||||
<span>Transaktion erfolgreich gebucht!</span>
|
||||
<span>Transaction successfully recorded!</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1239,7 +1239,7 @@ export default function SandboxDemo() {
|
||||
type="submit"
|
||||
className={`w-full font-bold py-2.5 px-4 rounded-lg transition-all active:scale-[0.98] mt-2 shadow-lg ${tradeType === 'BUY' ? 'bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-slate-950 shadow-emerald-500/10' : 'bg-gradient-to-r from-rose-500 to-pink-500 hover:from-rose-600 hover:to-pink-600 text-white shadow-rose-500/10'}`}
|
||||
>
|
||||
Order an den Markt senden
|
||||
Submit Order to Market
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1254,10 +1254,10 @@ export default function SandboxDemo() {
|
||||
<div className="xl:col-span-2 bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl space-y-4">
|
||||
<div className="flex justify-between items-center border-b border-slate-800 pb-3">
|
||||
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
||||
<TrendingUp className="text-emerald-400 w-5 h-5" /> Depotbestände ({activePortfolio.holdings.length})
|
||||
<TrendingUp className="text-emerald-400 w-5 h-5" /> Portfolio Holdings ({activePortfolio.holdings.length})
|
||||
</h3>
|
||||
<div className="text-xs text-slate-400 flex items-center gap-4">
|
||||
<span>Barguthaben: <span className="font-mono text-emerald-400 font-bold">${activePortfolio.cash.toLocaleString()}</span></span>
|
||||
<span>Cash Balance: <span className="font-mono text-emerald-400 font-bold">${activePortfolio.cash.toLocaleString()}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1266,18 +1266,18 @@ export default function SandboxDemo() {
|
||||
<thead>
|
||||
<tr className="border-b border-slate-800 text-slate-400 font-semibold bg-slate-900/40">
|
||||
<th className="p-3">Asset</th>
|
||||
<th className="p-3">Stücke</th>
|
||||
<th className="p-3">Einstand</th>
|
||||
<th className="p-3">Kurs</th>
|
||||
<th className="p-3">Hypothese</th>
|
||||
<th className="p-3 text-right">GuV</th>
|
||||
<th className="p-3">Shares</th>
|
||||
<th className="p-3">Avg Price</th>
|
||||
<th className="p-3">Price</th>
|
||||
<th className="p-3">Hypothesis</th>
|
||||
<th className="p-3 text-right">PnL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{activePortfolio.holdings.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="p-8 text-center text-slate-500">
|
||||
Keine Bestände in diesem Sandbox-Portfolio. Nutzen Sie die Order-Maske, um Werte hinzuzufügen.
|
||||
No holdings in this Sandbox portfolio. Use the order mask to add assets.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
@@ -1313,13 +1313,13 @@ export default function SandboxDemo() {
|
||||
<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">
|
||||
<div className="border-b border-slate-800 pb-3">
|
||||
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
||||
<Calendar className="text-emerald-400 w-5 h-5" /> Letzte Orderbuch-Einträge
|
||||
<Calendar className="text-emerald-400 w-5 h-5" /> Recent Order Book Entries
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="max-h-60 overflow-y-auto space-y-2 pr-1">
|
||||
{activePortfolio.transactions.length === 0 ? (
|
||||
<p className="text-xs text-slate-500 text-center py-8">Bislang keine Transaktionen in diesem Portfolio.</p>
|
||||
<p className="text-xs text-slate-500 text-center py-8">No transactions in this portfolio yet.</p>
|
||||
) : (
|
||||
activePortfolio.transactions.map((tx) => {
|
||||
const isBuy = tx.type === 'BUY';
|
||||
@@ -1328,7 +1328,7 @@ export default function SandboxDemo() {
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`px-1.5 py-0.5 rounded text-[9px] font-bold ${isBuy ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' : 'bg-rose-500/10 text-rose-400 border border-rose-500/20'}`}>
|
||||
{isBuy ? 'KAUF' : 'VERKAUF'}
|
||||
{isBuy ? 'BUY' : 'SELL'}
|
||||
</span>
|
||||
<span className="font-mono font-bold text-slate-200">{tx.symbol}</span>
|
||||
</div>
|
||||
@@ -1336,8 +1336,8 @@ export default function SandboxDemo() {
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between text-xs font-mono text-slate-400">
|
||||
<span>{tx.shares} Stk @ ${tx.price.toFixed(2)}</span>
|
||||
<span className="text-[10px] text-slate-500">Gebühr: ${tx.feeApplied.toFixed(2)}</span>
|
||||
<span>{tx.shares} shares @ ${tx.price.toFixed(2)}</span>
|
||||
<span className="text-[10px] text-slate-500">Fee: ${tx.feeApplied.toFixed(2)}</span>
|
||||
</div>
|
||||
|
||||
{tx.hypothesisTag && (
|
||||
@@ -1355,7 +1355,7 @@ export default function SandboxDemo() {
|
||||
|
||||
</div>
|
||||
|
||||
<PortfolioMathModal isOpen={isMathModalOpen} onClose={() => setIsMathModalOpen(false)} />
|
||||
<SandboxMathModal isOpen={isMathModalOpen} onClose={() => setIsMathModalOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
135
components/modules/sandbox/SandboxMathModal.tsx
Normal file
135
components/modules/sandbox/SandboxMathModal.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import React from 'react';
|
||||
import { BookOpen, X, TrendingUp, BarChart2, ShieldAlert } from 'lucide-react';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import { BlockMath, InlineMath } from 'react-katex';
|
||||
|
||||
interface SandboxMathModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function SandboxMathModal({ isOpen, onClose }: SandboxMathModalProps) {
|
||||
React.useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
if (isOpen) {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-955/90 backdrop-blur-md p-4 sm:p-6 md:p-8">
|
||||
<div className="bg-slate-900 border border-slate-800/80 rounded-3xl w-full max-w-4xl h-[80vh] flex flex-col overflow-hidden shadow-2xl relative text-slate-350">
|
||||
|
||||
{/* Modal Header */}
|
||||
<div className="flex justify-between items-center px-6 py-4 bg-slate-950/50 border-b border-slate-800/80">
|
||||
<div>
|
||||
<h2 className="text-base font-bold bg-gradient-to-r from-teal-400 to-emerald-400 bg-clip-text text-transparent flex items-center gap-2">
|
||||
<BookOpen className="w-5 h-5 text-teal-400" /> Portfolio Sandbox - Math & Sizing Handbook
|
||||
</h2>
|
||||
<p className="text-[10px] text-slate-500 font-mono">Institutional Portfolio Construction & Allocation Spec</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-slate-400 hover:text-slate-200 bg-slate-955/50 border border-slate-800 hover:border-slate-700 p-2 rounded-xl transition-all cursor-pointer flex items-center justify-center"
|
||||
aria-label="Close modal"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Modal Body */}
|
||||
<div className="flex-1 overflow-y-auto p-6 sm:p-8 space-y-8 text-slate-300 scrollbar-thin">
|
||||
|
||||
{/* Section A: Synthetic Portfolio Model & Asset Weightings */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono flex items-center gap-1.5">
|
||||
<TrendingUp className="w-3.5 h-3.5" /> A. Synthetic Portfolio Model & Asset Weightings
|
||||
</h4>
|
||||
<p className="text-xs leading-relaxed text-slate-400">
|
||||
The sandbox constructs a continuous synthetic asset representing your active weight allocations and its daily return track:
|
||||
</p>
|
||||
<div className="bg-slate-950/40 p-4 rounded-xl border border-slate-800/60 my-2 space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1">Active Percentage Weighting (<InlineMath math="w_i" />) Calculation:</p>
|
||||
<BlockMath math="w_i = \\frac{\\text{Shares}_i \\times P_{\\text{current}, i}}{\\sum_{j} \\text{Shares}_j \\times P_{\\text{current}, j}}" />
|
||||
</div>
|
||||
<div className="border-t border-slate-850 pt-3">
|
||||
<p className="text-xs text-slate-400 mb-1">Synthetic Portfolio Log Return (<InlineMath math="R_{pt}" />):</p>
|
||||
<BlockMath math="R_{pt} = \\sum_{i} w_i \\times \\ln\\left(\\frac{P_{t, i}}{P_{t-1, i}}\\right)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section B: Linear Mixed Effects Panel Regression (LMM) */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono flex items-center gap-1.5">
|
||||
<BarChart2 className="w-3.5 h-3.5" /> B. Linear Mixed Effects Panel Regression (LMM)
|
||||
</h4>
|
||||
<p className="text-xs leading-relaxed text-slate-400">
|
||||
Solves the system-wide macro response model across all historical event instances <InlineMath math="j" /> using a Swamy-Arora GLS estimator:
|
||||
</p>
|
||||
<div className="bg-slate-950/40 p-4 rounded-xl border border-slate-800/60 my-2">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1">Panel Model Specification with VIX Controls:</p>
|
||||
<BlockMath math="R_{ptj} = \\beta_0 + \\beta_{\\text{drift}} \\text{Pre}_t + \\beta_{\\text{impact}} \\text{Post}_t + \\beta_{\\text{VIX}} VIX_{tj} + u_j + e_{ptj}" />
|
||||
<p className="text-[10px] text-slate-500 mt-2 font-mono leading-relaxed">
|
||||
where:
|
||||
<br />
|
||||
- <InlineMath math="t \\in [-30, +30]" /> is the relative day offset from event date <InlineMath math="T_j" />.
|
||||
<br />
|
||||
- <InlineMath math="\\text{Pre}_t = \\mathbb{I}(t < 0)" /> and <InlineMath math="\\text{Post}_t = \\mathbb{I}(t > 0)" /> are relative phase indicators.
|
||||
<br />
|
||||
- <InlineMath math="VIX_{tj}" /> is the background market-wide volatility covariate.
|
||||
<br />
|
||||
- <InlineMath math="u_j \\sim N(0, \\sigma_u^2)" /> is the random group intercept (event instance shock).
|
||||
<br />
|
||||
- <InlineMath math="e_{ptj} \\sim N(0, \\sigma_e^2)" /> is the residual error.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section C: Kelly Criterion & Fractional Betting Sizing */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono flex items-center gap-1.5">
|
||||
<ShieldAlert className="w-3.5 h-3.5" /> C. Kelly Criterion & Fractional Betting Sizing
|
||||
</h4>
|
||||
<p className="text-xs leading-relaxed text-slate-400">
|
||||
The Kelly Criterion optimizes allocation sizing to maximize the expected value of the logarithm of wealth, balancing capital preservation with growth:
|
||||
</p>
|
||||
<div className="bg-slate-950/40 p-4 rounded-xl border border-slate-800/60 my-2 space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1">Theoretical Kelly Fraction (<InlineMath math="f^*" />) Equation:</p>
|
||||
<BlockMath math="f^* = \\frac{p \\cdot b - (1 - p)}{b}" />
|
||||
<p className="text-[10px] text-slate-500 mt-2 font-mono leading-relaxed">
|
||||
where:
|
||||
<br />
|
||||
- <InlineMath math="p" /> is the probability of a positive outcome (rebound or trade success).
|
||||
<br />
|
||||
- <InlineMath math="b" /> is the payout odds ratio (average win size divided by average loss size).
|
||||
</p>
|
||||
</div>
|
||||
<div className="border-t border-slate-850 pt-3">
|
||||
<p className="text-xs text-slate-400 mb-1">Fractional Kelly (Half-Kelly) Safety Buffer:</p>
|
||||
<BlockMath math="f_{\\text{applied}} = \\max\\left(0, 0.5 \\times f^*\\right)" />
|
||||
<p className="text-xs text-slate-400 leading-relaxed mt-2">
|
||||
To mitigate estimation variance and protect against catastrophic drawdown due to model error or fat-tailed market returns, the cockpit clips the suggested sizing to a **Half-Kelly** multiplier ($0.5$).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user