feat: add price movement context to flip-flop detection

Improved flip-flop penalty logic to distinguish between:
- Chop (bad): <2% price move from opposite signal → -25 penalty
- Reversal (good): ≥2% price move from opposite signal → allowed

Changes:
- lib/database/trades.ts: getRecentSignals() now returns oppositeDirectionPrice
- lib/trading/signal-quality.ts: Added currentPrice parameter, price movement check
- app/api/trading/check-risk/route.ts: Added currentPrice to RiskCheckRequest interface
- app/api/trading/execute/route.ts: Pass openResult.fillPrice as currentPrice
- app/api/analytics/reentry-check/route.ts: Pass currentPrice from metrics

Example scenarios:
- ETH $170 SHORT → $153 LONG (10% move) = reversal allowed 
- ETH $154.50 SHORT → $154.30 LONG (0.13% move) = chop blocked ⚠️

Deployed: 09:18 CET Nov 14, 2025
Container: trading-bot-v4
This commit is contained in:
mindesbunister
2025-11-14 07:46:28 +01:00
parent cf0de17aee
commit 77a9437d26
5 changed files with 46 additions and 6 deletions

View File

@@ -567,6 +567,7 @@ export async function getRecentSignals(params: {
totalSignals: number
oppositeDirectionInWindow: boolean
oppositeDirectionMinutesAgo?: number
oppositeDirectionPrice?: number
last3Trades: Array<{ direction: 'long' | 'short'; createdAt: Date }>
isAlternatingPattern: boolean
}> {
@@ -581,7 +582,7 @@ export async function getRecentSignals(params: {
symbol: params.symbol,
createdAt: { gte: timeAgo },
},
select: { direction: true, createdAt: true },
select: { direction: true, createdAt: true, entryPrice: true },
orderBy: { createdAt: 'desc' },
}),
prisma.blockedSignal.findMany({
@@ -589,15 +590,23 @@ export async function getRecentSignals(params: {
symbol: params.symbol,
createdAt: { gte: timeAgo },
},
select: { direction: true, createdAt: true },
select: { direction: true, createdAt: true, signalPrice: true },
orderBy: { createdAt: 'desc' },
}),
])
// Combine and sort all signals
// Combine and sort all signals with their prices
const allSignals = [
...trades.map(t => ({ direction: t.direction as 'long' | 'short', createdAt: t.createdAt })),
...blockedSignals.map(b => ({ direction: b.direction as 'long' | 'short', createdAt: b.createdAt })),
...trades.map(t => ({
direction: t.direction as 'long' | 'short',
createdAt: t.createdAt,
price: t.entryPrice
})),
...blockedSignals.map(b => ({
direction: b.direction as 'long' | 'short',
createdAt: b.createdAt,
price: b.signalPrice
})),
].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
// Check for opposite direction in last 15 minutes
@@ -629,6 +638,7 @@ export async function getRecentSignals(params: {
oppositeDirectionMinutesAgo: oppositeInLast15
? Math.floor((Date.now() - oppositeInLast15.createdAt.getTime()) / 60000)
: undefined,
oppositeDirectionPrice: oppositeInLast15?.price,
last3Trades: last3Trades.map(t => ({
direction: t.direction as 'long' | 'short',
createdAt: t.createdAt