feat: Calculate quality scores for all timeframes (not just 5min)

- Moved scoreSignalQuality() to BEFORE timeframe check (line 112)
- Data collection signals now have real quality scores (not hardcoded 0)
- Enables quality-filtered win rate comparison across 5min/15min/1H/4H/Daily
- Fixed TypeScript errors: added symbol/currentPrice params, fixed interface refs
- Added getMinQualityScoreForDirection import for threshold calculation
- BlockedSignal table now populated with:
  * signalQualityScore (real 0-100 score, not 0)
  * signalQualityVersion ('v9', not 'data-collection')
  * minScoreRequired (actual threshold, not 0)
  * scoreBreakdown with reasons array
- Implementation: Nov 26, 2025
- Container restarted: 14:12:00 UTC (11 minutes after commit)
- Purpose: Enable SQL queries like WHERE signalQualityScore >= minScoreRequired
  to compare quality-filtered win rates across timeframes
This commit is contained in:
mindesbunister
2025-11-26 15:15:32 +01:00
parent f2bc13dba0
commit dbada477b8
2 changed files with 777 additions and 25 deletions

View File

@@ -8,7 +8,7 @@
import { NextRequest, NextResponse } from 'next/server'
import { initializeDriftService } from '@/lib/drift/client'
import { openPosition, placeExitOrders, closePosition } from '@/lib/drift/orders'
import { normalizeTradingViewSymbol } from '@/config/trading'
import { normalizeTradingViewSymbol, getMinQualityScoreForDirection } from '@/config/trading'
import { getMergedConfig } from '@/config/trading'
import { getInitializedPositionManager, ActiveTrade } from '@/lib/trading/position-manager'
import { createTrade, updateTradeExit } from '@/lib/database/trades'
@@ -109,6 +109,30 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
console.log(`📊 Market data auto-cached for ${driftSymbol} from trade signal`)
}
// 📊 CALCULATE QUALITY SCORE BEFORE TIMEFRAME CHECK (Nov 26, 2025)
// CRITICAL: Score ALL signals (5min + data collection) for proper multi-timeframe analysis
// This enables quality-filtered win rate comparison across timeframes
const qualityResult = await scoreSignalQuality({
atr: body.atr || 0,
adx: body.adx || 0,
rsi: body.rsi || 0,
volumeRatio: body.volumeRatio || 0,
pricePosition: body.pricePosition || 0,
maGap: body.maGap, // V9: MA gap convergence scoring
timeframe: body.timeframe || '5',
direction: body.direction,
symbol: driftSymbol,
currentPrice: body.signalPrice || 0,
})
console.log(`📊 Signal quality: ${qualityResult.score} (${qualityResult.score >= 90 ? 'PASS' : 'BLOCKED'})`)
if (qualityResult.reasons?.length > 0) {
console.log(` Reasons: ${qualityResult.reasons.join(', ')}`)
}
// Get min quality threshold for this direction
const config = getMergedConfig()
const minQualityScore = getMinQualityScoreForDirection(body.direction, config)
// 🔬 MULTI-TIMEFRAME DATA COLLECTION
// Only execute trades from 5min timeframe OR manual Telegram trades
// Save other timeframes (15min, 1H, 4H, Daily) for analysis
@@ -128,7 +152,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
symbol: driftSymbol,
direction: body.direction,
blockReason: 'DATA_COLLECTION_ONLY',
blockDetails: `Multi-timeframe data collection: ${timeframe}min signals saved but not executed (only 5min executes)`,
blockDetails: `Multi-timeframe data collection: ${timeframe}min signals saved but not executed (only 5min executes). Quality score: ${qualityResult.score} (threshold: ${minQualityScore})`,
atr: body.atr,
adx: body.adx,
rsi: body.rsi,
@@ -136,12 +160,12 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
pricePosition: body.pricePosition,
timeframe: timeframe,
signalPrice: currentPrice,
signalQualityScore: 0, // Not scored since not executed
signalQualityVersion: 'data-collection',
minScoreRequired: 0,
scoreBreakdown: {},
signalQualityScore: qualityResult.score, // CRITICAL: Real quality score for analysis
signalQualityVersion: 'v9', // Current indicator version
minScoreRequired: minQualityScore,
scoreBreakdown: { reasons: qualityResult.reasons },
})
console.log(`${timeframe}min signal saved at $${currentPrice.toFixed(2)} for future analysis`)
console.log(`${timeframe}min signal saved at $${currentPrice.toFixed(2)} for future analysis (quality: ${qualityResult.score}, threshold: ${minQualityScore})`)
} catch (dbError) {
console.error(`❌ Failed to save ${timeframe}min signal:`, dbError)
}
@@ -154,6 +178,8 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
timeframe: timeframe,
symbol: driftSymbol,
direction: body.direction,
qualityScore: qualityResult.score,
threshold: minQualityScore,
saved: true,
}
}, { status: 200 }) // 200 not 400 - this is expected behavior
@@ -161,29 +187,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
console.log(`✅ 5min signal confirmed - proceeding with trade execution`)
// Get trading configuration
const config = getMergedConfig()
// Initialize Drift service and check account health before sizing
const driftService = await initializeDriftService()
const health = await driftService.getAccountHealth()
console.log(`🩺 Account health: Free collateral $${health.freeCollateral.toFixed(2)}`)
// Calculate quality score EARLY for adaptive leverage (Nov 24, 2025)
// This needs to happen before position sizing so leverage can be adjusted based on quality
const qualityResult = await scoreSignalQuality({
atr: body.atr || 0,
adx: body.adx || 0,
rsi: body.rsi || 0,
volumeRatio: body.volumeRatio || 0,
pricePosition: body.pricePosition || 0,
maGap: body.maGap, // V9: MA gap convergence scoring
direction: body.direction,
symbol: driftSymbol,
currentPrice: body.signalPrice || 0,
timeframe: body.timeframe,
})
console.log(`📊 Signal quality score: ${qualityResult.score} (calculated early for adaptive leverage)`)
// Quality score already calculated above (before timeframe check)
// Now use it for adaptive leverage and position sizing
console.log(`📊 Signal quality score: ${qualityResult.score} (using for adaptive leverage)`)
// Get symbol-specific position sizing with quality score for adaptive leverage
// ENHANCED Nov 25, 2025: Pass direction for SHORT-specific leverage tiers