Open Trades
diff --git a/app/api/trading/clear-manual-closes/route.ts b/app/api/trading/clear-manual-closes/route.ts
new file mode 100644
index 0000000..6de9545
--- /dev/null
+++ b/app/api/trading/clear-manual-closes/route.ts
@@ -0,0 +1,105 @@
+/**
+ * Clear Manually Closed Trades
+ *
+ * Deletes all "open" trades from database when user manually closed them in Drift UI
+ */
+
+import { NextRequest, NextResponse } from 'next/server'
+import { getPrismaClient } from '@/lib/database/trades'
+import { initializeDriftService } from '@/lib/drift/client'
+import { getMarketConfig } from '@/config/trading'
+
+export async function POST(request: NextRequest) {
+ try {
+ // Initialize Drift to check actual positions
+ const driftService = await initializeDriftService()
+ const prisma = getPrismaClient()
+
+ // Get all "open" trades from database
+ const openTrades = await prisma.trade.findMany({
+ where: {
+ status: 'open',
+ },
+ select: {
+ id: true,
+ symbol: true,
+ direction: true,
+ entryPrice: true,
+ positionId: true,
+ },
+ })
+
+ if (openTrades.length === 0) {
+ return NextResponse.json({
+ message: 'No open trades to clear',
+ cleared: 0,
+ })
+ }
+
+ console.log(`🔍 Checking ${openTrades.length} open trades against Drift positions...`)
+
+ // Check each trade against actual Drift position
+ const toClear: string[] = []
+
+ for (const trade of openTrades) {
+ try {
+ const marketConfig = getMarketConfig(trade.symbol)
+ const position = await driftService.getPosition(marketConfig.driftMarketIndex)
+
+ if (position === null || position.size === 0) {
+ // No position on Drift = manually closed
+ console.log(`✅ Trade ${trade.symbol} has no Drift position - marking for deletion`)
+ toClear.push(trade.id)
+ } else {
+ // Position exists - check if entry price matches (within 0.5%)
+ const entryPriceDiff = Math.abs(position.entryPrice - trade.entryPrice)
+ const entryPriceDiffPercent = (entryPriceDiff / trade.entryPrice) * 100
+
+ if (entryPriceDiffPercent > 0.5) {
+ // Entry prices don't match = different position = old trade was closed
+ console.log(`✅ Trade ${trade.symbol} entry mismatch (DB: $${trade.entryPrice.toFixed(4)}, Drift: $${position.entryPrice.toFixed(4)}) - marking for deletion`)
+ toClear.push(trade.id)
+ } else {
+ console.log(`⏭️ Trade ${trade.symbol} still has matching position on Drift - keeping`)
+ }
+ }
+ } catch (error) {
+ console.error(`⚠️ Failed to check ${trade.symbol}:`, error)
+ // On error, don't delete (safer to keep false positives than delete real trades)
+ }
+ }
+
+ // Delete the orphaned trades
+ if (toClear.length > 0) {
+ const result = await prisma.trade.deleteMany({
+ where: {
+ id: {
+ in: toClear,
+ },
+ },
+ })
+
+ console.log(`🗑️ Cleared ${result.count} manually closed trades`)
+
+ return NextResponse.json({
+ message: `Cleared ${result.count} manually closed trade${result.count > 1 ? 's' : ''}`,
+ cleared: result.count,
+ tradeIds: toClear,
+ })
+ } else {
+ return NextResponse.json({
+ message: 'All open trades have matching positions on Drift',
+ cleared: 0,
+ })
+ }
+
+ } catch (error) {
+ console.error('❌ Failed to clear manually closed trades:', error)
+ return NextResponse.json(
+ {
+ error: error instanceof Error ? error.message : 'Unknown error',
+ },
+ { status: 500 }
+ )
+ }
+}