spec: refine performance rules from implementation decisions (WealthLedger/main#54)
Conventional commits / commits (pull_request) Successful in 10s
Specs security / security (pull_request) Successful in 8s
Specs verify / verify (pull_request) Successful in 13s

This commit is contained in:
2026-11-08 14:21:09 +03:00
parent 5625c52b29
commit 5c11c71a19
7 changed files with 109 additions and 66 deletions
@@ -22,17 +22,19 @@ Issue: WealthLedger/main#54
| Результат счёта: рублёвый счёт с известным началом | API: ответ 200 со всеми полями; значения совпадают с эталоном (ниже) |
| Результат счёта: счёт недоступен через подключение | API: чужой `accountId` → 404, в теле нет финансовых полей |
| Внешние потоки: пополнения, вывод, дивиденды | Эталонный счёт A: `netInvested = 130000`, `profit = 10000` |
| Внешние потоки: перевод между счетами в `all` | Счета B и C с парой вывод/пополнение: `all.netInvested` не меняется от перевода |
| Внешние потоки: кандидат на перевод между счетами в `all` | Счета B и C с равными суммами в пределах 5 минут исключаются с `UNCONFIRMED_TRANSFER`, а не объявляются внутренней парой |
| Внешние потоки: перевод на счёт вне результата | Счёт B исключён (USD), его пополнение от C → вывод у C в `all` |
| Доходность: период больше года | Счёт D: 100 000 → 121 000 за 730 дн. → `return.value = 0.1`, `basis = annual` |
| Доходность: меньше года | Счёт E, 200 дн.: `basis = period`; в UI нет слова «годовых» ни в одном значении |
| Доходность: нет решения | Счёт F (текущая стоимость 0 после пополнений, без выводов): `NO_SOLUTION`, `profit` доступен, `outperformance = null` |
| Доходность: несколько решений | Набор потоков с двумя корнями XIRR: `AMBIGUOUS_RETURN`, прибыль доступна, `outperformance = null` |
| Индекс: пополнение и рост индекса | Подменённая история (1 000 → 1 100) или ручной расчёт по реальным закрытиям: `benchmark.value = 110000`, `outperformance.money = 5000` |
| Индекс: поток в выходной | Пополнение в субботу → используется закрытие пятницы (проверяется по `series` в день потока) |
| Индекс: отставание | Счёт со стоимостью ниже теневой: отрицательные `percentagePoints` и `money`, в UI «Отставание от рынка» |
| Справочный индекс недоступен | IMOEX нет, MCFTRR есть: `reference.unavailableReason` задан, `benchmark` и `outperformance` есть, в UI нет пунктирной линии |
| Недоступно: неизвестная дата открытия | Счёт с `historyBoundary = unknown`: `data: null`, `HISTORY_START_UNKNOWN`; в UI «Доходность недоступна», нет нулей |
| Недоступно: USD в `all` | 3 счёта, один с USD: `completeness = incomplete`, `excludedAccounts` содержит счёт и `NON_RUB_ACTIVITY`; в UI предупреждение |
| Недоступно: даты оценок в `all` различаются | Последние оценки A и B в разные московские дни → `data: null`, `ACCOUNT_VALUATION_DATES_DIFFER`; стоимость не складывается |
| Недоступно: индекс недоступен | MOEX заблокирован и истории нет: `profit` есть, `benchmark.unavailableReason = INDEX_HISTORY_UNAVAILABLE`, `series = []` |
| Недоступно: остальные причины | Перевод бумаг без пары → `UNVALUED_SECURITIES_TRANSFER`; позиция без цены → `PORTFOLIO_VALUATION_INCOMPLETE`; незавершённая синхронизация → `OPERATIONS_HISTORY_INCOMPLETE` |
| Вкладка: обгоняю, меньше года, неполный, недоступный, методика | UI по соответствующим данным; формулировки и тоны как в UI/UX Package; «Как считали» содержит все пункты спецификации |
@@ -130,7 +132,8 @@ Issue: WealthLedger/main#54
## Gaps / Blockers
- **Парность переводов** не подтверждена на реальных данных. Routing: System
Analysis (уточнить правило) по итогам ручной проверки до закрытия change.
- **Подтверждение переводов** отсутствует в текущем контракте T-Bank.
Routing: System Analysis при появлении нового признака связи; до этого
кандидаты исключаются с явной причиной, не выдавая сомнительный процент.
- **Sandbox T-Bank** может не воспроизвести все наборы. Routing: Test Data —
наполнение одноразовой SQLite-копии; не блокирует.