// Trades page — full filterable history + modal. const { Card, Pill, Copy, fmtTs, fmtNum } = window.UI; // adapt one apiTrade to the rich row this page renders. Fields the // backend doesn't yet ship (side, symbol, outbox_id, fills, fee) are // filled with conservative defaults so the existing UI keeps working. function adaptTradeRow(t, accountLabel) { const alt = parseFloat(t.alt_qty || '0') || 0; const crypto = parseFloat(t.crypto_qty || '0') || 0; const price = alt > 0 ? crypto / alt : 0; const symbol = (t.from && t.to) ? `${t.from}/${t.to}` : ''; return { id: t.id, ts: t.ts_ms, account: accountLabel, from: t.from || '—', to: t.to || '—', qty: alt, price, fee: 0, state: t.state || 'PENDING', client_order_id: t.client_order_id || '', side: '', // round-trip swap (sell+buy) — single side label doesn't apply symbol, outbox_id: 0, fills: [], }; } function PageTrades({ app }) { const [filters, setFilters] = React.useState({ account: 'all', state: 'all', side: 'all', symbol: '', from: '', to: '' }); const [open, setOpen] = React.useState(null); // Resolve numeric account id for the current selection; backend takes // account_id only, so "all accounts" in the UI maps to the active acct. const acct = (app.accounts || []).find(a => a.id === app.account) || null; const accountID = acct && acct.accountID; const { data: rawTrades, error, loading, refresh } = window.API.useAPI( () => accountID ? window.API.get('/api/v1/trades', { account_id: accountID, limit: 500 }) : Promise.resolve([]), [accountID], ); // No mock fallback: empty list = nothing yet. const source = (rawTrades && Array.isArray(rawTrades)) ? rawTrades.map(t => adaptTradeRow(t, acct ? acct.id : '')) : []; const filtered = source.filter(t => { if (filters.account !== 'all' && t.account !== filters.account) return false; if (filters.state !== 'all' && t.state !== filters.state) return false; if (filters.side !== 'all' && (t.side || '') !== filters.side) return false; if (filters.symbol && !((t.symbol || '').toLowerCase()).includes(filters.symbol.toLowerCase())) return false; if (filters.from && t.ts < new Date(filters.from).getTime()) return false; if (filters.to && t.ts > new Date(filters.to).getTime()) return false; return true; }); return (

trade history

{filtered.length} of {source.length} rows · click any row for fills breakdown · {' '} {loading && loading…} {error && api error} {!loading && !error && (source.length ? live : no trades yet)}

{/* filter bar */}
setFilters(f => ({...f, state: v}))} options={[ {v:'all',l:'all'},{v:'FILLED',l:'FILLED'},{v:'PARTIALLY_FILLED',l:'PARTIALLY_FILLED'}, {v:'CANCELED',l:'CANCELED'},{v:'REJECTED',l:'REJECTED'} ]}/> setFilters(f => ({...f, symbol: v}))} placeholder="BTC/USDT" icon={Icon.Search}/> setFilters(f => ({...f, from: v}))}/> setFilters(f => ({...f, to: v}))}/>
{filtered.map((t, i) => ( setOpen(t)} className={`border-b border-line-0 cursor-pointer hover:bg-bg-2/60 ${i % 2 ? 'bg-bg-1' : 'bg-bg-1/50'}`}> ))} {!filtered.length && ( )}
id time account from→to qty avg price fee state cid
#{t.id} {fmtTs(t.ts)} {t.account} {t.from} {t.to} {fmtNum(t.qty, 4)} {fmtNum(t.price, 2)} {fmtNum(t.fee, 4)} {t.state.replace('_',' ')}
no rows match filters
{open && setOpen(null)} />}
); } function Field({ label, children }) { return ( ); } function Select({ value, onChange, options }) { return (
); } function Input({ value, onChange, placeholder, type = 'text', icon: I }) { return (
{I && } onChange(e.target.value)} placeholder={placeholder} className={`w-full bg-bg-2 border border-line-1 mono text-[11.5px] h-7 ${I ? 'pl-7' : 'pl-2'} pr-2 text-ink-1 focus:border-line-2 outline-none`}/>
); } function TradeModal({ trade, onClose }) { return ( <>

trade #{trade.id}

{trade.state.replace('_',' ')}
} colspan={2} />
{trade.fills.length > 0 && (
fills · {trade.fills.length}
{trade.fills.map((f, i) => ( ))}
# price qty commission role
{i+1} {fmtNum(f.price, 2)} {fmtNum(f.qty, 4)} {fmtNum(f.commission, 4)} {f.isMaker ? 'maker' : 'taker'}
)}
originating outbox row #{trade.outbox_id}
); } function KV3({ k, v, colspan = 1 }) { return (
{k}
{v}
); } window.PageTrades = PageTrades;