100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
"""Обновление котировок 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 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 _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 previous_business_day()
|
||
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())
|