Compare commits
2
Commits
1f22510cb7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b20067ca7 | ||
|
|
142f7aa225 |
@@ -1,5 +1,27 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.2.1](https://git.personal.ksv741.ru/WealthLedger/specs/compare/v0.2.0...v0.2.1) (2026-11-06)
|
||||||
|
|
||||||
|
### Specifications
|
||||||
|
|
||||||
|
* align Events and CURRENCY portfolio behavior (WealthLedger/specs[#60](https://git.personal.ksv741.ru/WealthLedger/specs/issues/60)) ([142f7aa](https://git.personal.ksv741.ru/WealthLedger/specs/commit/142f7aa225b4c9ca478ca9b350649df6746d6bf4))
|
||||||
|
* allow warning and retry after exhausted read retries (WealthLedger/specs[#79](https://git.personal.ksv741.ru/WealthLedger/specs/issues/79)) ([#80](https://git.personal.ksv741.ru/WealthLedger/specs/issues/80)) ([0469756](https://git.personal.ksv741.ru/WealthLedger/specs/commit/0469756d3bc983e32811470b671d743cd5009739))
|
||||||
|
* treat MOEX response without requested block as source failure (WealthLedger/specs[#76](https://git.personal.ksv741.ru/WealthLedger/specs/issues/76)) ([#77](https://git.personal.ksv741.ru/WealthLedger/specs/issues/77)) ([aa1a57a](https://git.personal.ksv741.ru/WealthLedger/specs/commit/aa1a57ac202eed8352ffed29296432afca56a3e9))
|
||||||
|
|
||||||
|
### Refactoring
|
||||||
|
|
||||||
|
* group canonical specs by domain and archive account-top-positions ([#74](https://git.personal.ksv741.ru/WealthLedger/specs/issues/74)) ([3821d9e](https://git.personal.ksv741.ru/WealthLedger/specs/commit/3821d9ed587e71e3da662e9b84f0c27bd32ce977))
|
||||||
|
|
||||||
|
### CI
|
||||||
|
|
||||||
|
* check whitespace in committed PR/push range (WealthLedger/specs[#2](https://git.personal.ksv741.ru/WealthLedger/specs/issues/2)) ([7f73de7](https://git.personal.ksv741.ru/WealthLedger/specs/commit/7f73de76ac0e54327745cad61043922047c421ec))
|
||||||
|
* run native OpenSpec and local link validation in verify (WealthLedger/specs[#2](https://git.personal.ksv741.ru/WealthLedger/specs/issues/2)) ([1fd7e6a](https://git.personal.ksv741.ru/WealthLedger/specs/commit/1fd7e6ad974193eca3390cf41749368c5c318858))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
* **deps:** set Renovate release age, PR limit and update groups (WealthLedger/main[#6](https://git.personal.ksv741.ru/WealthLedger/specs/issues/6)) ([#81](https://git.personal.ksv741.ru/WealthLedger/specs/issues/81)) ([8fc8e0a](https://git.personal.ksv741.ru/WealthLedger/specs/commit/8fc8e0a750a561b54251add5bac03d5ef58a94e3))
|
||||||
|
* drop OpenSpec CLI version pin and strict validation (WealthLedger/specs[#67](https://git.personal.ksv741.ru/WealthLedger/specs/issues/67)) ([#78](https://git.personal.ksv741.ru/WealthLedger/specs/issues/78)) ([8b2b742](https://git.personal.ksv741.ru/WealthLedger/specs/commit/8b2b742e52773ee3ab21fafaf6043420927640ac))
|
||||||
|
|
||||||
## [0.2.0](https://git.personal.ksv741.ru/WealthLedger/specs/compare/v0.1.6...v0.2.0) (2026-11-01)
|
## [0.2.0](https://git.personal.ksv741.ru/WealthLedger/specs/compare/v0.1.6...v0.2.0) (2026-11-01)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -91,13 +91,16 @@ Instrument в Future Cash Event MUST иметь тот же доменный с
|
|||||||
|
|
||||||
Страница Brokerage Account MUST предоставлять действие перехода на отдельную
|
Страница Brokerage Account MUST предоставлять действие перехода на отдельную
|
||||||
страницу «Будущие события». Эта страница MUST показывать все полученные события
|
страницу «Будущие события». Эта страница MUST показывать все полученные события
|
||||||
компактным списком без фильтров, пагинации и суммарного итога списка, с датой, типом,
|
компактным списком за выбранный период без пагинации и суммарного итога списка, с датой, типом,
|
||||||
инструментом и прогнозной суммой в исходной валюте, если она известна. Прогнозный итог
|
инструментом и прогнозной суммой в исходной валюте, если она известна. Прогнозный итог
|
||||||
визуальной группы режима «Все счета» не является суммарным итогом списка. Страница
|
визуальной группы режима «Все счета» не является суммарным итогом списка. Страница
|
||||||
MUST объяснять, что прогноз основан на позиции счёта, указан до налогов и может
|
MUST объяснять, что прогноз основан на позиции счёта, указан до налогов и может
|
||||||
быть неполным из-за доступности данных источника. Это предметное ограничение
|
быть неполным из-за доступности данных источника. Это предметное ограничение
|
||||||
прогноза MUST NOT превращаться в отчёт о технической синхронизации.
|
прогноза MUST NOT превращаться в отчёт о технической синхронизации.
|
||||||
|
|
||||||
|
Страница MUST показывать исходные `calculatedAt` и включительное окно сохранённого прогноза,
|
||||||
|
в том числе когда выбранный период отличается от окна прогноза.
|
||||||
|
|
||||||
При временном сбое обновления страница MUST сохранять ранее доступный результат
|
При временном сбое обновления страница MUST сохранять ранее доступный результат
|
||||||
той же выборки с его исходными `calculatedAt` и окном, без индикации
|
той же выборки с его исходными `calculatedAt` и окном, без индикации
|
||||||
собственного чтения и ручного повтора до исчерпания автоматических повторов
|
собственного чтения и ручного повтора до исчерпания автоматических повторов
|
||||||
@@ -110,11 +113,11 @@ MUST скрывать личные данные независимо от нал
|
|||||||
|
|
||||||
#### Scenario: Просмотр ближайших событий
|
#### Scenario: Просмотр ближайших событий
|
||||||
- **WHEN** User открывает «Будущие события» из обзора Brokerage Account
|
- **WHEN** User открывает «Будущие события» из обзора Brokerage Account
|
||||||
- **THEN** он видит отсортированный список событий ближайших 30 дней, где offer обозначен без суммы
|
- **THEN** он видит отсортированный список событий выбранного периода (по умолчанию -- ближайшие 7 дней), где offer обозначен без суммы
|
||||||
|
|
||||||
#### Scenario: Пустое состояние страницы
|
#### Scenario: Пустое состояние страницы
|
||||||
- **WHEN** запрос будущих событий успешно возвращает полный пустой список
|
- **WHEN** запрос будущих событий успешно возвращает полный пустой список
|
||||||
- **THEN** страница сообщает, что на ближайшие 30 дней событий не найдено, и объясняет предметные ограничения покрытия источника
|
- **THEN** страница сообщает, что за выбранный период событий не найдено, и объясняет предметные ограничения покрытия источника
|
||||||
|
|
||||||
#### Scenario: Ошибка страницы
|
#### Scenario: Ошибка страницы
|
||||||
- **WHEN** запрос будущих событий завершается временной ошибкой
|
- **WHEN** запрос будущих событий завершается временной ошибкой
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
### Requirement: Полные оценки Portfolio сохраняются как история
|
### Requirement: Полные оценки Portfolio сохраняются как история
|
||||||
Система SHALL сохранять для каждого доступного Brokerage Account точку оценки по каждой исходной валюте только после успешного получения полного Portfolio с вычислимой стоимостью всех его позиций. Точка MUST содержать точное значение без валютной конвертации и фактическое время успешного получения Portfolio. Система MUST NOT подставлять ноль за отсутствующую стоимость и MUST NOT создавать точку для неполной оценки. Повторная публикация одной попытки MUST NOT создавать дубликат точки.
|
Система SHALL сохранять для каждого доступного Brokerage Account точку оценки по каждой исходной валюте только после успешного получения полного Portfolio с вычислимой стоимостью всех его позиций. Точка MUST содержать точное значение без валютной конвертации и фактическое время успешного получения Portfolio. Система MUST NOT подставлять ноль за отсутствующую стоимость и MUST NOT создавать точку для неполной оценки. Повторная публикация одной попытки MUST NOT создавать дубликат точки.
|
||||||
|
|
||||||
|
Точки, сохранённые до исправления двойного учёта `CURRENCY`/Cash Balance (WealthLedger/backend#140), сброшены миграцией. История оценок начинается заново после её применения; старые точки MUST NOT показываться как сопоставимые с новыми.
|
||||||
|
|
||||||
#### Scenario: Полная мультивалютная оценка
|
#### Scenario: Полная мультивалютная оценка
|
||||||
- **WHEN** успешный полный Portfolio содержит вычислимые итоги RUB и USD
|
- **WHEN** успешный полный Portfolio содержит вычислимые итоги RUB и USD
|
||||||
- **THEN** система сохраняет две отдельные точки с исходными значениями и валютами
|
- **THEN** система сохраняет две отдельные точки с исходными значениями и валютами
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Portfolio Snapshot является результатом наблюдения
|
|||||||
- **THEN** система отвечает `404` без Portfolio Snapshot
|
- **THEN** система отвечает `404` без Portfolio Snapshot
|
||||||
|
|
||||||
### Requirement: Нормализованный состав Portfolio
|
### Requirement: Нормализованный состав Portfolio
|
||||||
Система MUST возвращать только обычные Portfolio Position с ненулевым количеством и доступные Cash Balance с ненулевой суммой. Cash Balance MUST быть отдельным от Portfolio Position элементом и агрегироваться до одного значения на валюту. Portfolio Position с типом `CURRENCY` MUST означать позицию по торгуемому валютному Instrument и MUST NOT смешиваться с Cash Balance. Порядок Portfolio Position и Cash Balance MUST соответствовать upstream-порядку; система MUST NOT сортировать или объединять Portfolio Position с одинаковым FIGI. Виртуальные позиции и заблокированные денежные остатки MUST NOT входить в Portfolio.
|
Система MUST возвращать только обычные Portfolio Position с ненулевым количеством и доступные Cash Balance с ненулевой суммой. Cash Balance MUST быть отдельным от Portfolio Position элементом и агрегироваться до одного значения на валюту. Portfolio Position с типом `CURRENCY` MUST сохранять исходное количество и идентификаторы для контракта, но отражает тот же кэш, что и Cash Balance, и MUST NOT повторно учитываться в валютных итогах и структуре. Клиент MUST показывать этот кэш один раз как Cash Balance в разделе «Деньги», без отдельной карточки `CURRENCY`. Порядок Portfolio Position и Cash Balance MUST соответствовать upstream-порядку; система MUST NOT сортировать или объединять Portfolio Position с одинаковым FIGI. Виртуальные позиции и заблокированные денежные остатки MUST NOT входить в Portfolio.
|
||||||
|
|
||||||
Каждая Portfolio Position MUST содержать нормализованный тип `STOCK`, `BOND`, `FUND`, `CURRENCY`, `FUTURE`, `OPTION`, `STRUCTURED_NOTE`, `DIGITAL_FINANCIAL_ASSET` либо `OTHER`, строковое `quantity`, nullable `currentValue`, nullable `averagePurchasePrice`, nullable `absoluteReturn`, nullable `isin`, nullable `ticker` и nullable `logoName` — имя файла логотипа бренда инструмента, nullable `sector` — сектор инструмента и nullable `issuer` — эмитент позиции. Имя файла логотипа MUST заполняться из metadata инструмента, когда upstream предоставляет бренд с непустым именем файла логотипа; во всех остальных случаях оно MUST быть `null`. Логотип является presentation metadata: он не влияет на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot. Поле `sector` позиции типа `STOCK`, `BOND` или `FUND` MUST содержать значение сектора из metadata инструмента в том виде, как его определяет T-Bank, без перекодирования и собственной классификации, с обрезанными пробелами в начале и конце; значение из одних пробелов считается отсутствующим. Во всех остальных случаях, включая остальные типы позиций, оно MUST быть `null`. Поле `issuer` позиции типа `STOCK`, `BOND` или `FUND`, включая фонд, MUST быть объектом с непустыми строками `id` — идентификатор бренда актива инструмента — и `name` — название этого бренда (не название компании), когда T-Bank предоставляет для инструмента бренд актива с идентификатором и названием, непустыми после обрезки пробелов в начале и конце; значение из одних пробелов считается отсутствующим. Во всех остальных случаях, включая остальные типы позиций, оно MUST быть `null`. Система MUST NOT определять эмитента по совпадению названия, тикера или ISIN. Сектор и эмитент не влияют на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot: их отсутствие либо недоступность MUST NOT делать Snapshot неуспешным или устаревшим и MUST NOT скрывать позицию или её финансовые значения, кроме отказа token по требованию «Свежесть и fallback полного Portfolio Snapshot». Система MUST искать metadata по instrument UID при его наличии, затем по FIGI. Отображаемый идентификатор MUST предпочитать name, затем ticker, а при недоступности metadata использовать FIGI, не скрывая финансовые значения.
|
Каждая Portfolio Position MUST содержать нормализованный тип `STOCK`, `BOND`, `FUND`, `CURRENCY`, `FUTURE`, `OPTION`, `STRUCTURED_NOTE`, `DIGITAL_FINANCIAL_ASSET` либо `OTHER`, строковое `quantity`, nullable `currentValue`, nullable `averagePurchasePrice`, nullable `absoluteReturn`, nullable `isin`, nullable `ticker` и nullable `logoName` — имя файла логотипа бренда инструмента, nullable `sector` — сектор инструмента и nullable `issuer` — эмитент позиции. Имя файла логотипа MUST заполняться из metadata инструмента, когда upstream предоставляет бренд с непустым именем файла логотипа; во всех остальных случаях оно MUST быть `null`. Логотип является presentation metadata: он не влияет на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot. Поле `sector` позиции типа `STOCK`, `BOND` или `FUND` MUST содержать значение сектора из metadata инструмента в том виде, как его определяет T-Bank, без перекодирования и собственной классификации, с обрезанными пробелами в начале и конце; значение из одних пробелов считается отсутствующим. Во всех остальных случаях, включая остальные типы позиций, оно MUST быть `null`. Поле `issuer` позиции типа `STOCK`, `BOND` или `FUND`, включая фонд, MUST быть объектом с непустыми строками `id` — идентификатор бренда актива инструмента — и `name` — название этого бренда (не название компании), когда T-Bank предоставляет для инструмента бренд актива с идентификатором и названием, непустыми после обрезки пробелов в начале и конце; значение из одних пробелов считается отсутствующим. Во всех остальных случаях, включая остальные типы позиций, оно MUST быть `null`. Система MUST NOT определять эмитента по совпадению названия, тикера или ISIN. Сектор и эмитент не влияют на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot: их отсутствие либо недоступность MUST NOT делать Snapshot неуспешным или устаревшим и MUST NOT скрывать позицию или её финансовые значения, кроме отказа token по требованию «Свежесть и fallback полного Portfolio Snapshot». Система MUST искать metadata по instrument UID при его наличии, затем по FIGI. Отображаемый идентификатор MUST предпочитать name, затем ticker, а при недоступности metadata использовать FIGI, не скрывая финансовые значения.
|
||||||
|
|
||||||
@@ -124,7 +124,11 @@ Portfolio Snapshot является результатом наблюдения
|
|||||||
### Requirement: Точные значения и валютные агрегаты
|
### Requirement: Точные значения и валютные агрегаты
|
||||||
Все количества и финансовые значения в Portfolio Snapshot MUST передаваться десятичными строками без экспоненциальной записи и без незначащих завершающих нулей. Денежное значение MUST быть единым Money-значением с ISO-кодом валюты и десятичной строкой значения. Система MUST корректно нормализовать отрицательные целую и дробную части upstream-значений. Отсутствующее значение MUST передаваться как `null`, а не как ноль.
|
Все количества и финансовые значения в Portfolio Snapshot MUST передаваться десятичными строками без экспоненциальной записи и без незначащих завершающих нулей. Денежное значение MUST быть единым Money-значением с ISO-кодом валюты и десятичной строкой значения. Система MUST корректно нормализовать отрицательные целую и дробную части upstream-значений. Отсутствующее значение MUST передаваться как `null`, а не как ноль.
|
||||||
|
|
||||||
`currentValue` Portfolio Position MUST быть точным произведением количества на текущую цену при наличии валидных множителей и валюты цены. `absoluteReturn` MUST быть точной величиной `(currentPrice - averagePurchasePrice) * quantity` только когда обе цены имеют одинаковую известную валюту. Позиция без вычислимой текущей стоимости MUST оставаться в ответе, но MUST NOT входить в агрегаты. `totalsByCurrency` MUST содержать точную сумму currentValue позиций и Cash Balance в каждой валюте без валютной конвертации.
|
`currentValue` Portfolio Position MUST быть точным произведением количества на текущую цену при наличии валидных множителей и валюты цены. `absoluteReturn` MUST быть точной величиной `(currentPrice - averagePurchasePrice) * quantity` только когда обе цены имеют одинаковую известную валюту. Позиция без вычислимой текущей стоимости MUST оставаться в ответе, но MUST NOT входить в агрегаты. `totalsByCurrency` MUST содержать точную сумму currentValue позиций, кроме `CURRENCY`, и Cash Balance в каждой валюте без валютной конвертации. Структура Portfolio MUST учитывать `CURRENCY` только как `CASH` из Cash Balance, без второго вклада.
|
||||||
|
|
||||||
|
#### Scenario: Валютная позиция отражает денежный остаток
|
||||||
|
- **WHEN** Portfolio содержит `CURRENCY` position стоимостью 100 RUB и Cash Balance 100 RUB
|
||||||
|
- **THEN** система сохраняет `CURRENCY` position с количеством в `positions`, но возвращает итог 100 RUB и один вклад `CASH` 100 RUB в структуре; клиент показывает 100 RUB в «Деньги» без отдельной валютной карточки
|
||||||
|
|
||||||
#### Scenario: Мультивалютный Portfolio
|
#### Scenario: Мультивалютный Portfolio
|
||||||
- **WHEN** Portfolio содержит финансовые значения в RUB и USD
|
- **WHEN** Portfolio содержит финансовые значения в RUB и USD
|
||||||
@@ -282,7 +286,7 @@ Portfolio и предложить возврат к списку счетов.
|
|||||||
- **THEN** клиент показывает ISIN и не заменяет его тикером
|
- **THEN** клиент показывает ISIN и не заменяет его тикером
|
||||||
|
|
||||||
#### Scenario: Карточка позиции отображает доступные метрики
|
#### Scenario: Карточка позиции отображает доступные метрики
|
||||||
- **WHEN** позиция, не относящаяся к `STOCK`, имеет значение текущей стоимости, абсолютного дохода, средней цены покупки и ISIN
|
- **WHEN** позиция, не относящаяся к `STOCK` или `CURRENCY`, имеет значение текущей стоимости, абсолютного дохода, средней цены покупки и ISIN
|
||||||
- **THEN** клиент показывает идентификатор, количество, текущую стоимость, абсолютный доход, среднюю цену покупки, долю позиции среди позиций той же валюты и ISIN
|
- **THEN** клиент показывает идентификатор, количество, текущую стоимость, абсолютный доход, среднюю цену покупки, долю позиции среди позиций той же валюты и ISIN
|
||||||
|
|
||||||
#### Scenario: Отсутствующие метрики скрыты
|
#### Scenario: Отсутствующие метрики скрыты
|
||||||
@@ -295,7 +299,7 @@ Portfolio и предложить возврат к списку счетов.
|
|||||||
|
|
||||||
#### Scenario: Доля позиции в валюте
|
#### Scenario: Доля позиции в валюте
|
||||||
- **WHEN** позиция имеет вычислимую текущую стоимость в валюте
|
- **WHEN** позиция имеет вычислимую текущую стоимость в валюте
|
||||||
- **THEN** клиент отображает её долю среди всех позиций той же валюты, не включая Cash Balance
|
- **THEN** клиент отображает её долю среди всех отображаемых позиций той же валюты, не включая Cash Balance и зеркальные `CURRENCY` positions
|
||||||
|
|
||||||
#### Scenario: Одноколоночные секции акций и облигаций
|
#### Scenario: Одноколоночные секции акций и облигаций
|
||||||
- **WHEN** User открывает загруженный Portfolio с позициями `STOCK` или `BOND` на широком экране
|
- **WHEN** User открывает загруженный Portfolio с позициями `STOCK` или `BOND` на широком экране
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "wealth-ledger-specs",
|
"name": "wealth-ledger-specs",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "wealth-ledger-specs",
|
"name": "wealth-ledger-specs",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "21.2.4",
|
"@commitlint/cli": "21.2.4",
|
||||||
"@commitlint/config-conventional": "21.2.4",
|
"@commitlint/config-conventional": "21.2.4",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "wealth-ledger-specs",
|
"name": "wealth-ledger-specs",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "24.22.1"
|
"node": "24.22.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user