import { useState, useEffect } from 'react'; import { sentimentAPI } from '../api/client'; import TickerSearch from '../components/TickerSearch'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Cell, PieChart, Pie, RadialBarChart, RadialBar } from 'recharts'; import { TrendingUp, TrendingDown, Minus, Search, Activity, BarChart3, Newspaper, ArrowUpRight, ArrowDownRight, Eye } from 'lucide-react'; const MOOD_COLORS: Record = { bullish: '#0a8f5c', bearish: '#c23030', neutral: '#b8860b' }; export default function Sentiment() { const [tab, setTab] = useState<'market' | 'search'>('market'); const [marketMood, setMarketMood] = useState(null); const [loading, setLoading] = useState(true); const [searchTicker, setSearchTicker] = useState(''); const [tickerResult, setTickerResult] = useState(null); const [searchLoading, setSearchLoading] = useState(false); useEffect(() => { loadMarketMood(); }, []); const loadMarketMood = async () => { setLoading(true); try { const res = await sentimentAPI.marketMood(); setMarketMood(res.data); } catch { /* empty */ } setLoading(false); }; const handleSearch = async (e: React.FormEvent) => { e.preventDefault(); if (!searchTicker.trim()) return; setSearchLoading(true); try { const res = await sentimentAPI.ticker(searchTicker.trim().toUpperCase()); setTickerResult(res.data); } catch { /* empty */ } setSearchLoading(false); }; if (loading) { return
Analyzing market sentiment...
; } const moodColor = MOOD_COLORS[(marketMood?.mood as string) || 'neutral']; const MoodIcon = marketMood?.mood === 'bullish' ? TrendingUp : marketMood?.mood === 'bearish' ? TrendingDown : Minus; const score = marketMood?.score || 0; // Gauge data: normalize score from [-1,1] to [0,100] const gaugeValue = ((score + 1) / 2) * 100; const gaugeData = [{ name: 'Sentiment', value: gaugeValue, fill: moodColor }]; return (

Sentiment Analysis

Real-time market sentiment derived from financial news and headlines

{/* Market Mood Header */} {marketMood && (
{/* Mood Summary */}
Market Sentiment

{marketMood.mood}

Score: {score.toFixed(3)} | {marketMood.tickers_analyzed} tickers analyzed
{/* Sentiment Gauge */}
Sentiment Gauge
Bearish Neutral Bullish
{score.toFixed(3)} / 1.000
)} {/* Breakdown Chart */} {marketMood?.breakdown?.length > 0 && (

Ticker Sentiment Breakdown

(Number(v)).toFixed(3)} contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem', boxShadow:'var(--shadow-md)' }} /> {marketMood.breakdown.map((d: any, i: number) => ( 0.15 ? '#0a8f5c' : d.score < -0.15 ? '#c23030' : '#b8860b'} /> ))}
)} {/* Tabs */}
{/* Market Overview */} {tab === 'market' && marketMood?.breakdown && (
{marketMood.breakdown.map((t: any) => (

{t.ticker}

{t.sentiment === 'bullish' ? : t.sentiment === 'bearish' ? : } {t.sentiment}
{(t.score >= 0 ? '+' : '')}{t.score?.toFixed(3)} sentiment score
))}
)} {/* Ticker Search */} {tab === 'search' && (
setSearchTicker(v)} onSelect={t => setSearchTicker(t.symbol)} placeholder="Search stocks, ETFs, futures, crypto..." style={{ flex: 1 }} /> {tickerResult && ( <> {/* Summary Cards */}
Sentiment
{tickerResult.sentiment}
Score
{tickerResult.avg_score?.toFixed(3)}
Headlines
{tickerResult.headline_count}
Distribution
{tickerResult.bullish} / {tickerResult.neutral} / {tickerResult.bearish}
{/* Pie Chart */}

Sentiment Distribution

`${name} ${((percent ?? 0) * 100).toFixed(0)}%`} labelLine={{ stroke: 'var(--chart-axis)', strokeWidth: 1 }} />
{/* Headlines */}

Recent Headlines

{tickerResult.headlines?.map((h: any, i: number) => (
{h.url ? ( {h.title} ) : ( {h.title} )}
{h.source} {h.score}
))}
)}
)}
); }