← back to Ken

src/app/api/trades/route.js

101 lines

import { NextResponse } from 'next/server';
const { query } = require('../../../lib/db');
const crypto = require('crypto');

export const dynamic = 'force-dynamic';

export async function GET() {
  try {
    const [orders, trades] = await Promise.all([
      query('SELECT * FROM orders ORDER BY submitted_ts DESC LIMIT 100'),
      query('SELECT * FROM trades ORDER BY match_time DESC NULLS LAST LIMIT 100'),
    ]);
    return NextResponse.json({ orders: orders.rows, trades: trades.rows });
  } catch (err) {
    return NextResponse.json({ error: err.message }, { status: 500 });
  }
}

export async function POST(req) {
  const body = await req.json();

  if (body.action === 'paper_order') {
    try {
      const orderId = `paper_${crypto.randomUUID().substring(0, 12)}`;
      const { asset_id, condition_id, side, outcome, price, size_usd, prediction_id } = body;

      // Insert order
      await query(`
        INSERT INTO orders (order_id, asset_id, condition_id, side, outcome, price, size, size_usd, status, paper_trade, prediction_id)
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'filled', TRUE, $9)
      `, [orderId, asset_id, condition_id, side, outcome, price, size_usd / price, size_usd, prediction_id]);

      // Simulate instant fill (paper trading)
      const tradeId = `ptrade_${crypto.randomUUID().substring(0, 12)}`;
      const feeRate = 0; // Paper trades have no fees
      const feeUsd = 0;

      await query(`
        INSERT INTO trades (trade_id, order_id, match_time, price, size, size_usd, fee_rate_bps, fee_usd, pnl, paper_trade)
        VALUES ($1, $2, NOW(), $3, $4, $5, $6, $7, 0, TRUE)
      `, [tradeId, orderId, price, size_usd / price, size_usd, feeRate, feeUsd]);

      // Update order status
      await query("UPDATE orders SET status = 'filled', filled_ts = NOW() WHERE order_id = $1", [orderId]);

      // Log bankroll change
      const lastBal = await query('SELECT balance_after FROM bankroll_ledger ORDER BY ts DESC LIMIT 1');
      const currentBal = parseFloat(lastBal.rows[0]?.balance_after || 50);
      const newBal = currentBal - size_usd; // Debit for position entry

      await query(`
        INSERT INTO bankroll_ledger (event_type, amount, balance_after, trade_id, note)
        VALUES ('trade_pnl', $1, $2, $3, $4)
      `, [-size_usd, newBal, tradeId, `Paper ${side} ${outcome} @ ${price}`]);

      // Update risk state
      await query('UPDATE risk_state SET open_exposure = open_exposure + $1, updated_at = NOW() WHERE id = (SELECT id FROM risk_state ORDER BY id DESC LIMIT 1)', [size_usd]);

      return NextResponse.json({
        order_id: orderId,
        trade_id: tradeId,
        status: 'filled',
        paper: true,
        size_usd,
        price,
      });
    } catch (err) {
      return NextResponse.json({ error: err.message }, { status: 500 });
    }
  }

  if (body.action === 'settle') {
    // Settle a paper trade (mark outcome)
    try {
      const { trade_id, outcome_yes } = body;
      const tradeRes = await query('SELECT t.*, o.outcome, o.price as order_price, o.size_usd FROM trades t JOIN orders o ON t.order_id = o.order_id WHERE t.trade_id = $1', [trade_id]);
      if (!tradeRes.rows[0]) return NextResponse.json({ error: 'Trade not found' }, { status: 404 });

      const trade = tradeRes.rows[0];
      const won = (trade.outcome === 'YES' && outcome_yes) || (trade.outcome === 'NO' && !outcome_yes);
      const pnl = won ? parseFloat(trade.size_usd) * (1 / parseFloat(trade.order_price) - 1) : -parseFloat(trade.size_usd);

      await query('UPDATE trades SET pnl = $1 WHERE trade_id = $2', [pnl, trade_id]);

      // Update bankroll
      const lastBal = await query('SELECT balance_after FROM bankroll_ledger ORDER BY ts DESC LIMIT 1');
      const currentBal = parseFloat(lastBal.rows[0]?.balance_after || 50);
      const returnAmount = won ? parseFloat(trade.size_usd) + pnl : 0;

      await query(`INSERT INTO bankroll_ledger (event_type, amount, balance_after, trade_id, note) VALUES ('trade_pnl', $1, $2, $3, $4)`,
        [returnAmount, currentBal + returnAmount, trade_id, `Settlement: ${won ? 'WIN' : 'LOSS'} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(4)}`]);

      return NextResponse.json({ settled: true, trade_id, won, pnl });
    } catch (err) {
      return NextResponse.json({ error: err.message }, { status: 500 });
    }
  }

  return NextResponse.json({ error: 'Unknown action' }, { status: 400 });
}