import Alpaca from "@alpacahq/alpaca-trade-api"; function makeAlpaca(mode: 'paper' | 'live' = 'paper') { const isLive = mode === 'live'; const keyId = isLive ? (process.env.ALPACA_API_KEY_LIVE || process.env.ALPACA_API_KEY) : process.env.ALPACA_API_KEY; const secretKey = isLive ? (process.env.ALPACA_SECRET_KEY_LIVE || process.env.ALPACA_SECRET_KEY) : process.env.ALPACA_SECRET_KEY; const baseUrl = isLive ? (process.env.ALPACA_LIVE_BASE_URL || "https://api.alpaca.markets") : (process.env.ALPACA_BASE_URL || "https://paper-api.alpaca.markets"); const dataBaseUrl = isLive ? (process.env.ALPACA_LIVE_DATA_URL || "https://data.alpaca.markets") : (process.env.ALPACA_DATA_URL || "https://data.alpaca.markets"); return new Alpaca({ keyId, secretKey, baseUrl, dataBaseUrl, retryOnError: false, }); } export async function fetchAccount(mode: 'paper' | 'live' = 'paper') { try { const client = makeAlpaca(mode); const account = await client.getAccount(); return { cash: parseFloat(account.cash), buying_power: parseFloat(account.buying_power), portfolio_value: parseFloat(account.portfolio_value), }; } catch (err: any) { console.error("alpacaClient: fetchAccount failed:", err); throw new Error(err?.message || String(err)); } } export async function fetchRecentCloses(ticker: string, days = 30, mode: 'paper' | 'live' = 'paper') { try { const client = makeAlpaca(mode); const startDate = new Date(); startDate.setDate(startDate.getDate() - days); const barsIter = await client.getBarsV2(ticker, { timeframe: "1D", start: startDate.toISOString().split("T")[0], limit: 1000 }); const barsArray: any[] = []; for await (const b of barsIter) barsArray.push(b); const closes = barsArray.map((bar: any) => (typeof bar.ClosePrice === 'number' ? bar.ClosePrice : (typeof bar.c === 'number' ? bar.c : 0))).filter((c: number) => c > 0); if (closes.length) return closes; // fallback to latest trade try { const trade: any = await client.getLatestTrade(ticker); const price = trade?.Price || trade?.price || 0; if (price) return [price]; } catch (tErr) { console.warn("alpacaClient: getLatestTrade fallback failed:", tErr); } throw new Error("No recent price data available from Alpaca"); } catch (err: any) { console.error("alpacaClient: fetchRecentCloses failed:", err); throw new Error(err?.message || String(err)); } } export async function fetchLatestBar(ticker: string, timeframe = '1Min', mode: 'paper' | 'live' = 'paper') { try { const client = makeAlpaca(mode); const barsIter = await client.getBarsV2(ticker, { timeframe, limit: 1 }); const barsArr: any[] = []; for await (const b of barsIter) barsArr.push(b); const last = barsArr[barsArr.length - 1]; return last || null; } catch (err: any) { console.error('alpacaClient: fetchLatestBar failed:', err); throw new Error(err?.message || String(err)); } } export async function fetchBars(ticker: string, timeframe = '1D', options: any = {}, mode: 'paper' | 'live' = 'paper') { try { const client = makeAlpaca(mode); const barsIter = await client.getBarsV2(ticker, { timeframe, ...options }); const barsArr: any[] = []; for await (const b of barsIter) barsArr.push(b); return barsArr; } catch (err: any) { console.error('alpacaClient: fetchBars failed:', err); throw new Error(err?.message || String(err)); } }