docs: describe stock pages and portfolio navigation
Document ticker aliases, instrument identity, market data and dividend history, portfolio links, and updated brokerage account summaries.
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
# Страница акции
|
||||
|
||||
Авторизованный пользователь открывает `/stocks/SBERP` по тикеру. Старые
|
||||
адреса `/stocks/:uid` продолжают работать. Сервер возвращает `slug`: проверенный
|
||||
однозначный тикер либо исходный UID. При открытии UUID-ссылки страница заменяет
|
||||
адрес на тикер только при совпадении UID, сохраняя период и фильтр событий.
|
||||
Прямое открытие и обновление страницы с тикером сначала разрешает его в UID;
|
||||
цены, позиция и личные операции затем запрашиваются только по этому UID.
|
||||
Ссылки находятся в названиях и логотипах акций отдельного портфеля и обзора
|
||||
«Все счета». Названия сохраняют обычный цвет текста; буквенная заглушка
|
||||
логотипа также ведёт на страницу акции. Карточки получают проверенный `slug`
|
||||
через существующий запрос акции по UID: после его загрузки оба `href` сразу
|
||||
содержат тикер. Одинаковые UID в разных счетах используют один запрос и общий
|
||||
кэш страницы акции. До загрузки и при ошибке остаётся рабочая ссылка по UID;
|
||||
загрузка адресов не блокирует портфель.
|
||||
Раздел «Акции» в боковой и мобильной навигации ведёт на `/stocks`: поиск
|
||||
по названию или тикеру через каталог Т-Банка, начиная с двух символов.
|
||||
Результаты разных выпусков, включая обыкновенные и привилегированные акции,
|
||||
имеют отдельные UID. Карточка результата показывает логотип, последнюю цену
|
||||
с её временем, тип бумаги, площадку, код режима, ISIN и доступность торгов через
|
||||
API. Поиск показывает одну карточку на нормализованный ISIN. До ограничения
|
||||
в 30 результатов выбирается представитель выпуска: доступность через API,
|
||||
точное совпадение тикера, затем режим TQBR; равные варианты упорядочены по UID.
|
||||
Записи без ISIN сохраняются отдельно по UID. Обыкновенные и привилегированные
|
||||
акции с разными ISIN не объединяются. Цена и ссылка принадлежат выбранному
|
||||
UID; данные разных режимов не смешиваются. Прямые ссылки из портфеля сохраняют
|
||||
исходный UID, даже если поиск выбрал другой режим этого выпуска; такая страница
|
||||
сохраняет UUID в адресе. Если один точный тикер относится к разным выпускам,
|
||||
сервер возвращает `409 STOCK_TICKER_AMBIGUOUS`, а страница предлагает поиск.
|
||||
Поиск тикера нечувствителен к регистру, но использует точное совпадение:
|
||||
SBER не заменяется на SBERP. Однобуквенные тикеры также поддерживаются.
|
||||
Прямая ссылка не требует предварительного открытия портфеля. Хлебные крошки
|
||||
«Акции → название» возвращают к поиску.
|
||||
|
||||
## Содержание и состояния
|
||||
|
||||
Верхний блок объединяет название, тип акции, текущую цену и показатели компании.
|
||||
Денежные показатели используют символ валюты; описание компании видно сразу
|
||||
под логотипом и названием, перед ценой. Далее идут график, моя позиция, предстоящие
|
||||
дивиденды и личная история событий.
|
||||
Логотип использует существующий CDN helper и буквенный fallback.
|
||||
Позиция отсутствующей или полностью проданной бумаги не создаёт пустого блока;
|
||||
рыночные данные и личная история остаются доступны.
|
||||
|
||||
Каждый раздел загружается независимо. Первичная загрузка показывает structural
|
||||
skeleton, ошибка — локальную повторную попытку. При ошибке фонового обновления
|
||||
сохраняются предыдущие данные и предупреждение. Цена содержит собственное время
|
||||
UTC, которое может быть старше времени получения ответа. Статус торгов не
|
||||
выводится из свежести цены. Отсутствующее значение не заменяется нулём.
|
||||
|
||||
## API и владельцы
|
||||
|
||||
Все методы требуют Bearer session и текущее подключение Т-Банка.
|
||||
Новые endpoints принадлежат `StockController` / `StockService` существующего
|
||||
`TbankConnectionModule`:
|
||||
|
||||
| Метод | Содержимое |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/stocks?query=...` | До 30 результатов с признаками выпуска/режима, логотипом и последней ценой. |
|
||||
| `GET /api/v1/stocks/:uid` | Точный инструмент: название, тикер, `slug`, тип, сектор, лот, валюта, начало дневной истории. |
|
||||
| `GET /api/v1/stocks/by-ticker/:ticker` | Разрешение точного тикера и карточка выбранного выпуска с UID. |
|
||||
| `GET /api/v1/stocks/:uid/quote` | Последняя цена и время, статус, изменение и явно указанная база. |
|
||||
| `GET /api/v1/stocks/:uid/candles?period=...` | Дневные цены для `month`, `halfYear`, `year`, `all`. |
|
||||
| `GET /api/v1/stocks/:uid/position` | Точная сводка открытой позиции и распределение по счетам. |
|
||||
| `GET /api/v1/stocks/:uid/dividends` | События компании за пять прошлых лет и следующий год, личные оценки. |
|
||||
| `GET /api/v1/stocks/:uid/company` | Доступное описание и финансовый срез. |
|
||||
|
||||
Личная история использует существующий
|
||||
`GET /api/v1/brokerage-accounts/all/operations?instrumentUid=...`.
|
||||
Фильтр UID валидируется и передаётся upstream как `instrumentId`; ответ также
|
||||
проверяется по UID. Без `from` такой запрос начинается с самой ранней известной
|
||||
даты открытия доступных счетов; если даты неполны, нижняя граница — Unix epoch.
|
||||
Обычный журнал без фильтра инструмента сохраняет начало текущего года.
|
||||
UID включён в ключ кэша и подпись общего курсора: курсор другой бумаги,
|
||||
пользователя, поколения подключения или фильтра отклоняется. Истечение курсора
|
||||
восстанавливается переходом на первую страницу с повторным запросом.
|
||||
|
||||
Сервис портфеля остаётся владельцем снимков и оценки позиций. Новая сводка
|
||||
фильтрует `STOCK` по точному UID и использует decimal/BigInt, без FX.
|
||||
Средняя цена равна сумме количеств, умноженных на средние цены, делённой на
|
||||
общее количество; она округляется сервером до девяти знаков, проценты — до шести.
|
||||
Неполные или разновалютные денежные итоги недоступны. Для нулевого количества
|
||||
и сочетаний длинных/коротких позиций общая средняя и процент не синтезируются.
|
||||
Результат открытой позиции исключает продажи и дивиденды и не называется полной
|
||||
доходностью. Время и `isStale` сохраняют семантику исходного Portfolio Snapshot.
|
||||
|
||||
## Рыночная цена и график
|
||||
|
||||
Изменение рассчитывается относительно последней завершённой дневной свечи
|
||||
строго до календарной даты последней цены по Москве. Поиск базы охватывает
|
||||
20 дней до последней цены; если подходящей свечи нет, сравнение недоступно.
|
||||
Дата базы указана рядом с изменением. `GetClosePrices` не используется:
|
||||
его основное закрытие может относиться к текущему дню.
|
||||
|
||||
График показывает дневные `close`, сохраняет `isComplete` и предупреждает
|
||||
о формирующейся свече. Окна RPC не превышают 365 дней, общие границы
|
||||
дедуплицируются. «Вся история» начинается с `first_1dayCandleDate`; если источник
|
||||
не сообщает начало истории, этот период недоступен, остальные работают.
|
||||
Пустое окно до начала торгов не считается ошибкой. Линия текущей средней
|
||||
покупки доступна только в валюте графика; шкала включает её значение.
|
||||
Таблица цен раскрывается под графиком: новые даты первыми, валюта в цене,
|
||||
статус завершённости свечи, закреплённая шапка и прокрутка с клавиатуры.
|
||||
Стили графиков подключены на уровне приложения и каталога, поэтому оформление
|
||||
подсказки не зависит от ранее посещённых страниц.
|
||||
|
||||
Метки старых сделок и дивидендов на графике не добавлены: соответствие шкалы
|
||||
после корпоративных изменений не подтверждено. Лента использует дату операции;
|
||||
количество сделки берётся из `quantityDone`, а не порученного `quantity`.
|
||||
Отсутствующее исполненное количество не подменяется порученным. Сами исполнения
|
||||
не превращаются в точные точки графика.
|
||||
|
||||
## Дивиденды и финансовые показатели
|
||||
|
||||
События компании отделены от личных зачислений. Известные будущие выплаты
|
||||
определяются по `paymentDate`, при её отсутствии — по `recordDate`; прошедшие
|
||||
и недатированные события помещены в отдельную историю компании. Отменённые
|
||||
события исключаются. Личная оценка умножает сумму на акцию на текущее положительное
|
||||
количество. Для уже прошедшего реестра оценка не строится. Время позиции и
|
||||
предупреждение о stale/недоступной оценке показаны отдельно.
|
||||
|
||||
Оценка не доказывает право на выплату, не учитывает будущие сделки и не обещает
|
||||
индивидуальную дату зачисления. Статусы «рекомендован/утверждён», сумма отдельного
|
||||
зачисления после налога и налоговый смысл `dividend_net` не выводятся из API.
|
||||
Пустой список означает отсутствие известных событий только за указанный период.
|
||||
|
||||
`GetAssetFundamentals` декодируется отдельным Instruments client с
|
||||
`defaults: false`: отсутствующие поля не превращаются в нули. Остальные
|
||||
существующие клиенты сохраняют прежнюю настройку декодирования. Финансовый сектор
|
||||
показывает капитализацию, P/E, P/B, ROE и чистую прибыль; остальные — капитализацию,
|
||||
P/E, выручку, EBITDA, чистую прибыль, общий долг и чистый долг / EBITDA, если
|
||||
значения доступны. Денежные показатели без валюты не показываются.
|
||||
TTM означает последние 12 месяцев, MRQ — последний отчётный квартал;
|
||||
время получения не выдаётся за дату отчётности. Годовых финансовых рядов нет.
|
||||
|
||||
## Границы и проверка
|
||||
|
||||
Новые RPC проходят существующий `TbankClient`: TLS, rate limit, очередь,
|
||||
deadline и retry. Доступ/расшифровка остаются у `TbankAccessService` и
|
||||
`TbankTokenService`. Успешные ответы и совместные незавершённые запросы кэшируются
|
||||
в bounded LRU на 30 секунд по пользователю, поколению подключения и параметрам.
|
||||
Для поиска `FindInstrument` дополняется сведениями `ShareBy` через тот же
|
||||
кэш карточки инструмента и одним пакетным `GetLastPrices` для списка. Ошибка
|
||||
дополнительной загрузки или исчезнувшая карточка не скрывает исходную запись
|
||||
каталога; недоступные логотипы и цены имеют fallback. Ошибки авторизации
|
||||
не подавляются.
|
||||
Ошибка целого запроса не кэшируется; отсутствие дополнительного источника
|
||||
обозначается в ответе. Cache subtree `/stocks` сбрасывается вместе с финансовыми
|
||||
данными при изменении подключения; logout использует существующий session reset.
|
||||
|
||||
Контракт генерируется из runtime Nest OpenAPI, затем обновляются frontend snapshot
|
||||
и generated types. Unit/HTTP/MSW tests проверяют точность, разделение инструментов,
|
||||
фильтрацию и курсоры, валидацию, отсутствие позиции и локальные ошибки.
|
||||
Браузерная проверка использует синтетические ответы и широкий/узкий viewport.
|
||||
Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и
|
||||
временные зоны расчётов не меняются. Личная история использует общий с портфелем
|
||||
`LedgerEntryRow`, дополненный количеством, ценой и ссылкой на счёт. История
|
||||
дивидендов компании раскрывается в оформленном блоке с отдельными карточками
|
||||
выплат, датой реестра, доступной датой выплаты и суммой на акцию.
|
||||
Реальные ограничения источника описаны в
|
||||
[результатах исследования](../../design/stock-detail-api-live-findings.md).
|
||||
История открытых счетов не обещает полную историю владения, включая закрытые
|
||||
счета, переводы и универсальные корпоративные события.
|
||||
Reference in New Issue
Block a user