Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
364f6bd8d0 | ||
|
|
6d7d4c7b28 | ||
|
|
40ea47a7c2 | ||
|
|
37430da4c9 | ||
|
|
abe5b16241 | ||
|
|
dcb528fa9f | ||
|
|
8c6c2f84ce | ||
|
|
f9098d8409 | ||
|
|
4216eb815f | ||
|
|
24ad250c02 | ||
|
|
e8e3ef6285 | ||
|
|
11f819ed31 | ||
|
|
550143aea1 | ||
|
|
56885f450b | ||
|
|
1c89d321b1 | ||
|
|
ad1978c75e | ||
|
|
77c1f44726 | ||
|
|
8a2a62c611 | ||
|
|
7b61eb8e6b | ||
|
|
cad0f31615 | ||
|
|
03110e99ea | ||
|
|
03fb045a9e |
@@ -2,7 +2,7 @@ name: Conventional commits
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
concurrency:
|
||||
group: docs-commits-${{ gitea.event.pull_request.number }}
|
||||
|
||||
@@ -24,7 +24,12 @@
|
||||
## Ветки и релизы
|
||||
|
||||
Работа — в issue-linked ветке от `develop`, затем PR в `develop`; в `main` напрямую
|
||||
не пушить. Release/hotfix и синхронизация веток — по [README](README.md#релизы).
|
||||
не пушить. PR сливается squash или rebase-merge; merge-коммит не используется.
|
||||
`main` обновляется только fast-forward из `develop`, поэтому `origin/main`
|
||||
всегда предок `origin/develop`:
|
||||
`git merge-base --is-ancestor origin/main origin/develop`. Срочный fix —
|
||||
обычная ветка `fix/*` от `develop`; пути hotfix от `main` нет.
|
||||
Релиз — по [README](README.md#релизы).
|
||||
Commits и заголовки PR — Conventional Commits со ссылкой на Issue (кроме release);
|
||||
для спецификаций допустим `spec:`.
|
||||
Перед подготовкой релиза прочитай местный README; версии вручную не повышать.
|
||||
|
||||
@@ -1,5 +1,48 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.8](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.7...v0.1.8) (2026-11-06)
|
||||
|
||||
### Maintenance
|
||||
|
||||
* **deps:** set Renovate release age, PR limit and update groups (WealthLedger/main[#6](https://git.personal.ksv741.ru/WealthLedger/docs/issues/6)) ([#64](https://git.personal.ksv741.ru/WealthLedger/docs/issues/64)) ([6d7d4c7](https://git.personal.ksv741.ru/WealthLedger/docs/commit/6d7d4c7b286535b7338d7e398d2daf7d8137a501))
|
||||
|
||||
## [0.1.7](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.6...v0.1.7) (2026-11-01)
|
||||
|
||||
### Documentation
|
||||
|
||||
* describe all-account row grouping (WealthLedger/main[#47](https://git.personal.ksv741.ru/WealthLedger/docs/issues/47)) ([#59](https://git.personal.ksv741.ru/WealthLedger/docs/issues/59)) ([dcb528f](https://git.personal.ksv741.ru/WealthLedger/docs/commit/dcb528fa9f76f0d7aa98bd827cc7ee39024dbb13))
|
||||
* describe concentration tab and stored instrument classification (WealthLedger/main[#48](https://git.personal.ksv741.ru/WealthLedger/docs/issues/48)) ([#60](https://git.personal.ksv741.ru/WealthLedger/docs/issues/60)) ([37430da](https://git.personal.ksv741.ru/WealthLedger/docs/commit/37430da4c99cb571ef5bc0676cad7b3a47bcb6f0))
|
||||
|
||||
### Maintenance
|
||||
|
||||
* **deps:** resolve markdownlint audit advisory (WealthLedger/main[#49](https://git.personal.ksv741.ru/WealthLedger/docs/issues/49)) ([#61](https://git.personal.ksv741.ru/WealthLedger/docs/issues/61)) ([abe5b16](https://git.personal.ksv741.ru/WealthLedger/docs/commit/abe5b16241d3002baa5d39c2a12949f23a75b968))
|
||||
|
||||
## [0.1.6](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.5...v0.1.6) (2026-10-24)
|
||||
|
||||
### Maintenance
|
||||
|
||||
* bump Node to 24.22.1 (WealthLedger/backend[#122](https://git.personal.ksv741.ru/WealthLedger/docs/issues/122)) ([4216eb8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4216eb815f59b7a284e679ccb1b725438da07882))
|
||||
* **deps:** update non-major dependencies (WealthLedger/docs[#56](https://git.personal.ksv741.ru/WealthLedger/docs/issues/56)) ([f9098d8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/f9098d84098467e41143bad5044085d6329099d9))
|
||||
|
||||
## [0.1.5](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.4...v0.1.5) (2026-10-18)
|
||||
|
||||
### Documentation
|
||||
|
||||
* align current pages with hourly freshness model (WealthLedger/docs[#34](https://git.personal.ksv741.ru/WealthLedger/docs/issues/34)) ([#42](https://git.personal.ksv741.ru/WealthLedger/docs/issues/42)) ([7b61eb8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/7b61eb8e6ba93a765b1ae27ca31d77b5e422e149))
|
||||
* describe ASCII 8-72 password policy and drop resolved discrepancy (WealthLedger/docs[#46](https://git.personal.ksv741.ru/WealthLedger/docs/issues/46)) ([#47](https://git.personal.ksv741.ru/WealthLedger/docs/issues/47)) ([56885f4](https://git.personal.ksv741.ru/WealthLedger/docs/commit/56885f450bc1298d383d496174d16e591926e047))
|
||||
* describe events default period and picker bounds (WealthLedger/frontend[#63](https://git.personal.ksv741.ru/WealthLedger/docs/issues/63)) ([8a2a62c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/8a2a62c611e1acc1c1e69048a88b049bbf0d9087))
|
||||
* describe quiet reading and visible source state ([#51](https://git.personal.ksv741.ru/WealthLedger/docs/issues/51)) ([e8e3ef6](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e8e3ef62854dae8448deaa66ac26e5b15d5c7d6a))
|
||||
* document income without comparisons and unified payouts (WealthLedger/main[#33](https://git.personal.ksv741.ru/WealthLedger/docs/issues/33)) ([#40](https://git.personal.ksv741.ru/WealthLedger/docs/issues/40)) ([03fb045](https://git.personal.ksv741.ru/WealthLedger/docs/commit/03fb045a9ee8a36775599bda66e7f6a08362bd84))
|
||||
* document Nexus delivery with shared account (WealthLedger/docs[#18](https://git.personal.ksv741.ru/WealthLedger/docs/issues/18)) ([#48](https://git.personal.ksv741.ru/WealthLedger/docs/issues/48)) ([550143a](https://git.personal.ksv741.ru/WealthLedger/docs/commit/550143aea1f0ac90630f326dd927091b0ec4f38e))
|
||||
|
||||
### CI
|
||||
|
||||
* rerun commits proof on PR title edit (WealthLedger/docs[#35](https://git.personal.ksv741.ru/WealthLedger/docs/issues/35)) ([03110e9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/03110e99ea58600bacd4c091fda8591d374a5a8d))
|
||||
|
||||
### Maintenance
|
||||
|
||||
* adopt linear git model in docs instructions ([#49](https://git.personal.ksv741.ru/WealthLedger/docs/issues/49)) ([11f819e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/11f819ed31226a05524ac35a893064525713e41b))
|
||||
|
||||
## [0.1.4](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.3...v0.1.4) (2026-10-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -30,7 +30,7 @@ WealthLedger помогает частному инвестору понимат
|
||||
| [Аутентификация](domains/authentication/overview.md) | Регистрация, вход, восстановление и завершение сессии |
|
||||
| [Счета](domains/accounts/overview.md) | Доступные счета, сводки и состояния каждого счёта |
|
||||
| [Банковские карты](domains/cards/overview.md) | Ручные остатки, операции, категории, возвраты, месячные сводки и исправления |
|
||||
| [Портфель](domains/portfolio/overview.md) | Позиции, остатки, суммы, структура и свежесть |
|
||||
| [Портфель](domains/portfolio/overview.md) | Позиции, остатки, суммы, структура, [концентрация](domains/portfolio/concentration.md) и свежесть |
|
||||
| [Все счета](domains/portfolio/all-accounts.md) | Общий портфель, происхождение строк и сводные разделы |
|
||||
| [Операции](domains/operations/overview.md) | Исполненные операции, фильтры и пагинация |
|
||||
| [Доход](domains/income/overview.md) | Полученные купоны и дивиденды, периоды и состав расчёта |
|
||||
@@ -74,35 +74,58 @@ WealthLedger помогает частному инвестору понимат
|
||||
Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые
|
||||
релизы. Обычная работа: issue-linked ветка от актуального `develop`, PR в `develop`;
|
||||
общие правила — в Engineering Workflow по ссылке из AGENTS.
|
||||
Обычные PR допускают squash; release и обратная синхронизация сохраняют историю.
|
||||
PR сливается squash или rebase-merge; merge-коммит не используется. `main`
|
||||
обновляется только fast-forward из `develop`, поэтому история линейна:
|
||||
|
||||
```text
|
||||
<type>/<issue>-<slug> --squash--> develop --ff-only--> main --> tag vX.Y.Z
|
||||
```
|
||||
|
||||
Инвариант: `origin/main` всегда предок `origin/develop`. Проверка:
|
||||
|
||||
```bash
|
||||
git fetch -q origin
|
||||
git merge-base --is-ancestor origin/main origin/develop && echo ok || echo BROKEN
|
||||
```
|
||||
|
||||
Commits и PR titles — Conventional Commits со ссылкой на Issue (кроме release),
|
||||
включая `spec:`. Версии вручную не повышай.
|
||||
|
||||
1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что
|
||||
`develop` содержит последний релиз. Создай `release/<name>` от проверенного
|
||||
`develop`; `nvm use --silent`, `npm ci`. Hotfix начинается от `main` в
|
||||
`release/hotfix-<name>` и проходит тот же процесс.
|
||||
1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Проверь
|
||||
инвариант. Создай `release/X.Y.Z` от проверенного `develop`;
|
||||
`nvm use --silent`, `npm ci`.
|
||||
2. На чистой ветке выполни `npm run release:preview`, просмотри версию/changelog,
|
||||
затем `npm run release:prepare`. Команда создаёт `chore(release): X.Y.Z`,
|
||||
обновляет manifest/lockfile/CHANGELOG; тег и push не выполняет. Не обходи
|
||||
guards ветки, чистого дерева, отсутствующих тегов или уже подготовленной версии.
|
||||
3. Проверь diff и штатные gates этого репозитория. Отправь только release-ветку,
|
||||
открой PR в `main` с заголовком `chore(release): X.Y.Z`. После зелёных
|
||||
`verify`, `security`, `commits` выполни fast-forward-only merge. Если `main`
|
||||
изменился, согласуй историю и повтори проверки кандидата.
|
||||
4. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией
|
||||
открой PR в **`develop`** с заголовком `chore(release): X.Y.Z`. После зелёных
|
||||
`verify`, `security`, `commits` выполни fast-forward-only merge. Если
|
||||
`develop` изменился, обнови release-ветку и повтори проверки кандидата.
|
||||
4. Открой PR `develop → main` с тем же заголовком и слей fast-forward-only.
|
||||
5. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией
|
||||
manifest/lockfile. Создай аннотированный `vX.Y.Z` именно на этом SHA:
|
||||
`git tag -a vX.Y.Z <release-sha> -m "Release X.Y.Z"`, затем отправь только
|
||||
`git push origin refs/tags/vX.Y.Z`. Не запускай release-it повторно на `main`.
|
||||
5. Верни release-коммит через PR `main → develop` без squash/rebase; проверь,
|
||||
что `develop` содержит тег. Опубликованные теги не перемещай. При сбое после
|
||||
merge создай отсутствующий тег без нового bump; до merge исправь и проверь
|
||||
существующий кандидат без повторного повышения версии.
|
||||
|
||||
Обратная синхронизация не нужна: release-коммит попал в `develop` на шаге 3.
|
||||
Опубликованные теги не перемещай. При сбое после merge создай отсутствующий тег
|
||||
без нового bump; до merge исправь и проверь существующий кандидат без
|
||||
повторного повышения версии.
|
||||
|
||||
Freeze релиза не поддерживается: пока открыт релизный PR, `develop` не двигают.
|
||||
Срочный fix — обычная ветка `fix/*` от `develop` с последующим релизом;
|
||||
пути от `main` нет, он сделал бы `main` не-предком `develop`. Исключение
|
||||
требует явного решения: `release/hotfix-<name>` от `main`, PR в `main`, тег,
|
||||
затем merge `main → develop` для восстановления инварианта.
|
||||
|
||||
`feat:` повышает minor, `fix:`/`perf:` и прочие разрешённые типы — patch;
|
||||
`!`/`BREAKING CHANGE:` — major, включая версии до 1.0. `chore(release):`
|
||||
исключён из рекомендации и changelog. Конфигурация — `.release-it.json`.
|
||||
В Gitea для `main`/`develop` должны быть запрещены прямой/force push и обход
|
||||
администратором, обязательны `verify`, `security`, `commits`; теги `v*` защищены.
|
||||
администратором, включена блокировка устаревшей ветки, обязательны `verify`,
|
||||
`security`, `commits`; теги `v*` защищены. Линейность обеспечивают merge styles
|
||||
репозитория: merge-коммиты и rebase `--no-ff` выключены, доступны squash,
|
||||
rebase-merge и fast-forward-only; стиль по умолчанию — squash.
|
||||
Workflow сам не включает эти настройки. Отдельное review approval для одного
|
||||
владельца не требуется. Npm publishing, deploy и Gitea Releases не включены.
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
**Status:** Accepted. Заменяет request-driven, manual-refresh и
|
||||
no-periodic-walk части [ADR 017](017-local-tbank-reads-and-background-sync.md)
|
||||
и дополняет [ADR 018](018-minimal-runtime-coordination.md).
|
||||
и дополняет [ADR 018](018-minimal-runtime-coordination.md). Правило «справочные
|
||||
сведения запрашиваются онлайн» дополнено исключением для сектора и бренда
|
||||
актива позиции — [ADR
|
||||
022](022-stored-instrument-classification.md).
|
||||
|
||||
## Decision
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# 021. Тихое чтение и редкие подтверждения
|
||||
|
||||
**Status:** Accepted. Заменяет manual-refresh часть
|
||||
[ADR 019](019-hourly-reads-and-online-instrument-reference.md) на стороне
|
||||
интерфейса и уточняет клиентскую часть
|
||||
[ADR 007](007-react-query-fetch-policy.md). Не меняет владельцев обновления
|
||||
и границы чтения из ADR 019.
|
||||
|
||||
## Context
|
||||
|
||||
Экраны просмотра перекладывали на пользователя контроль над получением данных:
|
||||
кнопки «Обновить» и «Повторить», индикатор фонового обновления и сообщение
|
||||
о неудачной синхронизации повторялись в разных разделах. Пользователь приходит
|
||||
смотреть финансы, а не обслуживать загрузку; восстановление после временного
|
||||
сбоя не должно зависеть от нажатия кнопки.
|
||||
|
||||
При этом состояние самих данных остаётся значимым: время последнего успешного
|
||||
результата источника, устаревание и исход часовой попытки влияют на доверие
|
||||
к показанным суммам и не могут быть просто скрыты.
|
||||
|
||||
## Decision
|
||||
|
||||
- Автоматическое чтение клиента не сообщает о себе. Экраны просмотра не
|
||||
показывают кнопок ручного обновления и повтора, индикаторов фонового
|
||||
перечитывания и технических предупреждений о собственном запросе.
|
||||
- Временные сбои GET восстанавливаются сами: ограниченный срок ожидания запроса
|
||||
и не более двух повторов с возрастающими паузами, затем возврат к штатной
|
||||
работе при появлении результата. Пользователь не инициирует восстановление.
|
||||
- Состояние источника из `meta` остаётся видимым. Время последнего успешного
|
||||
результата, признак устаревания, исход последней часовой попытки и неполнота
|
||||
показываются текстом либо доступно именованным элементом, а не только цветом.
|
||||
Это свойство данных, а не отчёт о техническом процессе, и оно не превращается
|
||||
в действие пользователя.
|
||||
- Доступный результат сохраняется при обновлении и временной ошибке. Неполнота
|
||||
и подтверждённая пустота остаются различимыми: неизвестная сумма не становится
|
||||
нулём, незавершённый расчёт не выдаётся за окончательный, незагруженный список
|
||||
не выдаётся за отсутствие записей.
|
||||
- Первичное ожидание ограничено по времени и показывает структуру будущего
|
||||
содержимого. По его истечении экран переходит в спокойную локальную
|
||||
недоступность вместо бесконечной загрузки и сам заменяет её содержимым после
|
||||
восстановления. Сбой одного блока не скрывает остальные доступные блоки.
|
||||
- Ошибки явных действий пользователя сохраняются и остаются видимыми. Решение
|
||||
касается только чтения; отправка формы, изменение данных и сброс фильтра
|
||||
по-прежнему сообщают результат, в том числе неуспешный.
|
||||
|
||||
## Consequences
|
||||
|
||||
Пользователь не может ускорить обновление вручную: нормальным владельцем
|
||||
обновления остаётся часовой обход из ADR 019, и отсутствие кнопки не меняет его
|
||||
срок. Если источник недоступен долго, экран показывает прежние данные с
|
||||
обозначением устаревания, а не приглашение повторить.
|
||||
|
||||
Автоматический повтор ограничен: он покрывает короткие сетевые сбои, но не
|
||||
длительную недоступность. Ошибки доступа, домена и отмены не повторяются,
|
||||
mutations не повторяются вовсе.
|
||||
|
||||
Состояние источника и состояние клиентского чтения описывают разные вещи и
|
||||
не подменяют друг друга. `Portfolio.isStale` сохраняет смысл возраста снимка;
|
||||
ошибка клиентского перечитывания его не меняет.
|
||||
|
||||
Детали поведения — в
|
||||
[ожидании и восстановлении интерфейса](../architecture/cross-cutting/ui-resilience.md)
|
||||
и [свежести данных](../architecture/cross-cutting/data-freshness.md).
|
||||
Согласованные требования принадлежат workspace-спецификациям
|
||||
`frontend-user-feedback`, `frontend-data-resilience` и
|
||||
`frontend-loading-presentation`.
|
||||
@@ -0,0 +1,56 @@
|
||||
# 022. Хранимая классификация инструментов T-Bank
|
||||
|
||||
**Status:** Accepted. Дополняет [ADR
|
||||
019](019-hourly-reads-and-online-instrument-reference.md) исключением из
|
||||
правила «справочные сведения запрашиваются онлайн». Решение утверждено
|
||||
владельцем в [Issue
|
||||
WealthLedger/main#48](https://git.personal.ksv741.ru/WealthLedger/main/issues/48).
|
||||
|
||||
## Context
|
||||
|
||||
ADR 019 установил, что справочные сведения инструментов (detail, lookup,
|
||||
каталоги) запрашиваются онлайн и не сохраняются как долговременный справочник:
|
||||
«старые справочные строки не являются источником актуальности». Вкладка
|
||||
«Концентрация» требует для каждой позиции её сектора и эмитента — бренда
|
||||
актива по данным T-Bank. Сектор есть только в описаниях отдельных типов
|
||||
инструментов (`share`, `bond`, `etf`), а эмитент — в описании актива по
|
||||
`asset_uid` инструмента; из названия, тикера, ISIN или `logoName` его вывести
|
||||
нельзя. Запрашивать эти поля онлайн при каждом открытии экрана нельзя:
|
||||
ответ Portfolio не ждёт T-Bank, а разрезы должны считаться по уже сохранённому
|
||||
снимку.
|
||||
|
||||
Существующий metadata-кэш (`TbankInstrument`) не подходит: общий ответ
|
||||
`GetInstrumentBy` не содержит `sector`, а его `brand` — данные логотипа,
|
||||
а не `{uid, name}` бренда актива.
|
||||
|
||||
## Decision
|
||||
|
||||
- Сектор и бренд актива позиции хранятся как последнее известное значение в
|
||||
глобальной таблице `TbankInstrumentClassification` (провайдер, ключ
|
||||
инструмента, сектор и время его получения, uid актива и uid/название бренда
|
||||
с временем получения). Данные публичные, без привязки к пользователю или
|
||||
подключению; ограждение по connection revision к ним не применяется, публикация
|
||||
Snapshot ограждается как прежде.
|
||||
- Каждое поле обновляется в часовом обходе при его первой попытке, начавшейся
|
||||
не ранее чем через 24 часа после последнего успешного получения этого поля;
|
||||
пока значение не получено, попытка повторяется в каждом обходе. Сектор и
|
||||
бренд обновляются независимо.
|
||||
- Неуспешное получение не мешает публикации Portfolio Snapshot и не влияет на
|
||||
его свежесть; сохранённое значение остаётся доступным. Отказ token
|
||||
обрабатывается как отказ token при обходе Portfolio. Ответ «инструмент не
|
||||
найден» сохраняет прежнее значение и перезапускает отсчёт 24 часов.
|
||||
- Классификация не входит в `meta.dependencies` Portfolio и не меняет
|
||||
`isStale` и `lastSuccessfulAt`.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Это первый долговременный справочник T-Bank в системе: ADR 019 теряет
|
||||
характер «только онлайн» для этой области и дополняется, а не заменяется.
|
||||
- Разрезы концентрации считаются по снимку без обращений к T-Bank; число
|
||||
вызовов растёт ограниченно — не более одной попытки на поле в сутки на
|
||||
позицию, при первом обходе после выката возможен разовый всплеск, сглаженный
|
||||
общим лимитером и очередью.
|
||||
- Значение может быть устаревшим до 24 часов; это приемлемо для группировки
|
||||
позиций, но классификация не является источником актуальных данных брокера.
|
||||
- Второй потребитель хранимой классификации — условие пересмотра решения
|
||||
(выделение независимого процесса обновления, отдельные сроки).
|
||||
@@ -25,3 +25,5 @@
|
||||
- [018 — Минимальная runtime-координация](018-minimal-runtime-coordination.md)
|
||||
- [019 — Часовой обход и онлайн-справочник инструментов](019-hourly-reads-and-online-instrument-reference.md)
|
||||
- [020 — Evidence for hourly sync read boundaries](020-hourly-sync-verification-evidence.md)
|
||||
- [021 — Тихое чтение и редкие подтверждения](021-quiet-reading-and-rare-confirmations.md)
|
||||
- [022 — Хранимая классификация инструментов T-Bank](022-stored-instrument-classification.md)
|
||||
|
||||
@@ -140,12 +140,20 @@ membership текущей revision перед каждым финансовым
|
||||
личные данные. Ротация refresh token остаётся атомарной.
|
||||
|
||||
SQLite хранит полный последний portfolio, операции и их подтверждённые
|
||||
диапазоны, private прогнозы, общие instrument/market sources и intent/staging.
|
||||
диапазоны, private прогнозы, сохранённые точки истории оценки, общие
|
||||
instrument/market sources, классификацию инструментов (сектор и бренд актива
|
||||
как последнее известное значение с временем каждого получения,
|
||||
`TbankInstrumentClassification`) и intent/staging.
|
||||
Нормализованные финансовые значения сохраняются decimal-строками. Полнота
|
||||
отделена от свежести и пагинации. Ответ не ждёт внешних RPC; запрос при
|
||||
необходимости запускает работу в том же backend process через существующий
|
||||
лимитер. На startup импорт не запускается; восстановление требует следующего
|
||||
запроса. Подробности — [модель свежести](cross-cutting/data-freshness.md).
|
||||
отделена от свежести и пагинации. Ответ не ждёт внешних RPC; обновлением
|
||||
личных данных владеет часовой обход в том же backend process через
|
||||
существующий лимитер, недостающие общие источники он же ставит в фоновую
|
||||
очередь. На startup обход начинает следующую попытку по обычному часовому
|
||||
сроку; после нового подключения первая попытка начинается сразу.
|
||||
Классификация — публичные данные без привязки к пользователю и подключению;
|
||||
её поля обновляются в том же обходе не чаще раза в сутки
|
||||
([ADR 022](../adr/022-stored-instrument-classification.md)).
|
||||
Подробности — [модель свежести](cross-cutting/data-freshness.md).
|
||||
|
||||
PUT подключения сначала проверяет token у T-Bank, затем одной транзакцией
|
||||
сохраняет encrypted token, новую revision и проверенный membership.
|
||||
@@ -195,9 +203,8 @@ access token и вращаемый refresh token. [Политика query-кэш
|
||||
|
||||
Авторизация доступа, недопущение token в публичный ответ и атомарность
|
||||
ротации сессии — отдельные свойства; наличие типизированного клиента
|
||||
не доказывает их. Известное расхождение регистрации отмечено в
|
||||
[аутентификации](../domains/authentication/overview.md#регистрация-и-вход),
|
||||
а действующий формат ошибок описан в [HTTP API](http-api.md#error-contract).
|
||||
не доказывает их. Действующий формат ошибок описан в
|
||||
[HTTP API](http-api.md#error-contract).
|
||||
|
||||
Поддерживается локальный Compose и ограниченная single-server topology с SQLite.
|
||||
Origin браузера, адрес API и cookie policy должны быть согласованы.
|
||||
|
||||
@@ -8,8 +8,12 @@ T-Bank. Нормальным владельцем обновления служ
|
||||
простоя выполняется не более одна актуальная попытка, а перекрывающийся срок
|
||||
пропускается, не ставится в очередь. Общие справочные данные инструментов
|
||||
запрашиваются онлайн, когда это требуется карточке; финансовый HTTP-ответ не
|
||||
ждёт T-Bank. Модель принята в
|
||||
[ADR 019](../../adr/019-hourly-reads-and-online-instrument-reference.md).
|
||||
ждёт T-Bank. Единственное исключение — сектор и бренд актива позиции, которые
|
||||
хранятся как последнее известное значение с обновлением не чаще раза в сутки;
|
||||
свежесть и полнота Portfolio от них не зависят. Модель принята в
|
||||
[ADR 019](../../adr/019-hourly-reads-and-online-instrument-reference.md),
|
||||
исключение — в
|
||||
[ADR 022](../../adr/022-stored-instrument-classification.md).
|
||||
|
||||
## Клиентский и серверный снимки
|
||||
|
||||
@@ -18,7 +22,10 @@ T-Bank. Нормальным владельцем обновления служ
|
||||
HTTP-перечитывания в той же границе доступа. Это не серверный fallback и не
|
||||
состояние часового обхода. SQLite независимо от браузера хранит membership,
|
||||
последний полный portfolio, исполненные операции, прогнозы, общие сведения об
|
||||
инструментах, котировки, свечи и календари.
|
||||
инструментах и классификацию инструментов (сектор и бренд актива как
|
||||
последнее известное значение, [ADR
|
||||
022](../../adr/022-stored-instrument-classification.md)), котировки, свечи и
|
||||
календари.
|
||||
|
||||
[Полнота периода](../../glossary.md#полнота-истории-счёта) и свежесть различаются.
|
||||
Успешно проверенный пустой результат полон и доступен; последняя страница
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
пока неизвестно, доступен public или protected маршрут. При загрузке
|
||||
отложенного маршрута уже доступный каркас остаётся видимым.
|
||||
|
||||
Фоновое обновление и действие пользователя сохраняют видимые данные или
|
||||
элемент управления с компактной индикацией процесса. В частности,
|
||||
Фоновое чтение не сообщает о себе: оно сохраняет видимые данные без индикации
|
||||
процесса и без технического предупреждения. В частности,
|
||||
[клиентский снимок](data-freshness.md#клиентский-и-серверный-снимки) счетов или
|
||||
портфеля остаётся доступным после ошибки refetch; сообщение и повтор
|
||||
относятся к обновлению, а не к отсутствию инвестиций.
|
||||
портфеля остаётся доступным после ошибки refetch, и эта ошибка не означает
|
||||
отсутствие инвестиций. Действие пользователя сохраняет видимые данные или
|
||||
элемент управления и сообщает свой результат, включая неуспешный.
|
||||
|
||||
Первичное ожидание ограничено по времени. По его истечении область переходит
|
||||
в спокойную локальную недоступность вместо бесконечной загрузки и сама
|
||||
заменяет её содержимым после восстановления. Решение принято в
|
||||
[ADR 021](../../adr/021-quiet-reading-and-rare-confirmations.md).
|
||||
|
||||
Серверная полнота не выводится из query lifecycle. Missing показывает
|
||||
skeleton только отсутствующих секций, partial — подтверждённые строки и
|
||||
@@ -28,20 +34,30 @@ skeleton только отсутствующих секций, partial — по
|
||||
готовятся независимо. Только complete empty означает отсутствие операций.
|
||||
Время источника не заменяется временем получения HTTP-ответа.
|
||||
|
||||
Ручное обновление сохраняет показанный результат и applied filters; кнопка
|
||||
недоступна во время уже запрошенной работы. Ошибка transport отличается от
|
||||
Экраны просмотра не предлагают ручное обновление или повтор чтения: временные
|
||||
сбои GET восстанавливаются сами ограниченным числом повторов, сохраняя
|
||||
показанный результат и applied filters. Ошибка transport отличается от
|
||||
ошибки source work. Status observation прекращается после terminal outcome
|
||||
или ухода; само истечение TTL не запускает импорт. Известный access loss
|
||||
очищает personal view, включая защиту от позднего ответа.
|
||||
|
||||
Состояние источника при этом видимо: время последнего успешного результата,
|
||||
устаревание, исход последней часовой попытки и неполнота показаны текстом либо
|
||||
доступно именованным элементом рядом с данными, к которым относятся. Это
|
||||
свойство данных, а не отчёт о техническом процессе, и оно не требует действия.
|
||||
|
||||
## Известная ошибка сценария
|
||||
|
||||
Ошибки счетов, портфеля и подключения T-Bank остаются локальными: рядом
|
||||
с затронутыми данными доступны предусмотренные сценарием retry и navigation
|
||||
actions. Устаревший серверный Portfolio Snapshot показывает сохранённый
|
||||
портфель с предупреждением. Он не превращается в глобальный fallback.
|
||||
Ошибки счетов, портфеля и подключения T-Bank остаются локальными: затронутый
|
||||
блок показывает собственное состояние и не скрывает успешно отображённые
|
||||
соседние блоки. Устаревший серверный Portfolio Snapshot показывает сохранённый
|
||||
портфель с обозначением устаревания. Он не превращается в глобальный fallback.
|
||||
Ошибка источника не означает нулевой баланс или отсутствие операций.
|
||||
|
||||
Предусмотренные сценарием navigation actions сохраняются: подтверждённая потеря
|
||||
доступа предлагает восстановить подключение, а `404` — вернуться к списку
|
||||
счетов. Это навигация и явные действия пользователя, а не повтор чтения.
|
||||
|
||||
## Непойманная ошибка маршрута
|
||||
|
||||
Непойманная runtime-ошибка при отображении public или protected route
|
||||
|
||||
@@ -12,7 +12,7 @@ Development-команды ниже выполняются из workspace с che
|
||||
|
||||
- Docker Engine с Compose plugin **не ниже 2.24.4** (`!override` в development),
|
||||
доступ к registry и достаточно свободного диска для non-root UID 1000.
|
||||
Production images `0.2.3` опубликованы для Linux amd64.
|
||||
Опубликованные images собраны для Linux amd64 (см. README комплекта).
|
||||
Другую архитектуру проверяют отдельно; multi-platform images не обещаны.
|
||||
- Nexus Docker hosted repository `docker-hosted`, доступный через
|
||||
`nexus.personal.ksv741.ru/docker-hosted/`. Docker Bearer Token Realm включён;
|
||||
@@ -26,7 +26,10 @@ Development-команды ниже выполняются из workspace с che
|
||||
используется сервером для загрузки образов. Для публикации нужны
|
||||
browse/read/add/edit на `docker-hosted`; фактически выданный набор прав
|
||||
настраивается в Nexus. Built-in
|
||||
workflow token не заменяет эту настройку.
|
||||
workflow token не заменяет эту настройку. Отдельной read-only учётной записи
|
||||
сервера нет: `wealthledger-ci` работает и как CI writer, и как server reader,
|
||||
поэтому её пароль на сервере даёт право публикации в `docker-hosted`. Это
|
||||
принятое владельцем решение; разделение ролей — возможное усиление.
|
||||
- На сервере Caddy уже завершает TLS и передаёт весь домен на
|
||||
`127.0.0.1:8080` (или выбранный frontend port). Отдельного `/api` upstream нет.
|
||||
DNS, TLS, firewall и provisioning Caddy выполняются вне этого runbook.
|
||||
@@ -81,7 +84,8 @@ Workflow отказывается перезаписывать существу
|
||||
Выберите опубликованные version tags, выполните pull и сравните OCI revision
|
||||
с release commit. Выполните smoke с disposable data/config до первого server
|
||||
rollout. В `release.env` комплекта хранится пара полных image references, например
|
||||
`nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:0.2.3`. Сохраните versions/revisions
|
||||
`nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:1.1.0` и
|
||||
`.../backend:2.1.0`. Сохраните versions/revisions
|
||||
в deployment record. Release tags не должны переназначаться другим images.
|
||||
|
||||
## Перенос существующих images из Gitea
|
||||
@@ -91,7 +95,9 @@ rollout. В `release.env` комплекта хранится пара полн
|
||||
`REGISTRY_USERNAME`/`REGISTRY_TOKEN` сохраните для отката старого workflow.
|
||||
Пароли вводятся интерактивно или через `--password-stdin`, не через аргументы.
|
||||
|
||||
Скопируйте каждый опубликованный release без пересборки. Например, для `0.2.0`
|
||||
Прежние images лежат в [Gitea packages](https://git.personal.ksv741.ru/WealthLedger/-/packages)
|
||||
(`git.personal.ksv741.ru/wealthledger/{frontend,backend}`); новые публикуются
|
||||
только в Nexus. Скопируйте каждый опубликованный release без пересборки. Например, для `0.2.0`
|
||||
с установленным Skopeo и доступом чтения Gitea/записи Nexus:
|
||||
|
||||
```bash
|
||||
@@ -114,7 +120,9 @@ artifact повторно не публикуйте; несовпадение т
|
||||
После копирования сравните digests через `skopeo inspect --raw` и SHA-256,
|
||||
выполните `docker pull` обоих images пользователем `wealthledger-ci` на сервере приложения,
|
||||
проверьте OCI revision и container smoke на disposable data.
|
||||
Только после этого меняйте production image references и запускайте обычную
|
||||
Серверные шаги (login, pull, smoke) в репозитории не подтверждены и
|
||||
выполняются оператором. Только после них меняйте
|
||||
production image references и запускайте обычную
|
||||
последовательность обновления ниже. Старые Gitea packages и references сохраните:
|
||||
откат registry возвращает прежние адреса тех же artifacts, не восстанавливает
|
||||
SQLite и не меняет encryption keys. Не переносите Git release tags и не
|
||||
|
||||
@@ -61,6 +61,17 @@ Envelope несовместим с прежними bare-array ответами:
|
||||
поставляются согласованной парой. Финальный artifact передаётся явно;
|
||||
локальная генерация и CI frontend читают только собственный snapshot.
|
||||
|
||||
## Income analytics
|
||||
|
||||
`GET /api/v1/brokerage-accounts/{accountId}/income-analytics` и общий маршрут
|
||||
`all` возвращают результат только выбранного периода: итоги по исходным
|
||||
валютам, точные записи выплат, разбиение по инструментам и временные
|
||||
интервалы. Данных сравнения с предыдущим периодом или годом ранее в ответе
|
||||
нет — ни в теле, ни среди зависимостей `meta.dependencies`, роль которых
|
||||
ограничена значением `selected`. Публичную машинную схему задаёт
|
||||
backend-generated OpenAPI; frontend получает её обновлённый snapshot отдельно.
|
||||
Точную семантику поведения описывает [доход](../domains/income/overview.md).
|
||||
|
||||
## Validation и identity
|
||||
|
||||
Backend преобразует и проверяет входные данные на общей HTTP-границе.
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
|
||||
- `409 TBANK_NOT_CONNECTED`: список не запрашивается у T-Bank, экран ведёт в настройки.
|
||||
- `400 INVALID_TBANK_TOKEN`: сохранённый токен не удаляется, экран ведёт в настройки для замены или удаления.
|
||||
- `503 TBANK_UNAVAILABLE`: экран предлагает повторить загрузку или открыть настройки.
|
||||
- Неизвестная ошибка: экран предлагает только повторить загрузку.
|
||||
- `503 TBANK_UNAVAILABLE`: экран показывает спокойное состояние недоступности; чтение возобновляется автоматически.
|
||||
- Неизвестная ошибка: то же состояние недоступности без действия повтора.
|
||||
- Успешный пустой ответ: штатное состояние отсутствия инвестиционных счетов.
|
||||
|
||||
Карточка ведёт на `/accounts/:accountId` по всей области и доступна с клавиатуры. Карточки расположены в одну колонку при любой ширине. Карточка показывает название, локализованный тип, крупный валютный итог и вторую валюту при наличии, число остальных валют, дату открытия без времени и видимое действие открытия. Отдельные цветные блоки «Акции» и «Облигации» показывают количество наименований бумаг. Недоступная или пустая сводка отображается локально; отсутствие сумм не скрывает известные количества бумаг, а недоступная сводка не подменяет их нулями. Наведение и видимый клавиатурный фокус подчёркивают переход; настройка уменьшения движения отключает анимацию.
|
||||
@@ -71,9 +71,10 @@
|
||||
## Свежесть и границы
|
||||
|
||||
Обычное открытие использует query owner и локальный server snapshot.
|
||||
«Обновить» запрашивает membership и сводки доступных счетов; активная работа
|
||||
наблюдается без нового импорта. Истечение TTL само не запускает запрос.
|
||||
Membership имеет пятиминутную свежесть, portfolio — минутную. Время списка
|
||||
«Обновить» перечитывает сохранённые membership и сводки доступных счетов;
|
||||
активная работа наблюдается без нового импорта. Истечение TTL само не
|
||||
запускает запрос: обновлением личных данных владеет часовой обход.
|
||||
Membership и portfolio source имеют часовую свежесть. Время списка
|
||||
не подменяет время каждой сводки. Список не заменяет полный
|
||||
[портфель](../portfolio/overview.md), [журнал](../operations/overview.md)
|
||||
или историю закрытых счетов. Для общего результата доступен
|
||||
|
||||
@@ -11,14 +11,20 @@
|
||||
|
||||
Пользователь регистрируется или входит по email и паролю. Регистрация сразу
|
||||
создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся
|
||||
к нижнему регистру. Backend требует пароль не короче 12 символов и не длиннее
|
||||
72 UTF-8 байтов.
|
||||
к нижнему регистру.
|
||||
|
||||
**Известное расхождение:** сохранённое требование регистрации и проверка
|
||||
в форме frontend считают нижнюю границу в UTF-8 байтах, а backend — в символах.
|
||||
Не-ASCII пароль может пройти форму и получить отказ API. Это не принятое
|
||||
изменение password policy; требуется отдельное согласованное решение
|
||||
о проверке пароля и подтверждение полного HTTP-потока.
|
||||
Пароль при регистрации — от 8 до 72 печатных ASCII-символов: латинские
|
||||
буквы, цифры, спецсимволы и пробел. Кириллица, emoji, другие не-ASCII и
|
||||
управляющие символы отклоняются. Пароль не обрезается и не нормализуется, а
|
||||
обязательных классов символов нет. Верхняя граница вытекает из bcrypt, который
|
||||
учитывает только первые 72 байта: для ASCII символ равен байту, поэтому
|
||||
длинный пароль не обрезается молча. Frontend и backend проверяют одну и ту же
|
||||
политику.
|
||||
|
||||
При входе проверяется только то, что пароль непустой и не длиннее 72 UTF-8
|
||||
байтов. Минимум и набор символов не применяются, чтобы пользователь,
|
||||
зарегистрированный по прежним правилам (в том числе с не-ASCII паролем), мог
|
||||
войти. Невалидный пароль получает `400` с кодом `VALIDATION_ERROR`.
|
||||
|
||||
Access token действует 15 минут и хранится только в памяти браузера.
|
||||
Refresh token имеет серверный срок 7 дней, заменяется при восстановлении
|
||||
|
||||
+14
-29
@@ -4,7 +4,7 @@
|
||||
купонами и дивидендами за выбранный период». Пользователь открывает
|
||||
`/accounts/:accountId/income` из обзора счёта или
|
||||
[общий доход](../portfolio/all-accounts.md) по доступным счетам.
|
||||
Экран показывает итоги по валютам, сравнение и отдельную карточку каждой
|
||||
Экран показывает итоги по валютам и отдельную карточку каждой
|
||||
включённой выплаты. Источником служат [исполненные операции](../operations/overview.md),
|
||||
а не изменение цены портфеля и не календарь будущих выплат.
|
||||
|
||||
@@ -38,11 +38,12 @@
|
||||
|
||||
При первой загрузке или новом периоде без сохранённых данных показан
|
||||
каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и
|
||||
дивидендов, сохраняя фильтры, налоговое пояснение и сравнения. Ошибка запроса
|
||||
или нарушение принадлежности выплат даёт локальное состояние с действием «Повторить».
|
||||
дивидендов, сохраняя фильтры и налоговое пояснение. Ошибка запроса
|
||||
или нарушение принадлежности выплат даёт локальное состояние недоступности
|
||||
без действия повтора; чтение восстанавливается автоматически.
|
||||
При фоновом обновлении того же периода сохранённый ответ и черновик
|
||||
остаются видимыми: загрузку обозначает индикатор, неуспешное обновление —
|
||||
предупреждение с повторной попыткой.
|
||||
остаются видимыми без индикации обновления и технического предупреждения.
|
||||
Неполнота применённого периода обозначается состоянием самого результата.
|
||||
|
||||
На узком экране поля и действия идут вертикально; все карточки выплат
|
||||
остаются в общем потоке страницы без раскрытия или внутренних областей прокрутки.
|
||||
@@ -51,32 +52,15 @@
|
||||
|
||||
- Backend проверяет доступ локально и читает сохранённые операции. Фоновая загрузка должна завершить все страницы; ошибка страницы или повреждённая цепочка курсоров не подтверждают полноту.
|
||||
- Только точные типы купона и дивиденда источника формируют результат. Суммы остаются точными десятичными строками в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и переводы дивидендов не входят в доход.
|
||||
- Итоги, сравнения, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Страница показывает сравнения с предыдущим периодом и годом ранее, когда календарная дата и история счёта позволяют. Неполное сравнение имеет отдельное сообщение, без нулевой базы.
|
||||
- Итоги, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Система не загружает другие периоды ради сравнения полученного дохода с выбранным.
|
||||
- Временные интервалы по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые интервалы. Страница дохода не строит из них график или текстовую динамику; ответ API не меняется.
|
||||
- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты по-прежнему разделены в итогах и сравнениях. Дополнительный запрос за составом не нужен.
|
||||
|
||||
Сравнение с непосредственно предыдущим периодом использует предыдущий полный
|
||||
месяц для полного выбранного месяца, иначе — непосредственно предшествующий
|
||||
интервал той же включительной длительности. Оно не подменяет сравнение годом
|
||||
ранее. Незагруженное сравнение и ошибка его источника отличаются от
|
||||
недоступности годового сравнения по календарю или дате открытия счёта.
|
||||
|
||||
Каждый раздел сравнения показывает цветные панели без общей белой подложки и
|
||||
внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей с русским форматом диапазона
|
||||
и UTC для календарных дат.
|
||||
Отдельного заголовка с датами и валютного бейджа нет; валюта остаётся у каждой суммы.
|
||||
Панель «Разница» сохраняет серверную абсолютную разницу и дополняет итог, купоны
|
||||
и дивиденды процентом: абсолютная разница / сумма годом ранее × 100.
|
||||
Это округлённый показатель для отображения, а не пересчёт Money или инвестиционная
|
||||
доходность. Форматирование сохраняет знак и не более одного десятичного разряда.
|
||||
При нулевой или отрицательной базе либо непредставимом числовом отношении
|
||||
процентный бейдж не отображается; абсолютная разница остаётся видимой.
|
||||
- Раздел «Выплаты» показывает карточки выплат в исходном порядке `entries`. В отдельном счёте каждая выплата — отдельная карточка. В режиме «Все счета» выплаты одного инструмента, дня и точного типа образуют группу, если их две и более ([правила группировки](../operations/overview.md#группировка-в-режиме-все-счета)). Группа стоит на месте первой выплаты; каждая строка счёта показывает название счёта и свою сумму со знаком. Визуальный итог группы — точная сумма по валютам по показанным выплатам; он скрыт, если полнота периода не `complete`, или у выплаты нет суммы, или сумма равна нулю. Это клиентский расчёт и не заменяет серверные итоги аналитики дохода, которые по-прежнему приходят из ответа API и не пересчитываются. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой выплате. Дополнительный запрос за составом не нужен.
|
||||
|
||||
## Полнота и принадлежность выплат
|
||||
|
||||
Доходы читают сохранённые операции и coverage в согласованной транзакции.
|
||||
Selected, previous и yearAgo имеют независимую готовность. Неполный selected
|
||||
не получает окончательных итогов; неполное сравнение не создаёт нулевую базу.
|
||||
Только выбранный период определяет готовность результата. Неполный период
|
||||
не получает окончательных итогов.
|
||||
Подтверждённый пустой период отличается от ещё не загруженного. При смене
|
||||
периода прежний snapshot не показывается под новым названием.
|
||||
|
||||
@@ -96,18 +80,19 @@ Backend обеспечивает принадлежность выплат, ва
|
||||
доверяет типизированному contract, не восстанавливает формулу идентификации
|
||||
и не выполняет второй consistency validator. Полностью пустой ответ допустим.
|
||||
Состав и исходный серверный порядок записей и знак сумм
|
||||
сохраняются, frontend не пересчитывает денежные итоги. Стабильный ID в пределах подключения и счёта определяет одну операцию:
|
||||
сохраняются; серверные денежные итоги frontend не пересчитывает (визуальный итог группы в режиме «Все счета» отдельный, см. выше). Стабильный ID в пределах подключения и счёта определяет одну операцию:
|
||||
повторная сверка обновляет её без двойного учёта. Одинаковые ID разных счетов
|
||||
остаются разными выплатами. Изменение источника логотипа позволяет заново
|
||||
показать изображение после буквенной заглушки.
|
||||
|
||||
## Будущие события и ограничения
|
||||
|
||||
На странице дохода нет блоков «Динамика» и «Ближайший прогноз». Она не запрашивает
|
||||
На странице дохода нет блоков «Динамика» и «Ближайший прогноз», блоков
|
||||
сравнения периодов и сообщений об их недоступности. Она не запрашивает
|
||||
`/events` и не зависит от его доступности. Самостоятельная страница
|
||||
[«Будущие события»](upcoming-events.md) по-прежнему доступна из обзора
|
||||
выбранного счёта и сохраняет прежнее поведение. Будущие выплаты
|
||||
не включаются в итоги полученного дохода или сравнения.
|
||||
не включаются в итоги полученного дохода.
|
||||
|
||||
Аналитика читает сохранённую историю операций и не рассчитывает налоги, инвестиционную эффективность, доходность, валютную конвертацию или общий мультивалютный итог.
|
||||
|
||||
|
||||
@@ -39,13 +39,23 @@
|
||||
|
||||
## Период и список
|
||||
|
||||
Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней
|
||||
после начала, обе даты включены. `from` и `to` принимают только строгие даты
|
||||
`YYYY-MM-DD`; без `from` используется текущая дата UTC, без `to` — `from + 30`
|
||||
календарных дней. Поля «С» и «По» сохраняют выбранные календарные даты в URL,
|
||||
а «Сбросить» возвращает период по умолчанию.
|
||||
Без фильтра страница применяет период по умолчанию из семи календарных
|
||||
дат включительно: сегодняшней и шести следующих календарных дней. API при
|
||||
отсутствии параметров использует своё окно (от текущей даты UTC до `from + 30`
|
||||
календарных дней), обе даты включены; `from` и `to` принимают только строгие
|
||||
даты `YYYY-MM-DD`. Календарь фильтра разрешает выбор сегодняшней и будущих дат
|
||||
и не позволяет выбрать даты раньше сегодняшней. Прямая ссылка с валидным
|
||||
прошедшим периодом сохраняет указанные даты без замены; некорректный формат,
|
||||
несуществующая календарная дата или обратный диапазон нормализуются к периоду
|
||||
по умолчанию с удалением параметров из URL. Поля «С» и «По» сохраняют
|
||||
выбранные календарные даты в URL, а «Сбросить» возвращает период по умолчанию.
|
||||
|
||||
События упорядочены по календарной дате. У каждой строки видны тип,
|
||||
События упорядочены по календарной дате. Купоны и дивиденды показываются
|
||||
единым предметным представлением выплаты с явной пометкой прогноза — тем же,
|
||||
что у исполненных выплат в доходе и журнале; неизвестная сумма остаётся
|
||||
«сумма неизвестна» и не превращается в ноль или полученные деньги.
|
||||
Дата дивиденда обозначена как дата реестра. Offer и погашение сохраняют своё
|
||||
значение без суммы выплаты. У каждой строки видны тип,
|
||||
инструмент, дата и доступная сумма в исходной валюте. Список не имеет
|
||||
пагинации и не конвертирует валюты. Пустой результат означает отсутствие
|
||||
известных поддерживаемых событий в выбранном окне, а не доказательство
|
||||
@@ -61,7 +71,16 @@
|
||||
без замены периода и без запуска личной работы.
|
||||
Фильтрация на сервере предшествует сортировке и вычислению
|
||||
`incomeTotalsByCurrency`, включающего только купоны и дивиденды.
|
||||
Итоги остаются в ответе API; полный экран показывает список без общего итога.
|
||||
Итоги остаются в ответе API; полный экран показывает список без общего итога списка.
|
||||
|
||||
В режиме «Все счета» события одного инструмента, даты, точного типа и `dateKind` образуют
|
||||
группу, если их две и более, в том числе одного счёта. Ключ дополнительно включает `dateKind`,
|
||||
поэтому дата реестра дивиденда и иная дата того же инструмента не объединяются. Группа стоит на месте первого
|
||||
события. Полный список группирует все полученные события; превью обзора сначала берёт пять
|
||||
событий и группирует только их. Каждая строка счёта остаётся отдельной и показывает название
|
||||
счёта и свою прогнозную сумму. Визуальный итог группы — точная сумма по валютам; он показывается
|
||||
только если полнота прогноза `complete` и у каждого события группы известна прогнозная сумма.
|
||||
Иначе итог скрыт, а строки остаются. Этот итог не равен итогам ответа API и не относится к полученному доходу.
|
||||
|
||||
## Получение и отказы
|
||||
|
||||
|
||||
@@ -89,7 +89,11 @@ HTML-источник, общий кэш и поведение при измен
|
||||
|
||||
Календарь охватывает пять прошлых лет и следующий год. Будущие купоны
|
||||
сортируются по дате выплаты, прошедшие и недатированные доступны отдельно.
|
||||
Показаны сумма на облигацию, номер, тип, длительность периода и дата реестра,
|
||||
Будущие и прошедшие события купонного графика показываются единым предметным
|
||||
представлением выплаты с пометкой события по графику — тем же, что у
|
||||
исполненных выплат и прогноза; исполнение подтверждает только личная история.
|
||||
Показаны сумма на облигацию, отдельная личная оценка, номер, тип, длительность
|
||||
периода и дата реестра,
|
||||
если они известны. Неопределённый будущий купон не заменяется нулём.
|
||||
|
||||
Личная оценка доступна только при положительных позициях, известной сумме,
|
||||
@@ -117,8 +121,8 @@ HTML-источник, общий кэш и поведение при измен
|
||||
portfolio; они не попадают в общие источники.
|
||||
|
||||
BondBy переиспользуется зависимостями выпуска. Discovery принадлежит query,
|
||||
свежесть — конкретному source/range. Missing/stale зависимости запрашиваются
|
||||
в фоне через прежний limiter; обычный HTTP-ответ не ждёт брокера.
|
||||
свежесть — конкретному source/range. Missing/stale зависимости ставятся
|
||||
в фоновую очередь через прежний limiter; обычный HTTP-ответ не ждёт брокера.
|
||||
Покрытые завершённые свечи не перечитываются после restart. Открытый хвост
|
||||
остаётся изменяемым; ручной refresh охватывает показанный период.
|
||||
|
||||
@@ -127,7 +131,8 @@ BondBy переиспользуется зависимостями выпуск
|
||||
выплат. Наблюдение статуса не запускает повторный импорт. Политика источников
|
||||
и границы — в [модели свежести](../../architecture/cross-cutting/data-freshness.md).
|
||||
|
||||
Разделы загружаются независимо, показывают каркас загрузки и локальную повторную попытку.
|
||||
Разделы загружаются независимо, показывают каркас загрузки и локальное
|
||||
спокойное состояние недоступности без действия повтора.
|
||||
Ошибка фонового обновления сохраняет последние данные. Смена подключения
|
||||
сбрасывает поддерево клиентских запросов облигаций вместе с остальными финансовыми запросами;
|
||||
выход очищает кэш сессии. Общие график, позиция и личные события
|
||||
@@ -146,6 +151,6 @@ BondBy переиспользуется зависимостями выпуск
|
||||
|
||||
## Online reference
|
||||
|
||||
Bond detail, search, and ticker lookup are read from the upstream integration
|
||||
on each request. Saved quotes, candles, events, ratings, and portfolio labels
|
||||
retain their own freshness and failure boundaries.
|
||||
Страница облигации, поиск и переход по тикеру обращаются к онлайн-справочнику
|
||||
брокера; сохранённые quotes, свечи, события, ratings и метки портфеля
|
||||
сохраняют собственные границы свежести и ошибок.
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
время получения списка. Изменение условий не заменяет текущий список до
|
||||
следующего нажатия «Найти». Повторный поиск получает новый каталог; страницы
|
||||
одного результата не пересчитываются. Подтверждённый пустой результат отличается
|
||||
от ошибки источника, для которой интерфейс предлагает повторить поиск.
|
||||
от ошибки источника: она сообщается рядом с полем поиска, а повторить запрос
|
||||
пользователь может обычным «Найти» — отдельного действия повтора чтения нет.
|
||||
|
||||
Ссылка карточки принадлежит выбранному UID; данные разных режимов не смешиваются.
|
||||
Прямые ссылки из портфеля сохраняют
|
||||
@@ -63,8 +64,9 @@
|
||||
рыночные данные и личная история остаются доступны.
|
||||
|
||||
Каждый раздел загружается независимо. Первичная загрузка показывает каркас
|
||||
раздела, ошибка — локальную повторную попытку. При ошибке фонового обновления
|
||||
сохраняются предыдущие данные и предупреждение. Цена содержит собственное время
|
||||
раздела, затянувшаяся недоступность — локальное спокойное состояние без
|
||||
повторной попытки. При ошибке фонового обновления сохраняются предыдущие
|
||||
данные без технического предупреждения. Цена содержит собственное время
|
||||
UTC, которое может быть старше времени получения ответа. Статус торгов не
|
||||
выводится из свежести цены. Отсутствующее значение не заменяется нулём.
|
||||
|
||||
@@ -124,7 +126,11 @@ UID включён в ключ кэша и подпись общего курс
|
||||
|
||||
## Дивиденды и финансовые показатели
|
||||
|
||||
События компании отделены от личных зачислений. Известные будущие выплаты
|
||||
События компании отделены от личных зачислений. Будущие и прошедшие события
|
||||
дивидендного графика показываются единым предметным представлением выплаты
|
||||
с пометкой «событие по графику» — тем же, что у исполненных выплат и прогноза;
|
||||
сумма на акцию, отдельная личная оценка и дата реестра сохраняют свои подписи
|
||||
и не выдаются за личное зачисление. Известные будущие выплаты
|
||||
определяются по `paymentDate`, при её отсутствии — по `recordDate`; прошедшие
|
||||
и недатированные события помещены в отдельную историю компании. Отменённые
|
||||
события исключаются. Личная оценка умножает сумму на акцию на текущее положительное
|
||||
@@ -155,14 +161,16 @@ TTM означает последние 12 месяцев, MRQ — послед
|
||||
источники переиспользуются между пользователями, но личные позиции и выплаты
|
||||
всегда проверяют доступ.
|
||||
|
||||
Обычное чтение не ждёт RPC; missing/stale зависимости запрашиваются в фоне
|
||||
через существующий T-Bank limiter. Минутная свежесть применяется к quotes
|
||||
и открытому хвосту свечей, суточная — к metadata/company/календарям.
|
||||
Обычное чтение не ждёт RPC; missing/stale зависимости ставятся в фоновую
|
||||
очередь через существующий T-Bank limiter. Часовая свежесть применяется
|
||||
к quotes и открытому хвосту свечей; metadata/company/календари, будучи
|
||||
сохранёнными, не перечитываются обычным чтением.
|
||||
Закрытые полные свечные интервалы, включая пустые, сохраняются после restart.
|
||||
Новый день не делает прежнюю незавершённую свечу полной.
|
||||
|
||||
«Обновить» передаёт текущий инструмент, выбранный график и personal-history
|
||||
scope. Частичные строки видны с предупреждением; неполные личные оценки
|
||||
Отдельного действия «Обновить» на странице нет: чтение текущего инструмента,
|
||||
выбранного графика и personal-history scope выполняется автоматически.
|
||||
Частичные строки видны с обозначением неполноты; неполные личные оценки
|
||||
не становятся нулями. Статус и `sync=observe` не создают новый импорт.
|
||||
Смена подключения и известный access loss очищают личное представление;
|
||||
поздние ответы не возвращают прежние данные. Детали —
|
||||
@@ -193,7 +201,6 @@ scope. Частичные строки видны с предупреждени
|
||||
|
||||
## Online reference
|
||||
|
||||
Stock detail, company data, search, and ticker lookup are read from the
|
||||
upstream integration on each request. Saved positions, quotes, candles, and
|
||||
events remain independent local resources and are not replaced by a reference
|
||||
read failure.
|
||||
Страница акции, поиск и переход по тикеру обращаются к онлайн-справочнику
|
||||
брокера; сохранённые позиции, quotes, свечи и события остаются независимыми
|
||||
локальными ресурсами и не подменяются ошибкой справочника.
|
||||
|
||||
@@ -13,9 +13,27 @@
|
||||
Исполненная операция содержит тип события, его категорию, описание,
|
||||
момент совершения, доступные сведения об инструменте и денежное движение.
|
||||
Запросы отменённых или ожидающих исполнения поручений не образуют этот журнал.
|
||||
Суммы остаются точными десятичными строками в исходной валюте; frontend
|
||||
только форматирует их. Положительное и отрицательное движение сохраняют
|
||||
знак. Отсутствие `payment` означает «Без движения денег», а не нулевую сумму.
|
||||
Суммы остаются точными десятичными строками в исходной валюте. Frontend
|
||||
форматирует их и считает визуальный итог группы по показанным записям
|
||||
(см. [группировку](#группировка-в-режиме-все-счета)). Положительное и отрицательное
|
||||
движение сохраняют знак. Отсутствие `payment` означает «Без движения денег», а не нулевую сумму.
|
||||
|
||||
## Группировка в режиме «Все счета»
|
||||
|
||||
Только в режиме `all` записи журнала и превью обзора, которые относятся к одному
|
||||
дню экрана, одному инструменту и точному типу операции, объединяются в группу.
|
||||
Ключом служат день, `instrumentUid` (иначе `figi`) и тип; группа образуется при
|
||||
двух и более записях, в том числе одного счёта. Группа стоит на месте первой записи.
|
||||
Каждая запись остаётся отдельной строкой с названием счёта простым текстом и своей суммой.
|
||||
Одиночный счёт не группируется.
|
||||
|
||||
Группировка выполняется после лимита: превью обзора сначала берёт пять записей,
|
||||
затем группирует только их. Полный журнал группирует страницу, уже загруженную
|
||||
курсором; группы не переносятся между страницами. Курсор и порядок записей не меняются.
|
||||
|
||||
Итог группы — точная сумма по каждой валюте, без конвертации. Он показывается только
|
||||
если полнота журнала `complete`, у каждой записи группы есть `payment`, и ни одна сумма
|
||||
не равна нулю. Иначе итог скрыт, а строки счетов остаются.
|
||||
|
||||
Категории объединяют разные события для фильтрации: сделка (`trade`),
|
||||
доход (`income`), налог (`tax`), комиссия (`fee`), перевод (`transfer`)
|
||||
@@ -84,8 +102,10 @@ Coverage/status без изменения rowset cursor не ломают. Пе
|
||||
|
||||
Полный журнал и превью используют одинаковую компактную строку: логотип
|
||||
или буквенная заглушка, описание, дата, инструмент и денежное движение.
|
||||
Для купона и дивиденда денежная метка называет точное событие; для прочих
|
||||
операций — категорию. Она показывается один раз, без повтора рядом с датой.
|
||||
Исполненные купоны и дивиденды показываются единым предметным представлением
|
||||
выплаты — тем же, что на странице дохода, в прогнозе и графиках инструментов;
|
||||
денежная метка называет точное исполненное событие. Для прочих
|
||||
операций метка называет категорию. Она показывается один раз, без повтора рядом с датой.
|
||||
Знак и цвет обозначают направление денег; отсутствие движения нейтрально.
|
||||
Если описание отсутствует, показывается локализованный тип операции.
|
||||
|
||||
|
||||
@@ -53,13 +53,10 @@ Backend публикует статические маршруты под `/api/
|
||||
- Журнал объединяет операции по убыванию времени, при равном времени использует
|
||||
порядок идентификаторов счетов и операций. ID
|
||||
операций разных счетов не считаются дублями. Переводы не схлопываются.
|
||||
- Доходы рассчитываются из согласованной локальной выборки операций за общие периоды.
|
||||
Итоги, сравнения, временные интервалы и принадлежность выплат рассчитывает
|
||||
сервис дохода.
|
||||
Для сравнения с годом ранее используется текущий состав счетов; новый счёт
|
||||
добавляет только реально полученные выплаты. При известных датах открытия всех счетов
|
||||
доступность сравнения определяется самым ранним открытием в этом составе.
|
||||
Закрытые счета в историческое сравнение не добавляются.
|
||||
- Доходы собираются из всех страниц операций каждого счёта за выбранный период.
|
||||
Итоги, временные интервалы и принадлежность выплат рассчитывает
|
||||
сервис дохода. Данные сравнения периодов не загружаются и не возвращаются;
|
||||
состав счетов влияет только на фактические выплаты выбранного периода.
|
||||
- Будущие события сортируются по дате и сохраняют отдельную строку каждого счёта.
|
||||
Прогноз не включается в реализованные доходы.
|
||||
|
||||
@@ -84,6 +81,12 @@ Backend публикует статические маршруты под `/api/
|
||||
применённые фильтры. Coverage/status без изменения строк не ломают cursor.
|
||||
Продолжение не зависит от RAM-кэша или перезапуска.
|
||||
|
||||
Визуальная группировка строк одного инструмента в общих списках выполняется только
|
||||
в пределах уже полученного списка: в журнале — на загруженной странице, без переноса группы
|
||||
на следующую; в полном списке будущих событий и выплатах дохода — по всем полученным строкам
|
||||
периода. Группировка не меняет порядок, cursor или ответ API. Протокол курсора прежний.
|
||||
Правила группировки — в [журнале](../operations/overview.md#группировка-в-режиме-все-счета).
|
||||
|
||||
Повторная сверка полного показанного периода публикует все счета и интервалы
|
||||
одной транзакцией; успешное первое окно не смешивается с прежними остальными.
|
||||
Подробнее — [журнал](../operations/overview.md#получение-и-полнота).
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# Концентрация портфеля
|
||||
|
||||
Вкладка «Концентрация» на странице счёта `/accounts/:accountId` показывает,
|
||||
какие акции, облигации, секторы и эмитенты занимают наибольшую часть текущей
|
||||
оценки Portfolio. Она отвечает на вопрос «где сконцентрированы деньги»,
|
||||
который не решают карточки позиций и [структура
|
||||
активов](overview.md#как-читать-экран). Точный смысл полей и долей — в
|
||||
[справочнике данных](data-reference.md).
|
||||
|
||||
## Вкладка и разрезы
|
||||
|
||||
Вкладка есть только у отдельного счёта: в режиме [«Все счета»](all-accounts.md)
|
||||
её нет. Появление вкладки не меняет вкладку, открытую по умолчанию («Позиции»),
|
||||
и содержание остальных вкладок. Пока Portfolio загружается, при его ошибке или
|
||||
штатном пустом составе вкладки нет; при Portfolio хотя бы с одной позицией или
|
||||
денежным остатком вкладка показывается.
|
||||
|
||||
Вкладка содержит четыре разреза и показывает один из них одновременно:
|
||||
|
||||
| Разрез | Что ранжирует | Типы позиций |
|
||||
| --- | --- | --- |
|
||||
| «Акции» | Пять крупнейших позиций акций | `STOCK` |
|
||||
| «Облигации» | Пять крупнейших позиций облигаций | `BOND` |
|
||||
| «Секторы» | Пять крупнейших секторов по данным T-Bank | `STOCK`, `BOND`, `FUND` |
|
||||
| «Эмитенты» | Пять крупнейших эмитентов (брендов актива) | `STOCK`, `BOND` |
|
||||
|
||||
Фонды участвуют только в разрезе «Секторы»: их эмитент — управляющая компания,
|
||||
а не компания внутри фонда. Позиции типов `CURRENCY`, `FUTURE`, `OPTION`,
|
||||
`STRUCTURED_NOTE`, `DIGITAL_FINANCIAL_ASSET` и `OTHER` во вкладку не входят.
|
||||
|
||||
Позиции без сектора образуют явную группу «Сектор не указан», без бренда —
|
||||
«Эмитент не указан»; обе группы показываются с видимой долей. Государственные
|
||||
облигации определяются значением сектора T-Bank `government` с подписью
|
||||
«Государственный сектор»; других собственных классификаций WealthLedger
|
||||
не вводит. Название элемента в разрезе эмитентов — название бренда; если у
|
||||
одного бренда разные названия, показывается название позиции с наибольшей
|
||||
стоимостью.
|
||||
|
||||
## Топ-5 и «Прочее»
|
||||
|
||||
В каждом разрезе показаны пять крупнейших элементов по текущей денежной оценке,
|
||||
каждый с суммой и долей. Рядом показан суммарный показатель «Топ-5 · N %».
|
||||
Если в разрезе семь и более элементов, все остальные объединены в одну строку
|
||||
«Прочее» с числом элементов, суммой и долей. При шести и меньшем числе
|
||||
элементов каждый показан своей строкой. По нажатию на «Прочее» раскрывается
|
||||
полный перечень остальных элементов, каждый со своей суммой и долей; топ-5
|
||||
и диаграмма при этом не меняются.
|
||||
|
||||
Доли считаются от суммы стоимостей позиций разреза в выбранной валюте и
|
||||
в сумме дают ровно 100 %; округление — до десятой доли процента, очень малые
|
||||
доли показываются как «< 0,1 %». Отрицательные и нулевые стоимости, а также
|
||||
позиции без оценки в разрез не входят. Конвертации валют нет: каждая позиция
|
||||
входит только в валюту своей оценки.
|
||||
|
||||
## Валюта
|
||||
|
||||
У вкладки свой список валют — валюты оценок позиций акций, облигаций и фондов,
|
||||
отсортированные по ISO-коду, — и свой выбор, независимый от «Структуры
|
||||
портфеля». По умолчанию выбрана первая валюта списка. При одной валюте
|
||||
переключатель не показывается. Общего итога по нескольким валютам нет.
|
||||
Выбранные разрез и валюта сохраняются, пока пользователь остаётся на странице
|
||||
счёта, включая переключение её вкладок, и сбрасываются при новом заходе и при
|
||||
смене счёта.
|
||||
|
||||
## Диаграмма и неполные данные
|
||||
|
||||
Рядом с перечнем показана кольцевая диаграмма, согласованная с перечнем;
|
||||
информация передаётся не только цветом, есть текстовый эквивалент. Если ответ
|
||||
отмечен как неполный (`completeness: "incomplete"` — часть позиций не имеет
|
||||
оценки), над разрезом появляется предупреждение «Данные неполные», а под
|
||||
списком — сноска «Не учтено позиций: N» с числом позиций, не вошедших в разрез.
|
||||
Пустой разрез (нет позиций с положительной оценкой в выбранной валюте)
|
||||
показывает текстовое состояние вместо диаграммы.
|
||||
|
||||
## Откуда берутся сектор и эмитент
|
||||
|
||||
Сектор и эмитент — часть данных Portfolio: их получает backend в часовом
|
||||
обходе и сохраняет в снимке вместе с остальными полями позиций
|
||||
([поток загрузки](flows/portfolio-loading.md#обновление-сектора-и-эмитента),
|
||||
[интеграция T-Bank](../../integrations/tbank.md)). Вкладка считается по уже
|
||||
загруженному снимку и не обращается к T-Bank.
|
||||
@@ -32,7 +32,9 @@
|
||||
| Cash Balance | Суммы из `GetPositions.money`, объединённые по валюте | Заблокированные деньги не прибавляются; торгуемый инструмент `CURRENCY` остаётся позицией |
|
||||
| `totalsByCurrency` | Стоимости позиций + доступные деньги той же валюты | Не включает невычислимую стоимость; не обещает полную оценку портфеля |
|
||||
| `lastSuccessfulAt` | Время формирования успешного снимка, UTC | Не является временем каждой котировки; запасной снимок сохраняет исходное время |
|
||||
| `isStale` | Истёк минутный срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса |
|
||||
| `isStale` | Истёк часовой срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса |
|
||||
| `sector` | Сектор инструмента в том виде, как его определяет T-Bank, с обрезанными пробелами | Только для позиций `STOCK`, `BOND` и `FUND`; значение из одних пробел считается отсутствующим. Во всех остальных случаях — `null` |
|
||||
| `issuer` | Бренд актива инструмента по данным T-Bank: `{ id, name }` | Только для позиций `STOCK`, `BOND` и `FUND`, когда у актива известны uid и название бренда. Во всех остальных случаях — `null`. Не влияет на количество, стоимость и свежесть снимка; используется вкладкой [«Концентрация»](concentration.md) |
|
||||
|
||||
Ненулевые денежные остатки одной валюты суммируются. Позиции одинаковых
|
||||
инструментов в ответе отдельного счёта не объединяются; визуальная группировка
|
||||
@@ -58,6 +60,8 @@ ISIN, тикер и `logoName` могут быть `null`. `logoName` — имя
|
||||
или метаданных не меняет количество, суммы и свежесть снимка и не скрывает
|
||||
финансовые данные. Metadata обновляется независимо; отсутствие имени не задерживает выдачу
|
||||
финансового снимка и оставляет доступный идентификатор.
|
||||
`logoName` не равен эмитенту из вкладки «Концентрация»: он берётся из metadata
|
||||
инструмента, а эмитент — из [классификации инструмента](flows/portfolio-loading.md#обновление-сектора-и-эмитента).
|
||||
|
||||
## Структура и доли
|
||||
|
||||
|
||||
@@ -11,9 +11,28 @@
|
||||
они не ждут и не вызывают T-Bank и не предлагают ручную синхронизацию. Часовой
|
||||
обход текущего подключения обновляет Portfolio независимо от экрана. Для нового
|
||||
полного snapshot ему нужны оба финансовых ответа `GetPortfolio` и
|
||||
`GetPositions`; суммы и позиции публикуются вместе. Metadata инструмента
|
||||
дополняет представление независимо, с доступным UID/FIGI вместо имени до её
|
||||
онлайн-загрузки.
|
||||
`GetPositions`; суммы и позиции публикуются вместе. Metadata инструмента и
|
||||
классификация (сектор и эмитент) дополняют представление независимо,
|
||||
с доступным UID/FIGI вместо имени до их загрузки.
|
||||
|
||||
## Обновление сектора и эмитента
|
||||
|
||||
Часовой обход также обновляет сектор и эмитента позиций типов `share`, `bond`
|
||||
и `etf`. Значения хранятся как последнее известное ([ADR
|
||||
022](../../../adr/022-stored-instrument-classification.md)): сектор — как его
|
||||
определяет T-Bank, эмитент — бренд актива (`uid` и `name`). Каждое поле
|
||||
обновляется при первом обходе, начавшемся не ранее чем через 24 часа после его
|
||||
последнего успешного получения; пока значение не получено, попытка повторяется
|
||||
в каждом обходе. Сектор и эмитент обновляются независимо.
|
||||
|
||||
Неуспешное получение не мешает публикации snapshot и не меняет
|
||||
`lastSuccessfulAt` и `isStale`; сохранённое значение остаётся доступным.
|
||||
Исключение — отказ token: он обрабатывается так же, как отказ token при
|
||||
обходе Portfolio. Инструмент ищется по UID, затем по FIGI; ответ «не найдено»
|
||||
сохраняет прежнее значение и отсчёт 24 часов начинается заново. Позиции без
|
||||
полученных значений участвуют в разрезах вкладки
|
||||
[«Концентрация»](../concentration.md) как «Сектор не указан» и
|
||||
«Эмитент не указан».
|
||||
|
||||
Открытый экран выполняет локальное перечитывание не чаще одного раза в
|
||||
30 минут; retry также повторяет только HTTP GET. `data: null` означает, что
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
величины. Убедитесь, что сравниваемые значения относятся к одному составу
|
||||
счетов и инструменту. Название или тикер сами по себе не доказывают
|
||||
совпадение выпуска; используйте доступные UID, FIGI и ISIN.
|
||||
2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и наличие
|
||||
предупреждения о клиентском обновлении. Серверный снимок не обещает время
|
||||
2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и показанный
|
||||
исход последней часовой попытки. Серверный снимок не обещает время
|
||||
каждой котировки, а общий портфель может соединять разные моменты.
|
||||
Если запрос завершился ошибкой, доступное действие «Повторить» должно
|
||||
обновить результат или оставить объяснимую ошибку. Перезагрузка backend
|
||||
Действия «Повторить» на экране нет: временный сбой чтения восстанавливается
|
||||
автоматически, поэтому расхождение проверяйте по показанному состоянию
|
||||
источника, а не по результату ручного обновления. Перезагрузка backend
|
||||
сохраняет последний полный снимок в SQLite, но не создаёт историю стоимости.
|
||||
3. **Проверьте состав суммы.** Для одной валюты итог складывается из
|
||||
вычислимых стоимостей позиций и доступных денег. Не добавляйте деньги
|
||||
|
||||
@@ -64,6 +64,10 @@ Backend получает данные через серверную
|
||||
без логотипа и с подписью результата. Отсутствующие показатели скрываются,
|
||||
а не подменяются нулём или заглушкой.
|
||||
|
||||
Рядом с вкладкой «Позиции» на странице отдельного счёта есть вкладка
|
||||
[«Концентрация»](concentration.md): она показывает, где сконцентрированы
|
||||
деньги — крупнейшие бумаги, секторы и эмитенты. В режиме «Все счета» её нет.
|
||||
|
||||
Существует расхождение: [согласованная спецификация представления позиций](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md#requirement-представление-карточек-позиций-на-клиенте)
|
||||
требует доступности всех иных типов в отдельной секции, а текущие frontend
|
||||
и его проверка исключают `CURRENCY` только из списка карточек. Такие позиции
|
||||
@@ -79,18 +83,23 @@ Backend получает данные через серверную
|
||||
не содержат данных и не получают фокус. При обычном фоновом обновлении
|
||||
сохраняются предыдущие суммы и время успешного получения в UTC.
|
||||
|
||||
Серверный `isStale` обозначает истечение минутной свежести снимка.
|
||||
Клиентское предупреждение означает неудачную попытку обновить уже показанный
|
||||
ответ. Эти состояния независимы и могут появиться одновременно.
|
||||
Успешный повторный запрос обновляет суммы и время, снимает клиентскую ошибку;
|
||||
серверное предупреждение определяется новым `isStale`.
|
||||
Серверный `isStale` обозначает возраст снимка старше часа: обновлением
|
||||
владеет часовой обход, а не запрос экрана. Он показывается рядом с суммами
|
||||
вместе с временем последнего успешного результата и исходом последней часовой
|
||||
попытки. Неудачная попытка клиента обновить уже показанный ответ о себе не
|
||||
сообщает: эти состояния независимы, но пользователю видимо только состояние
|
||||
источника. Успешное автоматическое перечитывание обновляет суммы и время;
|
||||
обозначение устаревания определяется новым `isStale`.
|
||||
Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md).
|
||||
|
||||
В текущем решении нет долговременной истории снимков, графиков стоимости
|
||||
портфеля, валютной конвертации, учёта виртуальных позиций или заблокированных
|
||||
средств. Последний полный снимок сохраняется после перезапуска; выполнение
|
||||
синхронизации рассчитано на один экземпляр backend. Действие «Обновить»
|
||||
перечитывает зависимости открытого экрана независимо от их свежести.
|
||||
Отдельного чтения истории стоимости нет, но каждый полный снимок
|
||||
сохраняет точку оценки по счёту и валюте для будущих поддерживаемых
|
||||
представлений; публичного графика в текущем решении нет. Нет также
|
||||
валютной конвертации, учёта виртуальных позиций или заблокированных
|
||||
средств. Последний полный снимок сохраняется после перезапуска;
|
||||
обновлением личных данных владеет часовой обход, рассчитанный на один
|
||||
экземпляр backend. Действие «Обновить» выполняет только локальное
|
||||
перечитывание открытого экрана и не запускает работу с T-Bank.
|
||||
|
||||
Для проверки конкретной суммы используйте
|
||||
[руководство по расхождениям](how-to/investigate-discrepancy.md).
|
||||
|
||||
+40
@@ -119,6 +119,46 @@ Instrument. Она остаётся в `positions` и не объединяет
|
||||
|
||||
_Не использовать_: денежный остаток, cash balance.
|
||||
|
||||
## Концентрация
|
||||
|
||||
Вкладка страницы одного Brokerage Account, показывающая, какие акции,
|
||||
облигации, секторы и эмитенты занимают наибольшую часть текущей оценки
|
||||
Portfolio, без валютной конвертации. Отличается от [структуры
|
||||
портфеля](domains/portfolio/data-reference.md#структура-и-доли): в её разрезах
|
||||
деньги не входят в знаменатель долей.
|
||||
|
||||
_Не использовать_: структура портфеля, диверсификация.
|
||||
|
||||
## Разрез
|
||||
|
||||
Одна из четырёх группировок вкладки «Концентрация»: «Акции», «Облигации»,
|
||||
«Секторы», «Эмитенты». Элемент разреза — единица ранжирования; сектор
|
||||
агрегирует позиции `STOCK`, `BOND` и `FUND`, эмитенты — `STOCK` и `BOND`.
|
||||
|
||||
_Не использовать_: фильтр, категория структуры.
|
||||
|
||||
## Сектор
|
||||
|
||||
Значение сектора инструмента в том виде, как его определяет T-Bank, без
|
||||
перекодирования и собственной классификации WealthLedger. Заполняется для
|
||||
позиций `STOCK`, `BOND` и `FUND`; государственные облигации определяются
|
||||
значением `government` с подписью «Государственный сектор». Позиция без
|
||||
сектора попадает в явную группу «Сектор не указан».
|
||||
|
||||
_Не использовать_: отрасль, собственная классификация приложения.
|
||||
|
||||
## Эмитент
|
||||
|
||||
В разрезе «Эмитенты» вкладки «Концентрация» — бренд актива по данным T-Bank,
|
||||
объединяющий акции и облигации одной компании; фонды в разрез не входят.
|
||||
Название эмитента — название бренда. **Оговорка:** в этом разрезе слово
|
||||
означает бренд актива T-Bank, а не юридического эмитента ценной бумаги;
|
||||
[рейтинг выпуска](integrations/nkr.md) не подставляется вместо рейтинга
|
||||
эмитента и наоборот. Позиция без бренда попадает в явную группу
|
||||
«Эмитент не указан».
|
||||
|
||||
_Не использовать_: юридический эмитент, эмитент рейтинга.
|
||||
|
||||
## Instrument
|
||||
|
||||
Ценная бумага или иной торгуемый актив T-Bank, на который ссылается Portfolio
|
||||
|
||||
+12
-1
@@ -14,8 +14,17 @@ WealthLedger обращается к Invest API на сервере по gRPC ov
|
||||
| Портфель и денежные остатки | Показать текущий состав инвестиций и итоги по исходным валютам |
|
||||
| Исполненные операции | Построить журнал и выделить полученные купоны и дивиденды |
|
||||
| Инструменты и рынок | Имена, идентификаторы, цены, свечи, параметры акций и облигаций |
|
||||
| Классификация инструментов | Сектор акций, облигаций и фондов и бренд актива для разрезов [концентрации](../domains/portfolio/concentration.md) |
|
||||
| Купоны и дивиденды | Построить будущие события по текущим позициям и календарь инструмента |
|
||||
|
||||
Сектор получается из описаний инструмента (отдельные описания для акций,
|
||||
облигаций и фондов, поиск по UID, затем по FIGI); эмитент — из описания актива
|
||||
по `asset_uid` инструмента: его uid и название бренда. Эмитент не выводится из
|
||||
названия, тикера или ISIN. Классификация хранится как последнее известное
|
||||
значение с обновлением не чаще раза в сутки ([ADR
|
||||
022](../adr/022-stored-instrument-classification.md)); все вызовы проходят
|
||||
через общий лимитер запросов T-Bank.
|
||||
|
||||
T-Bank определяет исходные данные и права token. Backend проверяет локальную
|
||||
сессию, выбирает подключение её пользователя и устанавливает доступ к счёту.
|
||||
Наличие идентификатора счёта в URL этого доступа не подтверждает.
|
||||
@@ -41,7 +50,9 @@ Backend передаёт имя файла `logoName` из метаданных
|
||||
|
||||
Логотипы используются в портфеле, операциях, будущих событиях, поиске и
|
||||
страницах инструментов. Отсутствующее имя или ошибка изображения оставляют
|
||||
буквенную заглушку и не меняют финансовые значения. Правило построения URL
|
||||
буквенную заглушку и не меняют финансовые значения. Логотип — это metadata
|
||||
инструмента; он не равен эмитенту из [концентрации](../domains/portfolio/concentration.md),
|
||||
который берётся из бренда актива. Правило построения URL
|
||||
принадлежит [форматированию frontend](https://git.personal.ksv741.ru/WealthLedger/frontend/src/commit/9f7e342333b80bfacfd5bc394a2dcd0613d33ff3/src/shared/lib/display.ts).
|
||||
|
||||
## Проверка и сохранение подключения
|
||||
|
||||
@@ -20,6 +20,7 @@ WealthLedger — локальное приложение для частного
|
||||
| Найти свой инвестиционный счёт | [Список счетов](domains/accounts/overview.md), их типы и доступные сводки |
|
||||
| Вести банковские карты | [Ручной учёт](domains/cards/overview.md): независимые балансы, расходы, пополнения, переводы, возвраты и месячные сводки |
|
||||
| Понять текущий состав инвестиций | [Портфель](domains/portfolio/overview.md): позиции, денежные остатки и итоги по валютам |
|
||||
| Увидеть, где сконцентрированы деньги | [Концентрация](domains/portfolio/concentration.md): крупнейшие бумаги, секторы и эмитенты счёта |
|
||||
| Разобрать уже совершённые движения | [Журнал операций](domains/operations/overview.md): исполненные операции с фильтрами и последовательной загрузкой истории |
|
||||
| Посчитать полученные выплаты | [Пассивный доход](domains/income/overview.md): исполненные купоны и дивиденды за календарный период |
|
||||
| Посмотреть ожидаемые события | [Будущие выплаты](domains/income/upcoming-events.md): купоны, дивиденды, погашения и оферты по текущим позициям |
|
||||
|
||||
Generated
+218
-711
File diff suppressed because it is too large
Load Diff
+16
-13
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "wealth-ledger-docs",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.8",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "24.21.0"
|
||||
"node": "24.22.1"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "markdownlint --dot --rules .markdownlint-rule-relative-links.cjs --ignore node_modules --ignore CHANGELOG.md -- \"**/*.md\" && markdownlint --config .markdownlint-changelog.json --rules .markdownlint-rule-relative-links.cjs -- CHANGELOG.md",
|
||||
@@ -13,23 +13,26 @@
|
||||
"commitlint": "commitlint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "21.2.2",
|
||||
"@commitlint/config-conventional": "21.2.2",
|
||||
"@release-it/conventional-changelog": "12.0.0",
|
||||
"conventional-changelog-conventionalcommits": "10.4.0",
|
||||
"@commitlint/cli": "21.2.4",
|
||||
"@commitlint/config-conventional": "21.2.4",
|
||||
"@release-it/conventional-changelog": "12.0.2",
|
||||
"conventional-changelog-conventionalcommits": "10.4.1",
|
||||
"markdownlint-cli": "0.49.1",
|
||||
"markdownlint-rule-relative-links": "5.1.3",
|
||||
"release-it": "21.0.2"
|
||||
"markdownlint-rule-relative-links": "5.1.4",
|
||||
"release-it": "21.1.1"
|
||||
},
|
||||
"overrides": {
|
||||
"katex": "0.19.0",
|
||||
"fast-uri": "3.1.9",
|
||||
"katex": "0.19.1",
|
||||
"markdownlint-cli@0.49.1": {
|
||||
"js-yaml": "5.4.2",
|
||||
"smol-toml": "1.9.0"
|
||||
"minimatch": "11.0.2",
|
||||
"smol-toml": "1.9.1"
|
||||
},
|
||||
"release-it": {
|
||||
"basic-ftp": "6.2.1",
|
||||
"undici": "7.29.1"
|
||||
}
|
||||
"basic-ftp": "6.2.3",
|
||||
"undici": "7.30.1"
|
||||
},
|
||||
"markdown-it": "15.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
+30
-1
@@ -2,7 +2,8 @@
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
":semanticCommitTypeAll(chore)"
|
||||
":semanticCommitTypeAll(chore)",
|
||||
"security:minimumReleaseAgeNpm"
|
||||
],
|
||||
"timezone": "Europe/Moscow",
|
||||
"labels": [
|
||||
@@ -10,6 +11,13 @@
|
||||
],
|
||||
"dependencyDashboard": true,
|
||||
"rangeStrategy": "pin",
|
||||
"prConcurrentLimit": 5,
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true,
|
||||
"schedule": [
|
||||
"before 4am on the first day of the month"
|
||||
]
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": [
|
||||
@@ -18,6 +26,27 @@
|
||||
],
|
||||
"groupName": "non-major dependencies"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"github-actions"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "non-major github actions"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"docker-compose",
|
||||
"dockerfile"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "non-major container images"
|
||||
},
|
||||
{
|
||||
"matchUpdateTypes": [
|
||||
"major"
|
||||
|
||||
Reference in New Issue
Block a user