import type { Metadata } from "next"; import { getAccount, getAllDeals, type Deal } from "@/lib/mt5"; import { formatCurrency, formatDate } from "@/lib/format"; import EquityChart from "@/components/EquityChart"; import TradesRefresher from "@/components/TradesRefresher"; export const metadata: Metadata = { title: "Trade History", description: "Live closed trades and equity curve for ARES — M5 Momentum FVG scalper on XAUUSDm and BTCUSDm.", }; export const dynamic = "force-dynamic"; const MAGIC = 19730; export default async function TradesPage() { let deals: Deal[] = []; let account = null; let error = false; try { [account, deals] = await Promise.all([getAccount(), getAllDeals()]); } catch { error = true; } const aresDeals = deals.filter(d => d.magic === MAGIC && d.type !== 2); const closed = aresDeals.filter(d => d.entry === 1); const wins = closed.filter(d => d.profit > 0); const losses = closed.filter(d => d.profit < 0); const netPnl = closed.reduce((s, d) => s + d.profit + d.swap + d.commission, 0); const totalFriction = closed.reduce((s, d) => s + d.swap + d.commission, 0); const grossW = wins.reduce((s, d) => s + d.profit, 0); const grossL = Math.abs(losses.reduce((s, d) => s + d.profit, 0)); const pf = grossL > 0 ? grossW / grossL : 0; const wr = closed.length > 0 ? (wins.length / closed.length) * 100 : 0; const avgWin = wins.length > 0 ? grossW / wins.length : 0; const avgLoss = losses.length > 0 ? grossL / losses.length : 0; const currency = account?.currency ?? "USD"; const bestTrade = closed.reduce((b, d) => !b || d.profit > b.profit ? d : b, null); const worstTrade = closed.reduce((b, d) => !b || d.profit < b.profit ? d : b, null); // Current streak const streak = (() => { if (closed.length === 0) return { count: 0, type: "none" as const }; const sorted = [...closed].sort((a, b) => +new Date(a.time) - +new Date(b.time)); const last = sorted[sorted.length - 1]; const isWin = last.profit > 0; let count = 0; for (let i = sorted.length - 1; i >= 0; i--) { if ((sorted[i].profit > 0) === isWin) count++; else break; } return { count, type: isWin ? "win" as const : "loss" as const }; })(); return ( <>

Live Forward Test

Trade History

All closed trades from ARES · magic {MAGIC}

{error ? (
Unable to fetch trade data.
) : ( <> {/* Stats */}
{[ { label: "Total Trades", value: closed.length.toString(), mono: true }, { label: "Win Rate", value: `${wr.toFixed(1)}%`, mono: true }, { label: "Profit Factor", value: pf > 0 ? pf.toFixed(2) : "—", mono: true }, { label: "Net P&L", value: formatCurrency(netPnl, currency), mono: true, colored: netPnl !== 0 ? netPnl > 0 : undefined }, ].map(({ label, value, mono, colored }) => (

{label}

{value}

))}
{/* Secondary stats row */}

Avg Win

{avgWin > 0 ? `+${formatCurrency(avgWin, currency)}` : "—"}

Avg Loss

{avgLoss > 0 ? `−${formatCurrency(avgLoss, currency)}` : "—"}

Total Friction

{formatCurrency(totalFriction, currency)}

Current Streak

{streak.count === 0 ? (

) : (

{streak.count}× {streak.type === "win" ? "W" : "L"}

)}
{/* Win/Loss bar */} {closed.length > 0 && (

Win / Loss Distribution

{wins.length}W · {losses.length}L

{wr.toFixed(1)}% wins {(100 - wr).toFixed(1)}% losses
)} {/* Best / Worst trade */} {(bestTrade || worstTrade) && (
{bestTrade && (

Best Trade

+{formatCurrency(bestTrade.profit, currency)}

{bestTrade.symbol} · {formatDate(bestTrade.time)}

{bestTrade.type === 0 ? "BUY" : "SELL"}
)} {worstTrade && (

Worst Trade

{formatCurrency(worstTrade.profit, currency)}

{worstTrade.symbol} · {formatDate(worstTrade.time)}

{worstTrade.type === 0 ? "BUY" : "SELL"}
)}
)} {/* Equity curve */}

Equity Curve

{/* Trade table */}

Closed Trades

{closed.length} trades

{closed.length === 0 ? (
No closed trades yet.
) : (
{["Time", "Symbol", "Side", "Volume", "Price", "P&L"].map(h => ( ))} {closed.slice().reverse().map((d) => ( ))}
{h}
{formatDate(d.time)} {d.symbol || "—"} {d.type === 0 ? "BUY" : "SELL"} {d.volume} {d.price.toFixed(2)} = 0 ? "text-bull" : "text-bear"}`}> {formatCurrency(d.profit, currency)}
)}
)} ); }