""" Overlay computed feature models - pre-computed scores served by the API """ from sqlalchemy import Column, String, Float, Boolean, Index, UniqueConstraint, JSON, Integer, Text from sqlalchemy.dialects.postgresql import UUID, TIMESTAMP from datetime import datetime, timezone import uuid from app.core.database import Base class OverlayFeatureRecord(Base): __tablename__ = "overlay_feature_records" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) symbol = Column(String(10), nullable=False) as_of_ts = Column(TIMESTAMP(timezone=True), nullable=False) feature_version = Column(String(20), default="v1") # z-scores per source headline_burst_z = Column(Float, nullable=True) youtube_influence_z = Column(Float, nullable=True) wiki_attention_z = Column(Float, nullable=True) theme_heat_z = Column(Float, nullable=True) crowding_stress_z = Column(Float, nullable=True) # headline detail headline_count_6h = Column(Integer, default=0) headline_count_24h = Column(Integer, default=0) publisher_breadth_24h = Column(Integer, default=0) # youtube detail youtube_mentions_24h = Column(Integer, default=0) youtube_weighted_views_24h = Column(Float, default=0.0) # wiki detail wiki_views_1d = Column(Integer, nullable=True) wiki_views_7d_avg = Column(Float, nullable=True) # finra crowding detail short_volume_ratio = Column(Float, nullable=True) short_volume_spike_zscore = Column(Float, nullable=True) # final scores overlay_score = Column(Float, nullable=False, default=0.0) overlay_confidence = Column(Float, nullable=False, default=0.0) overlay_band = Column(String(20), nullable=True) # silent/tepid/supportive/loud/frenzied source_presence_mask = Column(JSON, default=dict) # hints hold_extension_hint = Column(String(10), nullable=True) # extend/neutral/trim add_on_eligibility = Column(Boolean, nullable=True) created_at = Column( TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc) ) __table_args__ = ( UniqueConstraint("symbol", "as_of_ts", "feature_version", name="uq_overlay_feature_record"), Index("idx_overlay_feature_symbol", "symbol"), Index("idx_overlay_feature_as_of_ts", "as_of_ts"), Index("idx_overlay_feature_score", "overlay_score"), ) class OverlayJobLog(Base): __tablename__ = "overlay_job_log" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) job_type = Column(String(50), nullable=False) # rss_collect / wiki_collect / yt_collect / feature_build / score status = Column(String(20), nullable=False) # running / completed / failed / partial started_at = Column(TIMESTAMP(timezone=True), nullable=False) completed_at = Column(TIMESTAMP(timezone=True), nullable=True) records_processed = Column(Integer, default=0) error_message = Column(Text, nullable=True) created_at = Column( TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc) ) __table_args__ = ( Index("idx_overlay_job_log_type", "job_type"), Index("idx_overlay_job_log_started_at", "started_at"), )