Files
docs/features/stock-detail.md
T

18 KiB
Raw Blame History

Страница акции

Авторизованный пользователь открывает /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; при отсутствии изображения показывается буквенная заглушка. Позиция отсутствующей или полностью проданной бумаги не создаёт пустого блока; рыночные данные и личная история остаются доступны.

Каждый раздел загружается независимо. Первичная загрузка показывает structural skeleton, ошибка — локальную повторную попытку. При ошибке фонового обновления сохраняются предыдущие данные и предупреждение. Цена содержит собственное время UTC, которое может быть старше времени получения ответа. Статус торгов не выводится из свежести цены. Отсутствующее значение не заменяется нулём.

API и владельцы

Все методы требуют Bearer session и текущее подключение Т-Банка. Публичный API страницы акции обслуживает инвестиционная подсистема backend:

Метод Содержимое
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 и использует точную десятичную арифметику, без конвертации валют. Средняя цена равна сумме количеств, умноженных на средние цены, делённой на общее количество; она округляется сервером до девяти знаков, проценты — до шести. Неполные или разновалютные денежные итоги недоступны. Для нулевого количества и сочетаний длинных/коротких позиций общая средняя и процент не синтезируются. Результат открытой позиции исключает продажи и дивиденды и не называется полной доходностью. Время и 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 — последний отчётный квартал; время получения не выдаётся за дату отчётности. Годовых финансовых рядов нет.

Границы и проверка

Запросы T-Bank проходят общую серверную интеграцию с TLS, лимитом частоты, очередью, предельным временем ожидания и повторными попытками. Проверка доступа и расшифровка token выполняются только backend. Успешные ответы и совместные незавершённые запросы кэшируются в bounded LRU на 30 секунд по пользователю, поколению подключения и параметрам. Для поиска FindInstrument дополняется сведениями ShareBy через тот же кэш карточки инструмента и одним пакетным GetLastPrices для списка. Ошибка дополнительной загрузки или исчезнувшая карточка не скрывает исходную запись каталога; недоступные логотипы и цены имеют fallback. Ошибки авторизации не подавляются. Ошибка целого запроса не кэшируется; отсутствие дополнительного источника обозначается в ответе. Cache subtree /stocks сбрасывается вместе с финансовыми данными при изменении подключения; logout использует существующий session reset.

Контракт генерируется из runtime Nest OpenAPI, затем обновляются frontend snapshot и generated types. Проверки отдельных подсистем и HTTP-взаимодействия проверяют точность, разделение инструментов, фильтрацию и курсоры, валидацию, отсутствие позиции и локальные ошибки. Браузерная проверка использует синтетические ответы и широкий/узкий viewport. Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и временные зоны расчётов не меняются. Личная история использует общий с портфелем формат строки операции, дополненный количеством, ценой и ссылкой на счёт. История дивидендов компании раскрывается в оформленном блоке с отдельными карточками выплат, датой реестра, доступной датой выплаты и суммой на акцию. Проверка реальных ответов источника выявила ограничения: отсутствие финансового поля нельзя считать нулём, время получения не подтверждает дату отчётности, а налог нельзя однозначно связать с отдельным дивидендом по совпадению даты. Алгоритм корректировки исторических цен после корпоративных событий не подтверждён, поэтому точное наложение старых личных сделок на график не обещается. Выборка проверенных бумаг не доказывает полноту данных всего рынка. История открытых счетов не обещает полную историю владения, включая закрытые счета, переводы и универсальные корпоративные события.