'use client'; import React, { useState, useMemo } from 'react'; import { useSandboxStore, PortfolioHolding, Transaction } from '@/lib/store'; import { calculateEWMA, calculateKellyFraction, calculateAssetCovariance } from '@/lib/math/statistics'; 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 SandboxMathModal from './SandboxMathModal'; import SandboxBlueprintModal from './SandboxBlueprintModal'; import { TrendingUp, Wallet, ArrowDownRight, ArrowUpRight, Percent, Plus, FolderSync, HelpCircle, Settings, Calendar, DollarSign, Tag, Check, AlertCircle, ChevronDown, ChevronUp, Sparkles, BookOpen, Trash2 } from 'lucide-react'; export default function SandboxDemo() { const { portfolios, activePortfolioId, ewmaLambda, createPortfolio, setActivePortfolio, executeTransaction, setEwmaLambda, scannerAlerts, posteriorProbability, portfolio, watchlist, updatePortfolioAsset, removePortfolioAsset } = useSandboxStore(); // Selected portfolio const activePortfolio = useMemo(() => { return portfolios.find(p => p.id === activePortfolioId) || portfolios[0]; }, [portfolios, activePortfolioId]); const [mounted, setMounted] = useState(false); React.useEffect(() => { setMounted(true); }, []); // UI state const [showNewPortfolioModal, setShowNewPortfolioModal] = useState(false); const [newPortfolioName, setNewPortfolioName] = useState(''); const [newStartingBalance, setNewStartingBalance] = useState(50000); const [tradeSymbol, setTradeSymbol] = useState('AAPL'); const [tradeWknOrIsin, setTradeWknOrIsin] = useState('865985'); const [tradeShares, setTradeShares] = useState(10); const [tradePrice, setTradePrice] = useState(182); const [tradeType, setTradeType] = useState<'BUY' | 'SELL'>('BUY'); const [simulateFees, setSimulateFees] = useState(true); const [isBackfill, setIsBackfill] = useState(false); const [backfillDate, setBackfillDate] = useState('2026-05-20'); const [hypothesisTag, setHypothesisTag] = useState('Focus on AI Infrastructure'); const [orderError, setOrderError] = useState(null); const [orderSuccess, setOrderSuccess] = useState(false); const [showMathAccordion, setShowMathAccordion] = useState(false); const [isMathModalOpen, setIsMathModalOpen] = useState(false); const [isBlueprintModalOpen, setIsBlueprintModalOpen] = useState(false); const [showMsciBenchmark, setShowMsciBenchmark] = useState(true); // Kelly Position Sizing states const [kellySource, setKellySource] = useState<'scanner' | 'crypto' | 'econometric' | 'custom'>('custom'); const [customProb, setCustomProb] = useState(0.60); const [oddsRatio, setOddsRatio] = useState(1.5); // Systemic Macro Stress-Test States const [activeStressTab, setActiveStressTab] = useState<'FOMC Rates' | 'CPI Inflation' | 'Labor Market'>('FOMC Rates'); const [stressLoading, setStressLoading] = useState(false); const [stressData, setStressData] = useState(null); const [stressError, setStressError] = useState(null); React.useEffect(() => { const fetchStressTest = async () => { setStressLoading(true); setStressError(null); try { const response = await fetch('/api/sandbox/lmm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ portfolio: portfolio, eventType: activeStressTab }) }); if (response.ok) { const data = await response.json(); setStressData(data); } else { setStressError("Error loading stress test data."); } } catch (err) { console.error("Stress test fetch error:", err); setStressError("Network error loading stress test."); } finally { setStressLoading(false); } }; fetchStressTest(); }, [portfolio, activeStressTab]); // Ingested Portfolio Ingestion Cockpit States const [newAssetTicker, setNewAssetTicker] = useState(''); const [newAssetShares, setNewAssetShares] = useState(''); const [newAssetPrice, setNewAssetPrice] = useState(''); const [portfolioPrices, setPortfolioPrices] = useState>({}); const MOCK_PRICES: Record = { 'AAPL': 185.20, 'MSFT': 415.50, 'NVDA': 945.00, 'TSLA': 178.50, 'AMD': 160.20, 'SMCI': 820.00, 'NFLX': 610.00, 'AMZN': 182.40, 'GOOGL': 175.50, 'META': 475.00, 'WMT': 60.50, 'JNJ': 158.30, 'PG': 162.10, 'MRK': 128.40, 'PLTR': 21.50, 'BABA': 78.40, 'CVX': 155.20, 'XOM': 118.60, 'BAC': 38.20, 'JPM': 195.40, 'ASML': 920.00, 'SAP': 178.50, 'MC.PA': 810.00, 'OR.PA': 440.00, 'NESN': 92.40, 'NOVOB': 125.60, 'SHEL': 32.40, 'BP': 38.50, 'HSBC': 42.10, 'ALV.DE': 248.50, 'VOW3.DE': 118.40, 'BMW.DE': 98.60, 'SIE.DE': 172.40, 'DTE.DE': 22.10, 'MBG.DE': 68.45, 'BAS.DE': 48.20, 'SAN.MC': 4.50, 'BBVA.MC': 9.80, 'BTC-USD': 65420.00, 'ETH-USD': 3480.00, 'SOL-USD': 148.50, 'ADA-USD': 0.46, 'XRP-USD': 0.49, 'DOGE-USD': 0.14, 'DOT-USD': 6.20, 'LINK-USD': 15.40, 'LTC-USD': 78.50, 'AVAX-USD': 32.40, 'BNB-USD': 580.00, 'TRX-USD': 0.12, 'NEAR-USD': 5.80 }; const portfolioTickers = useMemo(() => { return portfolio.map(p => p.ticker); }, [portfolio]); React.useEffect(() => { const fetchPrices = async () => { if (portfolioTickers.length === 0) return; try { const response = await fetch(`/api/finance?tickers=${portfolioTickers.join(',')}`); if (response.ok) { const data = await response.json(); const pricesMap: Record = {}; data.results.forEach((r: any) => { if (!r.error) { pricesMap[r.ticker] = { currentPrice: r.currentPrice, name: r.name }; } }); setPortfolioPrices(prev => ({ ...prev, ...pricesMap })); } } catch (err) { console.error("Error fetching sandbox portfolio prices:", err); } }; fetchPrices(); }, [portfolioTickers]); const getTickerPrice = (ticker: string): number => { if (portfolioPrices[ticker]) return portfolioPrices[ticker].currentPrice; const w = watchlist.find(item => item.ticker === ticker); if (w) return w.currentPrice; const h = activePortfolio.holdings.find(item => item.symbol === ticker); if (h) return h.currentPrice; return MOCK_PRICES[ticker] || 100.0; }; const getTickerName = (ticker: string): string => { if (portfolioPrices[ticker]) return portfolioPrices[ticker].name; const w = watchlist.find(item => item.ticker === ticker); if (w) return w.ticker + ' Corp.'; const h = activePortfolio.holdings.find(item => item.symbol === ticker); if (h) return h.symbol + ' Corp.'; return ticker + ' Corp.'; }; const handleAddNewAsset = (e: React.FormEvent) => { e.preventDefault(); const ticker = newAssetTicker.trim().toUpperCase(); if (!ticker) return; const shares = Number(newAssetShares); const price = Number(newAssetPrice); if (isNaN(shares) || shares <= 0 || isNaN(price) || price <= 0) { alert("Please enter a valid number of shares and entry price."); return; } updatePortfolioAsset(ticker, shares, price); setNewAssetTicker(''); setNewAssetShares(''); setNewAssetPrice(''); }; const portfolioCalculated = useMemo(() => { let totalValue = 0; const items = portfolio.map((asset) => { const currentPrice = getTickerPrice(asset.ticker); const name = getTickerName(asset.ticker); const positionValue = asset.shares * currentPrice; totalValue += positionValue; const pnlUsd = asset.shares * (currentPrice - asset.entryPrice); const pnlPct = ((currentPrice - asset.entryPrice) / asset.entryPrice) * 100; return { ...asset, name, currentPrice, positionValue, pnlUsd, pnlPct }; }); const itemsWithWeights = items.map((item) => { const weight = totalValue > 0 ? item.positionValue / totalValue : 0; return { ...item, weight }; }); return { totalValue, items: itemsWithWeights }; }, [portfolio, portfolioPrices, watchlist, activePortfolio.holdings]); // Compute Net Worth const netWorth = useMemo(() => { const assetsVal = activePortfolio.holdings.reduce((sum, h) => sum + h.shares * h.currentPrice, 0); return Math.round((activePortfolio.cash + assetsVal) * 100) / 100; }, [activePortfolio]); // Dynamic winning probability (p) based on selected source const kellyProbability = useMemo(() => { if (kellySource === 'scanner') { const alert = scannerAlerts.find(a => a.ticker.toUpperCase() === tradeSymbol.toUpperCase()); return alert ? alert.overreactionScore / 100 : 0.52; } if (kellySource === 'crypto') { return posteriorProbability; // e.g. 0.72 } if (kellySource === 'econometric') { return 0.65; // ROC target probability } return customProb; }, [kellySource, customProb, tradeSymbol, scannerAlerts, posteriorProbability]); // Check potential cluster risk for the input symbol const potentialClusterRisk = useMemo(() => { if (!tradeSymbol) return false; const holdingsWithWeights = activePortfolio.holdings.map(h => ({ symbol: h.symbol, weight: (h.shares * h.currentPrice) / (netWorth || 1.0) })); const covResult = calculateAssetCovariance(holdingsWithWeights, tradeSymbol); return covResult.clusterRisk; }, [activePortfolio.holdings, tradeSymbol, netWorth]); // Compute Kelly fraction and recommended cash amount const kellyFraction = useMemo(() => { const rawKelly = calculateKellyFraction(kellyProbability, oddsRatio); // Cap at Half-Kelly already done in calculateKellyFraction, but we can scale by 50% if there is cluster risk return potentialClusterRisk ? rawKelly * 0.5 : rawKelly; }, [kellyProbability, oddsRatio, potentialClusterRisk]); const recommendedKellyCash = useMemo(() => { return activePortfolio.cash * kellyFraction; }, [activePortfolio.cash, kellyFraction]); // Compute returns based on active portfolio's historical value series const portfolioReturns = useMemo(() => { const vals = activePortfolio.historicalValues; if (vals.length < 2) return []; const r: number[] = []; for (let i = 1; i < vals.length; i++) { r.push((vals[i].value - vals[i - 1].value) / vals[i - 1].value); } return r; }, [activePortfolio.historicalValues]); // Calculate EWMA Volatility live const ewmaResult = useMemo(() => { return calculateEWMA(portfolioReturns, ewmaLambda); }, [portfolioReturns, ewmaLambda]); // Combine data for charting const chartData = useMemo(() => { const vals = activePortfolio.historicalValues; if (vals.length === 0) return []; // Normalize MSCI World index from the same starting value of the portfolio const baseValue = vals[0].value; let msciVal = baseValue; return vals.map((hv, idx) => { // Deterministic pseudo-random walk for MSCI World if (idx > 0) { const rand = Math.sin(idx * 57.8) * 0.45 + 0.05; // range: -0.4% to +0.5% return msciVal = msciVal * (1 + rand * 0.015); } const vol = ewmaResult.series[idx - 1] || 0; return { date: hv.date, Portfolio: hv.value, 'MSCI World (Benchmark)': Math.round(msciVal), 'EWMA Vol (%)': parseFloat(vol.toFixed(2)), }; }); }, [activePortfolio.historicalValues, ewmaResult]); if (!mounted) { return (
Loading Sandbox Module...
); } // Total gain/loss const totalGainLoss = netWorth - activePortfolio.startingBalance; const totalGainLossPct = (totalGainLoss / activePortfolio.startingBalance) * 100; const isPositiveOverall = totalGainLoss >= 0; const handleCreatePortfolio = (e: React.FormEvent) => { e.preventDefault(); if (!newPortfolioName.trim()) return; createPortfolio(newPortfolioName, newStartingBalance); setNewPortfolioName(''); setShowNewPortfolioModal(false); }; const handleTransactionSubmit = (e: React.FormEvent) => { e.preventDefault(); setOrderError(null); setOrderSuccess(false); if (tradeShares <= 0 || tradePrice <= 0) { setOrderError('Please enter a valid number of shares and price.'); return; } const ok = executeTransaction( activePortfolio.id, tradeSymbol, tradeWknOrIsin, tradeType, tradeShares, tradePrice, simulateFees, isBackfill, backfillDate, hypothesisTag ); if (ok) { setOrderSuccess(true); setTimeout(() => setOrderSuccess(false), 3000); } else { setOrderError( tradeType === 'BUY' ? 'Insufficient cash balance (including transaction fees).' : 'Insufficient shares in portfolio for sale.' ); } }; return (
{activePortfolio.riskProfile?.status === 'RED' && (
Critical Concentration Risks (Covariance RED): {activePortfolio.riskProfile.message}
)} {/* SECTION 1: Portfolio Selector & Stats Bar */}
Strategic Sandbox
{/* Net Worth Card */}
Total Value
${netWorth.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{/* Performance Card */}
PnL (Total)
{isPositiveOverall ? : } {isPositiveOverall ? '+' : ''}{totalGainLossPct.toFixed(2)}%
{/* Live EWMA Vol Card */}
EWMA Volatility
{ewmaResult.latest.toFixed(2)}%
{/* Covariance Risk Traffic Light Card */}
Covariance Traffic Light
{activePortfolio.riskProfile?.status || 'GREEN'} RISK
{/* Modal for creating a new Portfolio */} {showNewPortfolioModal && (

New Sandbox Portfolio

setNewPortfolioName(e.target.value)} />
setNewStartingBalance(Number(e.target.value))} />
)} {/* Accordion / Math Button */}
{showMathAccordion && (

1. EWMA Volatility Model

Volatility is calculated using the Exponentially Weighted Moving Average (EWMA) model. Recent returns receive a higher weighting than returns further in the past, controlled by the decay parameter (Lambda).

The daily volatility is extrapolated to an entire year (annualization) assuming 252 trading days:

RiskMetrics recommends a Lambda value of for daily financial data.

2. Kelly Criterion for Position Sizing

The Kelly formula determines the optimal fraction of capital () to invest in a trade to maximize the exponential growth of wealth:

To mitigate risks from inaccurate estimations, we apply the conservative Half-Kelly sizing and limit the result to (additionally constrained to ).

3. Covariance & Cluster Risk (Covariance Traffic Light)

The covariance between assets is determined by multiplying their pairwise correlation by their respective standard deviations (volatilities):

A concentration risk (Risk RED) is triggered when an asset exhibits a correlation to existing positions and these positions each exceed 15% of the portfolio.

)}
{/* SECTION: My Portfolio Ingestion Cockpit */}

