feat: add stock indicators route and Alpaca account info
- New /stocks route with StockViewer component - New /api/indicators endpoint with SMA, EMA, RSI, MACD - New /api/alpaca/account endpoint - AlpacaAccountInfo component on home page - Indicator calculation utilities - Tests for utilities and components - Vite proxy config for /api
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import type { AlpacaAccount } from "../../../types";
|
||||
|
||||
// Mock Alpaca account data – replace with actual API call
|
||||
async function fetchAlpacaAccount(): Promise<AlpacaAccount> {
|
||||
return {
|
||||
cash: 12345.67,
|
||||
buying_power: 8000.0,
|
||||
portfolio_value: 25000.0,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loader() {
|
||||
try {
|
||||
const account = await fetchAlpacaAccount();
|
||||
return Response.json(account);
|
||||
} catch {
|
||||
return Response.json(
|
||||
{ error: "Failed to fetch account info" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { type IndicatorData } from "../../types";
|
||||
import {
|
||||
calculateSMA,
|
||||
calculateEMA,
|
||||
calculateRSI,
|
||||
calculateMACD,
|
||||
} from "../../utils/indicators";
|
||||
|
||||
// Replace with actual Alpaca API call
|
||||
async function fetchHistoricPrices(symbol: string): Promise<number[]> {
|
||||
return [
|
||||
150.0, 152.3, 151.8, 153.5, 155.0, 154.2, 156.7, 158.1, 157.5, 159.0,
|
||||
160.2, 158.9, 161.5, 163.0, 162.5, 164.8, 166.3, 165.0, 167.5, 169.0,
|
||||
168.2, 170.5, 172.0, 171.5, 173.2,
|
||||
];
|
||||
}
|
||||
|
||||
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 = await fetchHistoricPrices(symbol.toUpperCase());
|
||||
if (prices.length === 0) {
|
||||
return Response.json(
|
||||
{ error: "No price data found" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
const sma = calculateSMA(prices);
|
||||
const ema = calculateEMA(prices);
|
||||
const rsi = calculateRSI(prices);
|
||||
const macd = calculateMACD(prices);
|
||||
|
||||
const data: IndicatorData = {
|
||||
symbol: symbol.toUpperCase(),
|
||||
indicators: { sma, ema, rsi, macd },
|
||||
};
|
||||
return Response.json(data);
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{ error: "Failed to fetch indicators" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -1,5 +1,6 @@
|
||||
import type { Route } from "./+types/home";
|
||||
import { Welcome } from "../welcome/welcome";
|
||||
import AlpacaAccountInfo from "../components/AlpacaAccountInfo";
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
return [
|
||||
@@ -9,5 +10,12 @@ export function meta({}: Route.MetaArgs) {
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return <Welcome />;
|
||||
return (
|
||||
<>
|
||||
<Welcome />
|
||||
<div className="container mx-auto p-4 max-w-2xl">
|
||||
<AlpacaAccountInfo />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import StockViewer from "../components/StockViewer";
|
||||
|
||||
export default function Stocks() {
|
||||
return (
|
||||
<main className="flex items-start justify-center pt-8 pb-4">
|
||||
<StockViewer />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user