Files
specs/openspec/changes/stock-screener/design.md
T
ksv741 ed6cf299f2
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 9s
spec: add stock screener
2026-10-11 10:44:30 +03:00

15 KiB
Raw Blame History

Engineering Design

Context and Constraints

Текущий текстовый поиск акций использует FindInstrument и ограничивает результат 30 выпусками до получения подробных сведений и цены. Он не может быть источником скринера. Новый поток использует полный Shares(INSTRUMENT_STATUS_ALL) и целевой контракт из System Analysis; существующие ссылки на страницы отдельных акций продолжают использовать точный UID. Справочные сведения остаются online-only, а личный портфель не является каталогом.

Technical Goals / Non-Goals

  • Получить полный список один раз при явном поиске, отфильтровать и зафиксировать упорядоченную выдачу до первой страницы; чтение страницы не вызывает новый поиск T-Invest.
  • Изолировать результат от другого пользователя и от другого поколения его подключения; обеспечить ограниченный расход памяти без нового долговременного справочника.
  • Не обогащать все инструменты ценами и фундаментальными показателями и не менять старый текстовый endpoint или поиск облигаций.

Implementation Impact Map

main

N/A: workspace, Compose и release orchestration не меняются.

frontend

Affected: раздел /stocks становится формой скринера с черновыми и применёнными условиями, явным запуском и страницами результата; использует опубликованный API contract.

backend

Affected: отдельные операции скринера, получение списка Shares, фильтрация и временный результат с безопасной выдачей страниц. Текущий поиск и публичные страницы отдельных акций остаются доступными.

qa

N/A: отдельного QA repository пока нет; системная проверка описана в qa-analysis.md.

Decisions

main

N/A.

frontend

На странице акций форма содержит необязательные текст, сектор, тип акции, валюту, площадку, страну риска и верхнюю границу лота. Черновик полей отделён от последнего применённого поиска; изменение поля само по себе не отправляет запрос и не заменяет результат. Нажатие «Найти» всегда отправляет новый POST, сбрасывает номер страницы и связывает ответ со своим searchId. Последующие страницы читаются GET; поздний ответ прежнего запуска не заменяет результат нового. Ошибка или потеря снимка отображается отдельно от подтверждённого пустого списка.

Повторно использовать имеющуюся карточку InstrumentSearchCard с точным instrumentUid в ссылке. Текущая карточка цены не подходит для скринера без цены: показывать только доступные каталожные поля и не приписывать им котировку. Список опций берётся из известной предметной лексики и доступных значений каталога, если они предоставлены; фильтрация выполняется на сервере. Если динамические опции требуют дополнительного контракта, вернуть этот вопрос в System Analysis до реализации, а не добавлять неоговорённый запрос.

backend

Расширить T-Bank client операцией Shares через существующий gRPC transport, очередь, deadline и retry. Перед выбором страницы проверить полноту полученного ответа, обязательные признаки покупки/торговли и все выбранные поля на каждом инструменте. Сначала отбор по инструментам, затем детерминированный представитель по нормализованному ISIN (при отсутствии — UID), сортировка по тикеру/UID, затем страницы. Не переносить имя, режим или характеристики между UID при дедупликации.

Временный снимок полного результата на backend, привязанный к пользователю и revision подключения, содержит выбранные элементы и asOf; случайный searchId — только указатель, не пропуск доступа. Держать снимок в ограниченной RAM с конечным сроком жизни и пределом числа результатов. Истечение, вытеснение или рестарт дают 404 при чтении страницы и предложение начать новый поиск; никогда не пересчитывать старую страницу по новому каталогу. Для одного экземпляра backend достаточно локального хранилища в памяти; распределённое хранение не требуется текущему deployment. При каждом GET проверить ownership и revision до выдачи страницы. Повторный POST всегда обращается к T-Invest, даже при одинаковых условиях.

qa

N/A.

Cross-cutting Implementation

Не передавать T-Bank token в браузер или результат; служебные события регистрируют исход поиска, время каталога, количество исходных инструментов/выпусков и отсутствие снимка без полного текста поискового условия. При смене подключения прежняя revision теряет доступ к снимку независимо от того, успел ли он вытесниться. Отказ источника не публикует частичный снимок и не превращается в пустую страницу. Ограничить размер памяти/число одновременных результатов; локальный rate limiter остаётся единственным владельцем gRPC-повторов.

