DRRK/app/repositories/nominals.py

89 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from collections.abc import Sequence
from datetime import date
from decimal import Decimal
from sqlalchemy import case, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.models import NominalSource, SecurityNominalHistory
from app.repositories._upsert import insert_for
_History = SecurityNominalHistory
# На одну дату ручное значение важнее значения MOEX (D11).
_MANUAL_FIRST = case((_History.source == NominalSource.MANUAL, 0), else_=1)
class NominalHistoryRepository:
def __init__(self, session: AsyncSession) -> None:
self.session = session
async def effective_on(
self, security_id: int, on_date: date
) -> SecurityNominalHistory | None:
"""Номинал, действующий на дату: последняя запись с valid_from <= даты."""
result = await self.session.execute(
select(_History)
.where(_History.security_id == security_id, _History.valid_from <= on_date)
.order_by(_History.valid_from.desc(), _MANUAL_FIRST)
.limit(1)
)
return result.scalar_one_or_none()
async def last_moex_on(
self, security_id: int, on_date: date
) -> SecurityNominalHistory | None:
result = await self.session.execute(
select(_History)
.where(
_History.security_id == security_id,
_History.source == NominalSource.MOEX,
_History.valid_from <= on_date,
)
.order_by(_History.valid_from.desc())
.limit(1)
)
return result.scalar_one_or_none()
async def upsert(
self,
*,
security_id: int,
valid_from: date,
face_value: Decimal,
source: str,
reason: str | None,
user: str,
) -> None:
stmt = insert_for(self.session, _History.__table__).values(
security_id=security_id,
valid_from=valid_from,
face_value=face_value,
source=source,
reason=reason,
created_by=user,
updated_by=user,
)
stmt = stmt.on_conflict_do_update(
index_elements=["security_id", "valid_from", "source"],
set_={
"face_value": stmt.excluded.face_value,
"reason": stmt.excluded.reason,
"updated_at": func.now(),
"updated_by": user,
},
)
await self.session.execute(stmt)
async def list_for_security(
self, security_id: int
) -> Sequence[SecurityNominalHistory]:
result = await self.session.execute(
select(_History)
.where(_History.security_id == security_id)
.order_by(_History.valid_from.desc(), _MANUAL_FIRST)
)
return result.scalars().all()