"""Instrument master (plan §1.3). Identity resolution order: ISIN -> FIGI -> tinvest_uid -> (ticker, board) -> alias. """ from __future__ import annotations import enum from datetime import date from decimal import Decimal from typing import Any from sqlalchemy import Boolean, ForeignKey, Index, Integer, String, UniqueConstraint, text from sqlalchemy.orm import Mapped, mapped_column from fintracker.db.base import Base, TimestampMixin, db_enum class AssetClass(enum.StrEnum): share = "share" bond = "bond" etf = "etf" fund = "fund" currency = "currency" market_index = "index" deposit = "deposit" real_estate = "real_estate" crypto = "crypto" custom = "custom" class Instrument(TimestampMixin, Base): __tablename__ = "instrument" __table_args__ = ( Index("uq_instrument_isin", "isin", unique=True, postgresql_where=text("isin IS NOT NULL")), Index("uq_instrument_figi", "figi", unique=True, postgresql_where=text("figi IS NOT NULL")), Index( "uq_instrument_tinvest_uid", "tinvest_uid", unique=True, postgresql_where=text("tinvest_uid IS NOT NULL"), ), Index( "uq_instrument_ticker_board", "ticker", "board", unique=True, postgresql_where=text("ticker IS NOT NULL AND board IS NOT NULL"), ), ) id: Mapped[int] = mapped_column(primary_key=True) asset_class: Mapped[AssetClass] = mapped_column(db_enum(AssetClass, "asset_class")) isin: Mapped[str | None] = mapped_column(String(12)) figi: Mapped[str | None] = mapped_column(String(12)) tinvest_uid: Mapped[str | None] = mapped_column(String(64)) ticker: Mapped[str | None] = mapped_column(String(32)) board: Mapped[str | None] = mapped_column(String(16)) """MOEX board / T-Invest class_code, e.g. TQBR, TQOB.""" exchange: Mapped[str | None] = mapped_column(String(32)) name: Mapped[str] = mapped_column(String(256)) issuer: Mapped[str | None] = mapped_column(String(256)) currency: Mapped[str] = mapped_column(String(3)) lot: Mapped[int] = mapped_column(Integer, default=1) nominal: Mapped[Decimal | None] nominal_currency: Mapped[str | None] = mapped_column(String(3)) maturity_date: Mapped[date | None] sector: Mapped[str | None] = mapped_column(String(64)) country: Mapped[str | None] = mapped_column(String(2)) is_active: Mapped[bool] = mapped_column(Boolean, default=True) meta: Mapped[dict[str, Any] | None] class InstrumentAlias(Base): """Source-specific keys resolving to an instrument, e.g. ('report_vtb', 'NAME:Газпром ао').""" __tablename__ = "instrument_alias" __table_args__ = (UniqueConstraint("source", "source_key"),) id: Mapped[int] = mapped_column(primary_key=True) instrument_id: Mapped[int] = mapped_column( ForeignKey("instrument.id", ondelete="CASCADE"), index=True ) source: Mapped[str] = mapped_column(String(32)) source_key: Mapped[str] = mapped_column(String(256))