← back to Ken
src/app/dashboard/trades/page.js
97 lines
'use client';
import { useState, useEffect } from 'react';
export default function TradesPage() {
const [orders, setOrders] = useState([]);
const [trades, setTrades] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function load() {
try {
const res = await fetch('/api/trades');
const data = await res.json();
setOrders(data.orders || []);
setTrades(data.trades || []);
} catch (err) { console.error(err); }
setLoading(false);
}
load();
const i = setInterval(load, 15000);
return () => clearInterval(i);
}, []);
if (loading) return <div className="flex items-center justify-center h-64"><div className="animate-spin w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full" /></div>;
const totalPnl = trades.reduce((s, t) => s + parseFloat(t.pnl || 0), 0);
const totalFees = trades.reduce((s, t) => s + parseFloat(t.fee_usd || 0), 0);
const winRate = trades.length > 0 ? (trades.filter(t => parseFloat(t.pnl || 0) > 0).length / trades.length * 100) : 0;
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">Trades & Orders</h1>
<p className="text-gray-500 text-sm">Execution history and paper trade log</p>
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div className="card"><p className="text-xs text-gray-500 uppercase mb-1">Orders</p><p className="stat-value text-blue-400">{orders.length}</p></div>
<div className="card"><p className="text-xs text-gray-500 uppercase mb-1">Fills</p><p className="stat-value text-green-400">{trades.length}</p></div>
<div className="card"><p className="text-xs text-gray-500 uppercase mb-1">Total P&L</p><p className={`stat-value ${totalPnl >= 0 ? 'text-green-400' : 'text-red-400'}`}>${totalPnl.toFixed(2)}</p></div>
<div className="card"><p className="text-xs text-gray-500 uppercase mb-1">Total Fees</p><p className="stat-value text-yellow-400">${totalFees.toFixed(2)}</p></div>
<div className="card"><p className="text-xs text-gray-500 uppercase mb-1">Win Rate</p><p className="stat-value text-purple-400">{winRate.toFixed(0)}%</p></div>
</div>
{/* Orders */}
<div className="card overflow-x-auto">
<h2 className="font-semibold mb-3">Orders</h2>
<table>
<thead><tr><th>ID</th><th>Market</th><th>Side</th><th>Outcome</th><th>Price</th><th>Size ($)</th><th>Status</th><th>Paper</th><th>Time</th></tr></thead>
<tbody>
{orders.length === 0 ? (
<tr><td colSpan={9} className="text-center text-gray-500 py-8">No orders yet</td></tr>
) : orders.map(o => (
<tr key={o.order_id}>
<td className="font-mono text-xs">{o.order_id.substring(0, 10)}...</td>
<td className="font-mono text-xs max-w-[120px] truncate">{o.condition_id?.substring(0, 12)}</td>
<td><span className={`badge ${o.side === 'buy' ? 'badge-green' : 'badge-red'}`}>{o.side?.toUpperCase()}</span></td>
<td className="font-medium">{o.outcome}</td>
<td className="font-mono">${parseFloat(o.price).toFixed(4)}</td>
<td className="font-mono">${parseFloat(o.size_usd || 0).toFixed(2)}</td>
<td><span className={`badge ${o.status === 'filled' ? 'badge-green' : o.status === 'rejected' ? 'badge-red' : 'badge-yellow'}`}>{o.status}</span></td>
<td>{o.paper_trade ? <span className="badge badge-yellow">PAPER</span> : <span className="badge badge-green">LIVE</span>}</td>
<td className="text-gray-400 text-sm">{new Date(o.submitted_ts).toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Trades */}
<div className="card overflow-x-auto">
<h2 className="font-semibold mb-3">Trade Fills</h2>
<table>
<thead><tr><th>ID</th><th>Price</th><th>Size ($)</th><th>Fee</th><th>P&L</th><th>Paper</th><th>Time</th></tr></thead>
<tbody>
{trades.length === 0 ? (
<tr><td colSpan={7} className="text-center text-gray-500 py-8">No trade fills yet</td></tr>
) : trades.map(t => (
<tr key={t.trade_id}>
<td className="font-mono text-xs">{t.trade_id.substring(0, 10)}...</td>
<td className="font-mono">${parseFloat(t.price).toFixed(4)}</td>
<td className="font-mono">${parseFloat(t.size_usd || 0).toFixed(2)}</td>
<td className="font-mono text-yellow-400">${parseFloat(t.fee_usd || 0).toFixed(4)}</td>
<td className={`font-mono ${parseFloat(t.pnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'}`}>${parseFloat(t.pnl || 0).toFixed(4)}</td>
<td>{t.paper_trade ? <span className="badge badge-yellow">PAPER</span> : <span className="badge badge-green">LIVE</span>}</td>
<td className="text-gray-400 text-sm">{t.match_time ? new Date(t.match_time).toLocaleString() : '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}