DRRK/app/jobs/update_market.py
Raykov-MS c1c73e96f5 Add funding rates, positions, business calendar and xlsx export
- 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
2026-10-29 11:20:15 +03:00

110 lines
3.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.

"""Обновление котировок MOEX и курса CNY за дату — для запуска по расписанию.
python -m app.jobs.update_market # предыдущий рабочий день
python -m app.jobs.update_market --date 2026-10-08
Работает от пользователя `system`. Код выхода: 0 — без ошибок (отсутствие
торгов ошибкой не считается), 1 — были ошибки по бумагам/курсу или сбой,
2 — неверные аргументы.
"""
from __future__ import annotations
import argparse
import logging
from collections.abc import Sequence
from datetime import date
from app.core.logging import setup_logging
from app.db.base import run_db
from app.db.types import SYSTEM_USER
from app.services import business_calendar, fx_rates, market_data
from app.services.dates import previous_business_day
logger = logging.getLogger("app.jobs.update_market")
def _parse_date(value: str) -> date:
try:
return date.fromisoformat(value)
except ValueError as exc:
raise argparse.ArgumentTypeError(
f"дата должна быть в формате ГГГГ-ММ-ДД, получено «{value}»"
) from exc
def _report_date() -> date:
"""Предыдущий рабочий день по производственному календарю (D30); если
календарь не прочитался — без праздников."""
try:
return run_db(business_calendar.report_date())
except Exception: # noqa: BLE001
logger.exception("Календарь: сбой чтения, рабочими считаются будни")
return previous_business_day()
def _update_quotes(on_date: date) -> bool:
try:
result = run_db(market_data.update(on_date, SYSTEM_USER))
except Exception: # noqa: BLE001
logger.exception("Котировки: сбой обновления")
return False
for message in result.messages:
logger.info("Котировки: %s", message)
logger.log(
logging.INFO if result.ok else logging.ERROR,
"Котировки на %s: обновлено %d, без торгов %d, ошибок %d",
on_date.isoformat(),
result.updated,
result.no_trades,
result.errors,
)
return result.ok
def _update_fx(on_date: date) -> bool:
try:
result = run_db(fx_rates.update(on_date, SYSTEM_USER))
except Exception: # noqa: BLE001
logger.exception("Курс CNY: сбой обновления")
return False
for message in result.messages:
logger.log(
logging.INFO if result.ok else logging.ERROR, "Курс CNY: %s", message
)
return result.ok
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(
prog="python -m app.jobs.update_market",
description="Обновить котировки MOEX и курс CNY за дату.",
)
parser.add_argument(
"--date",
type=_parse_date,
default=None,
help="Дата ГГГГ-ММ-ДД (по умолчанию — предыдущий рабочий день)",
)
args = parser.parse_args(argv)
setup_logging()
on_date = args.date or _report_date()
logger.info("Старт обновления рыночных данных на %s", on_date.isoformat())
# Курс обновляем, даже если котировки упали, — источники независимы.
quotes_ok = _update_quotes(on_date)
fx_ok = _update_fx(on_date)
ok = quotes_ok and fx_ok
logger.log(
logging.INFO if ok else logging.ERROR,
"Готово: %s",
"без ошибок" if ok else "есть ошибки",
)
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())