import { useState, useEffect } from "react"; import { Link } from "react-router"; import Navbar from "../components/Navbar"; import type { TradingDecision } from "../types/agents"; interface Indicators { rsi: number | null; sma20: number | null; sma50: number | null; ema12: number | null; ema26: number | null; macd: number | null; bbUpper: number | null; bbMiddle: number | null; bbLower: number | null; atr: number | null; avgVolume: number | null; } interface StockRow { id: string; ticker: string; currentPrice: number | null; position: number; indicators: Indicators; analysis: TradingDecision | null; loading: boolean; indicatorsLoading: boolean; } export const meta = () => [ { title: "Portfolio Analysis - AITrader" }, { name: "description", content: "Analyze your stock portfolio with AI trading insights" }, ]; function RsiBadge({ value }: { value: number }) { const color = value > 70 ? "bg-red-100 text-red-700" : value < 30 ? "bg-green-100 text-green-700" : "bg-gray-100 text-gray-700"; const label = value > 70 ? "Overbought" : value < 30 ? "Oversold" : "Neutral"; return {value.toFixed(0)} {label}; } function MacdBadge({ value }: { value: number }) { const color = value > 0 ? "text-green-600" : "text-red-600"; return {value > 0 ? "▲" : "▼"} {value.toFixed(2)}; } function PriceVsSma({ price, sma, label }: { price: number; sma: number; label: string }) { if (!price || !sma) return -; const above = price > sma; const pct = ((price - sma) / sma * 100).toFixed(1); return ( {above ? "▲" : "▼"} {pct}% ); } function SignalSummary({ price, indicators }: { price: number | null; indicators: Indicators }) { if (!price) return No data; const signals: string[] = []; if (indicators.rsi != null) { if (indicators.rsi > 70) signals.push("RSI overbought"); else if (indicators.rsi < 30) signals.push("RSI oversold"); } if (indicators.sma20 != null && indicators.sma50 != null) { if (indicators.sma20 > indicators.sma50) signals.push("SMA bullish cross"); else signals.push("SMA bearish cross"); } if (indicators.macd != null) { if (indicators.macd > 0) signals.push("MACD positive"); else signals.push("MACD negative"); } if (indicators.bbUpper != null && indicators.bbLower != null) { if (price > indicators.bbUpper) signals.push("Above BB upper"); else if (price < indicators.bbLower) signals.push("Below BB lower"); } if (signals.length === 0) return -; const bullish = signals.filter(s => s.includes("oversold") || s.includes("bullish") || s.includes("positive") || s.includes("Below BB")).length; const bearish = signals.filter(s => s.includes("overbought") || s.includes("bearish") || s.includes("negative") || s.includes("Above BB")).length; const net = bullish - bearish; const bias = net > 0 ? "bullish" : net < 0 ? "bearish" : "neutral"; const biasColor = bias === "bullish" ? "text-green-600" : bias === "bearish" ? "text-red-600" : "text-gray-500"; return (
No stocks added. Add a ticker to get started.
) : (| Ticker | Price | Position | Technical Summary | RSI | MACD | SMA 20/50 | AI Analysis | Actions |
|---|---|---|---|---|---|---|---|---|
| {stock.ticker} | {stock.currentPrice ? `$${stock.currentPrice.toFixed(2)}` : "-"} | {stock.position > 0 ? ( {stock.position} shares ) : ( - )} |
{stock.indicatorsLoading ? (
Loading...
) : (
<>
|
{stock.indicators.rsi != null ? (
|
{stock.indicators.macd != null ? (
|
{stock.currentPrice && stock.indicators.sma20 && stock.indicators.sma50 ? (
|
{stock.analysis ? (
{stock.analysis.action.toUpperCase()}
) : stock.loading ? (
Analyzing...
) : "-"}
{(stock.analysis.confidence ?? 0 * 100).toFixed(0)}%
|
|