Files
specs/openspec/changes/2026-11-07-investment-performance-vs-index/tasks.md
T
codex-bot 892800301b
Specs security / security (push) Successful in 8s
Specs verify / verify (push) Successful in 11s
spec: plan investment performance vs Moscow Exchange index (WealthLedger/main#54) (#85)
2026-11-08 16:30:32 +03:00

109 lines
11 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.
# Tasks
Issue: WealthLedger/main#54. Ветки в каждом repo:
`feature/54-investment-performance-vs-index` от актуального `develop`.
## 1. Backend: история индексов
- [x] 1.1 [backend] Хранилище дневных закрытий индексов и состояния догрузки (аддитивная миграция)
- **Covers:** `instruments/market-index-history` — `Дневная история индексов Мосбиржи`
- **Source:** design «Data / Storage Changes»
- **Verification:** `npx prisma migrate dev` на одноразовой копии БД; `npm run verify`
- **Depends on:** none
- [x] 1.2 [backend] `MoexClient.indexHistory(secid, from, till)`: пагинация `history.cursor`, строгий разбор, сбой без частичных строк
- **Covers:** `instruments/market-index-history` — `Дневная история индексов Мосбиржи`
- **Source:** system-analysis «Contracts» (MOEX ISS), [index-refresh.mmd](system-analysis/sequences/index-refresh.mmd); design backend 5
- **Verification:** `moex.client.spec.ts`: несколько страниц, невалидная страница, таймаут, не-2xx
- **Depends on:** none
- [x] 1.3 [backend] `MarketIndexHistoryService`: покрытие периода, догрузка не чаще раза в час, single-flight, upsert, лог `integration.moex.index_history.*`, сбой → недостаточно покрытия
- **Covers:** `instruments/market-index-history` — `Дневная история индексов Мосбиржи`, `Свежесть и недоступность истории индексов`
- **Source:** system-analysis «Индексы MOEX», «Error and Recovery Semantics»; design backend 5, «Cross-cutting Implementation»
- **Verification:** unit-тесты сервиса (покрытие ±10 дн., час, параллельные вызовы, сбой с историей и без); `npm run verify`
- **Depends on:** 1.1, 1.2
## 2. Backend: расчёт результата
- [x] 2.1 [backend] Вынести `isCompleteValuation(snapshot)` в portfolio domain и использовать его при записи истории оценки
- **Covers:** `portfolio/investment-performance` — `Недоступный и неполный результат`
- **Source:** design backend 2; qa-analysis «Regression Impact»
- **Verification:** существующие тесты `portfolio-value-history` и новые unit-тесты функции проходят; `npm run verify`
- **Depends on:** none
- [x] 2.2 [backend] Domain: маппинг типов операций → роли, внешние потоки по дате Europe/Moscow, отсечка по `fetchedAt`, кандидаты на переводы в `all` без автоматического подтверждения, причины недоступности в порядке приоритета
- **Covers:** `portfolio/investment-performance` — `Внешние денежные потоки`, `Недоступный и неполный результат`
- **Source:** system-analysis «Маппинг операций T-Bank», «Переводы между счетами в all», «Причины недоступности»; design backend 4, 7
- **Verification:** unit-тесты: счёт A (130 000 / 10 000), `UNCONFIRMED_TRANSFER` при совпадении операций, перевод на исключённый счёт, бумаги без пары, USD, порядок причин
- **Depends on:** none
- [x] 2.3 [backend] Domain: XIRR (Ньютон + бисекция, проверка знака), basis annual/period, теневой портфель MCFTRR/IMOEX, опережение, series
- **Covers:** `portfolio/investment-performance` — `Доходность с учётом сумм и дат потоков`, `Те же деньги в индексе Мосбиржи`, `Справочный индекс без дивидендов`, `График результата`
- **Source:** system-analysis «Расчёт»; design backend 3
- **Verification:** unit-тесты с эталонными константами (LibreOffice/scipy, допуск 1e-6): 10 % за 730 дн., 110 000 ₽ по индексу, поток в выходной, `NO_SOLUTION`, `AMBIGUOUS_RETURN`, period basis, отставание
- **Depends on:** none
- [x] 2.4 [backend] Модуль `performance`: маршруты `GET /brokerage-accounts/{accountId|all}/performance`, доступ, сборка ответа, DTO, `UnavailableReason`, лог `performance.calculated`
- **Covers:** `portfolio/investment-performance` — `Результат инвестирования выбранного счёта`, `Недоступный и неполный результат`; `instruments/market-index-history` — `Свежесть и недоступность истории индексов`
- **Source:** contract [investment-performance.openapi.yaml](system-analysis/contracts/investment-performance.openapi.yaml), [performance-read.mmd](system-analysis/sequences/performance-read.mmd); design backend 1, 6
- **Verification:** e2e на SQLite (`npm run test:sqlite`): 200 ready, `data: null`, `all` incomplete, 401/404/503, MOEX недоступен с историей и без; `npm run verify`
- **Depends on:** 1.3, 2.1, 2.2, 2.3
- [x] 2.5 [backend] Обновить generated OpenAPI и сверить его с целевым контрактом
- **Covers:** `portfolio/investment-performance` — `Результат инвестирования выбранного счёта`
- **Source:** design «Contract Implementation»
- **Verification:** `npm run openapi:generate`; `npm run openapi:check`; построчная сверка маршрутов, required, nullable, enum и форматов с `investment-performance.openapi.yaml`, результат — в описании PR
- **Depends on:** 2.4
## 3. Frontend
- [x] 3.1 [frontend] Обновить OpenAPI snapshot и типы, entity `performance` с `usePerformance(accountId)`
- **Covers:** `portfolio/investment-performance` — `Результат инвестирования выбранного счёта`
- **Source:** design frontend 1, «Contract Implementation»
- **Verification:** `npm run openapi:generate`; `npm run openapi:check`; `npm run typecheck`
- **Depends on:** 2.5
- [x] 3.2 [frontend] Форматтеры `formatSignedPercent`, `formatPercentagePoints` (знак U+2212, 1 знак)
- **Covers:** `portfolio/investment-performance` — `Вкладка «Результат» на странице счёта`
- **Source:** design frontend 4
- **Verification:** unit-тесты: положительное, отрицательное, ноль, округление
- **Depends on:** none
- [x] 3.3 [frontend] Вкладка «Результат»: container, VM, view, KPI, alerts, недоступно, skeleton, ошибка секции, «Как считали», hint, строки `portfolio.performance.*`
- **Covers:** `portfolio/investment-performance` — `Вкладка «Результат» на странице счёта`, `Недоступный и неполный результат`
- **Source:** design frontend 2, 3, 6, 7, «UI/UX Package»; prototype вариант A
- **Verification:** тесты VM и view (RTL + MSW): обгоняю, отстаю, меньше года, неполный, недоступно, неизвестная причина, загрузка, ошибка только во вкладке; `npm run verify`
- **Depends on:** 3.1, 3.2
- [x] 3.4 [frontend] График результата: LineChart с тремя сериями и точкой текущей стоимости, tooltip, текстовая альтернатива, пояснение об отсутствии прошлой стоимости, состояние без series
- **Covers:** `portfolio/investment-performance` — `График результата`, `Справочный индекс без дивидендов`
- **Source:** design frontend 5, «UI/UX Package»
- **Verification:** тесты view: серии, `aria-label` с итогами, отсутствие референсной линии, пустой series; browser-тест на 390px без горизонтальной прокрутки; `npm run verify`
- **Depends on:** 3.3
## 4. Docs
- [x] 4.1 [docs] ADR 023 и строка в `adr/README.md`
- **Covers:** `N/A` — документация решения
- **Source:** docs-impact: `docs/adr/023-investment-performance-vs-index.md`, `docs/adr/README.md`
- **Verification:** `npm run verify`
- **Depends on:** none
- [x] 4.2 [docs] Страница `domains/portfolio/investment-performance.md` и обновления `all-accounts.md`, `data-reference.md`, `portfolio/overview.md`
- **Covers:** `N/A` — документация текущего поведения
- **Source:** docs-impact: соответствующие строки таблицы
- **Verification:** `npm run verify`; сверка с реализованным поведением 2.4 и 3.3
- **Depends on:** 2.4, 3.3, 4.1
- [x] 4.3 [docs] Глоссарий, `overview.md`, `income/overview.md`: новые термины и снятие конфликтующих запретов
- **Covers:** `N/A` — терминология
- **Source:** docs-impact: `docs/glossary.md`, `docs/overview.md`, `docs/domains/income/overview.md`
- **Verification:** `npm run verify`; `grep -rn "доходност" .` — только новое значение или оговорки «не является»
- **Depends on:** 4.1
- [x] 4.4 [docs] `integrations/moex.md` (раздел «Индексы»), `architecture/context.md`, `architecture/http-api.md`
- **Covers:** `N/A` — документация интеграции и API
- **Source:** docs-impact: соответствующие строки таблицы
- **Verification:** `npm run verify`; сверка с generated OpenAPI из 2.5
- **Depends on:** 2.5
## 5. Завершение
- [x] 5.1 [specs] Commit planning artifacts и прототипа; PR в `develop`
- **Covers:** `N/A` — поставка planning artifacts
- **Source:** engineering workflow
- **Verification:** `npm run validate`; `git diff --check`
- **Depends on:** none
Ручная приёмка на реальных данных Change Owner (парность переводов,
правдоподобие результата) и semantic verify/archive выполняются по
qa-analysis до закрытия Issue. Это не implementation task.