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.

85 lines
3.2 KiB
Python

"""
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"),
)