import { NextRequest, NextResponse } from 'next/server' import { getMarketDataCache } from '@/lib/trading/market-data-cache' /** * Market Data Webhook Endpoint * * Receives real-time metrics from TradingView alerts. * Called every 1-5 minutes per symbol to keep cache fresh. * * TradingView Alert Message (JSON): * { * "action": "market_data", * "symbol": "{{ticker}}", * "timeframe": "{{interval}}", * "atr": {{ta.atr(14)}}, * "adx": {{ta.dmi(14, 14)}}, * "rsi": {{ta.rsi(14)}}, * "volumeRatio": {{volume / ta.sma(volume, 20)}}, * "pricePosition": {{(close - ta.lowest(low, 100)) / (ta.highest(high, 100) - ta.lowest(low, 100)) * 100}}, * "currentPrice": {{close}}, * "timestamp": {{timenow}} * } * * Webhook URL: https://your-domain.com/api/trading/market-data */ /** * Normalize TradingView symbol format to Drift format */ function normalizeTradingViewSymbol(tvSymbol: string): string { if (tvSymbol.includes('-PERP')) return tvSymbol const symbolMap: Record = { 'SOLUSDT': 'SOL-PERP', 'SOLUSD': 'SOL-PERP', 'SOL': 'SOL-PERP', 'ETHUSDT': 'ETH-PERP', 'ETHUSD': 'ETH-PERP', 'ETH': 'ETH-PERP', 'BTCUSDT': 'BTC-PERP', 'BTCUSD': 'BTC-PERP', 'BTC': 'BTC-PERP' } return symbolMap[tvSymbol.toUpperCase()] || `${tvSymbol.toUpperCase()}-PERP` } export async function POST(request: NextRequest) { try { const body = await request.json() console.log('📡 Received market data webhook:', { action: body.action, symbol: body.symbol, atr: body.atr, adx: body.adx }) // Validate it's a market data update if (body.action !== 'market_data') { console.log(`❌ Invalid action: ${body.action} (expected "market_data")`) return NextResponse.json( { error: 'Invalid action - expected "market_data"' }, { status: 400 } ) } // Validate required fields if (!body.symbol) { return NextResponse.json( { error: 'Missing symbol' }, { status: 400 } ) } const driftSymbol = normalizeTradingViewSymbol(body.symbol) // Store in cache for immediate use const marketCache = getMarketDataCache() marketCache.set(driftSymbol, { symbol: driftSymbol, atr: Number(body.atr) || 0, adx: Number(body.adx) || 0, rsi: Number(body.rsi) || 50, volumeRatio: Number(body.volumeRatio) || 1.0, pricePosition: Number(body.pricePosition) || 50, maGap: Number(body.maGap) || undefined, currentPrice: Number(body.currentPrice) || 0, timestamp: Date.now(), timeframe: body.timeframe || '5' }) // CRITICAL (Dec 2, 2025): Store ALL 1-minute data in database for historical analysis // User directive: "we want to store the data for 4 weeks" // Purpose: Enable granular 8-hour analysis of blocked signals with full indicator data try { const { getPrismaClient } = await import('@/lib/database/trades') const prisma = getPrismaClient() await prisma.marketData.create({ data: { symbol: driftSymbol, timeframe: body.timeframe || '1', price: Number(body.currentPrice) || 0, atr: Number(body.atr) || 0, adx: Number(body.adx) || 0, rsi: Number(body.rsi) || 50, volumeRatio: Number(body.volumeRatio) || 1.0, pricePosition: Number(body.pricePosition) || 50, maGap: Number(body.maGap) || undefined, volume: Number(body.volume) || undefined, timestamp: new Date(body.timestamp || Date.now()) } }) console.log(`💾 Stored 1-minute data in database for ${driftSymbol}`) } catch (dbError) { console.error('❌ Failed to store market data in database:', dbError) // Don't fail the request if database save fails - cache still works } console.log(`✅ Market data cached for ${driftSymbol}`) return NextResponse.json({ success: true, symbol: driftSymbol, message: 'Market data cached and stored successfully', expiresInSeconds: 300 }) } catch (error) { console.error('❌ Market data webhook error:', error) return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ) } } /** * GET endpoint to view currently cached data (for debugging) */ export async function GET(request: NextRequest) { try { const marketCache = getMarketDataCache() const availableSymbols = marketCache.getAvailableSymbols() const cacheData: Record = {} for (const symbol of availableSymbols) { const data = marketCache.get(symbol) if (data) { const ageSeconds = marketCache.getDataAge(symbol) cacheData[symbol] = { ...data, ageSeconds } } } return NextResponse.json({ success: true, availableSymbols, count: availableSymbols.length, cache: cacheData }) } catch (error) { console.error('❌ Market data GET error:', error) return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ) } }