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
@@ -65,17 +65,21 @@ Issue: WealthLedger/main#54
приближение, точная десятичная степень для неё бессмысленна. Поэтому
решатель работает на float64 и выдаёт результат до 6 знаков (точности
хватает с запасом).
- Алгоритм: Ньютон от r₀ = 0,1, переход на бисекцию по `[−0.9999, 100]`
при выходе за интервал или отсутствии сходимости за 50 шагов. Не больше
200 итераций, критерий — |NPV| ≤ 0,01 ₽.
- Перед поиском проверяется смена знака NPV на концах интервала; если её
нет, результат `NO_SOLUTION`.
- *Отклонено:* npm-пакет XIRR (новая зависимость ради ~40 строк); чистая
бисекция (медленнее, но остаётся запасным путём).
4. **Переводы в `all`.** Деньги и бумаги сопоставляются в domain по правилам
System Analysis. Каждая операция участвует не более чем в одной паре:
сортировка по `occurredAt`, жадное сопоставление с ближайшей подходящей
операцией.
- При обычном наборе потоков с одной сменой знака по датам Descartes'
rule доказывает единственность корня: уточнить его бисекцией в
`[−0.9999, 100]` до |NPV| ≤ 0,01 ₽. Нет корня → `NO_SOLUTION`.
При нескольких сменах знака нет доказательства единственности — пока
консервативно вернуть `AMBIGUOUS_RETURN`, даже если фактически корень
единственный. Полное выделение всех корней добавлять только при
подтверждённой потребности.
- *Отклонено:* Ньютон от одного старта и проверка только знаков на концах:
они не выявляют несколько решений. Новая зависимость не нужна.
4. **Переводы в `all`.** Деньги и бумаги сопоставляются в domain только
для выявления кандидатов (сумма/время либо инструмент/количество).
Кандидат без независимого признака связи не исключается из потоков:
затронутые счета исключаются из результата `all` с причиной
`UNCONFIRMED_TRANSFER`. Существующий T-Bank contract не даёт такого
признака, поэтому автоматически подтверждённых пар сейчас нет.
5. **История индексов: `MarketIndexHistoryService`** в
`src/modules/investments/market-index/`. В `MoexClient` добавляется метод
`indexHistory(secid, from, till)` с пагинацией по `history.cursor`. Если
@@ -197,8 +201,8 @@ model MarketIndexSyncState {
LibreOffice `XIRR` или Python `scipy.optimize`, и записываются как
константы с допуском 1e-6;
- примеры из спецификации: 10 % на 730 дней, 110 000 ₽ по индексу;
- `basis = period`, `NO_SOLUTION`, поток в выходной;
- парность переводов, порядок причин;
- `basis = period`, `NO_SOLUTION`, `AMBIGUOUS_RETURN` (несколько корней), поток в выходной;
- кандидаты на переводы без подтверждённой связи, порядок причин;
- тест клиента MOEX: пагинация, невалидная страница, таймаут — на
подменённом fetch, как в `moex.client.spec.ts`;
- e2e на SQLite: маршрут, доступ, `all`, сбой MOEX с сохранённой и без
@@ -248,10 +252,10 @@ backend — маршруты и drop таблиц индексов.
## Technical Risks
- **Парность переводов** может не совпасть с реальными данными T-Bank.
Меры: проверка на данных пользователя до закрытия change, правило
изолировано в domain.
- **Нестабильность XIRR** при крупных выводах (несколько корней). Мера:
проверка смены знака и `NO_SOLUTION` вместо сомнительного корня.
- **Неподтверждённые переводы** делают результат `all` неполным. Меры:
показать исключённые счета и причину; проверить на реальных данных,
появится ли надёжный признак связи.
- **Неоднозначность XIRR** при крупных выводах (несколько корней). Мера:
поиск возможных корней и `AMBIGUOUS_RETURN`, а не произвольный корень.
- **Формат ISS** может измениться. Мера: строгий разбор, сбой вместо
частичных данных, сохранённая история остаётся.