69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
from __future__ import annotations
|
||
|
||
from datetime import date, datetime
|
||
from decimal import Decimal
|
||
from typing import Any
|
||
|
||
from sqlalchemy import (
|
||
BigInteger,
|
||
Date,
|
||
DateTime,
|
||
ForeignKey,
|
||
Index,
|
||
String,
|
||
UniqueConstraint,
|
||
func,
|
||
)
|
||
from sqlalchemy.orm import Mapped, mapped_column
|
||
|
||
from app.core.config import settings
|
||
from app.db.base import Base
|
||
from app.db.types import (
|
||
AuditMixin,
|
||
IdMixin,
|
||
JsonType,
|
||
Percent,
|
||
Price,
|
||
Quantity,
|
||
qualified,
|
||
)
|
||
|
||
|
||
class MarketData(IdMixin, AuditMixin, Base):
|
||
"""Котировка бумаги за дату (строка истории MOEX ISS по выбранному борду).
|
||
|
||
Повторная загрузка за ту же дату — upsert по (security_id, market_date).
|
||
"""
|
||
|
||
__tablename__ = "market_data"
|
||
__table_args__ = (
|
||
UniqueConstraint(
|
||
"security_id", "market_date", name="uq_market_data_security_date"
|
||
),
|
||
Index("ix_market_data_market_date", "market_date"),
|
||
{"schema": settings.DATABASE_SCHEMA} if settings.DATABASE_SCHEMA else {},
|
||
)
|
||
|
||
security_id: Mapped[int] = mapped_column(
|
||
BigInteger, ForeignKey(qualified("security.id")), nullable=False
|
||
)
|
||
market_date: Mapped[date] = mapped_column(Date, nullable=False)
|
||
board: Mapped[str] = mapped_column(String(12), nullable=False)
|
||
clean_price: Mapped[Decimal | None] = mapped_column(Price)
|
||
clean_price_source: Mapped[str | None] = mapped_column(String(20))
|
||
yield_value: Mapped[Decimal | None] = mapped_column(Percent)
|
||
yield_source: Mapped[str | None] = mapped_column(String(20))
|
||
# В днях, как отдаёт MOEX; в годы переводит расчёт.
|
||
duration_days: Mapped[Decimal | None] = mapped_column(Quantity)
|
||
accrued_interest: Mapped[Decimal | None] = mapped_column(Price)
|
||
# Текущий номинал: у амортизируемых бумаг меняется.
|
||
face_value: Mapped[Decimal | None] = mapped_column(Price)
|
||
coupon_rate: Mapped[Decimal | None] = mapped_column(Percent)
|
||
num_trades: Mapped[int | None] = mapped_column(BigInteger)
|
||
volume: Mapped[Decimal | None] = mapped_column(Quantity)
|
||
# Исходная строка истории ISS — для разборов «откуда цифра».
|
||
raw: Mapped[dict[str, Any] | None] = mapped_column(JsonType)
|
||
loaded_at: Mapped[datetime] = mapped_column(
|
||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||
)
|