Files
specs/openspec/changes/account-top-positions/proposal.md
T
codex-bot c66a5cd962
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 9s
spec: record account-top-positions planning and implementation (WealthLedger/main#48) (#69)
2026-11-01 19:29:11 +03:00

153 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Proposal
Issue: WealthLedger/main#48
## Why
Страница счёта показывает состав Portfolio списком позиций и распределением по классам активов. Ответа на вопрос «где сконцентрированы деньги» нет: чтобы понять, какие бумаги, отрасли и компании занимают основную часть счёта, владельцу приходится вручную сравнивать карточки позиций. Распределение по классам активов этого не даёт: оно не показывает ни крупнейшие позиции, ни отраслевой или эмитентский перекос.
## What Changes
- На странице счёта рядом с вкладкой «Позиции» появляется вкладка «Концентрация».
- Вкладка содержит четыре разреза текущего состава Portfolio выбранного счёта: «Акции», «Облигации», «Секторы», «Эмитенты».
- В каждом разрезе показаны пять крупнейших элементов по текущей денежной оценке, каждый с суммой и долей, а при семи и более элементах все остальные объединены в одну строку «Прочее» (при шести или меньше элементах каждый показан своей строкой). По нажатию на «Прочее» раскрывается полный перечень остальных элементов, каждый с суммой и долей; пять первых строк при этом не меняются. Рядом с перечнем показана диаграмма распределения, согласованная с перечнем, и суммарная доля пяти первых элементов как показатель концентрации.
- «Акции» и «Облигации» ранжируют позиции типов `STOCK` и `BOND` соответственно. Фонды в эти два разреза не входят.
- «Секторы» агрегируют только позиции типов `STOCK`, `BOND` и `FUND`. Позиции типов `CURRENCY`, `FUTURE`, `OPTION`, `STRUCTURED_NOTE`, `DIGITAL_FINANCIAL_ASSET` и `OTHER` в разрез не входят. Позиции перечисленных трёх типов без сектора попадают в явную группу «Сектор не указан».
- «Секторы» используют сектор инструмента в том виде, как его определяет T-Bank, с русской подписью; государственные облигации определяются значением сектора T-Bank `government` с подписью «Государственный сектор», других собственных классификаций WealthLedger не вводит.
- «Эмитенты» объединяют акции и облигации одной компании в один элемент. Эмитент — бренд актива по данным T-Bank; название эмитента — название бренда (при разных названиях одного бренда — название позиции с наибольшей стоимостью). Эмитент заполняется в данных Portfolio и у фондов, а разрез «Эмитенты» фонды отбрасывает. Позиции без бренда попадают в явную группу «Эмитент не указан» с видимой долей; если бренда нет у многих позиций, разрез всё равно остаётся в MVP, а объём scope не меняется. Фонды в разрез не входят: их эмитент — управляющая компания, а не компании внутри фонда.
- Значения показываются в одной валюте за раз. У вкладки свой список валют и свой выбор, независимые от «Структуры портфеля»: в списке валюты позиций акций, облигаций и фондов; по умолчанию выбрана первая по ISO-коду. При одной валюте выбор не показывается. Конвертации нет, общего итога по нескольким валютам нет. Выбранные разрез и валюта сохраняются, пока User остаётся на странице счёта, и сбрасываются при новом заходе и перезагрузке. Доли показываются в формате карточек позиций (обычное округление до одного знака, «< 0,1 %» для очень малых).
- Для разрезов «Секторы» и «Эмитенты» в данных позиций Portfolio появляются сектор и признак эмитента. Это расширение ответа Portfolio новыми необязательными данными; существующие данные позиций и их смысл не меняются. Изменение не является BREAKING.
## Scope
### In Scope
- Вкладка «Концентрация» на странице одного счёта.
- Разрезы «Акции», «Облигации», «Секторы», «Эмитенты» с топ-5, «Прочее», суммой и долей по элементам, диаграммой распределения и суммарной долей топ-5.
- Раскрытие «Прочее» по нажатию с полным перечнем остальных элементов.
- Явные группы «Сектор не указан» и «Эмитент не указан» с видимой долей.
- Выбор одной валюты, пересчёт всех разрезов вкладки под выбранную валюту.
- Состояния вкладки при отсутствии позиций в выбранной валюте, при отсутствии нужных данных (сектор, эмитент) и при ошибке или устаревании Portfolio — в рамках уже действующего поведения Portfolio.
- Расширение данных позиций Portfolio сектором и признаком эмитента (аддитивно).
- Обновление AS-IS документации домена Portfolio, интеграции T-Bank и глоссария (термины «Концентрация», «Сектор», «Эмитент»).
### Out of Scope / Non-Goals
- Общий топ-5 по всем типам позиций вместе (исходная формулировка Issue): рейтинги строятся только внутри разрезов «Акции», «Облигации», «Секторы», «Эмитенты».
- Страна риска и региональное распределение.
- Сроки погашения облигаций.
- Лидеры роста и падения, доход по позициям и вклад выплат в доход.
- Показатель `riskLevel` T-Bank и кредитный рейтинг агентств.
- Индекс концентрации (HHI) и эффективное число позиций.
- Конвертация валют, единый рейтинг или единая диаграмма по нескольким валютам.
- Замена, изменение или повтор существующей «Структуры портфеля» и вкладки «Позиции».
- Режим «Все счета»: вкладка относится только к странице одного счёта.
- Изменение порядка, состава и свежести Portfolio Snapshot.
## Capabilities
### New Capabilities
- `specs/portfolio-concentration/spec.md`: вкладка «Концентрация» — разрезы, ранжирование топ-5, «Прочее», доли и суммарная доля топ-5, выбор валюты, правила учёта позиций, состояния.
### Modified Capabilities
- `specs/portfolio-view/spec.md`: нормализованный состав Portfolio расширяется сектором и эмитентом позиции (требование «Нормализованный состав Portfolio»); добавляются требования о сроке обновления, последнем известном значении и ошибках получения сектора и эмитента. Требование «Точные значения и валютные агрегаты» не меняется; новая capability `portfolio-concentration` на него ссылается.
## Impact
- Владелец счёта получает новый экран анализа; существующие экраны и их поведение не меняются.
- Затрагиваются backend (обогащение данных Portfolio), frontend (новая вкладка и её разрезы), specs и AS-IS документация.
- Ответ Portfolio расширяется аддитивно: клиенты, не знающие новых данных, продолжают работать без изменений. Публичные API-артефакты потребуют обновления.
- Часовая синхронизация с T-Bank дополнительно получает данные об инструментах и брендах для позиций; это может увеличить число обращений к T-Bank и время синхронизации. Оценка нагрузки и способ получения — в Design.
- Сбой или неполнота этих данных не должны ухудшать успешность и свежесть Portfolio Snapshot (решено, см. Open Decisions); без новых данных позиции показываются в явных группах «Сектор не указан» и «Эмитент не указан».
- Сектор и бренд актива обновляются не чаще раза в сутки (значения могут отставать от T-Bank не более чем на сутки плюс интервал часового обхода); при неуспешном обновлении сохраняется последнее известное значение. Пробелы по краям обрезаются, значение из одних пробелов считается отсутствующим. Отказ token при получении сектора или бренда обрабатывается как существующая недействительность подключения T-Bank.
- На вкладке: в центре диаграммы «Топ-5 · N %» (при шести и более элементах) и сумма разреза, без логотипов и переходов в строках, предупреждение «Данные неполные» при неполном Portfolio; вкладки нет при загрузке, ошибке и пустом Portfolio.
- Существующие данные Portfolio, порядок позиций и итоги по валютам не меняются.
## Success Criteria
- На странице счёта вкладка «Концентрация» доступна рядом с «Позиции»; вкладка «Позиции» и «Структура портфеля» выглядят и работают как раньше.
- В каждом из четырёх разрезов при выбранной валюте при семи и более элементах видны пять строк и «Прочее», при шести или меньше — отдельные строки без «Прочее»; точная сумма строк равна общей сумме разреза в этой валюте.
- Доля каждого элемента и суммарная доля первых пяти считаются от одной и той же базы, а диаграмма и перечень показывают одни и те же элементы и значения; доли показаны в формате карточек позиций и могут в сумме отличаться от 100 % из-за округления.
- Суммы точны и не конвертируются: при выборе валюты в разрезах нет позиций других валют.
- Нажатие на «Прочее» раскрывает все остальные элементы разреза с суммой и долей каждого; состав и порядок топ-5 не меняются.
- В разрезе «Секторы» присутствуют только позиции `STOCK`, `BOND`, `FUND`; позиции других типов в нём отсутствуют. Позиция этих типов без сектора учитывается в группе «Сектор не указан».
- Акция и облигация одного эмитента в разрезе «Эмитенты» показываются одним элементом с общей суммой; фонд в этом разрезе отсутствует.
- Позиция без бренда учитывается в группе «Эмитент не указан», и её доля видна пользователю.
- Общего топ-5 по всем типам позиций на вкладке нет.
- Позиция без вычислимой текущей стоимости не влияет на суммы и доли.
## Evidence
Исследование: `analysis/research.md` (ссылки в формате его источников; здесь не копируются).
- [F] Потребность: показать крупнейшие акции, облигации и секторы по денежной оценке с переключением критерия и диаграммой распределения. Источник: запрос Change Owner, WealthLedger/main#48; `analysis/research.md`, Problem Statement.
- [F] Страница счёта уже содержит распределение по классам активов с выбором одной валюты и вкладку «Позиции» с разделением «Акции / Облигации / Прочее». Источник: `analysis/research.md`, Problem Statement.
- [F] Каноническая `portfolio-view` фиксирует отсутствие валютной конвертации и отдельные итоги по каждой валюте; позиция без вычислимой текущей стоимости остаётся в ответе, но не входит в агрегаты; в Portfolio входят только позиции с ненулевым количеством. Источник: `specs/portfolio-view/spec.md`, требования «Нормализованный состав Portfolio», «Точные значения и валютные агрегаты».
- [F] Карточки позиций показывают долю позиции среди позиций той же валюты. Источник: `specs/portfolio-view/spec.md`, требование «Представление карточек позиций на клиенте».
- [F] Текущие данные позиции Portfolio не содержат сектора и эмитента. Источник: `analysis/research.md` [L4].
- [F] Сектор есть у инструментов Share, Bond и Etf в интеграционном описании T-Bank. Источник: `backend/src/integrations/tbank/proto/contracts/instruments.proto` (Share, Bond, Etf, поле `sector`).
- [F] Бренд с компанией и сектором (`Brand`, :1220-1229) доступен в полном описании актива `AssetFull.brand` (:1051-1067). Краткое описание актива `Asset` бренда не содержит, а `BrandData` внутри инструментов хранит только данные логотипа. Ссылка инструмента на актив (`asset_uid`) есть у Share (:776), Bond (:543) и Etf (:657). Источник: тот же `instruments.proto`; `analysis/research.md` [L9].
- [F] Разрез «Эмитенты» перенесён из Later research в MVP решением Change Owner (комментарий в Issue #48, issuecomment-3077): условие research «после подтверждения issuer identity» снято наличием `AssetFull.brand`.
- [F] Группировка по эмитенту акций и облигаций описана в публичной справке Т-Инвестиций; секторное распределение подтверждено несколькими продуктами. Источник: `analysis/research.md`, Evidence & Sources [E1, E3, E6, E7].
- [F] Внешние продукты — источник кандидатов, но не доказательство спроса. Источник: `analysis/research.md`, Target Audience.
- [H] Бренд актива T-Bank пригоден как идентичность эмитента; полнота бренда на позициях владельца неизвестна. Группировка по совпадающему названию позиций ненадёжна и не используется. Fallback согласован владельцем: нет бренда → «Эмитент не указан»; полнота проверяется в data-mapping.
- [H] У позиций владельца сектор заполнен в большинстве случаев; доля стоимости без сектора и состав значений (включая «государственные ценные бумаги») неизвестны. Проверяется до Specs на данных Portfolio владельца.
- [H] Сектор у фондов в данных T-Bank присутствует не всегда. Проверяется вместе с предыдущей гипотезой.
- [H] Отрицательная текущая стоимость (короткая позиция) возможна в Portfolio. Спецификация допускает отрицательные значения, но наличие таких позиций у владельца не подтверждено.
- [H] Недоступность сектора или эмитента не должна ломать загрузку Portfolio — по аналогии с логотипом, который является только presentation metadata. Не подтверждено.
## Assumptions / Dependencies
- Базой для вкладки служит сохранённый Portfolio Snapshot со своей свежестью и статусами; вкладка не запускает собственных обновлений.
- У вкладки собственный выбор валюты, независимый от «Структуры портфеля» (решение владельца, [комментарий 3079](https://git.personal.ksv741.ru/WealthLedger/main/issues/48#issuecomment-3079)).
- «Структура портфеля» остаётся отдельным блоком; её замена вкладкой «Концентрация» не планируется.
- Данные о секторе и бренде берутся из T-Bank; способ получения и хранения — предмет Design.
- Пять элементов — фиксированный размер топа для всех разрезов; настройка размера не нужна.
- Элементы разрезов «Акции» и «Облигации» — отдельные позиции; одна и та же бумага на одном счёте с одинаковым FIGI не объединяется (действующее правило `portfolio-view`).
## Open Decisions
Все вопросы решает Change Owner. Ни один из них не меняет набор разрезов и не требует нового G1.
**Решены Change Owner до G1** (дата 2026-10-30; дословные формулировки — [комментарий в Issue #48](https://git.personal.ksv741.ru/WealthLedger/main/issues/48#issuecomment-3077); решения внесены в What Changes):
- Эмитент — бренд актива T-Bank; позиции без бренда показываются в группе «Эмитент не указан» с видимой долей. Если бренда нет у многих позиций, разрез остаётся, scope не меняется, новый G1 не нужен.
- Фонды в разрез «Эмитенты» не входят.
- Государственные облигации в «Секторах» — по сектору, как его определяет T-Bank, без собственной классификации.
- «Секторы» включают только `STOCK`, `BOND`, `FUND`; остальные типы позиций в разрез не входят.
- «Прочее» раскрывается по нажатию с полным перечнем остальных элементов; состав топ-5 не меняется.
- Общий топ-5 по всем типам из исходного Issue — вне scope.
- Отклонение от research: там «Эмитенты» были в Later «после подтверждения issuer identity». Условие снято, так как бренд актива T-Bank (`AssetFull.brand`) даёт стабильную идентичность компании; полнота бренда на позициях владельца остаётся гипотезой, её покрывает fallback «Эмитент не указан».
**Решены Change Owner до G2** (дата 2026-10-30; дословные формулировки — [комментарий в Issue #48](https://git.personal.ksv741.ru/WealthLedger/main/issues/48#issuecomment-3078); решения внесены в Delta Specs):
- Знаменатель доли — сумма учитываемых позиций разреза в выбранной валюте; денежные остатки в него не входят.
- Позиции с отрицательной стоимостью и без оценки исключаются из ранжирования, долей и диаграммы; сноска показывает, сколько позиций не учтено.
- Группы «Сектор не указан» и «Эмитент не указан» ранжируются наравне с остальными элементами и заметно обозначены.
- Отсутствие сектора или эмитента не влияет на успешность и свежесть Portfolio Snapshot, как и логотип; у позиций в таких случаях пустые значения.
**Решены Change Owner после G2-review** ([комментарий 3079](https://git.personal.ksv741.ru/WealthLedger/main/issues/48#issuecomment-3079); внесены в Delta Specs):
- Вкладка имеет собственный список валют и выбор, независимые от «Структуры портфеля».
- Доли показываются с округлением карточек позиций; точные доли в сумме дают 100 %.
- «Прочее» появляется только при семи и более элементах.
**Подтверждены Change Owner** ([комментарий 3080](https://git.personal.ksv741.ru/WealthLedger/main/issues/48#issuecomment-3080); внесены в Delta Specs):
- Список валют вкладки: валюты ненулевых `currentValue` позиций акций, облигаций и фондов, включая отрицательные; валюта только с нулевой стоимостью в список не входит.
- В сноске «Не учтено позиций» позиции без оценки считаются при любой выбранной валюте; при пропаже выбранной валюты берётся первая из списка; равные суммы упорядочиваются по названию.
- Название эмитента — название бренда T-Bank.
- Подпись сектора `government` — «Государственный сектор» (добавление WealthLedger к подписям скринера).
**Подтверждены Change Owner** ([комментарий 3081](https://git.personal.ksv741.ru/WealthLedger/main/issues/48#issuecomment-3081); внесены в Delta Specs):
- Эмитент заполняется у фондов в данных Portfolio, разрез «Эмитенты» их отбрасывает.
- Отказ token при получении сектора или бренда — как существующая недействительность подключения.
- Сектор и бренд обновляются раз в сутки; при ошибке сохраняется последнее известное значение.
- Пробелы обрезаются; при разных названиях одного эмитента берётся название позиции с наибольшей стоимостью.
- Разрез и валюта сохраняются в пределах страницы счёта; выбор валюты скрыт при одной валюте; метрика «доля топ-5» скрыта при пяти и менее элементах; при неполных данных — предупреждение «Данные неполные»; в центре диаграммы «Топ-5 · N %» и сумма; в «Эмитентах» без логотипов и переходов.
Открытых решений нет.