DRRK/app/ui/pages/funding.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

109 lines
4.0 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.

"""Ставки фондирования портфелей по валютам (D27)."""
from __future__ import annotations
import logging
from datetime import date
import streamlit as st
from app.db.base import run_db
from app.services import funding
from app.services.funding import FundingError, FundingOptions, FundingRateInput
from app.services.manual_operations import ManualOperationError, parse_decimal
from app.ui.common import as_table, call_db, current_user, download_xlsx
logger = logging.getLogger(__name__)
_ON_DATE_COLUMNS = {
"currency": "Валюта",
"rate": "Ставка, % год.",
"effective_from": "Действует с",
}
_HISTORY_COLUMNS = {
"effective_from": "Действует с",
"currency": "Валюта",
"rate": "Ставка, % год.",
"updated_by": "Кто",
"updated_at": "Когда",
}
def render() -> None:
st.header("Фондирование")
st.caption(
"Ставка портфеля в валюте, % годовых, действует с указанной даты до "
"следующей. Повторный ввод на ту же дату заменяет ставку. Стоимость "
"фондирования пока не считается."
)
options = call_db(funding.load_options(), "Не удалось загрузить справочники")
if options is None:
return
portfolio = st.selectbox(
"Портфель",
list(options.portfolios),
format_func=lambda c: f"{c} — {options.portfolios[c]}",
key="funding_portfolio",
)
on_date = st.date_input(
"Ставки на дату", value=date.today(), format="DD.MM.YYYY", key="funding_on"
)
rates = call_db(
funding.rates_on(portfolio, on_date), "Не удалось загрузить ставки на дату"
)
if rates is not None:
st.dataframe(as_table(rates, _ON_DATE_COLUMNS), hide_index=True)
st.subheader("Новая ставка")
_render_form(options, portfolio)
st.subheader("История ставок")
history = call_db(
funding.rate_history(portfolio), "Не удалось загрузить историю ставок"
)
if history:
st.dataframe(as_table(history, _HISTORY_COLUMNS), hide_index=True)
download_xlsx(
history,
_HISTORY_COLUMNS,
"Ставки фондирования",
f"ставки_фондирования_{portfolio}.xlsx",
)
elif history is not None:
st.caption("Ставок пока нет.")
def _render_form(options: FundingOptions, portfolio: str) -> None:
with st.form(f"funding_rate_{portfolio}", clear_on_submit=True):
currency = st.selectbox("Валюта", options.currencies)
effective_from = st.date_input(
"Действует с", value=date.today(), format="DD.MM.YYYY"
)
rate_text = st.text_input("Ставка, % годовых", placeholder="например 16,5")
submitted = st.form_submit_button("Сохранить ставку", type="primary")
if submitted:
_save(portfolio, currency, effective_from, rate_text)
def _save(portfolio: str, currency: str, effective_from: date, rate_text: str) -> None:
try:
value = FundingRateInput(
portfolio_code=portfolio,
currency_code=currency,
effective_from=effective_from,
rate=parse_decimal(rate_text, "Ставка"),
)
run_db(funding.set_rate(value, current_user()))
except (ManualOperationError, FundingError) as exc:
st.error(str(exc))
return
except Exception as exc: # noqa: BLE001
logger.exception("Ошибка сохранения ставки %s %s", portfolio, currency)
st.error(f"Не удалось сохранить ставку: {exc}")
return
st.toast(f"Ставка {portfolio}, {currency} с {effective_from:%d.%m.%Y} сохранена.")
st.rerun()