import { useLoaderData } from "react-router"; import TradingViewChart from "../components/TradingViewChart"; import Navbar from "../components/Navbar"; export const meta = () => [{ title: "Stock Detail - AITrader" }]; interface LoaderData { ticker: string; position: number | null; orders: any[]; bars: any[]; } export async function loader({ params, request }: { params: { ticker: string }; request: Request }) { const ticker = params.ticker?.toUpperCase() || ""; // Build base URL from request for server-side fetches const url = new URL(request.url); const baseUrl = `${url.protocol}//${url.host}`; // Fetch position const posRes = await fetch(`${baseUrl}/api/alpaca/positions`); const positions = posRes.ok ? await posRes.json() : []; const position = positions.find((p: any) => p.ticker === ticker)?.qty ?? null; // Fetch orders const ordRes = await fetch(`${baseUrl}/api/alpaca/orders`); const ordersData = ordRes.ok ? await ordRes.json() : { orders: [] }; 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, bars }); } export default function StockDetail() { const { ticker, position, orders, bars } = useLoaderData() as LoaderData; // Convert Alpaca bars to TradingView format (YYYY-MM-DD for time) const chartData = bars?.map((bar: any) => ({ time: bar.t ? new Date(bar.t).toISOString().split('T')[0] : "", open: bar.o, high: bar.h, low: bar.l, close: bar.c, })).filter((bar: any) => bar.time) || []; return (

{ticker} Detail

Position

{position ? `Quantity: ${position} shares` : "No position held"}

Recent Orders

{orders.length === 0 ? (

No orders found for {ticker}

) : (
{orders.map((order: any, i: number) => ( ))}
Side Qty Status Filled Price Filled At
{order.side?.toUpperCase()} {order.qty} {order.status} {order.filled_avg_price ? `$${parseFloat(order.filled_avg_price).toFixed(2)}` : "-"} {order.filled_at ? new Date(order.filled_at).toLocaleDateString() : "-"}
)}
); }