import { NextResponse } from 'next/server'; import { getClients } from '@/lib/clients'; import { loadTrades, getSetting, TRADES_SINCE_SETTING } from '@/lib/db'; import type { TradeView } from '@/types'; /** * GET /api/trades?accountId=… | ?firmId=… [&limit=…] — recorded positions, * newest first: one account, a firm's current accounts, or all of them. * * Who opened and closed each one: the bot marks its own orders as it sends * them, so after marking began (TRADES_SINCE_SETTING) anything unmarked was * someone else's. A close Vest calls "user_requested" is an order on the * account — the bot's if it marked it, else outside. Trades built from * futures fills (lib/trade-recorder.ts, DXtrade's record) carry their marks * already: 'bot', 'outside', or 'unknown' from before the bot noted its own * orders. */ export async function GET(req: Request) { const url = new URL(req.url); const accountId = Number(url.searchParams.get('accountId')); const firmId = url.searchParams.get('firmId'); const limit = Math.min(Math.max(Math.floor(Number(url.searchParams.get('limit'))) || 200, 1), 1000); let accountIds: number[] | undefined; if (Number.isFinite(accountId) && accountId > 0) accountIds = [accountId]; else if (firmId) accountIds = getClients().get(firmId)?.accountList.map((a) => a.id) ?? []; const since = Number(getSetting(TRADES_SINCE_SETTING) ?? NaN); const tracked = (at: number) => Number.isFinite(since) && at >= since; const trades: TradeView[] = loadTrades({ accountIds, limit }).map((t) => { const open = t.closedAt === null && t.closeReason === null && t.closedBy === null; const closedBy = open ? null : t.closedBy === 'bot' ? 'bot' : t.closeReason === 'user_requested' ? (t.closedBy === 'outside' || tracked(t.closedAt ?? t.openedAt) ? 'outside' : 'user_requested') : t.closeReason ?? (t.closedBy === 'outside' ? 'outside' : t.closedBy === 'unknown' ? 'user_requested' : 'unknown'); const net = open || t.pnl === null ? null : Math.round((t.pnl - Math.abs(t.fee ?? 0) + (t.funding ?? 0)) * 100) / 100; return { positionId: t.positionId, accountId: t.accountId, accountName: t.accountName, symbol: t.symbol, side: t.side, quantity: t.quantity, openPrice: t.openPrice, closePrice: open ? null : t.closePrice, pnl: open ? null : t.pnl, fee: t.fee, funding: t.funding, net, openedAt: t.openedAt, closedAt: t.closedAt, openedBy: t.openedBy === 'bot' ? 'bot' : t.openedBy === 'unknown' ? null : t.openedBy === 'outside' || tracked(t.openedAt) ? 'outside' : null, closedBy, }; }); return NextResponse.json(trades); }