You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
4.1 KiB
Python

"""
Attention subsystem models — event-centric attention data for backtest-friendly queries.
Completely separate from the overlay subsystem (which is real-time monitoring).
This subsystem is designed for fithia2 backtester to query attention data
relative to events (earnings, etc.).
"""
from sqlalchemy import Column, String, Float, Boolean, Integer, Text, Date, Index, UniqueConstraint, JSON
from sqlalchemy.dialects.postgresql import UUID, TIMESTAMP
from datetime import datetime, timezone
import uuid
from app.core.database import Base
class CompanyEntityMap(Base):
"""Ticker → canonical company entity mapping with Wikipedia and GDELT resolution."""
__tablename__ = "company_entity_map"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
ticker = Column(String(10), unique=True, nullable=False, index=True)
canonical_name = Column(String(500), nullable=False)
wiki_title = Column(String(500), nullable=True)
gdelt_query = Column(String(1000), nullable=True)
aliases_json = Column(JSON, default=list)
resolver_confidence = Column(Float, default=0.0)
is_manual_override = Column(Boolean, default=False)
created_at = Column(
TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)
)
updated_at = Column(
TIMESTAMP(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
__table_args__ = (
Index("idx_company_entity_map_ticker", "ticker"),
)
class WikiPageviewsDaily(Base):
"""Daily Wikipedia pageview counts per article title."""
__tablename__ = "wiki_pageviews_daily"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
wiki_title = Column(String(500), nullable=False)
date = Column(Date, nullable=False)
views = Column(Integer, nullable=False)
created_at = Column(
TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)
)
__table_args__ = (
UniqueConstraint("wiki_title", "date", name="uq_wiki_pageviews_daily"),
Index("idx_wiki_pageviews_daily_title_date", "wiki_title", "date"),
)
class GdeltArticleRaw(Base):
"""Raw GDELT article records matched to a ticker."""
__tablename__ = "gdelt_article_raw"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
url = Column(String(2000), unique=True, nullable=False)
title = Column(Text, nullable=True)
domain = Column(String(255), nullable=True)
published_at = Column(TIMESTAMP(timezone=True), nullable=True)
sourcecountry = Column(String(10), nullable=True)
matched_ticker = Column(String(10), nullable=False, index=True)
match_method = Column(String(50), nullable=True)
match_confidence = Column(Float, default=1.0)
created_at = Column(
TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)
)
__table_args__ = (
Index("idx_gdelt_article_raw_ticker", "matched_ticker"),
Index("idx_gdelt_article_raw_published_at", "published_at"),
)
class AttentionFeaturesDaily(Base):
"""Derived daily attention features per ticker — materialized from wiki + gdelt raw data."""
__tablename__ = "attention_features_daily"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
ticker = Column(String(10), nullable=False)
date = Column(Date, nullable=False)
# Wikipedia features
wiki_views = Column(Integer, nullable=True)
wiki_spike_10d = Column(Float, nullable=True)
wiki_zscore_20d = Column(Float, nullable=True)
# GDELT / news features
gdelt_article_count_1d = Column(Integer, default=0)
gdelt_article_count_3d = Column(Integer, default=0)
gdelt_unique_domains_3d = Column(Integer, default=0)
gdelt_us_article_count_3d = Column(Integer, default=0)
created_at = Column(
TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)
)
__table_args__ = (
UniqueConstraint("ticker", "date", name="uq_attention_features_daily"),
Index("idx_attention_features_daily_ticker_date", "ticker", "date"),
Index("idx_attention_features_daily_ticker", "ticker"),
)