My Portfolio Cockpit

Total Inventory Value: ${portfolioCalculated.totalValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{portfolioCalculated.items.length === 0 ? ( ) : ( portfolioCalculated.items.map((item) => { const isPositive = item.pnlUsd >= 0; const weightPct = item.weight * 100; return ( {/* Symbol & Name */} {/* Shares (Inline input) */} {/* Entry Price (Inline input) */} {/* Current Price */} {/* Position Value */} {/* PnL */} {/* Weighting Progress Bar */} {/* Actions */} ); }) )} {/* Adding Asset Inline Row */}
Asset / Ticker Shares Entry Price Current Price Position Value Performance (PnL) Weighting (w_i) Actions
No assets in the Ingestion Cockpit yet. Add an asset below.
{item.ticker}
{item.name}
{ const val = Number(e.target.value); if (val > 0) { updatePortfolioAsset(item.ticker, val, item.entryPrice); } else { e.target.value = String(item.shares); } }} className="w-20 bg-slate-950 border border-slate-800 rounded px-2 py-1 text-slate-100 font-mono text-center focus:border-emerald-500 focus:outline-none" /> { const val = Number(e.target.value); if (val > 0) { updatePortfolioAsset(item.ticker, item.shares, val); } else { e.target.value = String(item.entryPrice); } }} className="w-24 bg-slate-950 border border-slate-800 rounded px-2 py-1 text-slate-100 font-mono text-center focus:border-emerald-500 focus:outline-none" /> ${item.currentPrice.toFixed(2)} ${item.positionValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{isPositive ? '+' : ''}${item.pnlUsd.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{isPositive ? '+' : ''}{item.pnlPct.toFixed(2)}%
{weightPct.toFixed(1)}%
setNewAssetTicker(e.target.value)} /> setNewAssetShares(e.target.value === '' ? '' : Number(e.target.value))} /> setNewAssetPrice(e.target.value === '' ? '' : Number(e.target.value))} /> - - - -
{/* SECTION: Systemic Macro Stress Test (Portfolio LMM) */}

