import { type IndicatorData } from "../../types"; import { calculateSMA, calculateEMA, calculateRSI, calculateMACD, calculateBollingerBands, calculateATR, calculateVolumeAvg } from "../../utils/indicators"; import alpacaService from "../../lib/alpacaClient"; async function fetchBarsOnce(symbol: string): Promise<{ prices: number[]; volumes: number[]; highs: number[]; lows: number[] }> { const bars = await alpacaService.fetchBars(symbol, "1Day", { limit: 60 }); const prices: number[] = []; const volumes: number[] = []; const highs: number[] = []; const lows: number[] = []; for (const b of bars) { const c = b.ClosePrice ?? b.c ?? 0; const v = b.Volume ?? b.v ?? 0; const h = b.HighPrice ?? b.h ?? 0; const l = b.LowPrice ?? b.l ?? 0; if (typeof c === "number" && c > 0) prices.push(c); if (typeof v === "number" && v > 0) volumes.push(v); if (typeof h === "number" && h > 0) highs.push(h); if (typeof l === "number" && l > 0) lows.push(l); } return { prices, volumes, highs, lows }; } export async function loader({ request }: { request: Request }) { const url = new URL(request.url); const symbol = url.searchParams.get("symbol"); if (!symbol) { return Response.json({ error: "Symbol is required" }, { status: 400 }); } try { const { prices, volumes, highs, lows } = await fetchBarsOnce(symbol.toUpperCase()); if (prices.length < 26) { return Response.json({ error: "Insufficient price data" }, { status: 404 }); } const sma20 = calculateSMA(prices, 20); const sma50 = prices.length >= 50 ? calculateSMA(prices, 50) : 0; const ema12 = calculateEMA(prices, 12); const ema26 = calculateEMA(prices, 26); const rsi14 = calculateRSI(prices, 14); const macd = calculateMACD(prices); const bb = calculateBollingerBands(prices, 20); const atr = highs.length > 0 && lows.length > 0 ? calculateATR(highs, lows, prices, 14) : 0; const avgVol = volumes.length > 0 ? calculateVolumeAvg(volumes, 20) : 0; const data: IndicatorData = { symbol: symbol.toUpperCase(), indicators: { sma: sma20, sma50, ema12, ema26, rsi: rsi14, macd: macd.histogram, bbUpper: bb.upper, bbLower: bb.lower, bbMiddle: bb.middle, atr, avgVolume: avgVol, }, }; return Response.json(data); } catch (error) { console.error("Indicators error:", error); return Response.json({ error: "Failed to fetch indicators" }, { status: 500 }); } }