← 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>
  );
}