from __future__ import annotations from collections.abc import Sequence from datetime import date from decimal import Decimal from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.db.models import Currency, FundingRate from app.repositories._upsert import insert_for class FundingRateRepository: def __init__(self, session: AsyncSession) -> None: self.session = session async def effective_on( self, portfolio_id: int, currency_id: int, on_date: date ) -> FundingRate | None: """Ставка, действующая на дату: последняя запись с effective_from <= даты.""" result = await self.session.execute( select(FundingRate) .where( FundingRate.portfolio_id == portfolio_id, FundingRate.currency_id == currency_id, FundingRate.effective_from <= on_date, ) .order_by(FundingRate.effective_from.desc()) .limit(1) ) return result.scalar_one_or_none() async def upsert( self, *, portfolio_id: int, currency_id: int, effective_from: date, rate: Decimal, user: str, ) -> None: stmt = insert_for(self.session, FundingRate.__table__).values( portfolio_id=portfolio_id, currency_id=currency_id, effective_from=effective_from, rate=rate, created_by=user, updated_by=user, ) stmt = stmt.on_conflict_do_update( index_elements=["portfolio_id", "currency_id", "effective_from"], set_={ "rate": stmt.excluded.rate, "updated_at": func.now(), "updated_by": user, }, ) await self.session.execute(stmt) async def list_for_portfolio( self, portfolio_id: int ) -> Sequence[tuple[FundingRate, str]]: """(ставка, код валюты), новые сверху.""" result = await self.session.execute( select(FundingRate, Currency.code) .join(Currency, Currency.id == FundingRate.currency_id) .where(FundingRate.portfolio_id == portfolio_id) .order_by(FundingRate.effective_from.desc(), Currency.code) ) return result.tuples().all()