"""Platform snapshots and local research are intentionally stored separately.""" from datetime import datetime, timezone from sqlalchemy import JSON, Boolean, DateTime, Float, ForeignKey, Index, Integer, String, Text from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column def now() -> datetime: return datetime.now(timezone.utc) class Base(DeclarativeBase): pass class Admin(Base): __tablename__ = "admins" id: Mapped[int] = mapped_column(primary_key=True, default=1) username: Mapped[str] = mapped_column(String(100), unique=True) password_hash: Mapped[str] = mapped_column(Text) class LoginSession(Base): __tablename__ = "login_sessions" token_hash: Mapped[str] = mapped_column(String(64), primary_key=True) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True) class Account(Base): __tablename__ = "accounts" id: Mapped[int] = mapped_column(primary_key=True, default=1) email: Mapped[str | None] = mapped_column(String(254)) password_encrypted: Mapped[str | None] = mapped_column(Text) wq_user_id: Mapped[str | None] = mapped_column(String(100)) profile: Mapped[dict] = mapped_column(JSON, default=dict) connection_status: Mapped[str] = mapped_column(String(30), default="disconnected") connection_error: Mapped[str | None] = mapped_column(Text) verification_url: Mapped[str | None] = mapped_column(Text) last_synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) display_name: Mapped[str] = mapped_column(String(100), default="研究员") theme: Mapped[str] = mapped_column(String(10), default="light") timezone: Mapped[str] = mapped_column(String(64), default="Asia/Shanghai") page_size: Mapped[int] = mapped_column(Integer, default=25) class Alpha(Base): __tablename__ = "alphas" id: Mapped[str] = mapped_column(String(100), primary_key=True) name: Mapped[str | None] = mapped_column(Text) expression: Mapped[str | None] = mapped_column(Text) selection: Mapped[str | None] = mapped_column(Text) combo: Mapped[str | None] = mapped_column(Text) alpha_type: Mapped[str | None] = mapped_column(String(50), index=True) language: Mapped[str | None] = mapped_column(String(50)) stage: Mapped[str | None] = mapped_column(String(50), index=True) status: Mapped[str | None] = mapped_column(String(80), index=True) hidden: Mapped[bool] = mapped_column(Boolean, default=False) region: Mapped[str | None] = mapped_column(String(50), index=True) universe: Mapped[str | None] = mapped_column(String(100)) settings: Mapped[dict] = mapped_column(JSON, default=dict) is_metrics: Mapped[dict] = mapped_column(JSON, default=dict) os_metrics: Mapped[dict] = mapped_column(JSON, default=dict) checks: Mapped[list] = mapped_column(JSON, default=list) sharpe: Mapped[float | None] = mapped_column(Float, index=True) fitness: Mapped[float | None] = mapped_column(Float, index=True) returns: Mapped[float | None] = mapped_column(Float) turnover: Mapped[float | None] = mapped_column(Float) margin: Mapped[float | None] = mapped_column(Float) drawdown: Mapped[float | None] = mapped_column(Float) date_created: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True) date_submitted: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now) raw: Mapped[dict] = mapped_column(JSON, default=dict) __table_args__ = (Index("ix_alphas_region_status", "region", "status"),) class Research(Base): __tablename__ = "research" alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True) note: Mapped[str] = mapped_column(Text, default="") tags: Mapped[list] = mapped_column(JSON, default=list) favorite: Mapped[bool] = mapped_column(Boolean, default=False) state: Mapped[str] = mapped_column(String(30), default="inbox", index=True) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now) class Pnl(Base): __tablename__ = "pnl_cache" alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True) raw: Mapped[dict] = mapped_column(JSON) points: Mapped[list] = mapped_column(JSON) fetched_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now) class ResearchTag(Base): __tablename__ = "research_tags" alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True) tag: Mapped[str] = mapped_column(String(60), primary_key=True, index=True) class Job(Base): __tablename__ = "sync_jobs" id: Mapped[str] = mapped_column(String(36), primary_key=True) kind: Mapped[str] = mapped_column(String(30)) status: Mapped[str] = mapped_column(String(30), default="queued", index=True) payload: Mapped[dict] = mapped_column(JSON, default=dict) checkpoint: Mapped[dict] = mapped_column(JSON, default=dict) processed: Mapped[int] = mapped_column(Integer, default=0) failed: Mapped[int] = mapped_column(Integer, default=0) total: Mapped[int | None] = mapped_column(Integer) error: Mapped[str | None] = mapped_column(Text) next_retry_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) cancel_requested: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now) class JobItem(Base): __tablename__ = "job_items" job_id: Mapped[str] = mapped_column(ForeignKey("sync_jobs.id"), primary_key=True) alpha_id: Mapped[str] = mapped_column(String(100), primary_key=True) error: Mapped[str | None] = mapped_column(Text)