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 { getLastTradeTime, getLastTradeTimeForSymbol, getTradesInLastHour, getTodayPnL, createBlockedSignal } from '@/lib/database/trades'
|
||||||
import { getPythPriceMonitor } from '@/lib/pyth/price-monitor'
|
import { getPythPriceMonitor } from '@/lib/pyth/price-monitor'
|
||||||
import { scoreSignalQuality, SignalQualityResult } from '@/lib/trading/signal-quality'
|
import { scoreSignalQuality, SignalQualityResult } from '@/lib/trading/signal-quality'
|
||||||
|
import { initializeDriftService } from '@/lib/drift/client'
|
||||||
|
import { SUPPORTED_MARKETS } from '@/config/trading'
|
||||||
|
|
||||||
export interface RiskCheckRequest {
|
export interface RiskCheckRequest {
|
||||||
symbol: string
|
symbol: string
|
||||||
@@ -33,6 +35,41 @@ export interface RiskCheckResponse {
|
|||||||
qualityReasons?: string[]
|
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
|
* Position Scaling Validation
|
||||||
* Determines if adding to an existing position is allowed
|
* 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
|
// Save blocked signal if we have metrics
|
||||||
if (hasContextMetrics) {
|
if (hasContextMetrics) {
|
||||||
const priceMonitor = getPythPriceMonitor()
|
const currentPrice = await getCurrentPrice(body.symbol, body.currentPrice)
|
||||||
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
|
||||||
|
|
||||||
await createBlockedSignal({
|
await createBlockedSignal({
|
||||||
symbol: body.symbol,
|
symbol: body.symbol,
|
||||||
direction: body.direction,
|
direction: body.direction,
|
||||||
timeframe: body.timeframe,
|
timeframe: body.timeframe,
|
||||||
signalPrice: latestPrice?.price || 0,
|
signalPrice: currentPrice,
|
||||||
atr: body.atr,
|
atr: body.atr,
|
||||||
adx: body.adx,
|
adx: body.adx,
|
||||||
rsi: body.rsi,
|
rsi: body.rsi,
|
||||||
@@ -294,14 +330,13 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
|||||||
|
|
||||||
// Save blocked signal if we have metrics
|
// Save blocked signal if we have metrics
|
||||||
if (hasContextMetrics) {
|
if (hasContextMetrics) {
|
||||||
const priceMonitor = getPythPriceMonitor()
|
const currentPrice = await getCurrentPrice(body.symbol, body.currentPrice)
|
||||||
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
|
||||||
|
|
||||||
await createBlockedSignal({
|
await createBlockedSignal({
|
||||||
symbol: body.symbol,
|
symbol: body.symbol,
|
||||||
direction: body.direction,
|
direction: body.direction,
|
||||||
timeframe: body.timeframe,
|
timeframe: body.timeframe,
|
||||||
signalPrice: latestPrice?.price || 0,
|
signalPrice: currentPrice,
|
||||||
atr: body.atr,
|
atr: body.atr,
|
||||||
adx: body.adx,
|
adx: body.adx,
|
||||||
rsi: body.rsi,
|
rsi: body.rsi,
|
||||||
@@ -354,15 +389,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get current price for the blocked signal record
|
// Get current price for the blocked signal record
|
||||||
const priceMonitor = getPythPriceMonitor()
|
const currentPrice = await getCurrentPrice(body.symbol, body.currentPrice)
|
||||||
const latestPrice = priceMonitor.getCachedPrice(body.symbol)
|
|
||||||
|
|
||||||
// Save blocked signal to database for future analysis
|
// Save blocked signal to database for future analysis
|
||||||
await createBlockedSignal({
|
await createBlockedSignal({
|
||||||
symbol: body.symbol,
|
symbol: body.symbol,
|
||||||
direction: body.direction,
|
direction: body.direction,
|
||||||
timeframe: body.timeframe,
|
timeframe: body.timeframe,
|
||||||
signalPrice: latestPrice?.price || 0,
|
signalPrice: currentPrice,
|
||||||
atr: body.atr,
|
atr: body.atr,
|
||||||
adx: body.adx,
|
adx: body.adx,
|
||||||
rsi: body.rsi,
|
rsi: body.rsi,
|
||||||
|
|||||||
Reference in New Issue
Block a user