feat: add future events
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
## Context
|
||||
|
||||
См. motivation в `proposal.md`. Текущие Portfolio Position и Account Ledger получаются read-through из T-Bank; календаря или сохранённого расписания событий нет. `references/moex-vibe` подтверждает, что MOEX предоставляет дату и величину дивиденда, ближайший купон, дату и номинал погашения, а также дату оферты. Эти данные не содержат расписания будущей амортизации.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Вычислять account-scoped прогноз без хранения синтетических событий.
|
||||
- Сохранять отличия между датой реестра дивиденда и датой денежного события.
|
||||
- Опубликовать единый OpenAPI-контракт, из которого frontend сгенерирует типы.
|
||||
|
||||
**Non-Goals:**
|
||||
- Расчёт налогов, конвертация валют и общий итог.
|
||||
- Учёт будущих сделок или владения на дату реестра.
|
||||
- История фактических выплат, ручное обновление и фильтры.
|
||||
- Будущие частичные погашения до появления достоверного расписания амортизации.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Read-through projection of current positions
|
||||
|
||||
`GET /api/v1/brokerage-accounts/:accountId/events` получает актуальный Portfolio, обогащает его данными MOEX и собирает только события в rolling-окне 30 календарных дней. Ответ содержит `calculatedAt`; данные не сохраняются.
|
||||
|
||||
Это избегает отдельного хранилища и устаревших расписаний. Альтернатива с persisted calendar потребовала бы синхронизации с позициями и не повышает точность, поскольку расчёт всё равно основан на текущем количестве бумаг.
|
||||
|
||||
### Explicit event contract
|
||||
|
||||
Контракт события содержит тип (`coupon`, `dividend`, `maturity`, `offer`), календарную дату, семантику даты (`event` или `registry`), инструмент, исходную валюту и nullable прогнозную сумму. Для coupon, dividend и maturity сумма равна текущему количеству позиции, умноженному на выплату или номинал на одну бумагу; offer не получает выдуманную сумму.
|
||||
|
||||
События возвращаются без агрегированного итога, поскольку валюты не конвертируются. Альтернатива с одним полем даты скрыла бы отличие даты реестра от даты поступления дивиденда.
|
||||
|
||||
### Strict source completeness
|
||||
|
||||
Если запрос Portfolio или нужные данные MOEX не получены, endpoint отвечает ошибкой и не публикует неполный календарь. Инструмент с успешным ответом, но без поддерживаемого события, просто не добавляет строку.
|
||||
|
||||
Это отделяет техническую неполноту от корректного пустого результата. Альтернатива вернуть частичный список не позволяет пользователю отличить пропуск от отсутствия выплат.
|
||||
|
||||
### Account-local interface using established list patterns
|
||||
|
||||
Frontend добавляет маршрут `/accounts/:accountId/events`, breadcrumb и действие «Будущие события» на обзоре счёта рядом с Account Ledger. Обзор запрашивает тот же endpoint отдельно от Portfolio и показывает первые пять событий в локальном блоке. Страница использует существующие состояния loading/error/empty и компактные responsive rows; она показывает все события результата без локальных фильтров или pagination.
|
||||
|
||||
Это продолжает account-scoped навигацию `/accounts/:accountId/operations`. Альтернатива с глобальным календарём смешивала бы события разных счетов и требовала бы выбора счёта.
|
||||
|
||||
### Coverage limitation is part of the interface
|
||||
|
||||
UI постоянно поясняет, что суммы являются оценками до налогов по текущей позиции, а покрытие зависит от доступных данных MOEX. При пустом списке пояснение дополнительно называет отсутствие известных событий в ближайшие 30 дней. Будущее частичное погашение не имитируется датой maturity.
|
||||
|
||||
Это сохраняет полезный минимальный список без ложной точности. Альтернатива скрыть ограничение противоречит природе прогнозов и данным источника.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Позиция изменится до события] → обозначать сумму как прогноз по позиции на `calculatedAt`, не заявляя право на выплату.
|
||||
- [MOEX публикует дивидендную дату реестра, а не дату зачисления] → передавать и отображать отдельную семантику даты.
|
||||
- [MOEX даёт только ближайший купон] → ограничить окно 30 днями и явно раскрыть неполноту покрытия.
|
||||
- [Нет графика амортизации] → исключить будущие частичные погашения до добавления надёжного источника.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Развернуть backend endpoint и обновлённый OpenAPI.
|
||||
2. Сгенерировать и закоммитить frontend API-контракт, затем выпустить страницу.
|
||||
3. При откате удалить действие и маршрут frontend; endpoint можно удалить без миграции данных, поскольку состояние не сохраняется.
|
||||
Reference in New Issue
Block a user