from __future__ import annotations from datetime import date from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.db.models import MarketData, Security from app.integrations.moex import MarketQuote from app.repositories._upsert import insert_for class MarketDataRepository: def __init__(self, session: AsyncSession) -> None: self.session = session async def upsert(self, security_id: int, quote: MarketQuote, user: str) -> None: """Повторная загрузка за ту же дату перезаписывает котировку (created_* сохраняются, updated_*/loaded_at обновляются).""" values = { "board": quote.board, "clean_price": quote.clean_price, "clean_price_source": quote.clean_price_source, "yield_value": quote.yield_value, "yield_source": quote.yield_source, "duration_days": quote.duration_days, "accrued_interest": quote.accrued_interest, "face_value": quote.face_value, "coupon_rate": quote.coupon_percent, "num_trades": quote.num_trades, "volume": quote.volume, "raw": quote.raw, } stmt = insert_for(self.session, MarketData.__table__).values( security_id=security_id, market_date=quote.trade_date, created_by=user, updated_by=user, **values, ) stmt = stmt.on_conflict_do_update( index_elements=["security_id", "market_date"], set_={ **{name: stmt.excluded[name] for name in values}, "loaded_at": func.now(), "updated_at": func.now(), "updated_by": user, }, ) await self.session.execute(stmt) async def list_for_date( self, market_date: date ) -> list[tuple[MarketData, Security]]: result = await self.session.execute( select(MarketData, Security) .join(Security, Security.id == MarketData.security_id) .where(MarketData.market_date == market_date) .order_by(Security.isin) ) return [(row, security) for row, security in result.all()]