Data / Storage Changes

Prisma schema и миграция не нужны: новый результат поиска краткоживущий и не является самостоятельной долговременной копией каталога. После перезапуска backend прежние searchId более недоступны. Персональные сохранённые данные не меняются.

Contract Implementation

Backend DTO и Swagger metadata задают обе операции согласно target OpenAPI. Валидация входа и статусы 400/401/404/503 соответствуют утверждённым ошибкам; success shape включает items, searchId, asOf, page, hasNext. Сгенерированный backend OpenAPI сравнить семантически с target contract до переноса snapshot в frontend и проверить опубликованный artifact и сгенерированные клиентские типы. Не подменять утверждённый контракт отдельным frontend DTO. Старый GET /api/v1/stocks остаётся совместимым.

Developer Verification Strategy

Backend owner проверяет отбор по обоим флагам, все поля условий, граничный лот, дедупликацию после отбора, порядок страниц и привязку к пользователю/revision на собственных тестовых границах; отдельно HTTP validation и соответствие generated OpenAPI target. Frontend owner проверяет разделение черновика и применённых условий, повторный POST, переход между страницами и отображение пустого результата/ошибки на уровне страницы. На этапе реализации применяются соответствующие repo-local verify gates; проверки не требуют живого T-Invest token.

UI/UX Package

Applicability and scope: new/changed UI для страницы /stocks. Пакет полностью inline; графические материалы не требуются.

Package revision: 1, wealth-ledger-specs:openspec/changes/stock-screener/design.md.

Screens, states and interactions: необязательный текст, шесть фильтров и кнопка «Найти»; до запуска нет утверждения об отсутствии акций. После запуска — загрузка, карточки и время списка либо подтверждённое пустое состояние; ошибка источника отдельно с возможностью повторить поиск. Кнопки перехода к страницам показывают тот же снимок; при недоступном снимке — предложение нового поиска. Черновые изменения не меняют показанный результат до нажатия. Формулировка доступности указывает на признаки каталога, а не на возможность немедленной сделки.

Responsive behavior: форма перестраивается в одну колонку на узком экране, карточки и навигация доступны без горизонтального прокручивания.

Reused source and differences: повторно используются текущий раздел акций, его компоненты карточки и состояния загрузки/ошибки; точный вход в текущий AS-IS — frontend/src/pages/stocks/ui/StocksPage/StocksPage.view.tsx, frontend/src/features/instrument-search/ui/InstrumentSearch/InstrumentSearch.view.tsx. Скринер заменяет автоматический поиск при вводе на явный запуск и не обязан показывать цену. Долговременное обоснование текущего поведения — docs/domains/instruments/stocks.md; сам пакет остаётся по locator версии выше и доступен после архива.

Risks / Trade-offs

  • Полный Shares(ALL) может быть объёмным; предельное потребление памяти и допустимую длительность live-поиска нужно измерить до rollout. Локальный снимок экономит повторные вызовы и сохраняет порядок, но теряется при рестарте или вытеснении; предусмотрен явный повторный поиск.
  • Опции фильтров по сектору/стране могут изменяться вместе с каталогом. Нельзя обещать полноту пользовательского выбора, если показывать только фиксированные значения без подтверждения состава каталога.
  • Поток не знает права конкретного счёта и состояние торгов; текст интерфейса не должен обещать немедленную покупку.

Migration / Rollout / Rollback

Сначала backend реализует и публикует совместимый контракт, затем frontend получает новый artifact и использует скринер; действующий текстовый endpoint остаётся доступным. При откате frontend продолжает работать со старым endpoint; временные снимки можно удалить без миграции. Изменений личных данных и разрушительных миграций нет.

Solution Assessment

Сопоставлены: (1) фильтрация первых 30 результатов FindInstrument — дешёвая, но неверная полнота, отклонена; (2) сохранённый постоянный каталог с индексацией — облегчает массовые запросы, но противоречит online-only границе и добавляет синхронизацию; (3) полный online Shares и ограниченный краткоживущий снимок результата — соответствует полноте и стабильным страницам при текущем single-instance deployment. Последний вариант принят для изменения. При реальной потребности в нескольких экземплярах потребуется отдельное согласование shared storage или stateless модели страниц, а не молчаливое изменение контракта.