- PR1: Project scaffold, DB models, price loader - PR2: Congressional trade ingestion (House Stock Watcher) - PR3: Security enrichment + deployment infrastructure - 37 passing tests, 87%+ coverage - Docker + Proxmox deployment ready - Complete documentation - Works 100% offline with fixtures
227 lines
6.2 KiB
Markdown
227 lines
6.2 KiB
Markdown
# PR3 Summary: Security Enrichment + Deployment
|
|
|
|
**Status**: ✅ Complete
|
|
**Date**: 2025-12-14
|
|
|
|
## What was built
|
|
|
|
### 1. Security Enrichment (`src/pote/ingestion/security_enricher.py`)
|
|
- `SecurityEnricher` class for enriching securities with yfinance data
|
|
- Fetches: company names, sectors, industries, exchanges
|
|
- Detects asset type: stock, ETF, mutual fund, index
|
|
- Methods:
|
|
- `enrich_security(security, force)`: Enrich single security
|
|
- `enrich_all_securities(limit, force)`: Batch enrichment
|
|
- `enrich_by_ticker(ticker)`: Enrich specific ticker
|
|
- Smart skipping: only enriches unenriched securities (unless `force=True`)
|
|
|
|
### 2. Enrichment Script (`scripts/enrich_securities.py`)
|
|
- CLI tool for enriching securities
|
|
- Usage:
|
|
```bash
|
|
# Enrich all unenriched securities
|
|
python scripts/enrich_securities.py
|
|
|
|
# Enrich specific ticker
|
|
python scripts/enrich_securities.py --ticker AAPL
|
|
|
|
# Limit batch size
|
|
python scripts/enrich_securities.py --limit 10
|
|
|
|
# Force re-enrichment
|
|
python scripts/enrich_securities.py --force
|
|
```
|
|
|
|
### 3. Tests (9 new tests, all passing ✅)
|
|
**`tests/test_security_enricher.py`**:
|
|
- Successful enrichment with complete data
|
|
- ETF detection and classification
|
|
- Skip already enriched securities
|
|
- Force refresh functionality
|
|
- Handle missing/invalid data gracefully
|
|
- Batch enrichment
|
|
- Enrichment with limit
|
|
- Enrich by specific ticker
|
|
- Handle ticker not found
|
|
|
|
### 4. Deployment Infrastructure
|
|
- **`Dockerfile`**: Production-ready container image
|
|
- **`docker-compose.yml`**: Full stack (app + PostgreSQL)
|
|
- **`.dockerignore`**: Optimize image size
|
|
- **`docs/07_deployment.md`**: Comprehensive deployment guide
|
|
- Local development (SQLite)
|
|
- Single server (PostgreSQL + cron)
|
|
- Docker deployment
|
|
- Cloud deployment (AWS, Fly.io, Railway)
|
|
- Cost estimates
|
|
- Production checklist
|
|
|
|
## What works now
|
|
|
|
### Enrich Securities from Fixtures
|
|
```bash
|
|
# Our existing fixtures have these tickers: NVDA, MSFT, AAPL, TSLA, GOOGL
|
|
# They're created as "unenriched" (name == ticker)
|
|
|
|
python scripts/enrich_securities.py
|
|
|
|
# Output:
|
|
# Enriching 5 securities
|
|
# Enriched NVDA: NVIDIA Corporation (Technology)
|
|
# Enriched MSFT: Microsoft Corporation (Technology)
|
|
# Enriched AAPL: Apple Inc. (Technology)
|
|
# Enriched TSLA: Tesla, Inc. (Consumer Cyclical)
|
|
# Enriched GOOGL: Alphabet Inc. (Communication Services)
|
|
# ✓ Successfully enriched: 5
|
|
```
|
|
|
|
### Query Enriched Data
|
|
```python
|
|
from pote.db import SessionLocal
|
|
from pote.db.models import Security
|
|
from sqlalchemy import select
|
|
|
|
with SessionLocal() as session:
|
|
stmt = select(Security).where(Security.sector.isnot(None))
|
|
enriched = session.scalars(stmt).all()
|
|
|
|
for sec in enriched:
|
|
print(f"{sec.ticker}: {sec.name} ({sec.sector})")
|
|
```
|
|
|
|
### Docker Deployment
|
|
```bash
|
|
# Quick start
|
|
docker-compose up -d
|
|
|
|
# Run migrations
|
|
docker-compose exec pote alembic upgrade head
|
|
|
|
# Ingest trades from fixtures (offline)
|
|
docker-compose exec pote python scripts/ingest_from_fixtures.py
|
|
|
|
# Enrich securities (needs network in container)
|
|
docker-compose exec pote python scripts/enrich_securities.py
|
|
```
|
|
|
|
## Data Model Updates
|
|
|
|
No schema changes! The `securities` table already had all necessary fields:
|
|
- `name`: Now populated with full company name
|
|
- `sector`: Technology, Healthcare, Finance, etc.
|
|
- `industry`: Specific industry within sector
|
|
- `exchange`: NASDAQ, NYSE, etc.
|
|
- `asset_type`: stock, etf, mutual_fund, index
|
|
|
|
## Key Design Decisions
|
|
|
|
1. **Smart Skipping**: Only enrich securities where `name == ticker` (unenriched)
|
|
2. **Force Option**: Can re-enrich with `--force` flag
|
|
3. **Graceful Degradation**: Skip/log if yfinance data unavailable
|
|
4. **Batch Control**: `--limit` for rate limiting or testing
|
|
5. **Asset Type Detection**: Automatically classify ETFs, mutual funds, indexes
|
|
|
|
## Performance
|
|
|
|
- Enrich single security: ~1 second (yfinance API call)
|
|
- Batch enrichment: ~1-2 seconds per security
|
|
- Recommendation: Run weekly or when new tickers appear
|
|
- yfinance is free but rate-limited (be reasonable!)
|
|
|
|
## Integration with Existing System
|
|
|
|
### After Trade Ingestion
|
|
```python
|
|
# In production cron job:
|
|
# 1. Fetch trades
|
|
python scripts/fetch_congressional_trades.py --days 7
|
|
|
|
# 2. Enrich any new securities
|
|
python scripts/enrich_securities.py
|
|
|
|
# 3. Fetch prices for all securities
|
|
python scripts/update_all_prices.py # To be built in PR4
|
|
```
|
|
|
|
### Cron Schedule (Production)
|
|
```bash
|
|
# Daily at 6 AM: Fetch trades
|
|
0 6 * * * cd /path/to/pote && venv/bin/python scripts/fetch_congressional_trades.py --days 7
|
|
|
|
# Daily at 6:15 AM: Enrich new securities
|
|
15 6 * * * cd /path/to/pote && venv/bin/python scripts/enrich_securities.py
|
|
|
|
# Daily at 6:30 AM: Update prices
|
|
30 6 * * * cd /path/to/pote && venv/bin/python scripts/update_all_prices.py
|
|
```
|
|
|
|
## Deployment Options
|
|
|
|
| Option | Complexity | Cost/month | Best For |
|
|
|--------|-----------|------------|----------|
|
|
| **Local** | ⭐ | $0 | Development |
|
|
| **VPS + Docker** | ⭐⭐ | $10-20 | Personal deployment |
|
|
| **Railway/Fly.io** | ⭐ | $5-15 | Easy cloud |
|
|
| **AWS** | ⭐⭐⭐ | $20-50 | Scalable production |
|
|
|
|
See [`docs/07_deployment.md`](07_deployment.md) for detailed guides.
|
|
|
|
## Next Steps (PR4+)
|
|
|
|
Per `docs/00_mvp.md`:
|
|
- **PR4**: Analytics - abnormal returns, benchmarks
|
|
- **PR5**: Clustering & signals
|
|
- **PR6**: FastAPI + dashboard
|
|
|
|
## How to Use
|
|
|
|
### 1. Enrich All Securities
|
|
```bash
|
|
python scripts/enrich_securities.py
|
|
```
|
|
|
|
### 2. Enrich Specific Ticker
|
|
```bash
|
|
python scripts/enrich_securities.py --ticker NVDA
|
|
```
|
|
|
|
### 3. Re-enrich Everything
|
|
```bash
|
|
python scripts/enrich_securities.py --force
|
|
```
|
|
|
|
### 4. Programmatic Usage
|
|
```python
|
|
from pote.db import SessionLocal
|
|
from pote.ingestion.security_enricher import SecurityEnricher
|
|
|
|
with SessionLocal() as session:
|
|
enricher = SecurityEnricher(session)
|
|
|
|
# Enrich all unenriched
|
|
counts = enricher.enrich_all_securities()
|
|
print(f"Enriched {counts['enriched']} securities")
|
|
|
|
# Enrich specific ticker
|
|
enricher.enrich_by_ticker("AAPL")
|
|
```
|
|
|
|
## Test Coverage
|
|
|
|
```bash
|
|
pytest tests/ -v
|
|
|
|
# 37 tests passing
|
|
# Coverage: 87%+
|
|
|
|
# New tests:
|
|
# - test_security_enricher.py (9 tests)
|
|
```
|
|
|
|
---
|
|
|
|
**Cost**: Still $0 (yfinance is free!)
|
|
**Dependencies**: yfinance (already in `pyproject.toml`)
|
|
**Research-only reminder**: This tool is for transparency and descriptive analytics. Not investment advice.
|
|
|