Systemic Macro Stress Test (Portfolio LMM)

Analyzes the historical sensitivity of the portfolio to core macro events over the last 36 months.

{/* Event type tabs */}
{(['FOMC Rates', 'CPI Inflation', 'Labor Market'] as const).map((tab) => ( ))}
{stressLoading ? (
Calculating Swamy-Arora GLS estimators...
) : stressError || !stressData ? (
{stressError || 'No data loaded.'}
) : (
{/* LMM Summary Statistics */}
Regression Coefficients (GLS) {/* Fixed Effects list */}
{stressData.regressionResults?.fixedEffects.map((fe: any) => { const isPositive = fe.estimate >= 0; const isImpact = fe.name === 'Post-Event Impact'; return (
{fe.name === 'Intercept' ? 'Baseline Intercept' : fe.name === 'Pre-Event Drift' ? 'Pre-Event Trend (Drift)' : fe.name === 'Post-Event Impact' ? 'Systemic Portfolio Beta' : 'VIX Volatility Sensitivity'}
SE: {fe.se.toFixed(4)} | p-value: {fe.pVal.toFixed(4)}
{isPositive ? '+' : ''}{fe.estimate.toFixed(4)} {fe.sig}
); })}
{/* Model Fit metrics */}
R-Squared: {(stressData.regressionResults?.rSquared * 100).toFixed(1)}%
AIC: {stressData.regressionResults?.aic} BIC: {stressData.regressionResults?.bic} Events: {stressData.eventCount}
{/* Recharts Area/Line Chart (2/3 width) */}
Average cumulative return in the [-30, +30] days window Accumulated log returns
`T${v >= 0 ? '+' : ''}${v}`} /> `${v.toFixed(1)}%`} /> `Relative Day: T${label >= 0 ? '+' : ''}${label}`} />
)} {/* Quantitative Commentary Card */} {stressData && !stressLoading && (
Quantitative Analysis Evaluation

{(() => { const impactBeta = stressData.regressionResults?.fixedEffects.find((f: any) => f.name === 'Post-Event Impact')?.estimate || 0; const isNegative = impactBeta < 0; const absBeta = Math.abs(impactBeta).toFixed(2); 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 rate decisions' : activeStressTab === 'CPI Inflation' ? 'CPI inflation releases' : 'labor market updates'; let significanceText = isSignificant ? `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 `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 `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.`; } })()}

)}
{/* SECTION 2: Chart / Analytics & Order Form */}
{/* Left 2 Columns: Analytics Performance Plot */}

Portfolio Performance & Benchmark

`$${v.toLocaleString()}`} /> {showMsciBenchmark && ( )}
{/* EWMA parameter tuner slider */}

Parameter Tuning EWMA Lambda (λ)

Decay factor controls the shock sensitivity of the volatility model.

setEwmaLambda(parseFloat(e.target.value))} className="w-40 accent-emerald-400 cursor-pointer" /> λ = {ewmaLambda.toFixed(2)}
{/* Right 1 Column: Advanced Order Mask */}

Order Mask (Simulated)

setTradeSymbol(e.target.value.toUpperCase())} />
setTradeWknOrIsin(e.target.value)} />
setTradeShares(Number(e.target.value))} />
setTradePrice(Number(e.target.value))} />
{/* Order direction buttons */}
{/* Kelly Sizing Risk Recommendation Widget */} {tradeType === 'BUY' && (
Risk-Engine Sizing Kelly recommendation
setOddsRatio(Number(e.target.value))} className="w-full bg-slate-900 border border-slate-800 rounded px-1.5 py-1 text-[10px] text-slate-200 font-mono focus:outline-none" />
{kellySource === 'custom' && (
Probability of Success: {(kellyProbability * 100).toFixed(0)}%
setCustomProb(Number(e.target.value))} className="w-full accent-emerald-500 h-1 bg-slate-900 rounded" />
)} {kellySource !== 'custom' && (
System Probability: {(kellyProbability * 100).toFixed(1)}%
)} {potentialClusterRisk && (
Concentration Risk! Correlation > 0.70 to existing positions. Kelly recommendation halved by 50%.
)}
Kelly Fraction: {(kellyFraction * 100).toFixed(1)}% of Cash
Buy Volume: ${Math.round(recommendedKellyCash).toLocaleString()}
)} {/* Hypothesis input */}
setHypothesisTag(e.target.value)} />
{/* Fees Toggle */}
Simulate transaction fees setSimulateFees(e.target.checked)} className="rounded border-slate-800 text-emerald-500 focus:ring-0 accent-emerald-500 w-4 h-4 cursor-pointer" />
{/* Backfill Date Picker Toggle */}
Historical Backfill setIsBackfill(e.target.checked)} className="rounded border-slate-800 text-emerald-500 focus:ring-0 accent-emerald-500 w-4 h-4 cursor-pointer" />
{isBackfill && ( setBackfillDate(e.target.value)} /> )}
{orderError && (
{orderError}
)} {orderSuccess && (
Transaction successfully recorded!
)}
{/* SECTION 3: Holdings Table & Transactions Log */}
{/* Left 2 Columns: Holdings List */}

