Closes #014 - System-wide handbook sweep, English consolidation & Crypto state persistence
This commit is contained in:
@@ -9,7 +9,7 @@ import CryptoMathModal from './CryptoMathModal';
|
||||
import {
|
||||
Cpu, Search, RefreshCw, BarChart2, TrendingUp, AlertCircle, Info,
|
||||
ChevronDown, ChevronUp, ArrowUpRight, ArrowDownRight, Compass, ShieldAlert, Sparkles,
|
||||
BookOpen
|
||||
BookOpen, Check
|
||||
} from 'lucide-react';
|
||||
|
||||
interface CoinData {
|
||||
@@ -68,8 +68,26 @@ const defaultCoins: Record<string, CoinData> = {
|
||||
}
|
||||
};
|
||||
|
||||
interface Forecast {
|
||||
id: string;
|
||||
ticker: string;
|
||||
predictedDirection: 'UP' | 'DOWN';
|
||||
predictedProb: number;
|
||||
entryPrice: number;
|
||||
resolved: boolean;
|
||||
result?: 'SUCCESS' | 'FAILURE';
|
||||
timestamp: number;
|
||||
targetTime: number;
|
||||
}
|
||||
|
||||
export default function CryptoDemo() {
|
||||
const { alphaSuccess, betaFailure, addModelTrial } = useSandboxStore();
|
||||
const { addModelTrial } = useSandboxStore();
|
||||
|
||||
// Local state for alphaSuccess and betaFailure to satisfy SSR hydration safeguarding
|
||||
const [alphaSuccess, setAlphaSuccess] = useState<number>(394);
|
||||
const [betaFailure, setBetaFailure] = useState<number>(118);
|
||||
const [forecasts, setForecasts] = useState<Forecast[]>([]);
|
||||
const [learningLoopLog, setLearningLoopLog] = useState<string>('');
|
||||
|
||||
const [activeTicker, setActiveTicker] = useState<string>('BTC');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -80,6 +98,166 @@ export default function CryptoDemo() {
|
||||
const [simulatedTrialLogged, setSimulatedTrialLogged] = useState(false);
|
||||
const [lastTrialSuccess, setLastTrialSuccess] = useState(false);
|
||||
|
||||
// Safely load counters and forecasts from localStorage on client mount
|
||||
React.useEffect(() => {
|
||||
const savedAlpha = localStorage.getItem('crypto_bayes_alpha');
|
||||
const savedBeta = localStorage.getItem('crypto_bayes_beta');
|
||||
const savedForecasts = localStorage.getItem('crypto_bayes_forecasts');
|
||||
|
||||
let loadedAlpha = 394;
|
||||
let loadedBeta = 118;
|
||||
|
||||
if (savedAlpha !== null) {
|
||||
loadedAlpha = parseInt(savedAlpha, 10);
|
||||
setAlphaSuccess(loadedAlpha);
|
||||
} else {
|
||||
localStorage.setItem('crypto_bayes_alpha', '394');
|
||||
}
|
||||
|
||||
if (savedBeta !== null) {
|
||||
loadedBeta = parseInt(savedBeta, 10);
|
||||
setBetaFailure(loadedBeta);
|
||||
} else {
|
||||
localStorage.setItem('crypto_bayes_beta', '118');
|
||||
}
|
||||
|
||||
if (savedForecasts !== null) {
|
||||
setForecasts(JSON.parse(savedForecasts));
|
||||
} else {
|
||||
const now = Date.now();
|
||||
const mockForecasts: Forecast[] = [
|
||||
{
|
||||
id: 'mock-1',
|
||||
ticker: 'BTC',
|
||||
predictedDirection: 'UP',
|
||||
predictedProb: 0.68,
|
||||
entryPrice: 65000,
|
||||
resolved: true,
|
||||
result: 'SUCCESS',
|
||||
timestamp: now - 86400 * 1000 * 3,
|
||||
targetTime: now - 86400 * 1000 * 2,
|
||||
},
|
||||
{
|
||||
id: 'mock-2',
|
||||
ticker: 'ETH',
|
||||
predictedDirection: 'DOWN',
|
||||
predictedProb: 0.35,
|
||||
entryPrice: 3950,
|
||||
resolved: true,
|
||||
result: 'SUCCESS',
|
||||
timestamp: now - 86400 * 1000 * 3,
|
||||
targetTime: now - 86400 * 1000 * 2,
|
||||
},
|
||||
{
|
||||
id: 'mock-3',
|
||||
ticker: 'SOL',
|
||||
predictedDirection: 'UP',
|
||||
predictedProb: 0.72,
|
||||
entryPrice: 170,
|
||||
resolved: true,
|
||||
result: 'SUCCESS',
|
||||
timestamp: now - 86400 * 1000 * 2,
|
||||
targetTime: now - 86400 * 1000 * 1,
|
||||
},
|
||||
{
|
||||
id: 'mock-4',
|
||||
ticker: 'BTC',
|
||||
predictedDirection: 'UP',
|
||||
predictedProb: 0.62,
|
||||
entryPrice: 71000,
|
||||
resolved: true,
|
||||
result: 'FAILURE',
|
||||
timestamp: now - 86400 * 1000 * 2,
|
||||
targetTime: now - 86400 * 1000 * 1,
|
||||
},
|
||||
{
|
||||
id: 'mock-5',
|
||||
ticker: 'ETH',
|
||||
predictedDirection: 'UP',
|
||||
predictedProb: 0.58,
|
||||
entryPrice: 3900,
|
||||
resolved: true,
|
||||
result: 'FAILURE',
|
||||
timestamp: now - 86400 * 1000 * 2,
|
||||
targetTime: now - 86400 * 1000 * 1,
|
||||
}
|
||||
];
|
||||
setForecasts(mockForecasts);
|
||||
localStorage.setItem('crypto_bayes_forecasts', JSON.stringify(mockForecasts));
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Client-side background learning loop evaluating forecasts against actual live returns
|
||||
React.useEffect(() => {
|
||||
const runLearningLoop = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/finance?region=crypto');
|
||||
if (!res.ok) return;
|
||||
const data = await res.ok ? await res.json() : { results: [] };
|
||||
const results = data.results || [];
|
||||
|
||||
const pricesMap: Record<string, number> = {};
|
||||
results.forEach((r: any) => {
|
||||
pricesMap[r.ticker] = r.currentPrice;
|
||||
const cleanTicker = r.ticker.replace('-USD', '');
|
||||
pricesMap[cleanTicker] = r.currentPrice;
|
||||
});
|
||||
|
||||
let updatedAny = false;
|
||||
let newAlpha = alphaSuccess;
|
||||
let newBeta = betaFailure;
|
||||
|
||||
const updatedForecasts = forecasts.map((f) => {
|
||||
if (f.resolved) return f;
|
||||
|
||||
const currentPrice = pricesMap[f.ticker] || pricesMap[`${f.ticker}-USD`];
|
||||
if (!currentPrice) return f;
|
||||
|
||||
const now = Date.now();
|
||||
if (now >= f.targetTime) {
|
||||
const priceWentUp = currentPrice > f.entryPrice;
|
||||
const success = (f.predictedDirection === 'UP' && priceWentUp) || (f.predictedDirection === 'DOWN' && !priceWentUp);
|
||||
|
||||
updatedAny = true;
|
||||
if (success) {
|
||||
newAlpha += 1;
|
||||
} else {
|
||||
newBeta += 1;
|
||||
}
|
||||
|
||||
addModelTrial(success);
|
||||
|
||||
return {
|
||||
...f,
|
||||
resolved: true,
|
||||
result: success ? ('SUCCESS' as const) : ('FAILURE' as const)
|
||||
};
|
||||
}
|
||||
return f;
|
||||
});
|
||||
|
||||
if (updatedAny) {
|
||||
setAlphaSuccess(newAlpha);
|
||||
setBetaFailure(newBeta);
|
||||
setForecasts(updatedForecasts);
|
||||
localStorage.setItem('crypto_bayes_alpha', String(newAlpha));
|
||||
localStorage.setItem('crypto_bayes_beta', String(newBeta));
|
||||
localStorage.setItem('crypto_bayes_forecasts', JSON.stringify(updatedForecasts));
|
||||
setLearningLoopLog(`Processed active forecasts. New successes: ${newAlpha}, New failures: ${newBeta}`);
|
||||
setTimeout(() => setLearningLoopLog(''), 6000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Learning loop evaluation error:", err);
|
||||
}
|
||||
};
|
||||
|
||||
if (forecasts.length > 0) {
|
||||
runLearningLoop();
|
||||
}
|
||||
const interval = setInterval(runLearningLoop, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, [forecasts, alphaSuccess, betaFailure, addModelTrial]);
|
||||
|
||||
// Active Coin data retrieval
|
||||
const activeCoin = useMemo(() => {
|
||||
return customCoins[activeTicker] || defaultCoins[activeTicker] || defaultCoins['BTC'];
|
||||
@@ -98,14 +276,12 @@ export default function CryptoDemo() {
|
||||
|
||||
// Apply Bayesian online learning error-correction posterior update
|
||||
const correctedPredictions = useMemo(() => {
|
||||
// Correct short term probability
|
||||
const shortTermCorrected = calculateBetaPosterior(
|
||||
alphaSuccess,
|
||||
betaFailure,
|
||||
mlPredictions.shortTermProb,
|
||||
12 // pseudo weight/confidence scale
|
||||
12 // confidence scale
|
||||
);
|
||||
// Correct medium term probability
|
||||
const mediumTermCorrected = calculateBetaPosterior(
|
||||
alphaSuccess,
|
||||
betaFailure,
|
||||
@@ -119,7 +295,7 @@ export default function CryptoDemo() {
|
||||
};
|
||||
}, [mlPredictions, alphaSuccess, betaFailure]);
|
||||
|
||||
// Perform search check for Altcoins
|
||||
// Search/add new altcoin
|
||||
const handleAltcoinSearch = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSearchError(false);
|
||||
@@ -138,7 +314,6 @@ export default function CryptoDemo() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate simulated data for Altcoin
|
||||
const isBull = Math.random() > 0.45;
|
||||
const simulatedChange = isBull ? 3 + Math.random() * 8 : -2 - Math.random() * 6;
|
||||
const simulatedPrice = isBull ? 2 + Math.random() * 10 : 0.2 + Math.random() * 3;
|
||||
@@ -162,8 +337,44 @@ export default function CryptoDemo() {
|
||||
setSearchQuery('');
|
||||
};
|
||||
|
||||
// Manual logging of active forecast
|
||||
const handleLogManualForecast = () => {
|
||||
const entryPrice = parseFloat(activeCoin.price.replace(/[^0-9.]/g, ''));
|
||||
const predictedDirection = correctedPredictions.shortTerm > 0.5 ? 'UP' : 'DOWN';
|
||||
const predictedProb = correctedPredictions.shortTerm;
|
||||
|
||||
const newForecast: Forecast = {
|
||||
id: 'fc-' + Date.now(),
|
||||
ticker: activeCoin.ticker,
|
||||
predictedDirection,
|
||||
predictedProb,
|
||||
entryPrice,
|
||||
resolved: false,
|
||||
timestamp: Date.now(),
|
||||
targetTime: Date.now() + 60 * 1000 // resolves in 60s for direct visual validation
|
||||
};
|
||||
|
||||
const nextForecasts = [newForecast, ...forecasts];
|
||||
setForecasts(nextForecasts);
|
||||
localStorage.setItem('crypto_bayes_forecasts', JSON.stringify(nextForecasts));
|
||||
|
||||
setLearningLoopLog(`Registered active forecast for ${activeCoin.ticker} at $${entryPrice}. Evaluating returns in 60 seconds.`);
|
||||
setTimeout(() => setLearningLoopLog(''), 6000);
|
||||
};
|
||||
|
||||
// Simulator for calibration
|
||||
const handleSimulateTrial = (success: boolean) => {
|
||||
addModelTrial(success);
|
||||
setAlphaSuccess(prev => {
|
||||
const next = success ? prev + 1 : prev;
|
||||
localStorage.setItem('crypto_bayes_alpha', String(next));
|
||||
return next;
|
||||
});
|
||||
setBetaFailure(prev => {
|
||||
const next = !success ? prev + 1 : prev;
|
||||
localStorage.setItem('crypto_bayes_beta', String(next));
|
||||
return next;
|
||||
});
|
||||
setLastTrialSuccess(success);
|
||||
setSimulatedTrialLogged(true);
|
||||
setTimeout(() => setSimulatedTrialLogged(false), 2500);
|
||||
@@ -179,9 +390,9 @@ export default function CryptoDemo() {
|
||||
{/* Header */}
|
||||
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 border-b border-slate-800 pb-4 mb-6">
|
||||
<div>
|
||||
<span className="text-cyan-400 text-xs font-semibold uppercase tracking-wider">Element 4</span>
|
||||
<span className="text-cyan-400 text-xs font-semibold uppercase tracking-wider">Level 4</span>
|
||||
<h2 className="text-2xl font-bold bg-gradient-to-r from-cyan-400 to-sky-200 bg-clip-text text-transparent">
|
||||
Predictive Krypto-Modelle & Bayes Self-Correction
|
||||
Predictive Crypto Models & Bayes Self-Correction
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
@@ -195,13 +406,13 @@ export default function CryptoDemo() {
|
||||
className="flex items-center gap-1.5 px-4 py-2 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-cyan-400 justify-center h-11"
|
||||
>
|
||||
<BookOpen className="w-3.5 h-3.5" />
|
||||
<span>📖 Modulerklärung</span>
|
||||
<span>📖 Quantitative Handbook</span>
|
||||
</button>
|
||||
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-xl px-4 py-2 flex items-center gap-3 h-11">
|
||||
<Cpu className="text-cyan-400 w-5 h-5 animate-spin-slow" />
|
||||
<div>
|
||||
<p className="text-slate-400 text-xs">A-Priori Genauigkeit</p>
|
||||
<p className="text-slate-400 text-xs">Prior Accuracy</p>
|
||||
<p className="font-mono text-sm font-bold text-cyan-400">
|
||||
{priorAccuracy.toFixed(1)}% (n={totalTrials})
|
||||
</p>
|
||||
@@ -245,7 +456,7 @@ export default function CryptoDemo() {
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="Altcoin Ticker (z.B. LINK)"
|
||||
placeholder="Altcoin Ticker (e.g. LINK)"
|
||||
className="bg-slate-900 border border-slate-800 rounded-lg p-2 flex-1 text-slate-100 font-mono text-xs uppercase focus:outline-none focus:border-cyan-500"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
@@ -279,14 +490,14 @@ export default function CryptoDemo() {
|
||||
{/* Left Column: Metrics Widgets */}
|
||||
<div className="xl:col-span-2 space-y-6">
|
||||
<h3 className="text-base font-bold text-white flex items-center gap-2 border-b border-slate-800 pb-3">
|
||||
<BarChart2 className="text-cyan-400 w-5 h-5" /> On-Chain & Derivate-Indikatoren ({activeCoin.ticker})
|
||||
<BarChart2 className="text-cyan-400 w-5 h-5" /> On-Chain & Derivative Indicators ({activeCoin.ticker})
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
|
||||
{/* Funding & Open Interest Widget */}
|
||||
<div className="p-4 rounded-xl border border-slate-850 bg-slate-950/40 space-y-3">
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Ref-Zinssatz & Kontrakte (OI)</h4>
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Funding Rates & Open Interest</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm font-mono">
|
||||
<span className="text-slate-400 text-xs">Daily Funding Rate:</span>
|
||||
@@ -305,14 +516,14 @@ export default function CryptoDemo() {
|
||||
|
||||
{/* Long/Short & Liquidation Widget */}
|
||||
<div className="p-4 rounded-xl border border-slate-850 bg-slate-950/40 space-y-3">
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Positionierung & Liquidationen</h4>
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Positioning & Liquidations</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm font-mono">
|
||||
<span className="text-slate-400 text-xs">Long / Short Ratio:</span>
|
||||
<span className="text-slate-200 font-bold">{activeCoin.longShortRatio}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-xs font-mono">
|
||||
<span className="text-slate-400">Liq-Cluster:</span>
|
||||
<span className="text-slate-400">Liq Cluster:</span>
|
||||
<span className="text-rose-400">Long: {activeCoin.liqLong} | Short: {activeCoin.liqShort}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -320,32 +531,32 @@ export default function CryptoDemo() {
|
||||
|
||||
{/* Whale Flows Widget */}
|
||||
<div className="p-4 rounded-xl border border-slate-850 bg-slate-950/40 space-y-3">
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Whale-Ströme (Nettozufluss)</h4>
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Whale Flows (Net Inflow)</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm font-mono">
|
||||
<span className="text-slate-400 text-xs">Netto Inflow (Wallets):</span>
|
||||
<span className="text-slate-400 text-xs">Net Inflow (Wallets):</span>
|
||||
<span className={`font-bold ${activeCoin.whaleInflow >= 0 ? 'text-emerald-400' : 'text-rose-400'}`}>
|
||||
{activeCoin.whaleInflow > 0 ? '+' : ''}{activeCoin.whaleInflow} {activeCoin.ticker}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[10px] text-slate-500 leading-relaxed font-sans">
|
||||
Positive Werte signalisieren, dass Großinvestoren Bestände von Börsen auf private Wallets (Akkumulation) abziehen.
|
||||
Positive values signal that large investors are withdrawing assets from exchanges to private wallets (accumulation).
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Exchange Reserves Widget */}
|
||||
<div className="p-4 rounded-xl border border-slate-850 bg-slate-950/40 space-y-3">
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Börsenreserven (Spot)</h4>
|
||||
<h4 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Exchange Reserves (Spot)</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm font-mono">
|
||||
<span className="text-slate-400 text-xs">Reservenänderung (7d):</span>
|
||||
<span className="text-slate-400 text-xs">Reserve Change (7d):</span>
|
||||
<span className={`font-bold ${activeCoin.exchangeReserves <= 0 ? 'text-emerald-400' : 'text-rose-400'}`}>
|
||||
{activeCoin.exchangeReserves > 0 ? '+' : ''}{activeCoin.exchangeReserves}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[10px] text-slate-500 leading-relaxed font-sans">
|
||||
Sinkende Reserven an den Spot-Börsen reduzieren den verfügbaren Verkaufsdruck und begünstigen Squeezes.
|
||||
Falling reserves at spot exchanges reduce available selling pressure and favor squeezes.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -356,7 +567,7 @@ export default function CryptoDemo() {
|
||||
{/* Right Column: Predictive Gauges & Correction Calibration */}
|
||||
<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">
|
||||
<h3 className="text-base font-bold text-white flex items-center gap-2 border-b border-slate-800 pb-3">
|
||||
<Compass className="text-cyan-400 w-5 h-5" /> Vorhersage-Wahrscheinlichkeiten
|
||||
<Compass className="text-cyan-400 w-5 h-5" /> Prediction Probabilities
|
||||
</h3>
|
||||
|
||||
{/* Gauges / Progress Bars */}
|
||||
@@ -369,27 +580,25 @@ export default function CryptoDemo() {
|
||||
<span className="text-cyan-400 font-mono">{(correctedPredictions.shortTerm * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-slate-950 rounded-full h-3 overflow-hidden border border-slate-850 flex">
|
||||
{/* ML Baseline Overlay */}
|
||||
<div
|
||||
className="bg-cyan-500 h-full rounded-l transition-all duration-500 opacity-30"
|
||||
style={{ width: `${mlPredictions.shortTermProb * 100}%` }}
|
||||
/>
|
||||
{/* Corrected Posterior Marker */}
|
||||
<div
|
||||
className="bg-cyan-400 h-full rounded-r transition-all duration-500 -ml-[20%] shadow-[0_0_10px_rgba(34,211,238,0.5)]"
|
||||
style={{ width: `${correctedPredictions.shortTerm * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-[9px] text-slate-500 font-mono">
|
||||
<span>ML-Signal: {(mlPredictions.shortTermProb * 100).toFixed(0)}%</span>
|
||||
<span className="text-cyan-400">Bayes-Korrigiert: {(correctedPredictions.shortTerm * 100).toFixed(0)}%</span>
|
||||
<span>ML Signal: {(mlPredictions.shortTermProb * 100).toFixed(0)}%</span>
|
||||
<span className="text-cyan-400">Bayes Corrected: {(correctedPredictions.shortTerm * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 14d Gauge */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-xs font-semibold">
|
||||
<span className="text-slate-300">14d Struktureller Bullish-Trend (Medium-Term)</span>
|
||||
<span className="text-slate-300">14d Structural Bullish Trend (Medium-Term)</span>
|
||||
<span className="text-teal-400 font-mono">{(correctedPredictions.mediumTerm * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-slate-950 rounded-full h-3 overflow-hidden border border-slate-850 flex">
|
||||
@@ -403,8 +612,8 @@ export default function CryptoDemo() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-[9px] text-slate-500 font-mono">
|
||||
<span>ML-Signal: {(mlPredictions.mediumTermProb * 100).toFixed(0)}%</span>
|
||||
<span className="text-teal-400">Bayes-Korrigiert: {(correctedPredictions.mediumTerm * 100).toFixed(0)}%</span>
|
||||
<span>ML Signal: {(mlPredictions.mediumTermProb * 100).toFixed(0)}%</span>
|
||||
<span className="text-teal-400">Bayes Corrected: {(correctedPredictions.mediumTerm * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -413,88 +622,161 @@ export default function CryptoDemo() {
|
||||
{/* Model Calibration Log & Simulation */}
|
||||
<div className="p-4 rounded-xl border border-slate-850 bg-slate-950/40 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-xs font-bold text-slate-300 uppercase">Bayes Modell-Kalibrierung</h4>
|
||||
<h4 className="text-xs font-bold text-slate-300 uppercase">Bayes Model Calibration</h4>
|
||||
<span className="text-[10px] text-slate-500 font-mono">n = {totalTrials} Trials</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 text-xs font-mono pb-2 border-b border-slate-900">
|
||||
<div className="text-slate-400">Erfolge (α):</div>
|
||||
<div className="text-slate-400">Successes (α):</div>
|
||||
<div className="text-emerald-400 font-bold">{alphaSuccess}</div>
|
||||
<div className="text-slate-400">Fehlalarme (β):</div>
|
||||
<div className="text-slate-400">False Alarms (β):</div>
|
||||
<div className="text-rose-400 font-bold">{betaFailure}</div>
|
||||
</div>
|
||||
|
||||
{/* Trial simulator */}
|
||||
<div className="space-y-2">
|
||||
<p className="text-[10px] text-slate-400">Modell-Drift simulieren: Fügen Sie richtige/falsche Outcomes hinzu, um die Beta-Verteilung anzupassen.</p>
|
||||
<p className="text-[10px] text-slate-400">Simulate model drift: Add correct/false outcomes to calibrate the Beta distribution.</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => handleSimulateTrial(true)}
|
||||
className="flex-1 bg-emerald-500/10 hover:bg-emerald-500/20 text-emerald-400 border border-emerald-500/20 py-1.5 rounded-lg text-xs font-semibold font-mono"
|
||||
>
|
||||
+1 Erfolg
|
||||
+1 Success
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSimulateTrial(false)}
|
||||
className="flex-1 bg-rose-500/10 hover:bg-rose-500/20 text-rose-400 border border-rose-500/20 py-1.5 rounded-lg text-xs font-semibold font-mono"
|
||||
>
|
||||
+1 Fehlalarm
|
||||
+1 False Alarm
|
||||
</button>
|
||||
</div>
|
||||
{simulatedTrialLogged && (
|
||||
<div className="text-[10px] text-cyan-400 font-mono text-center animate-pulse">
|
||||
Trial geloggt! Bayes-Prior wurde auf {lastTrialSuccess ? 'Erfolg' : 'Fehlalarm'} aktualisiert.
|
||||
Trial logged! Bayes prior updated to {lastTrialSuccess ? 'Success' : 'False Alarm'}.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* SECTION 3: Mathematical LaTeX Accordion */}
|
||||
{/* SECTION 3: Active Forecasts (Feedback Loop) */}
|
||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8 mt-6">
|
||||
<div className="xl:col-span-2 bg-slate-950/30 rounded-xl p-4 border border-slate-850 space-y-4">
|
||||
<div className="flex justify-between items-center border-b border-slate-800 pb-3">
|
||||
<h3 className="text-sm font-bold text-white flex items-center gap-2">
|
||||
<TrendingUp className="text-cyan-400 w-4 h-4" /> Active Learning Feedback Loop
|
||||
</h3>
|
||||
<button
|
||||
onClick={handleLogManualForecast}
|
||||
className="bg-cyan-500 hover:bg-cyan-600 text-slate-950 font-bold py-1.5 px-3 rounded-lg text-xs transition-all active:scale-[0.96]"
|
||||
>
|
||||
Log Forecast to Feedback Loop
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{learningLoopLog && (
|
||||
<div className="p-3 bg-cyan-950/20 border border-cyan-900/40 rounded-lg text-xs text-cyan-300 flex items-center gap-2">
|
||||
<Info className="w-4 h-4 shrink-0" />
|
||||
<span>{learningLoopLog}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="overflow-x-auto max-h-56 scrollbar-thin">
|
||||
<table className="w-full border-collapse text-left text-xs font-mono">
|
||||
<thead>
|
||||
<tr className="border-b border-slate-800 text-slate-400 font-semibold bg-slate-900/40">
|
||||
<th className="p-2">Ticker</th>
|
||||
<th className="p-2">Direction</th>
|
||||
<th className="p-2">Probability</th>
|
||||
<th className="p-2">Entry Price</th>
|
||||
<th className="p-2">Status</th>
|
||||
<th className="p-2 text-right">Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{forecasts.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="p-4 text-center text-slate-500 italic">No forecasts registered yet.</td>
|
||||
</tr>
|
||||
) : (
|
||||
forecasts.map((fc) => (
|
||||
<tr key={fc.id} className="border-b border-slate-900 hover:bg-slate-850/10">
|
||||
<td className="p-2 text-slate-200 font-bold">{fc.ticker}</td>
|
||||
<td className="p-2">
|
||||
<span className={`px-1.5 py-0.5 rounded text-[10px] font-bold ${fc.predictedDirection === 'UP' ? 'bg-emerald-500/10 text-emerald-400' : 'bg-rose-500/10 text-rose-400'}`}>
|
||||
{fc.predictedDirection}
|
||||
</span>
|
||||
</td>
|
||||
<td className="p-2 text-slate-350">{(fc.predictedProb * 100).toFixed(0)}%</td>
|
||||
<td className="p-2 text-slate-300">${fc.entryPrice.toLocaleString()}</td>
|
||||
<td className="p-2 text-slate-400">{fc.resolved ? 'RESOLVED' : 'PENDING'}</td>
|
||||
<td className={`p-2 text-right font-bold ${fc.result === 'SUCCESS' ? 'text-emerald-400' : fc.result === 'FAILURE' ? 'text-rose-400' : 'text-slate-500'}`}>
|
||||
{fc.result || '-'}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Informational overlay */}
|
||||
<div className="bg-slate-950/40 rounded-xl p-4 border border-slate-850 text-xs text-slate-400 space-y-2">
|
||||
<h4 className="font-bold text-slate-300 uppercase">Econometric Feedback Loop Spec</h4>
|
||||
<p className="leading-relaxed">
|
||||
The learning loop automatically evaluates active forecast parameters in the background against actual price histories returned by <code className="text-cyan-400 font-mono">/api/finance?region=crypto</code>.
|
||||
</p>
|
||||
<p className="leading-relaxed">
|
||||
When a logged forecast passes its evaluation target timestamp, it resolves against live market data, updating the Bayesian online calibration metrics <InlineMath math="\alpha" /> and <InlineMath math="\beta" />.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SECTION 4: Mathematical LaTeX Accordion */}
|
||||
<div className="border-t border-slate-850 pt-4 mt-6">
|
||||
<button
|
||||
onClick={() => setShowMathAccordion(!showMathAccordion)}
|
||||
className="flex items-center gap-1.5 text-xs text-slate-400 hover:text-cyan-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 Formulierung (Beta-Update & Random Forest)</span>
|
||||
<span className="font-semibold uppercase tracking-wider">Mathematical Formulation (Beta Update & Random Forest)</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-bold text-cyan-400 mb-1">1. Bayesianische Beta-Konjugierte Fehlerkorrektur</h4>
|
||||
<h4 className="font-bold text-cyan-400 mb-1">1. Bayesian Beta-Conjugate Error Correction</h4>
|
||||
<p className="mb-2">
|
||||
Wir modellieren das Vertrauensintervall über die Fehlerraten des Modells mittels einer Beta-Verteilung. Der A-Priori Fehlerzustand wird durch die Parameter <InlineMath math="\alpha" /> (Erfolge) und <InlineMath math="\beta" /> (Fehlalarme) dargestellt:
|
||||
We model the error rate confidence interval of the model using a Beta distribution. The prior error state is represented by the parameters <InlineMath math="\alpha" /> (Successes) and <InlineMath math="\beta" /> (False Alarms):
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto text-slate-200">
|
||||
<BlockMath math="P \sim \text{Beta}(\alpha, \beta) \quad \text{mit Erwartungswert } \mathbb{E}[P] = \frac{\alpha}{\alpha + \beta}" />
|
||||
<BlockMath math="P \\sim \\text{Beta}(\\alpha, \\beta) \\quad \\text{with expected value } \\mathbb{E}[P] = \\frac{\\alpha}{\\alpha + \\beta}" />
|
||||
</div>
|
||||
<p className="mb-2">
|
||||
Bei einem neuen ML-Signal <InlineMath math="P_{\text{ML}}" /> führen wir ein konjugiertes Bayes-Update mit einem Vertrauensgewicht <InlineMath math="w" /> aus:
|
||||
With a new ML signal <InlineMath math="P_{\\text{ML}}" />, we perform a conjugate Bayes update with a confidence weight <InlineMath math="w" />:
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto text-slate-200">
|
||||
<BlockMath math="\alpha_{\text{post}} = \alpha + w \cdot P_{\text{ML}}, \quad \beta_{\text{post}} = \beta + w \cdot (1 - P_{\text{ML}})" />
|
||||
<BlockMath math="\\alpha_{\\text{post}} = \\alpha + w \\cdot P_{\\text{ML}}, \\quad \\beta_{\\text{post}} = \\beta + w \\cdot (1 - P_{\\text{ML}})" />
|
||||
</div>
|
||||
<div className="py-2 overflow-x-auto text-slate-200">
|
||||
<BlockMath math="P_{\text{Posterior}} = \frac{\alpha_{\text{post}}}{\alpha_{\text{post}} + \beta_{\text{post}}}" />
|
||||
<BlockMath math="P_{\\text{Posterior}} = \\frac{\\alpha_{\\text{post}}}{\\alpha_{\\text{post}} + \\beta_{\\text{post}}}" />
|
||||
</div>
|
||||
<p className="text-slate-400">
|
||||
Ist das Modell historisch sehr instabil (hohes <InlineMath math="\beta" />), korrigiert der Bayesianische Term ein übermütiges ML-Signal nach unten, was die Robustheit des Gesamtsystems schützt.
|
||||
If the model is historically highly unstable (high <InlineMath math="\\beta" />), the Bayesian term corrects an overconfident ML signal downwards, safeguarding the robustness of the system.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-slate-900 pt-3">
|
||||
<h4 className="font-bold text-cyan-400 mb-1">2. Random Forest Nicht-Lineares Signalmapping</h4>
|
||||
<h4 className="font-bold text-cyan-400 mb-1">2. Random Forest Non-Linear Signal Mapping</h4>
|
||||
<p className="mb-2">
|
||||
Der Random Forest simuliert ein Ensemble von 10 schwachen Entscheidungsbäumen. Jeder Baum spaltet die Daten nach Schwellenwerten (z.B. „Funding-Rate < -0.04%“ und „Open Interest > 10%“) auf:
|
||||
The Random Forest simulates an ensemble of 10 weak decision trees. Each tree splits the data based on threshold criteria (e.g., 'Funding Rate < -0.04%' and 'Open Interest > 10%'):
|
||||
</p>
|
||||
<div className="py-2 overflow-x-auto text-slate-200">
|
||||
<BlockMath math="\text{ML}_{\text{prob}} = \frac{1}{M} \sum_{m=1}^{M} T_m(\mathbf{x})" />
|
||||
<BlockMath math="\\text{ML}_{\\text{prob}} = \\frac{1}{M} \\sum_{m=1}^{M} T_m(\\mathbf{x})" />
|
||||
</div>
|
||||
<p className="text-slate-400">
|
||||
wobei <InlineMath math="T_m(\mathbf{x})" /> der prognostizierte Ausgabewert des <InlineMath math="m" />-ten Entscheidungsbaumes für den Featurevektor <InlineMath math="\mathbf{x}" /> ist.
|
||||
where <InlineMath math="T_m(\\mathbf{x})" /> is the predicted output value of the <InlineMath math="m" />-th decision tree for the feature vector <InlineMath math="\\mathbf{x}" />.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user