Skip to content

Latest commit

 

History

History
111 lines (101 loc) · 8.67 KB

File metadata and controls

111 lines (101 loc) · 8.67 KB

SentimentPulse — Architecture

Data Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│  External Sources                                                           │
│  ┌──────────────┐  ┌───────────────────┐  ┌──────────┐  ┌──────────────┐  │
│  │ SEC EDGAR    │  │ Yahoo Finance RSS  │  │ Reuters  │  │ GNews API    │  │
│  │ 8-K / 6-K   │  │ (50 tickers)       │  │ RSS      │  │ (optional)   │  │
│  └──────┬───────┘  └────────┬──────────┘  └────┬─────┘  └──────┬───────┘  │
└─────────┼───────────────────┼──────────────────┼───────────────┼───────────┘
          └─────────┬─────────┘                  └───────┬───────┘
                    ▼                                     ▼
          ┌──────────────────────────────────────────────────────┐
          │  sentiment-ingestor  (Python · APScheduler · 60s)    │
          │  Redis dedup (48h TTL) → drop seen URLs              │
          └──────────────────────────┬───────────────────────────┘
                                     │  Kafka PRODUCE
                                     ▼
                          ┌──────────────────────┐
                          │  raw.headlines        │
                          │  4 partitions         │
                          │  key = source         │
                          │  retention = 24h      │
                          └──────────┬────────────┘
                                     │  Kafka CONSUME (batch 64)
                                     ▼
          ┌──────────────────────────────────────────────────────┐
          │  sentiment-classifier  (Python · FinBERT · MPS/GPU)  │
          │  ┌─────────────┐   ┌──────────────────────────────┐  │
          │  │ Validation  │   │ FinBERT batch inference       │  │
          │  │ empty / non │   │ sentimentScore = P(pos)-P(neg)│  │
          │  │ -English    │   │ p50 latency: ~15 ms/headline  │  │
          │  └──────┬──────┘   └─────────────┬────────────────┘  │
          │         │ invalid                 │ valid             │
          │         ▼                         ▼                   │
          │  sentiment.dlq          spaCy EntityRuler NER         │
          │  (2 partitions)         ticker / company / sector     │
          └──────────────────────────────────┬────────────────────┘
                                             │  Kafka PRODUCE
                                             ▼
                          ┌──────────────────────────┐
                          │  enriched.sentiment       │
                          │  12 partitions            │
                          │  key = ticker             │
                          │  retention = 7 days       │
                          └──────────┬────────────────┘
                                     │  Kafka CONSUME
                                     ▼
          ┌──────────────────────────────────────────────────────┐
          │  sentiment-persistence  (Python · psycopg2 · Redis)  │
          │                                                       │
          │  TimescaleDB                Redis                     │
          │  ┌───────────────────┐     ┌─────────────────────┐   │
          │  │ sentiment_events  │     │ HSET sentiment:{tk}  │   │
          │  │ hypertable        │     │ latest snapshot      │   │
          │  │ 4 ticker shards   │     │                      │   │
          │  │ compress > 7d     │     │ PUBLISH sentiment.   │   │
          │  │                   │     │ updates.{ticker}     │   │
          │  │ sentiment_hourly  │     │                      │   │
          │  │ (cont. aggregate) │     │ drift z-score        │   │
          │  └───────────────────┘     │ LIST [720 pts]       │   │
          └──────────────────────────┬─┴─────────────────────┴───┘
                                     │
               ┌─────────────────────┴──────────────────────┐
               │                                            │
               ▼                                            ▼
   ┌───────────────────────────┐              ┌─────────────────────────┐
   │  sentiment-api  (FastAPI) │              │  Redis Pub/Sub          │
   │  asyncpg pool             │              │  sentiment.updates.*    │
   │  GET /v1/sentiment/       │              └───────────┬─────────────┘
   │    {ticker}/latest        │                          │ WS push
   │    {ticker}/history       │                          ▼
   │    sector/{sector}        │         ┌────────────────────────────┐
   │    drift-alerts           │         │  sentiment-dashboard       │
   │    health                 │         │  React + Vite + Tailwind   │
   │  WS /stream/{ticker} ─────┼─────────│  Recharts · ReactQuery     │
   └───────────────────────────┘         │  localhost:3001            │
                                         └────────────────────────────┘

Kafka Topics

Topic Partitions Key Retention Purpose
raw.headlines 4 source 24 h Raw ingestor output
enriched.sentiment 12 ticker 7 days FinBERT-classified, NER-enriched events
sentiment.dlq 2 30 days Empty / non-English / malformed events

Partitioning enriched.sentiment by ticker ensures all AAPL events land on the same partition — preserving ordering for drift detection.

Service Summary

Service Port Stack Scales
sentiment-ingestor Python · APScheduler · kafka-python Horizontal (multiple sources)
sentiment-classifier 8000 (Prometheus) Python · HuggingFace · spaCy Vertical (GPU)
sentiment-persistence Python · psycopg2 · redis-py Horizontal (more partitions)
sentiment-api 8081 FastAPI · asyncpg · redis.asyncio Horizontal (stateless)
sentiment-dashboard 3001 React · Vite · nginx CDN

Model

Fine-tuned from ProsusAI/finbert on:

  • FinancialPhraseBank (sentences_allagree) — 2,264 sentences
  • FiQA-SA (pauri32/fiqa-2018) — 1,173 sentence-level annotations

Training: 5 epochs, lr=2e-5, batch=16, weighted cross-entropy (inverse class frequency). Hardware: Apple MPS (~4.5 min).