fix: add bars data for TradingView chart from Alpaca
- Modify quote.ts to fetch historical bars for chart data - Update analyze.ticker.tsx to pass bars data to TradingViewChart - Chart now displays candlestick data from Alpaca API
This commit is contained in:
@@ -8,6 +8,7 @@ interface LoaderData {
|
|||||||
ticker: string;
|
ticker: string;
|
||||||
position: number | null;
|
position: number | null;
|
||||||
orders: any[];
|
orders: any[];
|
||||||
|
bars: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loader({ params, request }: { params: { ticker: string }; request: Request }) {
|
export async function loader({ params, request }: { params: { ticker: string }; request: Request }) {
|
||||||
@@ -27,11 +28,25 @@ export async function loader({ params, request }: { params: { ticker: string };
|
|||||||
const ordersData = ordRes.ok ? await ordRes.json() : { orders: [] };
|
const ordersData = ordRes.ok ? await ordRes.json() : { orders: [] };
|
||||||
const orders = ordersData.orders?.filter((o: any) => o.symbol === ticker) || [];
|
const orders = ordersData.orders?.filter((o: any) => o.symbol === ticker) || [];
|
||||||
|
|
||||||
|
// Fetch bars for chart
|
||||||
|
const barsRes = await fetch(`${baseUrl}/api/alpaca/quote/${ticker}`);
|
||||||
|
const barsData = barsRes.ok ? await barsRes.json() : null;
|
||||||
|
const bars = barsData?.bars || [];
|
||||||
|
|
||||||
return Response.json({ ticker, position, orders });
|
return Response.json({ ticker, position, orders });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StockDetail() {
|
export default function StockDetail() {
|
||||||
const { ticker, position, orders } = useLoaderData() as LoaderData;
|
const { ticker, position, orders, bars } = useLoaderData() as LoaderData;
|
||||||
|
|
||||||
|
// Convert Alpaca bars to TradingView format
|
||||||
|
const chartData = bars?.map((bar: any) => ({
|
||||||
|
time: bar.t,
|
||||||
|
open: bar.o,
|
||||||
|
high: bar.h,
|
||||||
|
low: bar.l,
|
||||||
|
close: bar.c,
|
||||||
|
})) || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50">
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50">
|
||||||
@@ -39,7 +54,7 @@ export default function StockDetail() {
|
|||||||
<div className="mx-auto max-w-7xl px-6 py-8">
|
<div className="mx-auto max-w-7xl px-6 py-8">
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-6">{ticker} Detail</h1>
|
<h1 className="text-3xl font-bold text-gray-900 mb-6">{ticker} Detail</h1>
|
||||||
|
|
||||||
<TradingViewChart ticker={ticker} />
|
<TradingViewChart ticker={ticker} data={chartData} />
|
||||||
|
|
||||||
<div className="mt-6 bg-white rounded-xl shadow-lg p-6 border border-gray-200">
|
<div className="mt-6 bg-white rounded-xl shadow-lg p-6 border border-gray-200">
|
||||||
<h2 className="text-xl font-bold text-gray-900 mb-4">Position</h2>
|
<h2 className="text-xl font-bold text-gray-900 mb-4">Position</h2>
|
||||||
|
|||||||
@@ -14,17 +14,36 @@ export async function loader({ params }: { params: { ticker: string } }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use latest trade instead of quote for real transaction price
|
// Get latest trade for current price
|
||||||
const trade = await alpaca.getLatestTrade(ticker);
|
const trade = await alpaca.getLatestTrade(ticker);
|
||||||
// trade has: Price, Size, Exchange, Timestamp, etc.
|
|
||||||
const price = (trade as { Price?: number }).Price || 0;
|
const price = (trade as { Price?: number }).Price || 0;
|
||||||
|
|
||||||
|
// Get historical bars for chart (last 30 days, daily)
|
||||||
|
const bars = await alpaca.getBarsV2(ticker, {
|
||||||
|
timeframe: "1Day",
|
||||||
|
limit: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert async generator to array
|
||||||
|
const barsArray = [];
|
||||||
|
for await (const bar of bars) {
|
||||||
|
barsArray.push({
|
||||||
|
t: (bar as any).Timestamp || (bar as any).t,
|
||||||
|
o: (bar as any).Open || (bar as any).o,
|
||||||
|
h: (bar as any).High || (bar as any).h,
|
||||||
|
l: (bar as any).Low || (bar as any).l,
|
||||||
|
c: (bar as any).Close || (bar as any).c,
|
||||||
|
v: (bar as any).Volume || (bar as any).v,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return Response.json({
|
return Response.json({
|
||||||
ticker,
|
ticker,
|
||||||
price,
|
price,
|
||||||
timestamp: (trade as { Timestamp?: string }).Timestamp,
|
bars: barsArray,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Alpaca trade error:", error);
|
console.error("Alpaca data error:", error);
|
||||||
return Response.json({ error: "Failed to fetch trade" }, { status: 500 });
|
return Response.json({ error: "Failed to fetch data" }, { status: 500 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user