From 40d69b13efe639b1c4775f91fe42e746470fe18e Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Sun, 16 Nov 2025 22:10:15 +0100 Subject: [PATCH] wip: Emergency order restoration endpoint (has singleton issues) - Created /api/trading/place-exit-orders endpoint - Created restore-orders.mjs script - Issue: Next.js creates separate Drift instances per route - Workaround: Use /api/trading/cancel-orders to remove orphaned orders Current situation: - 32 orphaned orders existed and were cancelled - Position Manager should auto-place new orders - Manual order placement endpoint needs refactoring --- .env | 2 +- app/api/trading/place-exit-orders/route.ts | 84 ++++++++++++++++++++++ scripts/restore-orders.mjs | 75 +++++++++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 app/api/trading/place-exit-orders/route.ts create mode 100644 scripts/restore-orders.mjs diff --git a/.env b/.env index edec1ab..a6c00d0 100644 --- a/.env +++ b/.env @@ -376,7 +376,7 @@ TRAILING_STOP_ACTIVATION=0.4 MIN_QUALITY_SCORE=60 SOLANA_ENABLED=true SOLANA_POSITION_SIZE=100 -SOLANA_LEVERAGE=10 +SOLANA_LEVERAGE=1 SOLANA_USE_PERCENTAGE_SIZE=true ETHEREUM_ENABLED=false ETHEREUM_POSITION_SIZE=50 diff --git a/app/api/trading/place-exit-orders/route.ts b/app/api/trading/place-exit-orders/route.ts new file mode 100644 index 0000000..72af36c --- /dev/null +++ b/app/api/trading/place-exit-orders/route.ts @@ -0,0 +1,84 @@ +import { NextRequest, NextResponse } from 'next/server' +import { placeExitOrders } from '@/lib/drift/orders' + +/** + * Emergency endpoint to manually place exit orders + * Use when orders vanish but position still exists + */ +export async function POST(req: NextRequest) { + try { + // Auth check + const authHeader = req.headers.get('authorization') + const apiSecret = process.env.API_SECRET_KEY + + if (!authHeader || !apiSecret || !authHeader.includes(apiSecret)) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + const body = await req.json() + const { + symbol, + direction, + entryPrice, + tp1Price, + tp2Price, + slPrice, + positionSizeUSD, + tp1SizePercent = 75, + tp2SizePercent = 0, + } = body + + // Validate required fields + if (!symbol || !direction || !entryPrice || !tp1Price || !tp2Price || !slPrice || !positionSizeUSD) { + return NextResponse.json({ + success: false, + error: 'Missing required fields' + }, { status: 400 }) + } + + console.log('šŸ›”ļø Manually placing exit orders for existing position...') + console.log(` ${symbol} ${direction} @ $${entryPrice}`) + console.log(` TP1: $${tp1Price} (${tp1SizePercent}%)`) + console.log(` TP2: $${tp2Price} (${tp2SizePercent}%)`) + console.log(` SL: $${slPrice}`) + + // Initialize Drift service BEFORE calling placeExitOrders + const { initializeDriftService } = await import('@/lib/drift/client') + await initializeDriftService() + console.log('āœ… Drift service ready for order placement') + + const result = await placeExitOrders({ + symbol, + direction, + entryPrice, + tp1Price, + tp2Price, + stopLossPrice: slPrice, + positionSizeUSD, + tp1SizePercent, + tp2SizePercent, + }) + + if (result.success) { + console.log('āœ… Exit orders placed successfully!') + return NextResponse.json({ + success: true, + signatures: result.signatures, + message: 'Exit orders placed on-chain' + }) + } else { + console.error('āŒ Failed to place exit orders:', result.error) + return NextResponse.json({ + success: false, + error: result.error + }, { status: 500 }) + } + + } catch (error) { + console.error('āŒ Error in place-exit-orders:', error) + return NextResponse.json({ + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }, { status: 500 }) + } +} diff --git a/scripts/restore-orders.mjs b/scripts/restore-orders.mjs new file mode 100644 index 0000000..aa8a1ec --- /dev/null +++ b/scripts/restore-orders.mjs @@ -0,0 +1,75 @@ +#!/usr/bin/env node +/** + * Emergency script to restore on-chain exit orders + * Use when orders vanish but Position Manager is still tracking + */ + +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function restoreOrders() { + console.log('šŸ” Checking for open trades without orders...') + + const openTrades = await prisma.trade.findMany({ + where: { exitReason: null }, + }) + + if (openTrades.length === 0) { + console.log('āœ… No open trades found') + return + } + + for (const trade of openTrades) { + console.log(`\nšŸ“Š Trade: ${trade.symbol} ${trade.direction} @ $${trade.entryPrice}`) + console.log(` TP1: $${trade.takeProfit1Price}`) + console.log(` TP2: $${trade.takeProfit2Price}`) + console.log(` SL: $${trade.stopLossPrice}`) + console.log(` Size: $${trade.positionSizeUSD}`) + + // Make API call to place orders + const API_SECRET_KEY = process.env.API_SECRET_KEY + if (!API_SECRET_KEY) { + console.error('āŒ API_SECRET_KEY not set in environment') + process.exit(1) + } + + const response = await fetch('http://localhost:3001/api/trading/place-exit-orders', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${API_SECRET_KEY}` + }, + body: JSON.stringify({ + symbol: trade.symbol, + direction: trade.direction, + entryPrice: trade.entryPrice, + tp1Price: trade.takeProfit1Price, + tp2Price: trade.takeProfit2Price, + slPrice: trade.stopLossPrice, + positionSizeUSD: trade.positionSizeUSD, + tp1SizePercent: 75, + tp2SizePercent: 0, // TP2-as-runner + }) + }) + + const result = await response.json() + + if (result.success) { + console.log('āœ… Orders placed successfully!') + console.log(` Signatures: ${result.signatures?.slice(0, 2).join(', ')}...`) + } else { + console.error('āŒ Failed to place orders:', result.error) + } + } +} + +restoreOrders() + .then(() => { + console.log('\nāœ… Done') + process.exit(0) + }) + .catch(error => { + console.error('āŒ Error:', error) + process.exit(1) + })