from datetime import date, datetime from decimal import Decimal from sqlalchemy import ( Boolean, Date, DateTime, ForeignKey, Integer, Numeric, String, UniqueConstraint, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base from app.modules.feishu_users.models import FeishuUser from app.modules.business.models.common import TimestampMixin class MarketInstrument(Base, TimestampMixin): __tablename__ = "market_instruments" id: Mapped[int] = mapped_column(Integer, primary_key=True) symbol: Mapped[str] = mapped_column(String(32), unique=True, index=True) name: Mapped[str] = mapped_column(String(128), index=True) exchange: Mapped[str | None] = mapped_column(String(16), nullable=True, index=True) instrument_type: Mapped[str] = mapped_column(String(16), default="stock", index=True) industry: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True) list_date: Mapped[date | None] = mapped_column(Date, nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, default=True, index=True) source_updated_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) class MarketDailyQuote(Base, TimestampMixin): __tablename__ = "market_daily_quotes" __table_args__ = (UniqueConstraint("symbol", "trade_date", name="uq_market_quote_day"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) symbol: Mapped[str] = mapped_column(String(32), index=True) trade_date: Mapped[date] = mapped_column(Date, index=True) open_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True) high_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True) low_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True) close_price: Mapped[Decimal] = mapped_column(Numeric(18, 4)) pre_close: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True) pct_change: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True) volume: Mapped[Decimal | None] = mapped_column(Numeric(20, 2), nullable=True) amount_cny: Mapped[Decimal | None] = mapped_column(Numeric(20, 2), nullable=True) pe: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True) pb: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True) total_market_value: Mapped[Decimal | None] = mapped_column(Numeric(20, 2), nullable=True) class MarketFinancialMetric(Base, TimestampMixin): __tablename__ = "market_financial_metrics" __table_args__ = (UniqueConstraint("symbol", "period_end", name="uq_market_financial_period"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) symbol: Mapped[str] = mapped_column(String(32), index=True) period_end: Mapped[date] = mapped_column(Date, index=True) revenue_yoy: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True) net_profit_yoy: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True) roe: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True) debt_to_assets: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True) operating_cashflow_yoy: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True) class MarketMacroIndicator(Base, TimestampMixin): __tablename__ = "market_macro_indicators" __table_args__ = (UniqueConstraint("code", "period_date", name="uq_market_macro_period"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) code: Mapped[str] = mapped_column(String(64), index=True) name: Mapped[str] = mapped_column(String(128)) period_date: Mapped[date] = mapped_column(Date, index=True) value: Mapped[Decimal] = mapped_column(Numeric(20, 6)) unit: Mapped[str | None] = mapped_column(String(32), nullable=True) source: Mapped[str] = mapped_column(String(64), default="tushare") class MarketAnnouncement(Base, TimestampMixin): __tablename__ = "market_announcements" id: Mapped[int] = mapped_column(Integer, primary_key=True) source_key: Mapped[str] = mapped_column(String(64), unique=True, index=True) symbol: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True) announcement_date: Mapped[date] = mapped_column(Date, index=True) published_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) title: Mapped[str] = mapped_column(String(512)) category: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True) source_url: Mapped[str | None] = mapped_column(String(1024), nullable=True) source: Mapped[str] = mapped_column(String(64), default="tushare") class MarketWatchlist(Base, TimestampMixin): __tablename__ = "market_watchlists" __table_args__ = ( UniqueConstraint("owner_id", "symbol", name="uq_market_watchlist_owner_symbol"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True) owner_id: Mapped[int | None] = mapped_column( ForeignKey("feishu_users.id", ondelete="CASCADE"), nullable=True, index=True, ) owner: Mapped[FeishuUser | None] = relationship() actor: Mapped[str] = mapped_column(String(128), index=True) symbol: Mapped[str] = mapped_column(String(32), index=True) enabled: Mapped[bool] = mapped_column(Boolean, default=True, index=True)