← back to Bertha
src/app/dashboard/risk/page.js
142 lines
'use client';
import { useState, useEffect } from 'react';
export default function RiskPage() {
const [risk, setRisk] = useState(null);
const [loading, setLoading] = useState(true);
async function loadRisk() {
try {
const res = await fetch('/api/risk');
const data = await res.json();
setRisk(data);
} catch (err) { console.error(err); }
setLoading(false);
}
useEffect(() => { loadRisk(); const i = setInterval(loadRisk, 10000); return () => clearInterval(i); }, []);
async function toggleKillSwitch() {
const action = risk?.state?.kill_switch_active ? 'deactivate' : 'activate';
const reason = action === 'activate' ? prompt('Kill switch reason:') : null;
if (action === 'activate' && !reason) return;
try {
await fetch('/api/risk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: `kill_switch_${action}`, reason }) });
loadRisk();
} catch (err) { alert('Error: ' + err.message); }
}
async function updateConfig(key, value) {
try {
const config = { ...risk.state.config, [key]: value };
await fetch('/api/risk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'update_config', config }) });
loadRisk();
} catch (err) { alert('Error: ' + err.message); }
}
if (loading) return <div className="flex items-center justify-center h-64"><div className="animate-spin w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full" /></div>;
const s = risk?.state || {};
const c = s.config || {};
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">Risk Management</h1>
<p className="text-gray-500 text-sm">Exposure limits, kill switches, and sizing controls</p>
</div>
<button onClick={toggleKillSwitch} className={`btn ${s.kill_switch_active ? 'btn-success' : 'btn-danger'}`}>
{s.kill_switch_active ? 'Deactivate Kill Switch' : 'Activate Kill Switch'}
</button>
</div>
{s.kill_switch_active && (
<div className="bg-red-500/10 border border-red-500/30 rounded-xl p-4 flex items-center gap-3">
<span className="text-2xl">🚨</span>
<div>
<p className="font-semibold text-red-400">KILL SWITCH ACTIVE</p>
<p className="text-sm text-gray-400">{s.kill_reason || 'No reason specified'}</p>
</div>
</div>
)}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="card">
<p className="text-xs text-gray-500 uppercase mb-1">Bankroll</p>
<p className="stat-value text-green-400">${parseFloat(s.bankroll || 50).toFixed(2)}</p>
</div>
<div className="card">
<p className="text-xs text-gray-500 uppercase mb-1">Daily P&L</p>
<p className={`stat-value ${parseFloat(s.daily_pnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'}`}>${parseFloat(s.daily_pnl || 0).toFixed(2)}</p>
</div>
<div className="card">
<p className="text-xs text-gray-500 uppercase mb-1">Open Exposure</p>
<p className="stat-value text-blue-400">${parseFloat(s.open_exposure || 0).toFixed(2)}</p>
</div>
<div className="card">
<p className="text-xs text-gray-500 uppercase mb-1">Max Drawdown</p>
<p className="stat-value text-purple-400">{parseFloat(s.max_drawdown_pct || 0).toFixed(1)}%</p>
</div>
</div>
{/* Risk Configuration */}
<div className="card">
<h2 className="font-semibold mb-4">Risk Configuration</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{[
{ key: 'paper_mode', label: 'Paper Trading Mode', type: 'toggle', desc: 'When ON, no real orders are placed' },
{ key: 'max_bet_usd', label: 'Max Bet Size ($)', type: 'number', step: 0.05 },
{ key: 'max_daily_loss_usd', label: 'Max Daily Loss ($)', type: 'number', step: 0.50 },
{ key: 'max_daily_loss_pct', label: 'Max Daily Loss (%)', type: 'number', step: 1 },
{ key: 'max_open_exposure_usd', label: 'Max Open Exposure ($)', type: 'number', step: 0.50 },
{ key: 'max_drawdown_pct', label: 'Max Drawdown (%)', type: 'number', step: 1 },
{ key: 'min_edge_threshold', label: 'Min Edge Threshold', type: 'number', step: 0.005 },
{ key: 'min_liquidity', label: 'Min Market Liquidity ($)', type: 'number', step: 10 },
{ key: 'fractional_kelly', label: 'Kelly Fraction', type: 'number', step: 0.05 },
].map(item => (
<div key={item.key} className="flex items-center justify-between bg-gray-800/30 rounded-lg p-3">
<div>
<p className="text-sm font-medium">{item.label}</p>
{item.desc && <p className="text-xs text-gray-500">{item.desc}</p>}
</div>
{item.type === 'toggle' ? (
<button onClick={() => updateConfig(item.key, !c[item.key])}
className={`w-12 h-6 rounded-full transition-colors ${c[item.key] ? 'bg-green-500' : 'bg-gray-600'} relative`}>
<span className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${c[item.key] ? 'left-6' : 'left-0.5'}`} />
</button>
) : (
<input type="number" step={item.step} value={c[item.key] ?? ''} onChange={e => updateConfig(item.key, parseFloat(e.target.value))}
className="w-24 bg-gray-800 border border-gray-700 rounded px-2 py-1 text-sm text-right font-mono" />
)}
</div>
))}
</div>
</div>
{/* Edge Gate Simulation */}
<div className="card">
<h2 className="font-semibold mb-3">Edge Gate Simulator</h2>
<p className="text-gray-500 text-sm mb-4">Test if a hypothetical trade would pass risk gates</p>
<div className="grid grid-cols-4 gap-3">
<div><label className="text-xs text-gray-500">P(Yes)</label><input id="sim-pyes" type="number" step="0.01" defaultValue="0.65" className="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1.5 text-sm font-mono" /></div>
<div><label className="text-xs text-gray-500">Market Mid</label><input id="sim-mid" type="number" step="0.01" defaultValue="0.55" className="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1.5 text-sm font-mono" /></div>
<div><label className="text-xs text-gray-500">Spread</label><input id="sim-spread" type="number" step="0.01" defaultValue="0.04" className="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1.5 text-sm font-mono" /></div>
<div><label className="text-xs text-gray-500">Liquidity ($)</label><input id="sim-liq" type="number" step="10" defaultValue="500" className="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1.5 text-sm font-mono" /></div>
</div>
<button onClick={async () => {
const res = await fetch('/api/risk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
action: 'evaluate',
pYes: parseFloat(document.getElementById('sim-pyes').value),
marketMid: parseFloat(document.getElementById('sim-mid').value),
spread: parseFloat(document.getElementById('sim-spread').value),
liquidity: parseFloat(document.getElementById('sim-liq').value),
})});
const data = await res.json();
alert(JSON.stringify(data.evaluation, null, 2));
}} className="btn btn-primary mt-3">Simulate</button>
</div>
</div>
);
}