diff --git a/web/src/components/terminal/TerminalDashboard.tsx b/web/src/components/terminal/TerminalDashboard.tsx index 151ec58d..42208f6c 100644 --- a/web/src/components/terminal/TerminalDashboard.tsx +++ b/web/src/components/terminal/TerminalDashboard.tsx @@ -1,8 +1,9 @@ import { useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' import type { CSSProperties } from 'react' -import useSWR from 'swr' +import useSWR, { mutate } from 'swr' import { api } from '../../lib/api' +import { confirmToast, notify } from '../../lib/notify' import type { SystemStatus, AccountInfo, @@ -116,6 +117,57 @@ export function TerminalDashboard({ const traderId = selectedTrader?.trader_id || selectedTraderId useTick(1000) const clock = new Date().toLocaleTimeString('en-GB', { hour12: false }) + const [closing, setClosing] = useState(null) + + async function closePositionRow(symbol: string, side: 'LONG' | 'SHORT') { + if (!traderId || closing) return + const ok = await confirmToast(`Market-close ${symbol} ${side}?`, { + title: 'Close position', + okText: 'Close', + cancelText: 'Cancel', + }) + if (!ok) return + setClosing(symbol) + try { + await api.closePosition(traderId, symbol, side) + notify.success(`${symbol} ${side} closed`) + await Promise.all([ + mutate(`positions-${traderId}`), + mutate(`account-${traderId}`), + ]) + } catch (err) { + notify.error(err instanceof Error ? err.message : 'Close failed') + } finally { + setClosing(null) + } + } + + async function closeAllPositions(open: Position[]) { + if (!traderId || closing || open.length === 0) return + const ok = await confirmToast( + `Market-close ALL ${open.length} open positions?`, + { title: 'Flatten book', okText: 'Close all', cancelText: 'Cancel' } + ) + if (!ok) return + setClosing('__all__') + let failed = 0 + // Sequential: parallel closes race on exchange nonces / rate limits. + for (const p of open) { + const side = /long|buy/i.test(p.side) ? 'LONG' : 'SHORT' + try { + await api.closePosition(traderId, p.symbol, side) + } catch { + failed++ + } + } + await Promise.all([ + mutate(`positions-${traderId}`), + mutate(`account-${traderId}`), + ]) + if (failed === 0) notify.success('All positions closed') + else notify.error(`${failed}/${open.length} closes failed`) + setClosing(null) + } const { data: realFullStats } = useSWR( traderId ? ['full-stats', traderId] : null, @@ -512,6 +564,26 @@ export function TerminalDashboard({ Positions Current positions · live {positions?.length ?? 0} open + {traderId && !on && positions && positions.length > 0 && ( + + )} {positions && positions.length > 0 ? ( @@ -523,6 +595,7 @@ export function TerminalDashboard({ + {traderId && !on && @@ -538,6 +611,29 @@ export function TerminalDashboard({ + {traderId && !on && ( + + )} ) })}
size PnL return%}
{fmtUsd(notional)} {fmtUsd(p.unrealized_pnl, true)} {(p.unrealized_pnl_pct ?? 0).toFixed(2)}% + +