/** * Validate Positions API Endpoint * * Compares current open positions against configured settings * POST /api/trading/validate-positions */ import { NextRequest, NextResponse } from 'next/server' import { getMergedConfig } from '@/config/trading' import { getInitializedPositionManager } from '@/lib/trading/position-manager' import { getDriftService } from '@/lib/drift/client' interface ValidationIssue { type: 'error' | 'warning' field: string expected: number | string actual: number | string message: string } interface PositionValidation { symbol: string direction: 'long' | 'short' entryPrice: number isValid: boolean issues: ValidationIssue[] } interface ValidationResponse { success: boolean timestamp: string config: { leverage: number positionSize: number tp1Percent: number tp2Percent: number stopLossPercent: number useDualStops: boolean hardStopPercent?: number } positions: PositionValidation[] summary: { totalPositions: number validPositions: number positionsWithIssues: number } } function calculateExpectedPrice(entry: number, percent: number, direction: 'long' | 'short'): number { if (direction === 'long') { return entry * (1 + percent / 100) } else { return entry * (1 - percent / 100) } } function calculateActualPercent(entry: number, price: number, direction: 'long' | 'short'): number { if (direction === 'long') { return ((price - entry) / entry) * 100 } else { return ((entry - price) / entry) * 100 } } export async function POST(request: NextRequest): Promise> { try { // Verify authorization const authHeader = request.headers.get('authorization') const expectedAuth = `Bearer ${process.env.API_SECRET_KEY}` if (!authHeader || authHeader !== expectedAuth) { return NextResponse.json( { success: false, timestamp: new Date().toISOString(), config: {} as any, positions: [], summary: { totalPositions: 0, validPositions: 0, positionsWithIssues: 0, }, }, { status: 401 } ) } console.log('🔍 Validating positions against settings...') // Get current configuration const config = getMergedConfig() // Get active positions from Position Manager const positionManager = await getInitializedPositionManager() const activeTrades = Array.from(positionManager.getActiveTrades().values()) console.log(`📊 Found ${activeTrades.length} active positions to validate`) const validations: PositionValidation[] = [] for (const trade of activeTrades) { const issues: ValidationIssue[] = [] // Validate leverage const expectedLeverage = config.leverage if (trade.leverage !== expectedLeverage) { issues.push({ type: 'warning', field: 'leverage', expected: expectedLeverage, actual: trade.leverage, message: `Leverage mismatch: expected ${expectedLeverage}x, got ${trade.leverage}x`, }) } // Calculate expected prices based on current config const expectedTP1 = calculateExpectedPrice(trade.entryPrice, config.takeProfit1Percent, trade.direction) const expectedTP2 = calculateExpectedPrice(trade.entryPrice, config.takeProfit2Percent, trade.direction) const expectedSL = calculateExpectedPrice(trade.entryPrice, config.stopLossPercent, trade.direction) // Validate TP1 (allow 0.1% tolerance) const tp1Diff = Math.abs((trade.tp1Price - expectedTP1) / expectedTP1) * 100 if (tp1Diff > 0.1) { const actualTP1Percent = calculateActualPercent(trade.entryPrice, trade.tp1Price, trade.direction) issues.push({ type: 'error', field: 'takeProfit1', expected: `${config.takeProfit1Percent}% ($${expectedTP1.toFixed(2)})`, actual: `${actualTP1Percent.toFixed(2)}% ($${trade.tp1Price.toFixed(2)})`, message: `TP1 price mismatch: expected ${config.takeProfit1Percent}%, actual ${actualTP1Percent.toFixed(2)}%`, }) } // Validate TP2 (allow 0.1% tolerance) const tp2Diff = Math.abs((trade.tp2Price - expectedTP2) / expectedTP2) * 100 if (tp2Diff > 0.1) { const actualTP2Percent = calculateActualPercent(trade.entryPrice, trade.tp2Price, trade.direction) issues.push({ type: 'error', field: 'takeProfit2', expected: `${config.takeProfit2Percent}% ($${expectedTP2.toFixed(2)})`, actual: `${actualTP2Percent.toFixed(2)}% ($${trade.tp2Price.toFixed(2)})`, message: `TP2 price mismatch: expected ${config.takeProfit2Percent}%, actual ${actualTP2Percent.toFixed(2)}%`, }) } // Validate Stop Loss (allow 0.1% tolerance) const slDiff = Math.abs((trade.stopLossPrice - expectedSL) / expectedSL) * 100 if (slDiff > 0.1) { const actualSLPercent = Math.abs(calculateActualPercent(trade.entryPrice, trade.stopLossPrice, trade.direction)) issues.push({ type: 'error', field: 'stopLoss', expected: `${Math.abs(config.stopLossPercent)}% ($${expectedSL.toFixed(2)})`, actual: `${actualSLPercent.toFixed(2)}% ($${trade.stopLossPrice.toFixed(2)})`, message: `Stop loss mismatch: expected ${Math.abs(config.stopLossPercent)}%, actual ${actualSLPercent.toFixed(2)}%`, }) } // Validate position size // Note: trade.positionSize is the TOTAL position value in USD (e.g., $800 with 10x leverage) // config.positionSize is the COLLATERAL amount (e.g., $80) // So: expectedPositionValueUSD = config.positionSize * config.leverage const expectedPositionValueUSD = config.positionSize * config.leverage const actualPositionValueUSD = trade.positionSize const sizeDiff = Math.abs((actualPositionValueUSD - expectedPositionValueUSD) / expectedPositionValueUSD) * 100 if (sizeDiff > 5) { // Allow 5% tolerance for position size issues.push({ type: 'warning', field: 'positionSize', expected: `$${expectedPositionValueUSD.toFixed(2)}`, actual: `$${actualPositionValueUSD.toFixed(2)}`, message: `Position size mismatch: expected $${expectedPositionValueUSD.toFixed(2)}, got $${actualPositionValueUSD.toFixed(2)}`, }) } const validation: PositionValidation = { symbol: trade.symbol, direction: trade.direction, entryPrice: trade.entryPrice, isValid: issues.length === 0, issues, } validations.push(validation) if (issues.length > 0) { console.log(`⚠️ Position ${trade.symbol} ${trade.direction} has ${issues.length} issue(s):`) issues.forEach(issue => { console.log(` ${issue.type === 'error' ? '❌' : '⚠️'} ${issue.message}`) }) } else { console.log(`✅ Position ${trade.symbol} ${trade.direction} is valid`) } } const summary = { totalPositions: validations.length, validPositions: validations.filter(v => v.isValid).length, positionsWithIssues: validations.filter(v => !v.isValid).length, } console.log(`📊 Validation complete: ${summary.validPositions}/${summary.totalPositions} positions valid`) return NextResponse.json({ success: true, timestamp: new Date().toISOString(), config: { leverage: config.leverage, positionSize: config.positionSize, tp1Percent: config.takeProfit1Percent, tp2Percent: config.takeProfit2Percent, stopLossPercent: config.stopLossPercent, useDualStops: config.useDualStops, hardStopPercent: config.useDualStops ? config.hardStopPercent : undefined, }, positions: validations, summary, }) } catch (error) { console.error('❌ Position validation error:', error) return NextResponse.json( { success: false, timestamp: new Date().toISOString(), config: {} as any, positions: [], summary: { totalPositions: 0, validPositions: 0, positionsWithIssues: 0, }, }, { status: 500 } ) } }