Add 'Clear Manual Closes' button to analytics

- New button in analytics page to clear orphaned trades
- API endpoint /api/trading/clear-manual-closes
- Intelligently checks Drift positions before deleting
- Only removes trades with no matching position or mismatched entry price
- Safe operation: keeps trades on error (false positives better than deletions)
- User-friendly confirmation dialog
This commit is contained in:
mindesbunister
2025-11-01 02:41:26 +01:00
parent c82da51bdc
commit 49a09ef04e
3 changed files with 139 additions and 2 deletions

View File

@@ -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 }
)
}
}