diff --git a/web/src/tabs/OverviewTab.tsx b/web/src/tabs/OverviewTab.tsx index 5a86fa2..25692e9 100644 --- a/web/src/tabs/OverviewTab.tsx +++ b/web/src/tabs/OverviewTab.tsx @@ -43,6 +43,30 @@ const WIN_RATE_WINDOW_OPTIONS: Array<{ key: WinRateWindow; label: string; ms?: n { key: 'all', label: 'All' } ]; const REFRESH_INTERVAL_MS = 30_000; +const overviewWarningBorder = '1px solid color-mix(in oklab, var(--bl-warning) 28%, transparent)'; +const overviewWarningSurface = 'var(--bl-warning-muted)'; +const overviewWarningText = 'var(--bl-warning)'; +const overviewDangerBorder = '1px solid color-mix(in oklab, var(--bl-danger) 28%, transparent)'; +const overviewDangerSurface = 'var(--bl-danger-muted)'; +const overviewDangerText = 'var(--bl-danger)'; +const overviewMutedText = 'var(--bl-text-faint)'; +const overviewQuietText = 'var(--bl-text-quiet)'; +const overviewInfoText = 'var(--bl-info-strong)'; +const overviewAttentionText = 'var(--bl-warning)'; +const overviewSuccessText = 'var(--bl-success)'; +const overviewNeutralText = 'var(--foreground)'; +const overviewSoftBorder = '1px solid var(--bl-border-subtle)'; +const overviewActiveBorder = '1px solid color-mix(in oklab, var(--bl-success) 45%, transparent)'; +const overviewActiveSurface = 'color-mix(in oklab, var(--bl-success) 8%, transparent)'; +const overviewMutedSurface = 'color-mix(in oklab, var(--foreground) 3%, transparent)'; +const overviewSectionBorder = '1px solid color-mix(in oklab, var(--bl-border) 70%, transparent)'; +const overviewCardGradient = 'linear-gradient(145deg, color-mix(in oklab, var(--bl-surface-overlay) 92%, var(--card-elevated)), color-mix(in oklab, var(--bl-surface-overlay) 84%, var(--background)))'; +const overviewCardBorder = '1px solid color-mix(in oklab, var(--bl-border) 55%, transparent)'; +const overviewDivider = '1px solid color-mix(in oklab, var(--bl-border) 45%, transparent)'; +const overviewBotMuted = 'color-mix(in oklab, var(--muted-foreground) 62%, var(--background))'; +const overviewBotQuiet = 'color-mix(in oklab, var(--muted-foreground) 52%, var(--background))'; +const overviewTagBorder = '1px solid color-mix(in oklab, var(--bl-border) 82%, transparent)'; + const formatUptime = (ms: number): string => { if (ms < 60000) return `${Math.floor(ms / 1000)}s`; @@ -569,9 +593,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t marginBottom: '12px', padding: '8px 10px', borderRadius: '8px', - border: '1px solid rgba(251, 191, 36, 0.25)', - background: 'rgba(251, 191, 36, 0.08)', - color: '#fbbf24', + border: overviewWarningBorder, + background: overviewWarningSurface, + color: overviewWarningText, fontSize: '0.75rem' }}> Canonical lifecycle is unavailable{canonicalLoading ? ' (loading)' : ''}. Showing fallback values from DB/runtime sources. @@ -583,9 +607,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t marginBottom: '12px', padding: '8px 10px', borderRadius: '8px', - border: '1px solid rgba(248, 113, 113, 0.28)', - background: 'rgba(248, 113, 113, 0.08)', - color: '#fca5a5', + border: overviewDangerBorder, + background: overviewDangerSurface, + color: overviewDangerText, fontSize: '0.75rem' }}> Canonical lifecycle snapshot is truncated ({canonicalSnapshot.diagnostics.orderRows} rows). Narrow scope before using this for operational decisions. @@ -593,7 +617,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t )}
- + Win Rate Window: {WIN_RATE_WINDOW_OPTIONS.map((option) => { @@ -603,9 +627,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t key={option.key} onClick={() => setWinRateWindow(option.key)} style={{ - border: active ? '1px solid rgba(0,255,136,0.45)' : '1px solid rgba(255,255,255,0.1)', - color: active ? '#00ff88' : '#a1a1aa', - background: active ? 'rgba(0,255,136,0.08)' : 'rgba(255,255,255,0.03)', + border: active ? overviewActiveBorder : overviewSoftBorder, + color: active ? overviewSuccessText : overviewQuietText, + background: active ? overviewActiveSurface : overviewMutedSurface, borderRadius: '8px', padding: '4px 8px', fontSize: '0.7rem', @@ -641,13 +665,13 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
Allocated: {formatUsd(allocatedCapital)} - {profileCount > 0 && ({profileCount} profiles)} + {profileCount > 0 && ({profileCount} profiles)}
Capital Used: - {formatUsd(capitalUsed)} + {formatUsd(capitalUsed)} {overAllocatedCapital > 1e-8 && ( - + capped (+{formatUsd(overAllocatedCapital)} raw) )} @@ -676,10 +700,10 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
Win Rate ({winRateWindowConfig.label}): - = 50 ? '#4ade80' : '#fbbf24' }}> + = 50 ? overviewSuccessText : overviewWarningText }}> {displayWindowAggregate.winRate.toFixed(1)}% - + {displayWindowAggregate.tradeCount} trades
@@ -693,7 +717,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t > {pnlWindow.durationLabel}
- + {pnlWindow.fromTs > 0 ? `From ${new Date(pnlWindow.fromTs).toLocaleDateString()}` : 'Awaiting trade history'} @@ -702,7 +726,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
{/* --- NEW: Alpaca Account Health --- */} -
+
Broker Balance (Alpaca): @@ -711,7 +735,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t : 'Waiting for snapshot...'} {botState.accountSnapshot && ( - + Cash: {formatUsd(botState.accountSnapshot.cash)} ({botState.accountSnapshot.currency}) )} @@ -725,28 +749,28 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
)} {/* Parity & Self-Healing Heartbeat */} -
+
Parity Heartbeat: {botState.health?.reconciliationParityMismatchTrades || 0} Mismatches {Number(botState.health?.reconciliationParityAutoClosedTrades || 0) > 0 && ( - + ({botState.health?.reconciliationParityAutoClosedTrades} Self-Healed) )} {Number(botState.health?.reconciliationParityQuarantinedTrades || 0) > 0 && ( - + ({botState.health?.reconciliationParityQuarantinedTrades} Quarantined) )}
{/* Recent Failures Summary */} {(botState.orderFailures || []).length > 0 && ( -
- Recent Rejections: - {(botState.orderFailures || []).length} - +
+ Recent Rejections: + {(botState.orderFailures || []).length} + Latest: {(botState.orderFailures || [])[0].symbol} ({(botState.orderFailures || [])[0].reason?.substring(0, 20)}...)
@@ -772,37 +796,37 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t profileCapitalRows.map((row) => { const overAllocated = row.overAllocatedAmount > 1e-8; const stateColor = row.runtimeTone === 'running' - ? '#4ade80' + ? overviewSuccessText : row.runtimeTone === 'cooldown' - ? '#facc15' + ? overviewWarningText : row.runtimeTone === 'armed' - ? '#38bdf8' + ? overviewInfoText : row.runtimeTone === 'blocked' - ? '#f87171' - : '#a1a1aa'; + ? overviewDangerText + : overviewQuietText; return ( {row.name} {formatUsd(row.allocated)} - {formatUsd(row.used)} - + {formatUsd(row.used)} + {formatUsd(row.remaining)} {overAllocated && ( -
+
raw +{formatUsd(row.overAllocatedAmount)}
)} - + {row.utilizationPct.toFixed(1)}% - = 50 ? '#4ade80' : '#fbbf24' }}> + = 50 ? overviewSuccessText : overviewWarningText }}> {row.winRate.toFixed(1)}% - + ({row.tradeCount}) - = 0 ? '#4ade80' : '#f87171' }}> + = 0 ? overviewSuccessText : overviewDangerText }}> {formatUsd(row.netPnl)} @@ -810,7 +834,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
{row.runtimeState}
-
+
{row.runtimeDetail}
@@ -822,11 +846,11 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t Account (Fallback) {formatUsd(allocatedCapital)} - {formatUsd(capitalUsed)} - 1e-8 ? '#f87171' : '#4ade80' }}> + {formatUsd(capitalUsed)} + 1e-8 ? overviewDangerText : overviewSuccessText }}> {formatUsd(remainingCapital)} {overAllocatedCapital > 1e-8 && ( -
+
raw +{formatUsd(overAllocatedCapital)}
)} @@ -835,9 +859,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t {allocatedCapital > 0 ? ((capitalUsed / allocatedCapital) * 100).toFixed(1) : '0.0'}% - - = 0 ? '#4ade80' : '#f87171' }}>{formatUsd(displayRealizedPnl)} + = 0 ? overviewSuccessText : overviewDangerText }}>{formatUsd(displayRealizedPnl)} - No signal + No signal )} @@ -848,7 +872,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t {/* NEW: Recent Order Rejections List */} {botState.orderFailures && botState.orderFailures.length > 0 && (
-

Recent Order Rejections

+

Recent Order Rejections

@@ -865,17 +889,17 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t {botState.orderFailures.slice(0, 10).map((fail, idx) => ( - + - - - + ))} @@ -908,7 +932,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t {profileSignals.length > 0 && (
{profileSignals.map((ps, idx) => ( - + {(ps.profileName || `P${idx + 1}`)}: {ps.signal} ))} @@ -943,18 +967,18 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t : 'NEUTRAL'; const directionLabel = bias4h === 'BULLISH' ? 'Uptrend' : bias4h === 'BEARISH' ? 'Downtrend' : 'Sideways'; - const directionColor = bias4h === 'BULLISH' ? '#00ff88' : bias4h === 'BEARISH' ? '#ff3366' : '#f59e0b'; + const directionColor = bias4h === 'BULLISH' ? overviewSuccessText : bias4h === 'BEARISH' ? overviewDangerText : overviewAttentionText; const rsi = data.indicators.rsi_1h || 50; const momentumLabel = rsi > 60 ? 'Building strongly' : rsi > 50 ? 'Gaining' : rsi > 40 ? 'Neutral' : 'Fading'; - const momentumColor = rsi > 55 ? '#00ff88' : rsi > 45 ? '#f59e0b' : '#ff3366'; + const momentumColor = rsi > 55 ? overviewSuccessText : rsi > 45 ? overviewAttentionText : overviewDangerText; const sessionIsOff = data.session === 'OFF' || !data.session; const sessionLabel = sessionIsOff ? 'Outside window' : 'Active now'; - const sessionColor = sessionIsOff ? '#555' : '#00ff88'; + const sessionColor = sessionIsOff ? overviewBotMuted : overviewSuccessText; const volLabel = data.volatility === 'HIGH' ? 'Very Active' : data.volatility === 'MEDIUM' ? 'Moderate' : data.volatility === 'LOW' ? 'Calm' : 'Normal'; - const volColor = data.volatility === 'HIGH' ? '#f59e0b' : data.volatility === 'MEDIUM' ? '#60a5fa' : '#a1a1aa'; + const volColor = data.volatility === 'HIGH' ? overviewAttentionText : data.volatility === 'MEDIUM' ? overviewInfoText : overviewQuietText; const botStatusEmoji = normalizedSignal === 'BUY' ? '📈' : normalizedSignal === 'SELL' ? '📉' : normalizedSignal === 'MIXED' ? '⚡' : '👀'; const botStatusLabel = @@ -963,17 +987,17 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t normalizedSignal === 'MIXED' ? 'Conditions mixed — holding off' : 'Watching markets for the right setup'; const botStatusColor = - normalizedSignal === 'BUY' ? '#00ff88' : - normalizedSignal === 'SELL' ? '#ff3366' : - normalizedSignal === 'MIXED' ? '#f59e0b' : - '#555'; + normalizedSignal === 'BUY' ? overviewSuccessText : + normalizedSignal === 'SELL' ? overviewDangerText : + normalizedSignal === 'MIXED' ? overviewAttentionText : + overviewBotMuted; const change24h = data.change24h || 0; return (
-
+
{symbol.split('/')[0]} - /{symbol.split('/')[1] || 'USDT'} + /{symbol.split('/')[1] || 'USDT'}
-
= 0 ? '#00ff88' : '#ff3366', marginTop: '2px', fontFamily: 'monospace' }}> +
= 0 ? overviewSuccessText : overviewDangerText, marginTop: '2px', fontFamily: 'monospace' }}> {change24h >= 0 ? '+' : ''}{change24h.toFixed(2)}% today
@@ -1009,19 +1033,19 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t { label: 'Market Activity', value: volLabel, color: volColor }, ].map(({ label, value, color }) => (
- {label} + {label}
- {value} + {value}
))}
{/* Bot Status Footer */} -
-
Bot Status
-
+
+
Bot Status
+
{botStatusLabel}
{new Date(fail.timestamp).toLocaleString()}{new Date(fail.timestamp).toLocaleString()} {fail.symbol} {fail.side} {fail.qty} + {fail.reason} + {compactTag(fail.subTag)} {fail.profileId || 'Unknown'}{fail.profileId || 'Unknown'}