- 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
109 lines
4.0 KiB
Python
109 lines
4.0 KiB
Python
"""Ставки фондирования портфелей по валютам (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()
|