Fix: Light mode, reset btn, history=settled only

This commit is contained in:
2026-03-15 17:36:09 -07:00
parent 3b1c594636
commit 23d8df2116

View File

@@ -1,15 +1,16 @@
'use client'; 'use client';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
const GREEN = '#28CC95'; const GREEN = '#16A34A';
const RED = '#FF6B6B'; const RED = '#DC2626';
const BLUE = '#4A90D9'; const BLUE = '#2563EB';
export default function PaperDashboard() { export default function PaperDashboard() {
const [data, setData] = useState(null); const [data, setData] = useState(null);
const [trades, setTrades] = useState({}); const [trades, setTrades] = useState({});
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [activeStrat, setActiveStrat] = useState(null); const [activeStrat, setActiveStrat] = useState(null);
const [resetting, setResetting] = useState(false);
useEffect(() => { useEffect(() => {
const fetchState = async () => { const fetchState = async () => {
@@ -31,7 +32,6 @@ export default function PaperDashboard() {
return () => clearInterval(interval); return () => clearInterval(interval);
}, [activeStrat]); }, [activeStrat]);
// Fetch trades for active strategy
useEffect(() => { useEffect(() => {
if (!activeStrat) return; if (!activeStrat) return;
@@ -50,10 +50,22 @@ export default function PaperDashboard() {
return () => clearInterval(interval); return () => clearInterval(interval);
}, [activeStrat]); }, [activeStrat]);
const handleReset = async () => {
if (!confirm('Reset ALL paper trading data? This clears all history, stats, and open positions for every strategy.')) return;
setResetting(true);
try {
await fetch('/api/reset', { method: 'POST' });
setTrades({});
} catch (e) {
console.error('Reset error:', e);
}
setTimeout(() => setResetting(false), 2000);
};
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"> <div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-[#28CC95] text-lg animate-pulse">Loading Paper Trading...</div> <div className="text-green-600 text-lg animate-pulse">Loading Paper Trading...</div>
</div> </div>
); );
} }
@@ -66,41 +78,50 @@ export default function PaperDashboard() {
const activeTrades = trades[activeStrat] || []; const activeTrades = trades[activeStrat] || [];
return ( return (
<div className="min-h-screen bg-[#0a0a0a] text-white font-sans pb-20"> <div className="min-h-screen bg-gray-50 text-gray-900 font-sans pb-20">
{/* Header */} {/* Header */}
<header className="sticky top-0 z-50 bg-[#0a0a0a]/95 backdrop-blur border-b border-white/10 px-4 py-3"> <header className="sticky top-0 z-50 bg-white/95 backdrop-blur border-b border-gray-200 px-4 py-3 shadow-sm">
<div className="flex items-center justify-between max-w-lg mx-auto"> <div className="flex items-center justify-between max-w-lg mx-auto">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<h1 className="text-lg font-bold" style={{ color: GREEN }}>Kalbot</h1> <h1 className="text-lg font-bold" style={{ color: GREEN }}>Kalbot</h1>
<span className="text-xs bg-yellow-500/20 text-yellow-400 px-2 py-0.5 rounded-full font-medium">PAPER</span> <span className="text-xs bg-amber-100 text-amber-700 px-2 py-0.5 rounded-full font-medium">PAPER</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<span className={`w-2 h-2 rounded-full ${data?.lastUpdate ? 'bg-green-400 animate-pulse' : 'bg-red-500'}`} /> <button
<span className="text-xs text-gray-400"> onClick={handleReset}
{data?.lastUpdate ? 'Live' : 'Offline'} disabled={resetting}
</span> className="text-[10px] px-2 py-1 rounded bg-red-50 text-red-600 border border-red-200 hover:bg-red-100 transition-colors disabled:opacity-50 font-medium"
>
{resetting ? 'Resetting...' : '🗑 Reset All'}
</button>
<div className="flex items-center gap-1.5">
<span className={`w-2 h-2 rounded-full ${data?.lastUpdate ? 'bg-green-500 animate-pulse' : 'bg-red-500'}`} />
<span className="text-xs text-gray-500">
{data?.lastUpdate ? 'Live' : 'Offline'}
</span>
</div>
</div> </div>
</div> </div>
</header> </header>
<main className="max-w-lg mx-auto px-4 pt-4 space-y-4"> <main className="max-w-lg mx-auto px-4 pt-4 space-y-4">
{/* Market Card (compact) */} {/* Market Card */}
<MarketCardCompact market={market} /> <MarketCardCompact market={market} />
{/* Strategy Tabs */} {/* Strategy Tabs */}
<div className="flex gap-1 bg-white/5 rounded-lg p-1 overflow-x-auto"> <div className="flex gap-1 bg-gray-100 rounded-lg p-1 overflow-x-auto">
{strategies.map(s => ( {strategies.map(s => (
<button <button
key={s.name} key={s.name}
onClick={() => setActiveStrat(s.name)} onClick={() => setActiveStrat(s.name)}
className={`flex-shrink-0 py-2 px-3 rounded-md text-xs font-medium transition-all whitespace-nowrap ${ className={`flex-shrink-0 py-2 px-3 rounded-md text-xs font-medium transition-all whitespace-nowrap ${
activeStrat === s.name activeStrat === s.name
? 'bg-white/10 text-white' ? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-500 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'
}`} }`}
> >
<span className={`inline-block w-1.5 h-1.5 rounded-full mr-1.5 ${ <span className={`inline-block w-1.5 h-1.5 rounded-full mr-1.5 ${
s.enabled && !s.paused ? 'bg-green-400' : 'bg-red-500' s.enabled && !s.paused ? 'bg-green-500' : 'bg-red-500'
}`} /> }`} />
{s.name} {s.name}
</button> </button>
@@ -121,8 +142,8 @@ export default function PaperDashboard() {
</main> </main>
{/* Worker Uptime */} {/* Worker Uptime */}
<div className="fixed bottom-0 left-0 right-0 bg-[#0a0a0a]/95 backdrop-blur border-t border-white/5 py-2 px-4"> <div className="fixed bottom-0 left-0 right-0 bg-white/95 backdrop-blur border-t border-gray-200 py-2 px-4">
<div className="max-w-lg mx-auto flex justify-between text-xs text-gray-600"> <div className="max-w-lg mx-auto flex justify-between text-xs text-gray-400">
<span>Worker: {formatUptime(data?.workerUptime)}</span> <span>Worker: {formatUptime(data?.workerUptime)}</span>
<span>{data?.lastUpdate ? new Date(data.lastUpdate).toLocaleTimeString() : 'never'}</span> <span>{data?.lastUpdate ? new Date(data.lastUpdate).toLocaleTimeString() : 'never'}</span>
</div> </div>
@@ -134,8 +155,8 @@ export default function PaperDashboard() {
function MarketCardCompact({ market }) { function MarketCardCompact({ market }) {
if (!market) { if (!market) {
return ( return (
<div className="bg-white/5 rounded-xl p-4 border border-white/10"> <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm">
<p className="text-gray-500 text-center text-sm">No active market waiting...</p> <p className="text-gray-400 text-center text-sm">No active market waiting...</p>
</div> </div>
); );
} }
@@ -143,15 +164,15 @@ function MarketCardCompact({ market }) {
const timeLeft = market.closeTime ? getTimeLeft(market.closeTime) : null; const timeLeft = market.closeTime ? getTimeLeft(market.closeTime) : null;
return ( return (
<div className="bg-white/5 rounded-xl p-4 border border-white/10"> <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div> <div>
<h2 className="font-bold text-sm">BTC Up or Down</h2> <h2 className="font-bold text-sm text-gray-900">BTC Up or Down</h2>
<p className="text-[10px] text-gray-500">15 min</p> <p className="text-[10px] text-gray-400">15 min</p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{timeLeft && ( {timeLeft && (
<span className="text-[10px] bg-white/10 px-2 py-0.5 rounded-full text-gray-300"> {timeLeft}</span> <span className="text-[10px] bg-gray-100 px-2 py-0.5 rounded-full text-gray-600"> {timeLeft}</span>
)} )}
<span className="text-lg"></span> <span className="text-lg"></span>
</div> </div>
@@ -159,24 +180,24 @@ function MarketCardCompact({ market }) {
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex-1"> <div className="flex-1">
<div className="flex justify-between text-xs mb-1"> <div className="flex justify-between text-xs mb-1">
<span>Up</span> <span className="text-gray-600">Up</span>
<span style={{ color: GREEN }}>{market.yesPct}%</span> <span style={{ color: GREEN }} className="font-medium">{market.yesPct}%</span>
</div> </div>
<div className="w-full bg-white/10 rounded-full h-1.5"> <div className="w-full bg-gray-100 rounded-full h-1.5">
<div className="h-1.5 rounded-full transition-all duration-500" style={{ width: `${market.yesPct}%`, backgroundColor: GREEN }} /> <div className="h-1.5 rounded-full transition-all duration-500" style={{ width: `${market.yesPct}%`, backgroundColor: GREEN }} />
</div> </div>
</div> </div>
<div className="flex-1"> <div className="flex-1">
<div className="flex justify-between text-xs mb-1"> <div className="flex justify-between text-xs mb-1">
<span>Down</span> <span className="text-gray-600">Down</span>
<span style={{ color: BLUE }}>{market.noPct}%</span> <span style={{ color: BLUE }} className="font-medium">{market.noPct}%</span>
</div> </div>
<div className="w-full bg-white/10 rounded-full h-1.5"> <div className="w-full bg-gray-100 rounded-full h-1.5">
<div className="h-1.5 rounded-full transition-all duration-500" style={{ width: `${market.noPct}%`, backgroundColor: BLUE }} /> <div className="h-1.5 rounded-full transition-all duration-500" style={{ width: `${market.noPct}%`, backgroundColor: BLUE }} />
</div> </div>
</div> </div>
</div> </div>
<div className="flex justify-between text-[10px] text-gray-600 mt-2"> <div className="flex justify-between text-[10px] text-gray-400 mt-2">
<span>${(market.volume || 0).toLocaleString()} vol</span> <span>${(market.volume || 0).toLocaleString()} vol</span>
<span className="font-mono">{market.ticker}</span> <span className="font-mono">{market.ticker}</span>
</div> </div>
@@ -191,13 +212,13 @@ function StrategyDetailView({ strategy, stats, trades }) {
return ( return (
<div className="space-y-3"> <div className="space-y-3">
{/* Strategy Stats */} {/* Strategy Stats */}
<div className="bg-white/5 rounded-xl p-4 border border-white/10"> <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${strategy.enabled && !strategy.paused ? 'bg-green-400' : 'bg-red-500'}`} /> <span className={`w-2 h-2 rounded-full ${strategy.enabled && !strategy.paused ? 'bg-green-500' : 'bg-red-500'}`} />
<h3 className="font-bold text-sm capitalize">{strategy.name}</h3> <h3 className="font-bold text-sm capitalize text-gray-900">{strategy.name}</h3>
</div> </div>
<span className="text-[10px] px-2 py-0.5 rounded-full bg-yellow-500/20 text-yellow-400">{strategy.mode}</span> <span className="text-[10px] px-2 py-0.5 rounded-full bg-amber-100 text-amber-700 font-medium">{strategy.mode}</span>
</div> </div>
<div className="grid grid-cols-4 gap-2 mb-3"> <div className="grid grid-cols-4 gap-2 mb-3">
@@ -208,18 +229,17 @@ function StrategyDetailView({ strategy, stats, trades }) {
</div> </div>
{/* Strategy Config */} {/* Strategy Config */}
<div className="space-y-1 text-xs text-gray-400 border-t border-white/5 pt-3"> <div className="space-y-1 text-xs text-gray-500 border-t border-gray-100 pt-3">
{strategy.config && Object.entries(strategy.config).map(([k, v]) => ( {strategy.config && Object.entries(strategy.config).map(([k, v]) => (
<div key={k} className="flex justify-between"> <div key={k} className="flex justify-between">
<span>{k}</span> <span>{k}</span>
<span className="text-gray-300">{typeof v === 'number' ? v : String(v)}</span> <span className="text-gray-700">{typeof v === 'number' ? v : String(v)}</span>
</div> </div>
))} ))}
{/* Strategy-specific extra fields */}
{strategy.consecutiveLosses !== undefined && ( {strategy.consecutiveLosses !== undefined && (
<div className="flex justify-between mt-1 pt-1 border-t border-white/5"> <div className="flex justify-between mt-1 pt-1 border-t border-gray-100">
<span>Consecutive Losses</span> <span>Consecutive Losses</span>
<span className={strategy.consecutiveLosses > 0 ? 'text-red-400' : 'text-gray-300'}> <span className={strategy.consecutiveLosses > 0 ? 'text-red-600' : 'text-gray-700'}>
{strategy.consecutiveLosses} {strategy.consecutiveLosses}
</span> </span>
</div> </div>
@@ -227,50 +247,50 @@ function StrategyDetailView({ strategy, stats, trades }) {
{strategy.currentBetSize !== undefined && ( {strategy.currentBetSize !== undefined && (
<div className="flex justify-between"> <div className="flex justify-between">
<span>Next Bet</span> <span>Next Bet</span>
<span className="text-gray-300">${strategy.currentBetSize}</span> <span className="text-gray-700">${strategy.currentBetSize}</span>
</div> </div>
)} )}
{strategy.round !== undefined && ( {strategy.round !== undefined && (
<div className="flex justify-between"> <div className="flex justify-between">
<span>Cycle Round</span> <span>Cycle Round</span>
<span className="text-gray-300">{strategy.round}/{strategy.maxRounds}</span> <span className="text-gray-700">{strategy.round}/{strategy.maxRounds}</span>
</div> </div>
)} )}
{strategy.cycleWins !== undefined && ( {strategy.cycleWins !== undefined && (
<div className="flex justify-between"> <div className="flex justify-between">
<span>Cycles Won/Lost</span> <span>Cycles Won/Lost</span>
<span className="text-gray-300">{strategy.cycleWins}W / {strategy.cycleLosses}L</span> <span className="text-gray-700">{strategy.cycleWins}W / {strategy.cycleLosses}L</span>
</div> </div>
)} )}
{strategy.cycleWinRate !== undefined && ( {strategy.cycleWinRate !== undefined && (
<div className="flex justify-between"> <div className="flex justify-between">
<span>Cycle Win Rate</span> <span>Cycle Win Rate</span>
<span className={strategy.cycleWinRate >= 50 ? 'text-green-400' : 'text-red-400'}> <span className={strategy.cycleWinRate >= 50 ? 'text-green-600' : 'text-red-600'}>
{strategy.cycleWinRate}% {strategy.cycleWinRate}%
</span> </span>
</div> </div>
)} )}
{strategy.paused && ( {strategy.paused && (
<div className="text-red-400 font-medium mt-1"> PAUSED max losses reached</div> <div className="text-red-600 font-medium mt-1"> PAUSED max losses reached</div>
)} )}
</div> </div>
</div> </div>
{/* Open Positions for this strategy */} {/* Open Positions */}
{s.openPositions.length > 0 && ( {s.openPositions.length > 0 && (
<div> <div>
<h4 className="text-[10px] text-gray-500 uppercase tracking-wider mb-2">Open Positions</h4> <h4 className="text-[10px] text-gray-400 uppercase tracking-wider mb-2 font-bold">Open Positions ({s.openPositions.length})</h4>
{s.openPositions.map((t, i) => <TradeRow key={i} trade={t} isOpen />)} {s.openPositions.map((t, i) => <TradeRow key={i} trade={t} isOpen />)}
</div> </div>
)} )}
{/* Trade History for this strategy */} {/* Trade History — only settled trades from DB */}
<div> <div>
<h4 className="text-[10px] text-gray-500 uppercase tracking-wider mb-2"> <h4 className="text-[10px] text-gray-400 uppercase tracking-wider mb-2 font-bold">
Trade History ({trades.length}) Trade History ({trades.length})
</h4> </h4>
{trades.length === 0 ? ( {trades.length === 0 ? (
<p className="text-gray-600 text-xs text-center py-4">No trades yet. Strategy is watching...</p> <p className="text-gray-400 text-xs text-center py-4">No settled trades yet.</p>
) : ( ) : (
trades.map((t, i) => <TradeRow key={i} trade={t} />) trades.map((t, i) => <TradeRow key={i} trade={t} />)
)} )}
@@ -289,22 +309,21 @@ function AllStrategiesOverview({ paperByStrategy, strategies }) {
if (!entries.length) return null; if (!entries.length) return null;
// Sort by PnL descending
entries.sort((a, b) => b.stats.totalPnL - a.stats.totalPnL); entries.sort((a, b) => b.stats.totalPnL - a.stats.totalPnL);
return ( return (
<div className="bg-white/5 rounded-xl border border-white/10 overflow-hidden"> <div className="bg-white rounded-xl border border-gray-200 overflow-hidden shadow-sm">
<div className="px-4 py-3 border-b border-white/5"> <div className="px-4 py-3 border-b border-gray-100">
<h3 className="text-xs text-gray-400 uppercase tracking-wider font-bold">📊 Strategy Leaderboard</h3> <h3 className="text-xs text-gray-500 uppercase tracking-wider font-bold">📊 Strategy Leaderboard</h3>
</div> </div>
{entries.map((e, i) => { {entries.map((e, i) => {
const pnlColor = e.stats.totalPnL >= 0 ? GREEN : RED; const pnlColor = e.stats.totalPnL >= 0 ? GREEN : RED;
return ( return (
<div key={e.name} className={`flex items-center justify-between px-4 py-3 ${i < entries.length - 1 ? 'border-b border-white/5' : ''}`}> <div key={e.name} className={`flex items-center justify-between px-4 py-3 ${i < entries.length - 1 ? 'border-b border-gray-100' : ''}`}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-xs text-gray-600 w-4">{i + 1}.</span> <span className="text-xs text-gray-400 w-4">{i + 1}.</span>
<span className={`w-1.5 h-1.5 rounded-full ${e.enabled && !e.paused ? 'bg-green-400' : 'bg-red-500'}`} /> <span className={`w-1.5 h-1.5 rounded-full ${e.enabled && !e.paused ? 'bg-green-500' : 'bg-red-500'}`} />
<span className="text-sm font-medium capitalize">{e.name}</span> <span className="text-sm font-medium capitalize text-gray-800">{e.name}</span>
</div> </div>
<div className="flex items-center gap-4 text-xs"> <div className="flex items-center gap-4 text-xs">
<span className="text-gray-400">{e.stats.totalTrades} trades</span> <span className="text-gray-400">{e.stats.totalTrades} trades</span>
@@ -322,39 +341,43 @@ function AllStrategiesOverview({ paperByStrategy, strategies }) {
function StatBox({ label, value, color }) { function StatBox({ label, value, color }) {
return ( return (
<div className="bg-white/5 rounded-lg p-2 border border-white/5 text-center"> <div className="bg-gray-50 rounded-lg p-2 border border-gray-100 text-center">
<p className="text-[9px] text-gray-500 uppercase tracking-wider">{label}</p> <p className="text-[9px] text-gray-400 uppercase tracking-wider">{label}</p>
<p className="text-xs font-bold mt-0.5" style={color ? { color } : {}}>{value}</p> <p className="text-xs font-bold mt-0.5" style={color ? { color } : { color: '#111827' }}>{value}</p>
</div> </div>
); );
} }
function TradeRow({ trade, isOpen }) { function TradeRow({ trade, isOpen }) {
const won = trade.pnl > 0; const won = trade.pnl > 0;
const pnlColor = trade.pnl == null ? 'text-gray-400' : won ? 'text-green-400' : 'text-red-400'; const pnlColor = trade.pnl == null ? 'text-gray-400' : won ? 'text-green-600' : 'text-red-600';
return ( return (
<div className="bg-white/5 rounded-lg p-3 border border-white/5 mb-2"> <div className="bg-white rounded-lg p-3 border border-gray-200 mb-2 shadow-sm">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{isOpen ? ( {isOpen ? (
<span className="w-2 h-2 rounded-full bg-yellow-400 animate-pulse" /> <span className="w-2 h-2 rounded-full bg-amber-400 animate-pulse" />
) : ( ) : (
<span>{won ? '✅' : '❌'}</span> <span>{won ? '✅' : '❌'}</span>
)} )}
<span className="text-sm font-medium capitalize">{trade.side}</span> <span className="text-sm font-medium capitalize text-gray-900">{trade.side}</span>
<span className="text-xs text-gray-500">@ {trade.price}¢</span> <span className="text-xs text-gray-400">@ {trade.price}¢</span>
<span className="text-[10px] text-gray-600">${trade.size}</span> <span className="text-[10px] text-gray-400">${trade.size}</span>
</div> </div>
<span className={`text-sm font-bold ${pnlColor}`}> <span className={`text-sm font-bold ${pnlColor}`}>
{trade.pnl != null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl}` : 'open'} {trade.pnl != null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl}` : 'open'}
</span> </span>
</div> </div>
<div className="flex justify-between mt-1"> <div className="flex justify-between mt-1">
<span className="text-[10px] text-gray-600">{trade.reason}</span> <span className="text-[10px] text-gray-400">{trade.reason}</span>
</div> </div>
<div className="flex justify-end"> <div className="flex justify-between items-center mt-0.5">
<span className="text-[10px] text-gray-600"> {trade.result && !isOpen && (
<span className="text-[10px] text-gray-400">Result: {trade.result}</span>
)}
{!trade.result && <span />}
<span className="text-[10px] text-gray-400">
{trade.entryTime ? new Date(trade.entryTime).toLocaleTimeString() : ''} {trade.entryTime ? new Date(trade.entryTime).toLocaleTimeString() : ''}
</span> </span>
</div> </div>