- 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
70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
"""Остатки выпусков в портфелях на дату (D29)."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import date
|
||
|
||
import streamlit as st
|
||
|
||
from app.services import positions
|
||
from app.ui.common import as_table, call_db, default_report_date, download_xlsx
|
||
|
||
_ALL = "все"
|
||
|
||
_COLUMNS = {
|
||
"portfolio": "Портфель",
|
||
"name": "Бумага",
|
||
"isin": "ISIN",
|
||
"currency": "Валюта",
|
||
"bought": "Куплено, шт",
|
||
"sold": "Продано, шт",
|
||
"balance": "Остаток, шт",
|
||
}
|
||
|
||
|
||
def render() -> None:
|
||
st.header("Остатки")
|
||
st.caption(
|
||
"Остаток на конец дня: куплено минус продано по сделкам с датой сделки "
|
||
"не позже выбранной. Купоны, амортизация и корректировки количество не "
|
||
"меняют."
|
||
)
|
||
portfolios = call_db(positions.list_portfolios(), "Не удалось загрузить портфели")
|
||
if portfolios is None:
|
||
return
|
||
|
||
date_col, portfolio_col = st.columns(2)
|
||
on_date: date = date_col.date_input(
|
||
"Дата", value=default_report_date(), format="DD.MM.YYYY"
|
||
)
|
||
portfolio = portfolio_col.selectbox(
|
||
"Портфель",
|
||
[_ALL, *portfolios],
|
||
format_func=lambda c: c if c == _ALL else f"{c} — {portfolios[c]}",
|
||
)
|
||
include_closed = st.checkbox("Показывать закрытые (остаток 0)")
|
||
|
||
rows = call_db(
|
||
positions.positions_on(
|
||
on_date,
|
||
None if portfolio == _ALL else portfolio,
|
||
include_closed=include_closed,
|
||
),
|
||
"Не удалось посчитать остатки",
|
||
)
|
||
if rows is None:
|
||
return
|
||
if not rows:
|
||
st.caption("Открытых позиций на эту дату нет.")
|
||
return
|
||
st.dataframe(as_table(rows, _COLUMNS), hide_index=True)
|
||
suffix = "" if portfolio == _ALL else f"_{portfolio}"
|
||
download_xlsx(rows, _COLUMNS, "Остатки", f"остатки{suffix}_{on_date:%Y%m%d}.xlsx")
|
||
negative = [row for row in rows if row.balance < 0]
|
||
if negative:
|
||
names = ", ".join(f"{row.name} ({row.portfolio})" for row in negative)
|
||
st.warning(
|
||
f"Продано больше, чем куплено: {names}. Возможно, не загружены "
|
||
"покупки за прошлые даты."
|
||
)
|