from __future__ import annotations from datetime import date import streamlit as st from app.services import fx_rates as fx_rates_service from app.services import market_data as market_data_service from app.services.dates import previous_business_day from app.services.fx_rates import FxRatesUpdateResult from app.services.market_data import MarketDataUpdateResult from app.ui.common import as_table, call_db, current_user _QUOTE_COLUMNS = { "isin": "ISIN", "name": "Наименование", "board": "Режим", "clean_price": "Чистая цена, %", "clean_price_source": "Источник цены", "yield_value": "Доходность, %", "yield_source": "Источник доходности", "duration_days": "Дюрация, дн.", "accrued_interest": "НКД", "face_value": "Номинал (тек.)", "coupon_rate": "Купон, %", "num_trades": "Сделок", "volume": "Объём, шт.", } _RATE_COLUMNS = { "rate_date": "Дата", "nominal": "Номинал", "rate": "Курс ЦБ", "rate_per_unit": "За 1 CNY", } def render() -> None: st.header("Рыночные данные") on_date: date = st.date_input( "Дата", value=previous_business_day(), max_value=date.today(), format="DD.MM.YYYY", ) # Внешние API — только по кнопке; ниже экран читает только БД. quotes_col, fx_col = st.columns(2) if quotes_col.button("Обновить котировки", use_container_width=True): with st.spinner("Запрос котировок MOEX…"): result = call_db( market_data_service.update(on_date, current_user()), "Не удалось обновить котировки", ) if result is not None: _show_market_result(result) if fx_col.button("Обновить курс CNY", use_container_width=True): with st.spinner("Запрос курса ЦБ…"): fx_result = call_db( fx_rates_service.update(on_date, current_user()), "Не удалось обновить курс CNY", ) if fx_result is not None: _show_fx_result(fx_result) st.subheader(f"Котировки на {on_date:%d.%m.%Y}") quotes = call_db( market_data_service.list_for_date(on_date), "Не удалось загрузить котировки" ) if quotes: st.dataframe(as_table(quotes, _QUOTE_COLUMNS), hide_index=True) elif quotes is not None: st.info("За эту дату котировок в базе нет.") st.subheader("Курс CNY (последние)") rates = call_db(fx_rates_service.list_recent(10), "Не удалось загрузить курсы") if rates: st.dataframe(as_table(rates, _RATE_COLUMNS), hide_index=True) elif rates is not None: st.info("Курсов CNY в базе пока нет.") def _show_market_result(result: MarketDataUpdateResult) -> None: summary = ( f"Обновлено: {result.updated}, без торгов: {result.no_trades}, " f"ошибок: {result.errors}." ) if result.errors: st.error(summary) elif result.no_trades: st.warning(summary) else: st.success(summary) with st.expander("Подробности по бумагам", expanded=bool(result.errors)): for message in result.messages: st.write(f"- {message}") def _show_fx_result(result: FxRatesUpdateResult) -> None: show = st.error if not result.ok else st.success for message in result.messages: show(message)