fix: use Pyth price data for flip-flop context check
CRITICAL FIX: Previous implementation showed incorrect price movements (100% instead of 0.2%) because currentPrice wasn't available in check-risk endpoint. Changes: - app/api/trading/check-risk/route.ts: Fetch current price from Pyth price monitor before quality scoring - lib/trading/signal-quality.ts: Added validation and detailed logging - Check if currentPrice available, apply penalty if missing - Log actual prices: $X → $Y = Z% - Include prices in penalty/allowance messages Example outputs: Flip-flop in tight range: 4min ago, only 0.20% move ($143.86 → $143.58) (-25 pts) Direction change after 10.2% move ($170.00 → $153.00, 12min ago) - reversal allowed This fixes the false positive that allowed a 0.2% flip-flop earlier today. Deployed: 09:42 CET Nov 14, 2025
This commit is contained in:
@@ -312,6 +312,11 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
|||||||
|
|
||||||
// 4. Check signal quality (if context metrics provided)
|
// 4. Check signal quality (if context metrics provided)
|
||||||
if (hasContextMetrics) {
|
if (hasContextMetrics) {
|
||||||
|
// Get current price from Pyth for flip-flop price context check
|
||||||
|
const priceMonitor = getPythPriceMonitor()
|
||||||
|
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
||||||
|
const currentPrice = latestPrice?.price || body.currentPrice
|
||||||
|
|
||||||
const qualityScore = await scoreSignalQuality({
|
const qualityScore = await scoreSignalQuality({
|
||||||
atr: body.atr || 0,
|
atr: body.atr || 0,
|
||||||
adx: body.adx || 0,
|
adx: body.adx || 0,
|
||||||
@@ -320,7 +325,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
|||||||
pricePosition: body.pricePosition || 0,
|
pricePosition: body.pricePosition || 0,
|
||||||
direction: body.direction,
|
direction: body.direction,
|
||||||
symbol: body.symbol,
|
symbol: body.symbol,
|
||||||
currentPrice: body.currentPrice,
|
currentPrice: currentPrice,
|
||||||
timeframe: body.timeframe, // Pass timeframe for context-aware scoring
|
timeframe: body.timeframe, // Pass timeframe for context-aware scoring
|
||||||
minScore: config.minSignalQualityScore // Use config value
|
minScore: config.minSignalQualityScore // Use config value
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -213,30 +213,40 @@ export async function scoreSignalQuality(params: {
|
|||||||
// BUT: Only penalize if price hasn't moved significantly (< 2% from opposite signal)
|
// BUT: Only penalize if price hasn't moved significantly (< 2% from opposite signal)
|
||||||
// This distinguishes chop (bad) from legitimate reversals (good)
|
// This distinguishes chop (bad) from legitimate reversals (good)
|
||||||
if (recentSignals.oppositeDirectionInWindow && recentSignals.oppositeDirectionPrice) {
|
if (recentSignals.oppositeDirectionInWindow && recentSignals.oppositeDirectionPrice) {
|
||||||
const priceChangePercent = Math.abs(
|
if (!params.currentPrice || params.currentPrice === 0) {
|
||||||
((params.currentPrice || 0) - recentSignals.oppositeDirectionPrice) / recentSignals.oppositeDirectionPrice * 100
|
// No current price available - apply penalty (conservative)
|
||||||
)
|
console.warn(`⚠️ Flip-flop check: No currentPrice available, applying penalty`)
|
||||||
|
|
||||||
if (priceChangePercent < 2.0) {
|
|
||||||
// Small price move = consolidation/chop = BAD
|
|
||||||
frequencyPenalties.flipFlop = -25
|
frequencyPenalties.flipFlop = -25
|
||||||
score -= 25
|
score -= 25
|
||||||
reasons.push(
|
reasons.push(`⚠️ Flip-flop detected: opposite direction ${recentSignals.oppositeDirectionMinutesAgo}min ago, no price data (-25 pts)`)
|
||||||
`⚠️ Flip-flop in tight range: ${recentSignals.oppositeDirectionMinutesAgo}min ago, ` +
|
|
||||||
`only ${priceChangePercent.toFixed(2)}% move (-25 pts)`
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// Large price move = potential reversal = ALLOW
|
const priceChangePercent = Math.abs(
|
||||||
reasons.push(
|
(params.currentPrice - recentSignals.oppositeDirectionPrice) / recentSignals.oppositeDirectionPrice * 100
|
||||||
`✅ Direction change after ${priceChangePercent.toFixed(1)}% move ` +
|
|
||||||
`(${recentSignals.oppositeDirectionMinutesAgo}min ago) - reversal allowed`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log(`🔍 Flip-flop price check: $${recentSignals.oppositeDirectionPrice.toFixed(2)} → $${params.currentPrice.toFixed(2)} = ${priceChangePercent.toFixed(2)}%`)
|
||||||
|
|
||||||
|
if (priceChangePercent < 2.0) {
|
||||||
|
// Small price move = consolidation/chop = BAD
|
||||||
|
frequencyPenalties.flipFlop = -25
|
||||||
|
score -= 25
|
||||||
|
reasons.push(
|
||||||
|
`⚠️ Flip-flop in tight range: ${recentSignals.oppositeDirectionMinutesAgo}min ago, ` +
|
||||||
|
`only ${priceChangePercent.toFixed(2)}% move ($${recentSignals.oppositeDirectionPrice.toFixed(2)} → $${params.currentPrice.toFixed(2)}) (-25 pts)`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Large price move = potential reversal = ALLOW
|
||||||
|
reasons.push(
|
||||||
|
`✅ Direction change after ${priceChangePercent.toFixed(1)}% move ` +
|
||||||
|
`($${recentSignals.oppositeDirectionPrice.toFixed(2)} → $${params.currentPrice.toFixed(2)}, ${recentSignals.oppositeDirectionMinutesAgo}min ago) - reversal allowed`
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (recentSignals.oppositeDirectionInWindow && !recentSignals.oppositeDirectionPrice) {
|
} else if (recentSignals.oppositeDirectionInWindow && !recentSignals.oppositeDirectionPrice) {
|
||||||
// Fallback: If we don't have price data, apply penalty (conservative)
|
// Fallback: If we don't have opposite price data, apply penalty (conservative)
|
||||||
frequencyPenalties.flipFlop = -25
|
frequencyPenalties.flipFlop = -25
|
||||||
score -= 25
|
score -= 25
|
||||||
reasons.push(`⚠️ Flip-flop detected: opposite direction ${recentSignals.oppositeDirectionMinutesAgo}min ago (-25 pts)`)
|
reasons.push(`⚠️ Flip-flop detected: opposite direction ${recentSignals.oppositeDirectionMinutesAgo}min ago, no historical price (-25 pts)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Penalty 3: Alternating pattern (last 3 trades flip directions)
|
// Penalty 3: Alternating pattern (last 3 trades flip directions)
|
||||||
|
|||||||
Reference in New Issue
Block a user