"""选股持久化表(M6.3)。 selection_snapshot:一次选股运行的查询与统计快照(复现/历史查询用) selection_result:逐候选行(symbol/rank/score + 可解释字段 JSON), 回答 v2 §8「某日为什么选这只股票」。 """ from __future__ import annotations from datetime import date, datetime from sqlalchemy import BigInteger, Date, DateTime, Integer, Numeric, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.infrastructure.persistence.sqlalchemy.base import Base PK_INT = BigInteger().with_variant(Integer, "sqlite") class SelectionSnapshotModel(Base): __tablename__ = "selection_snapshot" id: Mapped[str] = mapped_column(String(32), primary_key=True) as_of: Mapped[date] = mapped_column(Date, index=True) method: Mapped[str] = mapped_column(String(16)) query_json: Mapped[str] = mapped_column(Text) statistics_json: Mapped[str] = mapped_column(Text) created_at: Mapped[datetime] = mapped_column(DateTime, index=True) class SelectionResultModel(Base): __tablename__ = "selection_result" id: Mapped[int] = mapped_column(PK_INT, primary_key=True, autoincrement=True) selection_id: Mapped[str] = mapped_column(String(32), index=True) symbol: Mapped[str] = mapped_column(String(12)) rank: Mapped[int] = mapped_column(Integer) score: Mapped[float] = mapped_column(Numeric(14, 6)) factor_values_json: Mapped[str | None] = mapped_column(Text, nullable=True) filter_status_json: Mapped[str | None] = mapped_column(Text, nullable=True) reason_json: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime)