From 23d8df211679b68004b28664c5dcaaee65b0f692 Mon Sep 17 00:00:00 2001 From: multipleof4 Date: Sun, 15 Mar 2026 17:36:09 -0700 Subject: [PATCH] Fix: Light mode, reset btn, history=settled only --- app/paper/page.js | 169 ++++++++++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 73 deletions(-) diff --git a/app/paper/page.js b/app/paper/page.js index 18cddda..8693cf0 100644 --- a/app/paper/page.js +++ b/app/paper/page.js @@ -1,15 +1,16 @@ 'use client'; import { useState, useEffect } from 'react'; -const GREEN = '#28CC95'; -const RED = '#FF6B6B'; -const BLUE = '#4A90D9'; +const GREEN = '#16A34A'; +const RED = '#DC2626'; +const BLUE = '#2563EB'; export default function PaperDashboard() { const [data, setData] = useState(null); const [trades, setTrades] = useState({}); const [loading, setLoading] = useState(true); const [activeStrat, setActiveStrat] = useState(null); + const [resetting, setResetting] = useState(false); useEffect(() => { const fetchState = async () => { @@ -31,7 +32,6 @@ export default function PaperDashboard() { return () => clearInterval(interval); }, [activeStrat]); - // Fetch trades for active strategy useEffect(() => { if (!activeStrat) return; @@ -50,10 +50,22 @@ export default function PaperDashboard() { return () => clearInterval(interval); }, [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) { return ( -
-
Loading Paper Trading...
+
+
Loading Paper Trading...
); } @@ -66,41 +78,50 @@ export default function PaperDashboard() { const activeTrades = trades[activeStrat] || []; return ( -
+
{/* Header */} -
+

Kalbot

- PAPER + PAPER
-
- - - {data?.lastUpdate ? 'Live' : 'Offline'} - +
+ +
+ + + {data?.lastUpdate ? 'Live' : 'Offline'} + +
- {/* Market Card (compact) */} + {/* Market Card */} {/* Strategy Tabs */} -
+
{strategies.map(s => ( @@ -121,8 +142,8 @@ export default function PaperDashboard() {
{/* Worker Uptime */} -
-
+
+
Worker: {formatUptime(data?.workerUptime)} {data?.lastUpdate ? new Date(data.lastUpdate).toLocaleTimeString() : 'never'}
@@ -134,8 +155,8 @@ export default function PaperDashboard() { function MarketCardCompact({ market }) { if (!market) { return ( -
-

No active market โ€” waiting...

+
+

No active market โ€” waiting...

); } @@ -143,15 +164,15 @@ function MarketCardCompact({ market }) { const timeLeft = market.closeTime ? getTimeLeft(market.closeTime) : null; return ( -
+
-

BTC Up or Down

-

15 min

+

BTC Up or Down

+

15 min

{timeLeft && ( - โฑ {timeLeft} + โฑ {timeLeft} )} โ‚ฟ
@@ -159,24 +180,24 @@ function MarketCardCompact({ market }) {
- Up - {market.yesPct}% + Up + {market.yesPct}%
-
+
- Down - {market.noPct}% + Down + {market.noPct}%
-
+
-
+
${(market.volume || 0).toLocaleString()} vol {market.ticker}
@@ -191,13 +212,13 @@ function StrategyDetailView({ strategy, stats, trades }) { return (
{/* Strategy Stats */} -
+
- -

{strategy.name}

+ +

{strategy.name}

- {strategy.mode} + {strategy.mode}
@@ -208,18 +229,17 @@ function StrategyDetailView({ strategy, stats, trades }) {
{/* Strategy Config */} -
+
{strategy.config && Object.entries(strategy.config).map(([k, v]) => (
{k} - {typeof v === 'number' ? v : String(v)} + {typeof v === 'number' ? v : String(v)}
))} - {/* Strategy-specific extra fields */} {strategy.consecutiveLosses !== undefined && ( -
+
Consecutive Losses - 0 ? 'text-red-400' : 'text-gray-300'}> + 0 ? 'text-red-600' : 'text-gray-700'}> {strategy.consecutiveLosses}
@@ -227,50 +247,50 @@ function StrategyDetailView({ strategy, stats, trades }) { {strategy.currentBetSize !== undefined && (
Next Bet - ${strategy.currentBetSize} + ${strategy.currentBetSize}
)} {strategy.round !== undefined && (
Cycle Round - {strategy.round}/{strategy.maxRounds} + {strategy.round}/{strategy.maxRounds}
)} {strategy.cycleWins !== undefined && (
Cycles Won/Lost - {strategy.cycleWins}W / {strategy.cycleLosses}L + {strategy.cycleWins}W / {strategy.cycleLosses}L
)} {strategy.cycleWinRate !== undefined && (
Cycle Win Rate - = 50 ? 'text-green-400' : 'text-red-400'}> + = 50 ? 'text-green-600' : 'text-red-600'}> {strategy.cycleWinRate}%
)} {strategy.paused && ( -
โš ๏ธ PAUSED โ€” max losses reached
+
โš ๏ธ PAUSED โ€” max losses reached
)}
- {/* Open Positions for this strategy */} + {/* Open Positions */} {s.openPositions.length > 0 && (
-

Open Positions

+

Open Positions ({s.openPositions.length})

{s.openPositions.map((t, i) => )}
)} - {/* Trade History for this strategy */} + {/* Trade History โ€” only settled trades from DB */}
-

+

Trade History ({trades.length})

{trades.length === 0 ? ( -

No trades yet. Strategy is watching...

+

No settled trades yet.

) : ( trades.map((t, i) => ) )} @@ -289,22 +309,21 @@ function AllStrategiesOverview({ paperByStrategy, strategies }) { if (!entries.length) return null; - // Sort by PnL descending entries.sort((a, b) => b.stats.totalPnL - a.stats.totalPnL); return ( -
-
-

๐Ÿ“Š Strategy Leaderboard

+
+
+

๐Ÿ“Š Strategy Leaderboard

{entries.map((e, i) => { const pnlColor = e.stats.totalPnL >= 0 ? GREEN : RED; return ( -
+
- {i + 1}. - - {e.name} + {i + 1}. + + {e.name}
{e.stats.totalTrades} trades @@ -322,39 +341,43 @@ function AllStrategiesOverview({ paperByStrategy, strategies }) { function StatBox({ label, value, color }) { return ( -
-

{label}

-

{value}

+
+

{label}

+

{value}

); } function TradeRow({ trade, isOpen }) { 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 ( -
+
{isOpen ? ( - + ) : ( {won ? 'โœ…' : 'โŒ'} )} - {trade.side} - @ {trade.price}ยข - ${trade.size} + {trade.side} + @ {trade.price}ยข + ${trade.size}
{trade.pnl != null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl}` : 'open'}
- {trade.reason} + {trade.reason}
-
- +
+ {trade.result && !isOpen && ( + Result: {trade.result} + )} + {!trade.result && } + {trade.entryTime ? new Date(trade.entryTime).toLocaleTimeString() : ''}