/** * Trading Bot v4 - Signal Quality Version Comparison API * * Returns performance metrics comparing different signal quality scoring versions */ import { NextResponse } from 'next/server' import { getPrismaClient } from '@/lib/database/trades' export const dynamic = 'force-dynamic' interface VersionStats { version: string tradeCount: number winRate: number totalPnL: number avgPnL: number avgQualityScore: number | null avgMFE: number | null avgMAE: number | null extremePositions: { count: number avgADX: number | null weakADXCount: number winRate: number avgPnL: number } } export async function GET() { try { const prisma = getPrismaClient() // Get overall stats by version const versionStats = await prisma.$queryRaw>` SELECT COALESCE("signalQualityVersion", 'v1') as version, COUNT(*) as trades, SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins, SUM("realizedPnL") as total_pnl, AVG("realizedPnL") as avg_pnl, AVG("realizedPnL") as avg_pnl, ROUND(AVG("signalQualityScore")::numeric, 1) as avg_quality_score, ROUND(AVG("maxFavorableExcursion")::numeric, 2) as avg_mfe, ROUND(AVG("maxAdverseExcursion")::numeric, 2) as avg_mae FROM "Trade" WHERE "exitReason" IS NOT NULL AND "exitReason" NOT LIKE '%CLEANUP%' AND "isTestTrade" = false GROUP BY "signalQualityVersion" ORDER BY version DESC ` // Get extreme position stats by version (< 15% or > 85%) const extremePositionStats = await prisma.$queryRaw>` SELECT COALESCE("signalQualityVersion", 'v1') as version, COUNT(*) as count, ROUND(AVG("adxAtEntry")::numeric, 1) as avg_adx, COUNT(*) FILTER (WHERE "adxAtEntry" < 18) as weak_adx_count, SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins, AVG("realizedPnL") as avg_pnl FROM "Trade" WHERE "exitReason" IS NOT NULL AND "exitReason" NOT LIKE '%CLEANUP%' AND "isTestTrade" = false AND "pricePositionAtEntry" IS NOT NULL AND ("pricePositionAtEntry" < 15 OR "pricePositionAtEntry" > 85) GROUP BY "signalQualityVersion" ORDER BY version DESC ` // Build combined results const results: VersionStats[] = versionStats.map(stat => { const extremeStats = extremePositionStats.find(e => (e.version || 'v1') === (stat.version || 'v1') ) const trades = Number(stat.trades) const wins = Number(stat.wins) const extremeCount = extremeStats ? Number(extremeStats.count) : 0 const extremeWins = extremeStats ? Number(extremeStats.wins) : 0 return { version: stat.version || 'v1', tradeCount: trades, winRate: trades > 0 ? Math.round((wins / trades) * 100 * 10) / 10 : 0, totalPnL: Number(stat.total_pnl) || 0, avgPnL: Number(stat.avg_pnl) || 0, avgQualityScore: stat.avg_quality_score ? Number(stat.avg_quality_score) : null, avgMFE: stat.avg_mfe ? Number(stat.avg_mfe) : null, avgMAE: stat.avg_mae ? Number(stat.avg_mae) : null, extremePositions: { count: extremeCount, avgADX: extremeStats?.avg_adx ? Number(extremeStats.avg_adx) : null, weakADXCount: extremeStats ? Number(extremeStats.weak_adx_count) : 0, winRate: extremeCount > 0 ? Math.round((extremeWins / extremeCount) * 100 * 10) / 10 : 0, avgPnL: extremeStats?.avg_pnl ? Number(extremeStats.avg_pnl) : 0, } } }) // Get version descriptions const versionDescriptions: Record = { 'v1': 'Original logic (price < 5% threshold)', 'v2': 'Added volume compensation for low ADX', 'v3': 'Stricter: ADX > 18 required for positions < 15%', 'v4': 'Frequency penalties + blocked signals tracking (Nov 11-14)', 'v5': 'Buy/Sell Signal strategy (pre-Nov 12)', 'v6': 'HalfTrend + BarColor strategy (Nov 12+)', } return NextResponse.json({ success: true, versions: results, descriptions: versionDescriptions, timestamp: new Date().toISOString() }) } catch (error) { console.error('❌ Failed to fetch version comparison:', error) return NextResponse.json( { success: false, error: 'Failed to fetch version comparison data' }, { status: 500 } ) } }