10 KiB
MODIFIED Requirements
Requirement: Получение Portfolio выбранного Brokerage Account
Система SHALL предоставлять авторизованному User GET /api/v1/brokerage-accounts/:accountId/portfolio. Она MUST возвращать нормализованный полный Portfolio Snapshot только для открытого поддерживаемого Brokerage Account, доступного через текущий T-Bank connection User. Ответ MUST содержать lastSuccessfulAt в RFC 3339 UTC без миллисекунд, boolean isStale, totalsByCurrency, positions и cashBalances.
Portfolio Snapshot является результатом наблюдения Portfolio на конкретный момент. Он MUST NOT означать, что WealthLedger хранит Portfolio как долговременный агрегат или подтверждает доступность Brokerage Account вне текущего T-Bank connection.
Scenario: Получение текущего Portfolio
- WHEN User запрашивает Portfolio доступного Brokerage Account, а обязательные upstream-данные успешно получены
- THEN система отвечает
200полным нормализованным Portfolio Snapshot сisStale: falseи временем успешного получения финансового снимка
Scenario: Недоступный Brokerage Account
- WHEN
accountIdне принадлежит User, закрыт, неподдерживаем или больше не доступен через текущий T-Bank connection - THEN система отвечает
404без Portfolio Snapshot
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.
Каждая 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 logoName — имя файла логотипа бренда инструмента. Имя файла логотипа MUST заполняться из metadata инструмента, когда upstream предоставляет бренд с непустым именем файла логотипа; во всех остальных случаях оно MUST быть null. Логотип является presentation metadata: он не влияет на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot. Система MUST искать metadata по instrument UID при его наличии, затем по FIGI. Отображаемый идентификатор MUST предпочитать name, затем ticker, а при недоступности metadata использовать FIGI, не скрывая финансовые значения.
Scenario: Позиция с неизвестным upstream-типом
- WHEN upstream возвращает обычную ненулевую позицию с нераспознанным типом инструмента
- THEN система возвращает её в исходном порядке с типом
OTHER
Scenario: Позиция валютного инструмента не является денежным остатком
- WHEN upstream возвращает ненулевую Portfolio Position валютного инструмента и ненулевой Cash Balance той же валюты
- THEN система возвращает Portfolio Position с типом
CURRENCYотдельно от Cash Balance и не объединяет их между собой
Scenario: Позиция без metadata инструмента
- WHEN lookup metadata по FIGI не дал ticker или name либо временно недоступен
- THEN система возвращает позицию с FIGI как fallback-идентификатором и всеми доступными финансовыми значениями
Scenario: Имя инструмента доступно
- WHEN metadata инструмента по FIGI содержит непустое name
- THEN система возвращает name как отображаемый идентификатор позиции вместо FIGI или ticker
Scenario: FIGI не разрешается, но доступен instrument UID
- WHEN FIGI позиции не даёт metadata, а её instrument UID возвращает непустое name
- THEN система возвращает name, полученное по instrument UID
Scenario: ISIN инструмента доступен
- WHEN metadata инструмента позиции содержит непустой
isin - THEN система возвращает его в поле
isinпозиции
Scenario: ISIN инструмента отсутствует
- WHEN у инструмента позиции нет ISIN либо metadata недоступна
- THEN система возвращает позицию с
isin: null, не скрывая финансовые значения
Scenario: Имя файла логотипа бренда доступно
- WHEN metadata инструмента позиции содержит бренд с непустым именем файла логотипа
- THEN система возвращает его в поле
logoNameпозиции
Scenario: Имя файла логотипа бренда отсутствует
- WHEN у инструмента позиции нет бренда, имя файла логотипа пустое либо metadata недоступна
- THEN система возвращает позицию с
logoName: null, не скрывая финансовые значения
Scenario: Денежные остатки в одной валюте
- WHEN upstream возвращает несколько ненулевых доступных денежных остатков одной валюты
- THEN система возвращает один Cash Balance с их точной суммой
Scenario: Штатный пустой Portfolio
- WHEN у доступного Brokerage Account нет ненулевых обычных позиций и доступных денежных остатков
- THEN система отвечает
200с пустымиpositions,cashBalancesиtotalsByCurrency
Requirement: Точные значения и валютные агрегаты
Все количества и финансовые значения в 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 в каждой валюте без валютной конвертации.
Scenario: Мультивалютный Portfolio
- WHEN Portfolio содержит финансовые значения в RUB и USD
- THEN система возвращает отдельные агрегаты RUB и USD без общего сконвертированного итога
Scenario: Позиция без вычислимой стоимости
- WHEN у ненулевой позиции отсутствует либо невалидна цена, количество или валюта цены
- THEN система возвращает позицию с
currentValue: nullи не включает её вtotalsByCurrency
Scenario: Отрицательная дробная часть upstream-значения
- WHEN upstream возвращает сумму с
units: 0и отрицательной дробной частьюnano: -500000000 - THEN система передаёт её как строку
"-0.5"