Portfolio Holdings ({activePortfolio.holdings.length})

Cash Balance: ${activePortfolio.cash.toLocaleString()}
{activePortfolio.holdings.length === 0 ? ( ) : ( activePortfolio.holdings.map((hold) => { const profitLoss = (hold.currentPrice - hold.avgPrice) * hold.shares; const isPositive = profitLoss >= 0; return ( ); }) )}
Asset Shares Avg Price Price Hypothesis PnL
No holdings in this Sandbox portfolio. Use the order mask to add assets.
{hold.symbol}
{hold.wknOrIsin &&
WKN: {hold.wknOrIsin}
}
{hold.shares} ${hold.avgPrice.toFixed(2)} ${hold.currentPrice.toFixed(2)} {hold.hypothesisTag || '-'} {isPositive ? : } ${Math.abs(profitLoss).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{/* Right 1 Column: Transactions History */}

Recent Order Book Entries

{activePortfolio.transactions.length === 0 ? (

No transactions in this portfolio yet.

) : ( activePortfolio.transactions.map((tx) => { const isBuy = tx.type === 'BUY'; return (
{isBuy ? 'BUY' : 'SELL'} {tx.symbol}
{tx.timestamp}
{tx.shares} shares @ ${tx.price.toFixed(2)} Fee: ${tx.feeApplied.toFixed(2)}
{tx.hypothesisTag && (
{tx.hypothesisTag}
)}
); }) )}
setIsMathModalOpen(false)} /> setIsBlueprintModalOpen(false)} />
); }