fix(blocked-signals): Add 3-tier price fallback to prevent entryPrice=0
- Root cause: Pyth cache empty caused entryPrice=0 in BlockedSignal records - Solution: getCurrentPrice() helper with Pyth → Drift oracle → TradingView fallback - Updated 3 createBlockedSignal callsites (QUALITY, COOLDOWN, HOURLY_LIMIT) - Impact: Enables accurate what-if analysis for threshold optimization - Files: app/api/trading/check-risk/route.ts (added getCurrentPrice, updated 3 calls) - Deployed: Nov 25, 2025 22:55 UTC (container verified running new code) - Testing: Next blocked signal will verify entryPrice != 0 in database
This commit is contained in:
@@ -11,6 +11,8 @@ import { getInitializedPositionManager, ActiveTrade } from '@/lib/trading/positi
|
||||
import { getLastTradeTime, getLastTradeTimeForSymbol, getTradesInLastHour, getTodayPnL, createBlockedSignal } from '@/lib/database/trades'
|
||||
import { getPythPriceMonitor } from '@/lib/pyth/price-monitor'
|
||||
import { scoreSignalQuality, SignalQualityResult } from '@/lib/trading/signal-quality'
|
||||
import { initializeDriftService } from '@/lib/drift/client'
|
||||
import { SUPPORTED_MARKETS } from '@/config/trading'
|
||||
|
||||
export interface RiskCheckRequest {
|
||||
symbol: string
|
||||
@@ -33,6 +35,41 @@ export interface RiskCheckResponse {
|
||||
qualityReasons?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current price reliably using multiple fallback methods
|
||||
* Priority: Pyth cache → Drift oracle → TradingView signal price
|
||||
*/
|
||||
async function getCurrentPrice(symbol: string, fallbackPrice?: number): Promise<number> {
|
||||
// Try Pyth cache first (fastest)
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
const latestPrice = priceMonitor.getCachedPrice(symbol)
|
||||
if (latestPrice?.price && latestPrice.price > 0) {
|
||||
return latestPrice.price
|
||||
}
|
||||
|
||||
// Try Drift oracle (authoritative)
|
||||
try {
|
||||
const driftService = await initializeDriftService()
|
||||
const marketConfig = SUPPORTED_MARKETS[symbol]
|
||||
if (marketConfig && driftService) {
|
||||
const oraclePrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex)
|
||||
if (oraclePrice > 0) {
|
||||
return oraclePrice
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Failed to get Drift oracle price:', error)
|
||||
}
|
||||
|
||||
// Fallback to TradingView signal price
|
||||
if (fallbackPrice && fallbackPrice > 0) {
|
||||
return fallbackPrice
|
||||
}
|
||||
|
||||
console.error('❌ Unable to get current price from any source')
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Position Scaling Validation
|
||||
* Determines if adding to an existing position is allowed
|
||||
@@ -248,14 +285,13 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
||||
|
||||
// Save blocked signal if we have metrics
|
||||
if (hasContextMetrics) {
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
||||
const currentPrice = await getCurrentPrice(body.symbol, body.currentPrice)
|
||||
|
||||
await createBlockedSignal({
|
||||
symbol: body.symbol,
|
||||
direction: body.direction,
|
||||
timeframe: body.timeframe,
|
||||
signalPrice: latestPrice?.price || 0,
|
||||
signalPrice: currentPrice,
|
||||
atr: body.atr,
|
||||
adx: body.adx,
|
||||
rsi: body.rsi,
|
||||
@@ -294,14 +330,13 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
||||
|
||||
// Save blocked signal if we have metrics
|
||||
if (hasContextMetrics) {
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
||||
const currentPrice = await getCurrentPrice(body.symbol, body.currentPrice)
|
||||
|
||||
await createBlockedSignal({
|
||||
symbol: body.symbol,
|
||||
direction: body.direction,
|
||||
timeframe: body.timeframe,
|
||||
signalPrice: latestPrice?.price || 0,
|
||||
signalPrice: currentPrice,
|
||||
atr: body.atr,
|
||||
adx: body.adx,
|
||||
rsi: body.rsi,
|
||||
@@ -354,15 +389,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
||||
})
|
||||
|
||||
// Get current price for the blocked signal record
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
||||
const currentPrice = await getCurrentPrice(body.symbol, body.currentPrice)
|
||||
|
||||
// Save blocked signal to database for future analysis
|
||||
await createBlockedSignal({
|
||||
symbol: body.symbol,
|
||||
direction: body.direction,
|
||||
timeframe: body.timeframe,
|
||||
signalPrice: latestPrice?.price || 0,
|
||||
signalPrice: currentPrice,
|
||||
atr: body.atr,
|
||||
adx: body.adx,
|
||||
rsi: body.rsi,
|
||||
|
||||
Reference in New Issue
Block a user