# Страница акции Авторизованный пользователь открывает `/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). История открытых счетов не обещает полную историю владения, включая закрытые счета, переводы и универсальные корпоративные события.