Feat(api): support fetching bars from paper or live Alpaca (default paper) via alpacaClient.fetchBars\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,31 +1,24 @@
|
||||
import Alpaca from "@alpacahq/alpaca-trade-api";
|
||||
|
||||
const alpaca = new Alpaca({
|
||||
keyId: process.env.ALPACA_API_KEY!,
|
||||
secretKey: process.env.ALPACA_SECRET_KEY!,
|
||||
baseUrl: process.env.ALPACA_BASE_URL || "https://paper-api.alpaca.markets",
|
||||
dataBaseUrl: process.env.ALPACA_DATA_URL || "https://data.alpaca.markets",
|
||||
retryOnError: false,
|
||||
});
|
||||
import { fetchBars, fetchLatestBar } from "../../../lib/alpacaClient";
|
||||
|
||||
export async function loader({ request, params }: { request: Request; params: { ticker: string } }) {
|
||||
const ticker = params.ticker?.toUpperCase();
|
||||
const url = new URL(request.url);
|
||||
const timeframe = url.searchParams.get("timeframe") || "1D";
|
||||
const range = url.searchParams.get("range") || "1M"; // 1D, 1W, 1M, 3M, 1Y, 3Y, ALL
|
||||
const mode = url.searchParams.get('mode') === 'live' ? 'live' : 'paper';
|
||||
|
||||
if (!ticker) {
|
||||
return Response.json({ error: "Ticker is required" }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
// Get latest trade for current price
|
||||
// Get latest bar for current price (uses paper by default unless mode=live)
|
||||
let price = 0;
|
||||
try {
|
||||
const trade = await alpaca.getLatestTrade(ticker);
|
||||
price = (trade as { Price?: number }).Price || 0;
|
||||
const last = await fetchLatestBar(ticker, timeframe, mode as any);
|
||||
price = last ? (last.ClosePrice ?? last.c ?? 0) : 0;
|
||||
} catch (tradeErr) {
|
||||
console.error(`API quote/${ticker}: getLatestTrade failed`, tradeErr);
|
||||
console.error(`API quote/${ticker}: fetchLatestBar failed`, tradeErr);
|
||||
}
|
||||
|
||||
// Calculate start date based on range
|
||||
@@ -50,25 +43,14 @@ export async function loader({ request, params }: { request: Request; params: {
|
||||
startDate.setDate(startDate.getDate() - 30); // Default 30 days for intraday
|
||||
}
|
||||
|
||||
const barsOptions: any = { timeframe, limit: 1000 }; // High limit for time range
|
||||
const barsOptions: any = { limit: 1000 }; // High limit for time range
|
||||
if (!isIntraday && range !== "ALL") {
|
||||
barsOptions.start = startDate.toISOString().split('T')[0];
|
||||
} else if (!isIntraday) {
|
||||
barsOptions.start = startDate.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
const bars = await alpaca.getBarsV2(ticker, barsOptions);
|
||||
|
||||
// Convert async generator to array
|
||||
// Alpaca v2 API returns AlpacaBar with capitalized property names
|
||||
const barsArray = [];
|
||||
try {
|
||||
for await (const bar of bars) {
|
||||
barsArray.push(bar);
|
||||
}
|
||||
} catch (genErr) {
|
||||
console.error(`API quote/${ticker}: error iterating bars`, genErr);
|
||||
}
|
||||
const barsArray = await fetchBars(ticker, timeframe, barsOptions, mode as any);
|
||||
|
||||
// Transform to chart format
|
||||
const transformedBars = barsArray.map((bar: any) => {
|
||||
|
||||
Reference in New Issue
Block a user