9.4 KiB
Raw Blame History

Broker Events And Payouts Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Добавить в брокерский счёт T-Bank раздел предстоящих событий и ориентировочный прогноз будущих выплат по выбранному периоду.

Architecture: Backend добавляет отдельный read-only endpoint событий поверх уже существующих данных T-Bank и MOEX. Слой агрегации строит best-effort список событий по текущим позициям счёта и summary по будущим денежным потокам. Frontend расширяет shell брокерского счёта новой вкладкой События, отдельной страницей и компактным overview-виджетом ближайших событий.

Tech Stack: NestJS 10, Prisma, CacheService, MOEX client, T-Bank module, React 18, React Router 6, TanStack Query 5, TypeScript, Vitest, Testing Library.


Связанные SDD-артефакты

  • Epic: docs/epics/BrokerPortfolio.md
  • Spec: docs/features/broker-events-and-payouts/spec.md
  • Tasks: docs/features/broker-events-and-payouts/tasks.md

Gate перед реализацией

До начала кода пользователь должен отдельно подтвердить, что:

  • текущая spec остаётся источником истины;
  • plan.md и tasks.md согласованы;
  • первая версия действительно ограничивается T-Bank-сценарием и оценкой по текущим позициям;
  • follow-up по entitlement, налогам и полной купонной сетке остаются вне реализации.

Архитектурные решения

Backend endpoint

Новый endpoint:

GET /api/v1/broker/accounts/:accountId/events?from=YYYY-MM-DD&to=YYYY-MM-DD

Endpoint возвращает:

  • items — плоский список событий по текущим позициям счёта;
  • summary — агрегаты по денежным событиям за период;
  • asOf — момент построения read model.

Backend read model

Новый сервис BrokerEventsService живёт внутри TBankModule и использует:

  • BrokerAccountsService для проверки счёта;
  • BrokerPortfolioService или внутренний shared-path для получения текущих позиций;
  • BrokerInstrumentsService для сопоставления T-Bank instrument metadata;
  • MoexClientService для дивидендов и bond enrichment;
  • CacheService для кэширования результата по accountId + from + to.

Сервис не записывает события в Prisma и не вводит отдельные таблицы в первой версии.

Формирование событий

По акциям:

  • для каждой позиции, сопоставимой с MOEX share instrument, читаются dividend events;
  • в выборку попадают только события, чья registryCloseDate лежит в диапазоне;
  • estimatedAmount рассчитывается как текущий quantity * payoutPerUnit, если payout известен.

По облигациям:

  • используется уже доступный enrichment-слой MOEX;
  • nextCouponDate даёт событие coupon;
  • matDate даёт событие maturity;
  • offerDate даёт событие offer;
  • couponValue и faceValue используются для оценки estimatedAmount, когда они доступны.

Семантика summary

summary агрегирует только денежные события:

  • дивиденды;
  • купоны;
  • погашения.

Оферты остаются в общем списке событий, но не обязаны входить в сумму денежных потоков первой версии.

Частичная деградация

Сервис работает по принципу best-effort:

  • ошибка по одной бумаге не роняет весь ответ;
  • unsupported или несопоставимые позиции пропускаются;
  • если сумма события не может быть рассчитана, событие всё равно возвращается без суммы.

API и типы

Query contract

Новый query DTO:

type BrokerEventsQuery = {
  from: string;
  to: string;
};

Обе даты обязательны в первой версии, чтобы не вводить неочевидные дефолты по периоду.

Response contract

type BrokerPortfolioEvent = {
  id: string;
  type: 'dividend' | 'coupon' | 'maturity' | 'offer';
  category: 'cashflow' | 'corporate';
  eventDate: string;
  paymentDate: string | null;
  ticker: string | null;
  name: string | null;
  instrumentUid: string | null;
  instrumentType: 'share' | 'bond' | 'other';
  quantitySnapshot: number | null;
  payoutPerUnit: number | null;
  estimatedAmount: number | null;
  currency: string | null;
  estimateMode: 'current_position';
};

type BrokerEventsSummary = {
  eventCount: number;
  nearestEventDate: string | null;
  totalEstimatedCashflow: number;
  dividendsTotal: number;
  couponsTotal: number;
  principalRepaymentTotal: number;
};

Карта файлов

Backend

  • Create apps/backend/src/modules/tbank/dto/broker-events-query.dto.ts
  • Create apps/backend/src/modules/tbank/dto/broker-events-response.dto.ts
  • Modify apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts
  • Modify apps/backend/src/modules/tbank/types/broker.types.ts
  • Create apps/backend/src/modules/tbank/services/broker-events.service.ts
  • Create apps/backend/src/modules/tbank/services/broker-events.service.spec.ts
  • Modify apps/backend/src/modules/tbank/tbank.controller.ts
  • Modify apps/backend/src/modules/tbank/tbank.controller.spec.ts
  • Modify apps/backend/src/modules/tbank/tbank.module.ts

Frontend

  • Modify apps/frontend/src/shared/api/responses.ts
  • Modify apps/frontend/src/shared/api/index.ts
  • Modify apps/frontend/src/shared/api/types.ts through codegen if Swagger changes are published
  • Create apps/frontend/src/entities/broker-event/api/brokerEventApi.ts
  • Create apps/frontend/src/entities/broker-event/model/useBrokerEvents.ts
  • Create apps/frontend/src/entities/broker-event/index.ts
  • Create apps/frontend/src/pages/broker-events/ui/BrokerEventsPage.tsx
  • Create apps/frontend/src/pages/broker-events/index.ts
  • Create apps/frontend/src/widgets/broker-events-overview/ui/BrokerEventsOverview.tsx
  • Create apps/frontend/src/widgets/broker-events-overview/index.ts
  • Modify apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx
  • Modify apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx
  • Modify apps/frontend/src/app/routing/AppRoutes.tsx

Documentation

  • Modify apps/docs/docs/backend/tbank-invest.md
  • Modify apps/docs/docs/backend/modules.md if the public module description changes materially

Rollout shape

Overview

Overview получает компактный блок ближайших событий:

  • не более 5 событий;
  • summary по ближайшим выплатам не обязателен, если он перегружает карточку;
  • ссылка ведёт в полный раздел События.

Dedicated page

Страница События включает:

  • выбор диапазона дат;
  • summary cards;
  • список событий;
  • loading, empty и error states.

Verification strategy

  • Backend unit tests для event mapping, summary и partial failures.
  • Backend controller tests для query validation и response shape.
  • Frontend hook tests для query lifecycle.
  • Frontend page/component tests для фильтра периода, summary и overview-виджета.
  • Final verification:
    • npm test -w apps/backend
    • npm test -w apps/frontend
    • npm run build -w apps/backend
    • npm run build -w apps/frontend
    • npm run build -w apps/docs если публичные docs менялись
    • npm run codegen -w apps/frontend если Swagger-контракт обновлён

Frontend date range UI

Даты хранятся в URL-параметрах from / to для возможности поделиться ссылкой. На странице — два TextField[type=date] из дизайн-системы. dayjs для форматирования, валидации и дефолтов.

Поток данных:

useSearchParams → from/to → useBrokerEvents(query) → TanStack Query (автоrefetch)

Дефолт при первом визите без параметров: today today+7d. Валидация: to >= from. При невалидных датах — показ ошибки под полем, запрос не выполняется.