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:
2026-05-12 21:07:18 +02:00
parent aaafe8fa3f
commit 8429db504a
18 changed files with 2811 additions and 6 deletions
+54
View File
@@ -0,0 +1,54 @@
import { describe, it, expect } from "vitest";
import {
calculateSMA,
calculateEMA,
calculateRSI,
calculateMACD,
} from "../../utils/indicators";
describe("calculateSMA", () => {
it("returns 0 when prices length < period", () => {
expect(calculateSMA([1, 2], 5)).toBe(0);
});
it("calculates simple moving average correctly", () => {
expect(calculateSMA([1, 2, 3, 4, 5], 5)).toBe(3);
});
});
describe("calculateEMA", () => {
it("returns 0 when prices length < period", () => {
expect(calculateEMA([1, 2], 5)).toBe(0);
});
it("calculates exponential moving average", () => {
const prices = [10, 11, 12, 13, 14, 15];
const result = calculateEMA(prices, 3);
expect(result).toBeCloseTo(14.125, 2);
});
});
describe("calculateRSI", () => {
it("returns 0 when prices length < period + 1", () => {
expect(calculateRSI([1, 2, 3], 5)).toBe(0);
});
it("calculates relative strength index", () => {
const prices = [100, 102, 101, 105, 107, 110, 108, 115, 120, 119, 121];
const result = calculateRSI(prices, 5);
expect(result).toBeGreaterThan(50);
expect(result).toBeLessThan(100);
});
});
describe("calculateMACD", () => {
it("returns 0 when prices length < slowPeriod", () => {
expect(calculateMACD([1, 2, 3], 12, 26, 9)).toBe(0);
});
it("calculates MACD line", () => {
const prices = Array.from({ length: 30 }, (_, i) => 100 + i * 0.5);
const result = calculateMACD(prices);
expect(result).toBeCloseTo(-0.96, 2);
});
});