smaug/db/models.py
claude b119b9abae feat: SQLAlchemy ORM models, filing cache incremental fetch, yfinance price cache
- Replace db/schema.sql + raw sqlite3 with SQLAlchemy ORM (db/models.py)
  - Filing, Signal, PriceCache models with proper indexes
  - db/db.py uses SQLAlchemy sessions throughout; no raw SQL strings
- Add PriceCache table: stores daily close prices per ticker
  - backtest._fetch_prices checks DB first; skips yfinance for completed ranges
  - New data persisted via upsert_prices()
  - get_cached_prices() / upsert_prices() added to db.py
- EDGAR poller incremental fetch: get_latest_filed_date() returns newest
  filed_date in DB; fetch_and_store_new_filings skips entries older than
  that cutoff before even checking accession_exists
- Add get_signals_for_backtest() to db.py; backtest no longer opens its
  own sqlite3 connection
- requirements.txt: add sqlalchemy>=2.0.0

Co-authored-by: dodox <dodox@users.noreply.local>
2026-05-04 17:21:23 +00:00

82 lines
2.2 KiB
Python

from datetime import datetime
from sqlalchemy import (
Boolean,
Column,
DateTime,
Float,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
class Filing(Base):
__tablename__ = "filings"
id = Column(Integer, primary_key=True, autoincrement=True)
accession_number = Column(String, unique=True, nullable=False)
ticker = Column(String)
cik = Column(String)
insider_name = Column(String)
role = Column(String)
transaction_date = Column(String)
filed_date = Column(String)
shares = Column(Float)
price = Column(Float)
total_value = Column(Float)
flag = Column(String)
is_10b51 = Column(Boolean, default=False)
post_tx_shares = Column(Float)
created_at = Column(DateTime, default=datetime.utcnow)
__table_args__ = (
Index("idx_filings_ticker", "ticker"),
Index("idx_filings_transaction_date", "transaction_date"),
Index("idx_filings_filed_date", "filed_date"),
)
class Signal(Base):
__tablename__ = "signals"
id = Column(Integer, primary_key=True, autoincrement=True)
ticker = Column(String)
trigger_date = Column(String)
cluster_size = Column(Integer)
total_cluster_value = Column(Float)
score = Column(Float)
alerted = Column(Boolean, default=False)
executed = Column(Boolean, default=False)
executed_at = Column(DateTime)
closed = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
__table_args__ = (
Index("idx_signals_ticker", "ticker"),
Index("idx_signals_alerted", "alerted"),
Index("idx_signals_executed", "executed"),
)
class PriceCache(Base):
__tablename__ = "price_cache"
id = Column(Integer, primary_key=True, autoincrement=True)
ticker = Column(String, nullable=False)
date = Column(String, nullable=False)
close = Column(Float, nullable=False)
fetched_at = Column(DateTime, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint("ticker", "date", name="uq_price_cache_ticker_date"),
Index("idx_price_cache_ticker_date", "ticker", "date"),
)