- 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
31 lines
1.0 KiB
Python
31 lines
1.0 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import date, timedelta
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BusinessCalendar:
|
|
"""Нерабочие дни загруженных лет (D30). Для года без календаря рабочими
|
|
считаются будни."""
|
|
|
|
years: frozenset[int] = frozenset()
|
|
non_working: frozenset[date] = frozenset()
|
|
|
|
def is_business_day(self, day: date) -> bool:
|
|
if day.year in self.years:
|
|
return day not in self.non_working
|
|
return day.weekday() < 5
|
|
|
|
|
|
def previous_business_day(
|
|
today: date | None = None, calendar: BusinessCalendar | None = None
|
|
) -> date:
|
|
"""Дата отчёта по умолчанию — предыдущий рабочий день (в Excel —
|
|
WORKDAY.INTL по списку праздников)."""
|
|
calendar = calendar or BusinessCalendar()
|
|
day = (today or date.today()) - timedelta(days=1)
|
|
while not calendar.is_business_day(day):
|
|
day -= timedelta(days=1)
|
|
return day
|