docs: make project documentation self-contained
This commit is contained in:
+52
-13
@@ -1,16 +1,55 @@
|
||||
# Возможности
|
||||
|
||||
Здесь документируется фактическое поведение завершенных пользовательских фич: пользовательский поток, API-сценарий и ограничения. Не реализованные возможности сюда не добавляются.
|
||||
Карта текущих возможностей по предметным областям. Каждый сценарий описывает
|
||||
поведение, взаимодействие компонентов и ограничения. Границы системы —
|
||||
в [обзоре](../architecture/system-overview.md), значения терминов —
|
||||
в [глоссарии](../architecture/domain-glossary.md).
|
||||
|
||||
- [Аутентификация](authentication.md) — регистрация, сессии и refresh.
|
||||
- [Подключение T-Bank](tbank-connection.md) — безопасное управление
|
||||
подключением.
|
||||
- [Брокерские счета](brokerage-accounts.md) — список поддерживаемых счетов.
|
||||
- [Портфель](portfolio-view.md) — состав инвестиций и freshness states.
|
||||
- [Account Ledger](brokerage-account-ledger.md) — исполненные операции счёта.
|
||||
- [Future Cash Events](brokerage-account-upcoming-events.md) — прогнозируемые
|
||||
события по текущим позициям.
|
||||
- [Пассивный доход брокерского счёта](brokerage-account-income-analytics.md) —
|
||||
исполненные купоны и дивиденды.
|
||||
- [Frontend UI resilience](frontend-ui-resilience.md) — устойчивые состояния
|
||||
интерфейса.
|
||||
## Доступ и подключение
|
||||
|
||||
Аутентификация связывает локального пользователя с сессиями. Подключение
|
||||
T-Bank обеспечивает доступ к брокеру; наличие сессии не заменяет проверку
|
||||
подключения и доступа к конкретному счёту.
|
||||
|
||||
| Сценарий | Что объясняет | Ответственность компонентов |
|
||||
| --- | --- | --- |
|
||||
| [Аутентификация](authentication.md) | Вход, восстановление и завершение сессии | Backend проверяет credentials и управляет сессиями; frontend хранит текущее состояние входа и выбирает доступные экраны |
|
||||
| [Подключение T-Bank](tbank-connection.md) | Защита token, замена подключения и сброс кэшей | Backend проверяет и шифрует token; frontend показывает подтверждённый статус и обновляет финансовые данные |
|
||||
|
||||
## Инвестиционные данные
|
||||
|
||||
Сценарии используют текущий состав доступных счетов. Денежные расчёты принадлежат
|
||||
backend; frontend выбирает представление и формат. Исполненные операции,
|
||||
полученные выплаты и будущие события имеют разные источники и смысл.
|
||||
|
||||
| Сценарий | Что объясняет | Ответственность компонентов |
|
||||
| --- | --- | --- |
|
||||
| [Брокерские счета](brokerage-accounts.md) | Доступный состав и отдельные сводки | Backend устанавливает доступ и собирает сводки; frontend показывает каждый счёт и состояние его данных |
|
||||
| [Портфель](portfolio-view.md) | Позиции, остатки, точные суммы и свежесть | Подсистема портфеля нормализует снимки и рассчитывает итоги; frontend сохраняет различие актуальных и устаревших данных |
|
||||
| [Все счета](all-accounts.md) | Агрегация, происхождение строк и общая пагинация | Backend объединяет доступные счета по правилам каждого раздела; frontend сохраняет выбранный раздел и фильтры при переключении |
|
||||
| [Журнал операций](brokerage-account-ledger.md) | Исполненные операции, фильтры и пагинация | Backend получает и нормализует операции брокера; frontend управляет применёнными фильтрами и навигацией |
|
||||
| [Пассивный доход](brokerage-account-income-analytics.md) | Полученные купоны/дивиденды, периоды и сравнения | Backend вычисляет доход по исполненным выплатам; frontend показывает суммы, периоды и состав расчёта |
|
||||
| [Будущие выплаты](brokerage-account-upcoming-events.md) | Прогноз по текущим позициям и его ограничения | Backend сопоставляет календарь выплат и позиции; frontend отделяет прогноз от фактически полученного дохода |
|
||||
|
||||
## Инструменты
|
||||
|
||||
Рыночные данные выпуска отличаются от личной позиции и факта зачисления.
|
||||
Страницы акций и облигаций используют общий подход к показу графика, позиции
|
||||
и личных событий, сохраняя различия денежных и процентных шкал.
|
||||
|
||||
| Сценарий | Что объясняет | Ответственность компонентов |
|
||||
| --- | --- | --- |
|
||||
| [Акции](stock-detail.md) | Поиск, идентификация выпуска, котировки, дивиденды и личная история | Backend разрешает идентификатор и объединяет источники; frontend независимо показывает секции выбранной акции |
|
||||
| [Облигации](bond-detail.md) | Цена в процентах, купоны, доходности и рейтинг выпуска | Backend сопоставляет данные T-Bank, MOEX и НКР; frontend сохраняет происхождение, время и единицы каждого показателя |
|
||||
|
||||
## Состояния интерфейса
|
||||
|
||||
[Frontend UI resilience](frontend-ui-resilience.md) объясняет первичную загрузку,
|
||||
ошибки, пустые результаты и сохранённые клиентом данные. Frontend управляет
|
||||
доступом к экранам, доступностью запросов и восстановлением отдельных разделов.
|
||||
Ошибки фонового обновления не должны скрывать уже доступные данные текущего
|
||||
пользователя; смена сессии или подключения очищает данные прежней границы.
|
||||
|
||||
Новая возможность описывается после реализации вместе с ответственными
|
||||
подсистемами и ограничениями. Порядок обновления знаний —
|
||||
[ведение документации](../development/documentation.md).
|
||||
|
||||
@@ -33,7 +33,7 @@ Backend публикует статические endpoints под `/api/v1/brok
|
||||
OpenAPI, snapshot и frontend types генерируются из него.
|
||||
|
||||
- Портфель и доступные денежные остатки суммируются сервером точно, по исходной
|
||||
валюте, через существующую decimal-арифметику. FX-конвертации нет; правила
|
||||
валюте, с точной десятичной арифметикой. FX-конвертации нет; правила
|
||||
неизвестных значений и состава сумм наследуются от [портфеля](portfolio-view.md).
|
||||
- Одинаковые бумаги разных счетов показаны одной карточкой с общим названием
|
||||
и идентификатором. Внутри всегда видны строки счетов: ссылка на портфель,
|
||||
@@ -60,8 +60,8 @@ OpenAPI, snapshot и frontend types генерируются из него.
|
||||
|
||||
## Полнота, свежесть и доступ
|
||||
|
||||
Каждый общий запрос получает состав счетов и единую generation через
|
||||
`TbankAccessService`. Получение данных использует эту же connection boundary;
|
||||
Для каждого общего запроса backend определяет состав доступных счетов
|
||||
и единое поколение подключения. Получение данных использует эту же connection boundary;
|
||||
проверки сессии, доступа и существующий лимитер внешних запросов сохраняются.
|
||||
Финансовые данные не сохраняются в БД; новые зависимости не добавляются.
|
||||
|
||||
|
||||
@@ -1,21 +1,82 @@
|
||||
# Authentication
|
||||
|
||||
Текущее объяснение локальной identity и сессий WealthLedger. Auth связывает
|
||||
[User](../architecture/domain-glossary.md#user) с browser session; доступ к
|
||||
брокеру принадлежит отдельному [T-Bank connection](tbank-connection.md).
|
||||
Credentials и ротация принадлежат backend, auth state и route decisions — frontend.
|
||||
|
||||
## User flow
|
||||
|
||||
Пользователь регистрируется или входит по email и паролю. Регистрация сразу создаёт первую сессию. Email сохраняется в нормализованном виде; пароль должен содержать от 12 до 72 UTF-8 байтов.
|
||||
Пользователь регистрируется или входит по email и паролю. Регистрация сразу создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся к нижнему регистру. Backend требует пароль не короче 12 символов и не длиннее 72 UTF-8 байтов.
|
||||
|
||||
**Требует ручного решения:** сохранённое требование регистрации и проверка
|
||||
в форме frontend считают нижнюю границу в UTF-8 байтах, а backend — в символах.
|
||||
Не-ASCII пароль может пройти форму и получить отказ API. Это описание
|
||||
наблюдаемого рассогласования, а не принятое изменение password policy;
|
||||
исправление требует отдельного согласованного security решения и HTTP-проверки.
|
||||
|
||||
Access token живёт 15 минут только в памяти браузера. Refresh token живёт 7 дней в cookie `refresh_token` с `httpOnly` и `SameSite=Lax`; при восстановлении сессии он заменяется. После перезагрузки приложение восстанавливает access token через refresh-cookie до показа защищённых маршрутов. Пока результат восстановления неизвестен, frontend показывает нейтральный structural skeleton каркаса без public или protected route content; после завершения bootstrap сохраняются прежние decisions и redirects.
|
||||
|
||||
Все маршруты, кроме `/login` и `/register`, требуют сессию. После входа пользователь возвращается на исходный внутренний маршрут, если он был. Logout доступен в настройках, очищает локальную сессию независимо от состояния сети и отзывает серверную сессию при доступном refresh-cookie.
|
||||
|
||||
## Восстановление сессии
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant F as React SPA
|
||||
participant B as NestJS Auth
|
||||
participant D as SQLite Session
|
||||
Note over F: Reload: access token в памяти отсутствует
|
||||
F->>B: Refresh с httpOnly cookie
|
||||
B->>D: Проверить Session и текущий hash token
|
||||
alt Действительная сессия, ротация выиграна
|
||||
B->>D: Атомарно заменить hash и срок
|
||||
B-->>F: Access token, профиль, новая refresh-cookie
|
||||
F->>B: Protected request с Bearer
|
||||
else Cookie нет, token истёк или уже заменён
|
||||
B-->>F: 401 и очистка cookie
|
||||
Note over F: Готовое неавторизованное состояние
|
||||
end
|
||||
```
|
||||
|
||||
В одном экземпляре frontend одновременные refresh используют один незавершённый запрос;
|
||||
между вкладками используется Web Locks, когда browser его поддерживает.
|
||||
Backend всё равно защищает ротацию атомарно: browser coordination не заменяет
|
||||
server-side invariant. Обычный protected запрос с `401 UNAUTHORIZED` проходит
|
||||
refresh и один повтор через общий transport; auth endpoints из этого цикла
|
||||
исключены. Сам по себе истёкший access token не запускает фоновый таймер refresh.
|
||||
|
||||
## API
|
||||
|
||||
`POST /api/v1/auth/register`, `POST /api/v1/auth/login` и `POST /api/v1/auth/refresh` возвращают профиль и access token с HTTP `200`. `POST /api/v1/auth/logout` отвечает `204`. `GET /api/v1/auth/me` требует Bearer access token.
|
||||
Endpoints, статусы и формы публичных ответов определяет backend через OpenAPI;
|
||||
проверка snapshot описана в [contract workflow](../development/runtime-openapi-verification.md).
|
||||
Причины хранения и сроков tokens — [ADR 006](../adr/006-auth-session-policy.md).
|
||||
Refresh-cookie ограничена auth API. Признак Secure зависит от режима окружения:
|
||||
локальный HTTP предполагает development mode, см.
|
||||
[локальное окружение](../development/local-workspace.md).
|
||||
|
||||
Ошибки API имеют `application/problem+json` с полями `status` и `code`. Ошибки валидации содержат `errors` с JSON Pointer полей. HTTP- и транспортная ошибка refresh одинаково завершают cold start готовым неавторизованным состоянием; при refresh после protected `401` frontend очищает in-memory session и React Query cache, сохраняя исходный `401` для существующего login flow.
|
||||
Форма ошибок и граница её HTTP-проверки описаны у владельца
|
||||
[HTTP API](../architecture/http-api.md#error-contract).
|
||||
HTTP- и транспортная ошибка refresh одинаково завершают cold start готовым неавторизованным состоянием; при refresh после protected `401` frontend очищает in-memory session и React Query cache, сохраняя исходный `401` для существующего login flow.
|
||||
|
||||
## Limitations
|
||||
|
||||
MVP не включает подтверждение email, восстановление или смену пароля, смену email, удаление аккаунта, список устройств, logout всех устройств или rate limit попыток входа. Access token может оставаться действительным до 15 минут после logout.
|
||||
|
||||
Если несколько клиентов одновременно предъявляют один действительный refresh token, сервер завершает одну ротацию успешно, а остальные запросы получают `401`; сессия победившего запроса остаётся действительной. Уже заменённый refresh token также даёт `401`, но сам по себе не отзывает сессию: MVP не хранит историю token или grace-window, чтобы отличить конкурентный запрос от replay.
|
||||
|
||||
Это ограничение и его rationale приняты в
|
||||
[ADR 009](../adr/009-concurrent-refresh-rotation-policy.md). Refresh token имеет
|
||||
server-side срок, но cookie не задаёт собственные `maxAge`/`expires`: сохранение
|
||||
cookie после закрытия браузера не является обещанием семидневного входа.
|
||||
|
||||
## Ответственность и проверка
|
||||
|
||||
Backend проверяет credentials и tokens, хранит состояние Session и атомарно
|
||||
выполняет ротацию. Frontend управляет состоянием входа, восстановлением после
|
||||
reload и доступностью экранов; общий HTTP-транспорт передаёт Bearer token
|
||||
и выполняет описанный выше refresh/retry.
|
||||
|
||||
Проверки охватывают конкурентную ротацию, состояние входа в браузере и поведение
|
||||
транспорта. Изолированные проверки этих частей не заменяют полный HTTP-поток
|
||||
и реальную базу при проверке нового изменения безопасности.
|
||||
|
||||
@@ -59,7 +59,7 @@ FX-конвертация не выполняется, первоначальн
|
||||
отсутствия рейтингов других агентств. Покрытие и альтернативы исследованы
|
||||
в [обзоре источников](../research/bond-credit-ratings.md).
|
||||
|
||||
`NcrRatingsClient` разбирает HTML через Cheerio: штатного открытого API для
|
||||
Интеграция НКР читает публичную HTML-таблицу: штатного открытого API для
|
||||
этой таблицы не подтверждено. Общий публичный реестр кэшируется на шесть часов,
|
||||
незавершённые загрузки объединяются; повторные запросы после ошибки ограничены
|
||||
одной минутой. Изменение структуры таблицы, неоднозначные данные и ошибки
|
||||
@@ -98,8 +98,7 @@ FX-конвертация не выполняется, первоначальн
|
||||
|
||||
## Владельцы и восстановление
|
||||
|
||||
`BondController` и `BondService` принадлежат существующему
|
||||
`TbankConnectionModule`. Все `/api/v1/bonds` endpoints требуют Bearer session
|
||||
Страницу облигации обслуживает инвестиционная подсистема backend. Все `/api/v1/bonds` endpoints требуют Bearer session
|
||||
и актуального подключения Т-Банка: поиск, разрешение тикера, карточка по UID,
|
||||
`quote`, `candles`, `position`, `coupons`, `metrics`. Wire contract принадлежит runtime
|
||||
OpenAPI, frontend snapshot и types генерируются из него.
|
||||
@@ -124,4 +123,4 @@ Instruments client, сохраняющий отсутствие полей. Ры
|
||||
Ошибка фонового обновления сохраняет последние данные. Смена подключения
|
||||
сбрасывает subtree `/bonds` вместе с остальными финансовыми запросами;
|
||||
logout использует общий session reset. Общие график, позиция и личные события
|
||||
переиспользуются страницами акций и облигаций через `instrument-details`.
|
||||
имеют общее представление на страницах акций и облигаций.
|
||||
|
||||
@@ -43,7 +43,7 @@ skeleton. Пустой результат объясняет отсутстви
|
||||
- Только точные upstream types coupon и dividend формируют результат. Суммы остаются exact decimal strings в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и dividend transfer не входят в доход.
|
||||
- Итоги, сравнения, buckets и Instrument breakdown не смешивают валюты и не создают общий FX total. На странице показано только сравнение с годом ранее, когда календарная дата и история счёта позволяют. Предыдущий период остаётся в API response, но не отображается.
|
||||
- Buckets по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые buckets. Страница дохода не строит из них график или текстовую динамику; API response не меняется.
|
||||
- Раздел «Выплаты» показывает единый список LedgerEntryRow-карточек в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступная identity и signed Money видны в каждой карточке; валюты по-прежнему разделены в summary/comparisons. Дополнительный запрос за составом не нужен.
|
||||
- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступная identity и signed Money видны в каждой карточке; валюты по-прежнему разделены в summary/comparisons. Дополнительный запрос за составом не нужен.
|
||||
|
||||
Раздел «Годом ранее» показывает цветные панели без общей белой подложки и
|
||||
внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей через
|
||||
@@ -65,7 +65,7 @@ skeleton. Пустой результат объясняет отсутстви
|
||||
серверным правилом; клиент воспринимает ключ как непрозрачную строку.
|
||||
Общий Ledger endpoint не приобретает income-specific поле.
|
||||
|
||||
Income VM проверяет принадлежность entries по полученному ключу до подготовки
|
||||
Frontend проверяет принадлежность выплат по полученному ключу до подготовки
|
||||
плоского списка выплат. Отсутствующий, пустой или
|
||||
нестроковый ключ, неоднозначные instrument keys, неизвестная группа, другая
|
||||
валюта или несовпадение receipt count приводят к существующему локальному
|
||||
|
||||
@@ -38,7 +38,7 @@ filters и данные сохраняют существующую семант
|
||||
- Запрашиваются только исполненные операции T-Bank.
|
||||
- Полный журнал фильтруется по периоду (два DateInput «С»/«По» с форматом DD.MM.YYYY и min/max-ограничением, русская локаль календаря) и категориям (MultiSelect с мультивыбором из trade, income, tax, fee, transfer, other). Фильтры имеют черновик в контролах и применённое состояние: изменение черновика не вызывает новый запрос к серверу; «Применить» коммитит черновик и сбрасывает cursor-пагинацию на первую страницу; «Сбросить» очищает черновик и немедленно применяет пустой фильтр (период по умолчанию и все категории). Cursor-пагинация («Назад»/«Далее») работает по последнему применённому фильтру, не по черновику.
|
||||
- В полном журнале и пяти последних записях обзора одна и та же компактная строка показывает логотип инструмента либо буквенный fallback, описание операции, дату, инструмент и денежное движение. Денежная метка строки показывает подпись конкретного типа события: для income-записи — «Купон» или «Дивиденд» (по upstream-типу купона или дивиденда), для остальных — подпись категории («Сделка», «Налог», «Комиссия», «Перевод», «Прочее»). Подпись типа выводится один раз и не повторяется во вторичной метадате (дата · инструмент); направление передаётся знаком «+» / «−` и семантическим цветом, а не текстом. Записи без движения денег остаются нейтральными («Без движения денег»).
|
||||
- Полный журнал не показывает цену, количество, НКД и комиссию записи. Страница акции переиспользует тот же `LedgerEntryRow` с дополнительными количеством, ценой и ссылкой на счёт. Даты имеют формат ДД.ММ.ГГГГ, время операций явно обозначено UTC. При отсутствии описания используется локализованный тип операции.
|
||||
- Полный журнал не показывает цену, количество, НКД и комиссию записи. Страница акции использует тот же формат строки с дополнительными количеством, ценой и ссылкой на счёт. Даты имеют формат ДД.ММ.ГГГГ, время операций явно обозначено UTC. При отсутствии описания используется локализованный тип операции.
|
||||
- Backend хранит в памяти процесса до 100 страниц Account Ledger: свежая страница живёт до 30 секунд, а при переполнении вытесняется давно не использовавшаяся страница (LRU). Ключ включает непрозрачную generation текущего T-Bank connection, пользователя, счёт, нормализованные период и categories, cursor и limit; отсутствующий `to` приводится к текущему 30-секундному bucket. Проверка connection и доступа к счёту выполняется до выдачи кэшированной страницы. После успешного создания, замены или удаления connection сохранённая либо поздно завершившаяся страница предыдущей generation недоступна Operations, Income Summary и Income Analytics. Старые страницы удаляются обычными TTL/LRU limits; операции не сохраняются и не синхронизируются локально.
|
||||
- Журнал не рассчитывает остаток после операции, суммарные доходы или налоги, историческую стоимость Portfolio, конвертацию валют и рыночные изменения цены.
|
||||
- `409 TBANK_NOT_CONNECTED`, `400 INVALID_TBANK_TOKEN`, `404` недоступного счёта и `503 TBANK_UNAVAILABLE` используют существующую семантику ошибок T-Bank.
|
||||
|
||||
@@ -32,10 +32,10 @@ sequenceDiagram
|
||||
заголовка и event rows вместо отдельного spinner. Placeholder не представляет
|
||||
данные и не является интерактивным; при пустом результате фильтр остаётся доступным для изменения периода.
|
||||
|
||||
Page VM владеет account route input, authenticated entity query, выбором
|
||||
loading/error/loaded, пустого результата и retry. Container вызывает один VM и передаёт
|
||||
данные существующим state views. Query без account ID остаётся отключённым;
|
||||
выделение VM не меняет порядок выбора состояний или поведение фонового refetch.
|
||||
Frontend определяет выбранный счёт по адресу страницы, запрашивает данные
|
||||
в рамках текущей сессии и показывает загрузку, ошибку, пустой или успешный
|
||||
результат. Без идентификатора счёта запрос не выполняется. Повторная попытка
|
||||
и фоновое обновление сохраняют правила отображения уже доступных данных.
|
||||
|
||||
- По умолчанию период включает текущую календарную дату и следующие 30 дней. Полный список позволяет выбрать другой период полями «С» и «По», как в операциях: «Применить» сохраняет даты в URL и запрашивает события, «Сбросить» возвращает период по умолчанию. Обзор счёта сохраняет прогноз на 30 дней.
|
||||
- Суммы до налогов рассчитываются по текущему количеству бумаг на `calculatedAt`; они не учитывают будущие сделки или владение на дату реестра.
|
||||
|
||||
@@ -33,7 +33,7 @@ sequenceDiagram
|
||||
|
||||
`GET /api/v1/brokerage-accounts/:accountId/portfolio` требует Bearer access token. Успешный ответ содержит Portfolio Snapshot: `lastSuccessfulAt` в RFC 3339 UTC, `isStale`, `totalsByCurrency`, `positions` и `cashBalances`.
|
||||
|
||||
Все количества и суммы передаются decimal strings без экспоненциальной записи. Деньги передаются как Money `{ currency, value }` с ISO-кодом валюты. Backend выполняет точные операции через `BigInt`; frontend форматирует строки для русской локали и не выполняет финансовую арифметику кроме display-only оценки доли позиции.
|
||||
Все количества и суммы передаются decimal strings без экспоненциальной записи. Деньги передаются как Money `{ currency, value }` с ISO-кодом валюты. Backend выполняет точные денежные операции; frontend форматирует строки для русской локали и не выполняет финансовую арифметику кроме display-only оценки доли позиции.
|
||||
|
||||
`Portfolio Position` -- ненулевой обычный актив из T-Bank portfolio. Position с type `CURRENCY` означает торгуемый валютный Instrument и не является Cash Balance. `currentValue` равен количеству, умноженному на текущую цену, когда оба значения и валюта доступны. `absoluteReturn` доступен только при одинаковой известной валюте текущей и средней цены. `instrumentUid` передаётся из upstream для точной идентификации и перехода на [страницу акции](stock-detail.md). `isin` и `ticker` берутся из уже разрешённой metadata Instrument и передаются nullable-строками. Невычислимые значения передаются `null`, а не нулём. Позиции остаются в upstream-порядке; виртуальные позиции не входят в ответ.
|
||||
|
||||
|
||||
+15
-12
@@ -38,7 +38,7 @@ SBER не заменяется на SBERP. Однобуквенные тикер
|
||||
Денежные показатели используют символ валюты; описание компании видно сразу
|
||||
под логотипом и названием, перед ценой. Далее идут график, моя позиция, предстоящие
|
||||
дивиденды и личная история событий.
|
||||
Логотип использует существующий CDN helper и буквенный fallback.
|
||||
Логотип загружается из публичного CDN; при отсутствии изображения показывается буквенная заглушка.
|
||||
Позиция отсутствующей или полностью проданной бумаги не создаёт пустого блока;
|
||||
рыночные данные и личная история остаются доступны.
|
||||
|
||||
@@ -51,8 +51,7 @@ UTC, которое может быть старше времени получе
|
||||
## API и владельцы
|
||||
|
||||
Все методы требуют Bearer session и текущее подключение Т-Банка.
|
||||
Новые endpoints принадлежат `StockController` / `StockService` существующего
|
||||
`TbankConnectionModule`:
|
||||
Публичный API страницы акции обслуживает инвестиционная подсистема backend:
|
||||
|
||||
| Метод | Содержимое |
|
||||
| --- | --- |
|
||||
@@ -76,7 +75,7 @@ UID включён в ключ кэша и подпись общего курс
|
||||
восстанавливается переходом на первую страницу с повторным запросом.
|
||||
|
||||
Сервис портфеля остаётся владельцем снимков и оценки позиций. Новая сводка
|
||||
фильтрует `STOCK` по точному UID и использует decimal/BigInt, без FX.
|
||||
фильтрует `STOCK` по точному UID и использует точную десятичную арифметику, без конвертации валют.
|
||||
Средняя цена равна сумме количеств, умноженных на средние цены, делённой на
|
||||
общее количество; она округляется сервером до девяти знаков, проценты — до шести.
|
||||
Неполные или разновалютные денежные итоги недоступны. Для нулевого количества
|
||||
@@ -134,9 +133,9 @@ TTM означает последние 12 месяцев, MRQ — послед
|
||||
|
||||
## Границы и проверка
|
||||
|
||||
Новые RPC проходят существующий `TbankClient`: TLS, rate limit, очередь,
|
||||
deadline и retry. Доступ/расшифровка остаются у `TbankAccessService` и
|
||||
`TbankTokenService`. Успешные ответы и совместные незавершённые запросы кэшируются
|
||||
Запросы T-Bank проходят общую серверную интеграцию с TLS, лимитом частоты,
|
||||
очередью, предельным временем ожидания и повторными попытками. Проверка доступа
|
||||
и расшифровка token выполняются только backend. Успешные ответы и совместные незавершённые запросы кэшируются
|
||||
в bounded LRU на 30 секунд по пользователю, поколению подключения и параметрам.
|
||||
Для поиска `FindInstrument` дополняется сведениями `ShareBy` через тот же
|
||||
кэш карточки инструмента и одним пакетным `GetLastPrices` для списка. Ошибка
|
||||
@@ -148,15 +147,19 @@ deadline и retry. Доступ/расшифровка остаются у `Tban
|
||||
данными при изменении подключения; logout использует существующий session reset.
|
||||
|
||||
Контракт генерируется из runtime Nest OpenAPI, затем обновляются frontend snapshot
|
||||
и generated types. Unit/HTTP/MSW tests проверяют точность, разделение инструментов,
|
||||
и generated types. Проверки отдельных подсистем и HTTP-взаимодействия проверяют точность, разделение инструментов,
|
||||
фильтрацию и курсоры, валидацию, отсутствие позиции и локальные ошибки.
|
||||
Браузерная проверка использует синтетические ответы и широкий/узкий viewport.
|
||||
Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и
|
||||
временные зоны расчётов не меняются. Личная история использует общий с портфелем
|
||||
`LedgerEntryRow`, дополненный количеством, ценой и ссылкой на счёт. История
|
||||
временные зоны расчётов не меняются. Личная история использует общий с портфелем формат строки операции,
|
||||
дополненный количеством, ценой и ссылкой на счёт. История
|
||||
дивидендов компании раскрывается в оформленном блоке с отдельными карточками
|
||||
выплат, датой реестра, доступной датой выплаты и суммой на акцию.
|
||||
Реальные ограничения источника описаны в
|
||||
[результатах исследования](../../design/stock-detail-api-live-findings.md).
|
||||
Проверка реальных ответов источника выявила ограничения: отсутствие финансового
|
||||
поля нельзя считать нулём, время получения не подтверждает дату отчётности,
|
||||
а налог нельзя однозначно связать с отдельным дивидендом по совпадению даты.
|
||||
Алгоритм корректировки исторических цен после корпоративных событий не
|
||||
подтверждён, поэтому точное наложение старых личных сделок на график не обещается.
|
||||
Выборка проверенных бумаг не доказывает полноту данных всего рынка.
|
||||
История открытых счетов не обещает полную историю владения, включая закрытые
|
||||
счета, переводы и универсальные корпоративные события.
|
||||
|
||||
@@ -36,7 +36,7 @@ API, logs или errors. Поэтому сохранённый или поздн
|
||||
|
||||
После успешного `PUT` или `DELETE` frontend сначала коммитит подтверждённый
|
||||
статус connection, затем сбрасывает все generated GET query в
|
||||
`/api/v1/brokerage-accounts` и `/api/v1/stocks` subtrees. Активные выборки запрашиваются заново, а
|
||||
`/api/v1/brokerage-accounts`, `/api/v1/stocks` и `/api/v1/bonds` subtrees. Активные выборки запрашиваются заново, а
|
||||
неактивные загружаются при следующем использовании; данные предыдущей границы
|
||||
не остаются Client Cached Snapshot. Неуспешная mutation не меняет status,
|
||||
backend generation или financial cache. Обычная ошибка фонового refetch
|
||||
@@ -58,9 +58,10 @@ FIFO-ограничитель на экземпляр запускает не б
|
||||
## Ограничения
|
||||
|
||||
Подключение используется поставленными сценариями Brokerage Accounts,
|
||||
Portfolio, Account Ledger, Future Cash Events и [страницы акции](stock-detail.md); frontend не обращается к
|
||||
Portfolio, Account Ledger, Future Cash Events, [страницы акции](stock-detail.md)
|
||||
и [облигации](bond-detail.md); frontend не обращается к
|
||||
T-Bank напрямую. Token encryption key не ротируется, rate limit остаётся
|
||||
per-instance, а обновление connection не сериализуется между распределёнными
|
||||
экземплярами backend. Вендорные protobuf-контракты взяты из
|
||||
`references/moex-vibe`; при необходимости обновляйте их вручную по официальной
|
||||
документации T-Bank Invest API.
|
||||
экземплярами backend. Интеграция использует protobuf-контракт T-Bank Invest API. Автоматическое
|
||||
обновление внешнего протокола не предусмотрено; совместимость новой версии
|
||||
проверяется при отдельном обновлении интеграции.
|
||||
|
||||
Reference in New Issue
Block a user