- funding_rate table and "Фондирование" page: rate from date, history, rate on date (D27)
- positions by issue: BUY - SELL per portfolio, "Остатки" page (D29)
- quotes are loaded only for securities with an open position (D10)
- business calendar from xmlcalendar.ru: yearly file upload, report date
and update job use previous working day (D30)
- xlsx download for positions, quotes and funding rate history
- manual operation type as radio buttons
- QUIK instruments with purchase marks ("МТС 2Р-18_1") match the issue,
file name kept in operation.instrument (D17)
- migrations d3a6f8b1c254, e7b2c9d4a813, f1c4a7e9b352
- project.md: decisions and questions after customer meetings
72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
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()
|