docs: require russian specs
This commit is contained in:
parent
a3817edfc7
commit
a971d0c8bf
@ -20,6 +20,7 @@ npm workspaces монорепозиторий: `apps/backend` (NestJS), `apps/fr
|
||||
|
||||
- `apps/docs` — единственная опубликованная человекочитаемая документация проекта (Docusaurus).
|
||||
- Root `docs` хранит только согласованные SDD-спецификации в `docs/superpowers/specs/`.
|
||||
- Все SDD spec-файлы в `docs/superpowers/specs/` пишутся на русском языке; англоязычные термины допустимы для API, кода, протоколов и официальных названий.
|
||||
- ADR для опубликованной документации находятся в `apps/docs/docs/adr/`.
|
||||
- OpenAPI source of truth — live Swagger JSON бэкенда на `/api/docs-json`; frontend generated types находятся в `apps/frontend/src/api/types.ts`.
|
||||
- Superpowers plans и временные execution logs не коммитить по умолчанию. Если нужен план для ревью, держать его кратким и переносить устойчивые решения в spec/ADR/docs.
|
||||
|
||||
@ -1,53 +1,53 @@
|
||||
# T-Bank Broker Portfolios Design
|
||||
# Проектирование брокерских портфелей T-Bank
|
||||
|
||||
**Status:** Approved for implementation planning
|
||||
**Статус:** одобрено для планирования реализации
|
||||
|
||||
**Date:** 2026-06-16
|
||||
**Дата:** 2026-06-16
|
||||
|
||||
**Owner:** MoexVibe
|
||||
**Владелец:** MoexVibe
|
||||
|
||||
## Context
|
||||
## Контекст
|
||||
|
||||
MoexVibe already supports manually managed virtual portfolios based on MOEX market data. The next
|
||||
step is a read-only integration with T-Bank Invest so the application can show real brokerage
|
||||
accounts, positions, cash balances, and operation history.
|
||||
MoexVibe уже поддерживает вручную управляемые виртуальные портфели на основе рыночных данных MOEX.
|
||||
Следующий шаг — интеграция T-Bank Invest только для чтения, чтобы приложение показывало реальные
|
||||
брокерские счета, позиции, денежные остатки и историю операций.
|
||||
|
||||
The initial integration is personal and server-side. The backend reads one `T_BANK_TOKEN` from
|
||||
`apps/backend/.env`. Later versions may replace this with per-user token storage, but the first
|
||||
implementation must not require that larger secrets model.
|
||||
Первая версия интеграции персональная и серверная. Бэкенд читает один `T_BANK_TOKEN` из
|
||||
`apps/backend/.env`. В будущих версиях это можно заменить хранением токенов на уровне пользователя,
|
||||
но первая реализация не должна требовать более крупной модели секретов.
|
||||
|
||||
## External API Facts
|
||||
## Факты о внешнем API
|
||||
|
||||
T-Bank Invest API is a gRPC API. REST is available as a proxy and WebSocket exists for clients that
|
||||
need it, but gRPC is the primary protocol. Production endpoint is `invest-public-api.tbank.ru:443`;
|
||||
sandbox endpoint is `sandbox-invest-public-api.tbank.ru:443`.
|
||||
T-Bank Invest API — это gRPC API. REST доступен как прокси, а WebSocket существует для клиентов,
|
||||
которым он нужен, но основным протоколом является gRPC. Production endpoint:
|
||||
`invest-public-api.tbank.ru:443`; sandbox endpoint: `sandbox-invest-public-api.tbank.ru:443`.
|
||||
|
||||
Authorization is passed as gRPC metadata:
|
||||
Авторизация передается как gRPC-метаданные:
|
||||
|
||||
```text
|
||||
Authorization: Bearer <T_BANK_TOKEN>
|
||||
```
|
||||
|
||||
The relevant official services and methods are:
|
||||
Нужные официальные сервисы и методы:
|
||||
|
||||
| Need | T-Bank method | Notes |
|
||||
| Потребность | Метод T-Bank | Примечания |
|
||||
| --- | --- | --- |
|
||||
| Accounts | `UsersService/GetAccounts` | Filter by open brokerage accounts and IIS only. |
|
||||
| Current portfolio valuation | `OperationsService/GetPortfolio` | Returns totals, positions, daily yield, expected yield. |
|
||||
| Cash and settled positions | `OperationsService/GetPositions` | Returns money, blocked money, securities, futures, options. |
|
||||
| Operation history | `OperationsService/GetOperationsByCursor` | Cursor pagination, limit up to 1000, operation type filters. |
|
||||
| Instrument metadata | `InstrumentsService/GetInstrumentBy` | Resolve `instrument_uid`, ticker, class code, lot, name, ISIN. |
|
||||
| Счета | `UsersService/GetAccounts` | Фильтровать только открытые брокерские счета и ИИС. |
|
||||
| Текущая оценка портфеля | `OperationsService/GetPortfolio` | Возвращает итоги, позиции, дневную доходность, ожидаемую доходность. |
|
||||
| Деньги и рассчитанные позиции | `OperationsService/GetPositions` | Возвращает деньги, заблокированные деньги, бумаги, фьючерсы, опционы. |
|
||||
| История операций | `OperationsService/GetOperationsByCursor` | Курсорная пагинация, лимит до 1000, фильтры по типам операций. |
|
||||
| Метаданные инструмента | `InstrumentsService/GetInstrumentBy` | Получение `instrument_uid`, тикера, class code, лота, названия, ISIN. |
|
||||
|
||||
The official docs recommend staying below 50 requests per second across accounts and tokens. The
|
||||
documented unary limits include 100 requests per minute for the accounts service and 200 requests
|
||||
per minute for the operations service. T-Bank also exposes rate limit response metadata such as
|
||||
`x-ratelimit-limit`, `x-ratelimit-remaining`, and `x-ratelimit-reset`.
|
||||
Официальная документация рекомендует оставаться ниже 50 запросов в секунду суммарно по счетам и
|
||||
токенам. Документированные лимиты unary-запросов включают 100 запросов в минуту для сервиса счетов и
|
||||
200 запросов в минуту для сервиса операций. T-Bank также возвращает метаданные по лимитам запросов,
|
||||
например `x-ratelimit-limit`, `x-ratelimit-remaining` и `x-ratelimit-reset`.
|
||||
|
||||
Stream services exist for portfolio, positions, and operations updates. They are useful later, but
|
||||
the first implementation will use unary reads plus local cache/sync because it is simpler, testable,
|
||||
and less likely to hold unnecessary long-lived connections.
|
||||
Stream-сервисы существуют для обновлений портфеля, позиций и операций. Они пригодятся позже, но
|
||||
первая реализация использует unary-чтение плюс локальные кеширование и синхронизацию: это проще, тестируемее и не
|
||||
держит лишние долгоживущие соединения.
|
||||
|
||||
References:
|
||||
Ссылки:
|
||||
|
||||
- [T-Invest API intro](https://developer.tbank.ru/invest/intro/intro)
|
||||
- [gRPC protocol](https://developer.tbank.ru/invest/intro/developer/protocols/grpc/)
|
||||
@ -57,38 +57,40 @@ References:
|
||||
- [JS SDK](https://developer.tbank.ru/invest/sdk/faq_js)
|
||||
- [Official proto contracts](https://opensource.tbank.ru/invest/invest-contracts)
|
||||
|
||||
## Goals
|
||||
## Цели
|
||||
|
||||
- Show real T-Bank brokerage accounts and IIS accounts in a separate broker portfolios area.
|
||||
- Show current positions for each account: shares, bonds, other securities if returned, and cash.
|
||||
- Show operation history for each account: buys, sells, commissions, taxes, coupons, dividends,
|
||||
deposits, withdrawals, repayments, corrections, and other operation types returned by T-Bank.
|
||||
- Avoid excessive calls to T-Bank through backend rate limiting, short-lived read cache, and durable
|
||||
local operation sync.
|
||||
- Keep the frontend isolated from T-Bank. The backend remains the only external API client.
|
||||
- Publish human-readable architecture and usage documentation in `apps/docs`.
|
||||
- Показать реальные брокерские счета T-Bank и ИИС в отдельном разделе брокерских портфелей.
|
||||
- Показать текущие позиции по каждому счету: акции, облигации, другие бумаги, если они вернулись из
|
||||
API, и денежные остатки.
|
||||
- Показать историю операций по каждому счету: покупки, продажи, комиссии, налоги, купоны, дивиденды,
|
||||
пополнения, выводы, погашения, корректировки и другие типы операций, которые возвращает T-Bank.
|
||||
- Не создавать лишнюю нагрузку на T-Bank: использовать ограничение частоты запросов на бэкенде,
|
||||
короткоживущий кеш чтения и устойчивую локальную синхронизацию операций.
|
||||
- Изолировать фронтенд от T-Bank. Бэкенд остается единственным клиентом внешнего API.
|
||||
- Опубликовать человекочитаемую архитектурную и пользовательскую документацию в `apps/docs`.
|
||||
|
||||
## Non-Goals
|
||||
## Не цели
|
||||
|
||||
- No trading, order placement, transfers, or account funding in this phase.
|
||||
- No Invest Box, DFA smart account, debit account, savings account, or money market fund account in
|
||||
this phase.
|
||||
- No per-user T-Bank token management in this phase.
|
||||
- No always-on stream workers in the first version.
|
||||
- No tax calculation engine beyond showing broker-provided tax operations.
|
||||
- В этой фазе нет торговли, выставления заявок, переводов или пополнения счетов.
|
||||
- В этой фазе нет Инвесткопилки, ЦФА-счета, дебетового счета, накопительного счета или счета фонда
|
||||
денежного рынка.
|
||||
- В этой фазе нет управления T-Bank токенами на уровне пользователя.
|
||||
- В первой версии нет постоянно запущенных stream workers.
|
||||
- Нет собственного движка расчета налогов сверх отображения налоговых операций, которые предоставляет
|
||||
брокер.
|
||||
|
||||
## Account Scope
|
||||
## Область счетов
|
||||
|
||||
Use `UsersService/GetAccounts` and keep only:
|
||||
Используем `UsersService/GetAccounts` и оставляем только:
|
||||
|
||||
- `ACCOUNT_TYPE_TINKOFF`
|
||||
- `ACCOUNT_TYPE_TINKOFF_IIS`
|
||||
|
||||
Also keep only open accounts:
|
||||
Также оставляем только открытые счета:
|
||||
|
||||
- `ACCOUNT_STATUS_OPEN`
|
||||
|
||||
Excluded account types:
|
||||
Исключенные типы счетов:
|
||||
|
||||
- `ACCOUNT_TYPE_INVEST_BOX`
|
||||
- `ACCOUNT_TYPE_INVEST_FUND`
|
||||
@ -97,37 +99,38 @@ Excluded account types:
|
||||
- `ACCOUNT_TYPE_DFA`
|
||||
- `ACCOUNT_TYPE_UNSPECIFIED`
|
||||
|
||||
## Protocol Decision
|
||||
## Решение по протоколу
|
||||
|
||||
Use a thin backend gRPC integration instead of the REST proxy or a hard dependency on the official
|
||||
JS SDK.
|
||||
Используем тонкую gRPC-интеграцию на стороне бэкенда вместо REST-прокси или жесткой зависимости от
|
||||
официального JS SDK.
|
||||
|
||||
### Why gRPC
|
||||
### Почему gRPC
|
||||
|
||||
- It is the primary protocol of T-Bank Invest API.
|
||||
- It gives direct access to unary and future stream APIs.
|
||||
- It keeps request metadata, `x-tracking-id`, and rate-limit metadata visible at the transport
|
||||
boundary.
|
||||
- It fits the existing MoexVibe rule that backend is the only external data client.
|
||||
- Это основной протокол T-Bank Invest API.
|
||||
- Он дает прямой доступ к unary API и будущим stream API.
|
||||
- Он оставляет метаданные запроса, `x-tracking-id` и метаданные лимитов видимыми на границе
|
||||
транспорта.
|
||||
- Он соответствует правилу MoexVibe: бэкенд является единственным клиентом внешних данных.
|
||||
|
||||
### Why Not REST First
|
||||
### Почему не REST первым
|
||||
|
||||
REST is useful for manual debugging and Swagger examples, but it is a proxy over the same service
|
||||
surface. Building on REST would add translation overhead and make future stream support a separate
|
||||
design.
|
||||
REST полезен для ручной отладки и Swagger-примеров, но это прокси поверх той же поверхности сервисов.
|
||||
Если строиться на REST, появится лишний слой перевода, а будущая поддержка stream API потребует
|
||||
отдельного дизайна.
|
||||
|
||||
### Why Not SDK First
|
||||
### Почему не SDK первым
|
||||
|
||||
The official JS SDK exists and can be useful as a reference. Current npm metadata for
|
||||
`@tinkoff/invest-js` shows it already depends on gRPC libraries such as `@grpc/grpc-js`,
|
||||
`@grpc/proto-loader`, `nice-grpc`, and `protobufjs`. Using it directly would hide transport details
|
||||
that MoexVibe needs to own: rate limiting, metadata capture, test doubles, generated type updates,
|
||||
and later token sourcing. The module boundary should still allow replacing the internal transport
|
||||
adapter with SDK calls if that becomes clearly cheaper.
|
||||
Официальный JS SDK существует и может быть полезен как справочник. Текущие npm-метаданные
|
||||
`@tinkoff/invest-js` показывают, что SDK уже зависит от gRPC-библиотек, включая `@grpc/grpc-js`,
|
||||
`@grpc/proto-loader`, `nice-grpc` и `protobufjs`. Прямое использование SDK скроет транспортные
|
||||
детали, которыми MoexVibe должен владеть сам: ограничение частоты запросов, сбор метаданных,
|
||||
тестовые двойники, обновление сгенерированных типов и будущий источник токенов. Граница модуля при
|
||||
этом должна позволять заменить внутренний транспортный адаптер на вызовы SDK, если позже это станет очевидно
|
||||
дешевле.
|
||||
|
||||
## Backend Architecture
|
||||
## Архитектура бэкенда
|
||||
|
||||
Create a new feature module:
|
||||
Создать новый feature-модуль:
|
||||
|
||||
```text
|
||||
apps/backend/src/modules/tbank/
|
||||
@ -152,48 +155,50 @@ apps/backend/src/modules/tbank/
|
||||
└── operation.mapper.ts
|
||||
```
|
||||
|
||||
`TBankClientService` owns:
|
||||
`TBankClientService` отвечает за:
|
||||
|
||||
- gRPC channel creation.
|
||||
- `Authorization` metadata.
|
||||
- optional `x-app-name` metadata, for example `ksv741.moex-vibe`.
|
||||
- request timeout.
|
||||
- retry for transient gRPC failures.
|
||||
- service-level rate limiting.
|
||||
- capturing `x-tracking-id` and rate-limit metadata for logs and error responses.
|
||||
- создание gRPC channel;
|
||||
- метаданные `Authorization`;
|
||||
- опциональные метаданные `x-app-name`, например `ksv741.moex-vibe`;
|
||||
- таймаут запроса;
|
||||
- повторные попытки для временных gRPC-сбоев;
|
||||
- ограничение частоты запросов на уровне сервисов;
|
||||
- сбор `x-tracking-id` и метаданных лимитов для логов и ответов об ошибках.
|
||||
|
||||
Domain services depend on `TBankClientService`, not directly on generated gRPC clients. This keeps
|
||||
the later switch from one server token to per-user token providers local to the client layer.
|
||||
Доменные сервисы зависят от `TBankClientService`, а не напрямую от сгенерированных gRPC-клиентов.
|
||||
Это оставляет будущий переход от одного серверного токена к providers токенов на уровне пользователя
|
||||
локальным для клиентского слоя.
|
||||
|
||||
## Public Backend API
|
||||
## Публичный API бэкенда
|
||||
|
||||
All endpoints require the existing JWT authentication. In the first version, any authenticated
|
||||
MoexVibe user can technically read the same server-token broker data. Deployment must treat this as
|
||||
single-user/admin-only until per-user token storage is implemented.
|
||||
Все endpoints требуют существующую JWT-аутентификацию. В первой версии любой аутентифицированный
|
||||
пользователь MoexVibe технически сможет прочитать одни и те же брокерские данные серверного токена.
|
||||
До реализации хранилища токенов на уровне пользователя deployment должен считаться
|
||||
single-user/admin-only.
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| Endpoint | Метод | Описание |
|
||||
| --- | --- | --- |
|
||||
| `/api/v1/broker/accounts` | GET | List open T-Bank brokerage and IIS accounts. |
|
||||
| `/api/v1/broker/accounts/:accountId/portfolio` | GET | Current account portfolio, positions, totals, cash. |
|
||||
| `/api/v1/broker/accounts/:accountId/operations` | GET | Operation history with pagination and filters. |
|
||||
| `/api/v1/broker/accounts` | GET | Список открытых брокерских счетов T-Bank и ИИС. |
|
||||
| `/api/v1/broker/accounts/:accountId/portfolio` | GET | Текущий портфель счета, позиции, итоги, деньги. |
|
||||
| `/api/v1/broker/accounts/:accountId/operations` | GET | История операций с пагинацией и фильтрами. |
|
||||
|
||||
Operations query parameters:
|
||||
Параметры query для операций:
|
||||
|
||||
| Query | Type | Default | Notes |
|
||||
| Параметр | Тип | По умолчанию | Примечания |
|
||||
| --- | --- | --- | --- |
|
||||
| `from` | ISO datetime | start of current year | UTC in T-Bank request. |
|
||||
| `to` | ISO datetime | now | UTC in T-Bank request. |
|
||||
| `cursor` | string | absent | Passed to `GetOperationsByCursor`. |
|
||||
| `limit` | integer | 100 | Clamp to `1..1000`. |
|
||||
| `instrumentId` | string | absent | Supports figi, instrument UID, or `ticker_classCode`. |
|
||||
| `operationTypes` | string list | absent | Optional T-Bank operation type enum names. |
|
||||
| `state` | enum | `OPERATION_STATE_EXECUTED` | Default to executed operations for portfolio accounting views. |
|
||||
| `from` | ISO datetime | начало текущего года | UTC в запросе к T-Bank. |
|
||||
| `to` | ISO datetime | сейчас | UTC в запросе к T-Bank. |
|
||||
| `cursor` | string | отсутствует | Передается в `GetOperationsByCursor`. |
|
||||
| `limit` | integer | 100 | Ограничить до `1..1000`. |
|
||||
| `instrumentId` | string | отсутствует | Поддерживает figi, instrument UID или `ticker_classCode`. |
|
||||
| `operationTypes` | string list | отсутствует | Опциональные имена enum типов операций T-Bank. |
|
||||
| `state` | enum | `OPERATION_STATE_EXECUTED` | По умолчанию исполненные операции для учетных представлений портфеля. |
|
||||
|
||||
## Response Model
|
||||
## Модель ответа
|
||||
|
||||
Use JSON DTOs shaped for the frontend, not raw proto objects.
|
||||
Используем JSON DTO, удобные для фронтенда, а не сырые proto-объекты.
|
||||
|
||||
Money values are normalized from T-Bank `MoneyValue { currency, units, nano }` into:
|
||||
Денежные значения нормализуются из T-Bank `MoneyValue { currency, units, nano }` в:
|
||||
|
||||
```typescript
|
||||
type BrokerMoney = {
|
||||
@ -204,11 +209,11 @@ type BrokerMoney = {
|
||||
};
|
||||
```
|
||||
|
||||
`units` remains a string to preserve the original int64 value. `value` is a convenience decimal for
|
||||
display and charting. Financial calculations that require exact precision should use decimal helpers,
|
||||
not floating-point arithmetic.
|
||||
`units` остается строкой, чтобы сохранить исходное int64-значение. `value` — удобное decimal-число
|
||||
для отображения и графиков. Финансовые расчеты, которым нужна точная арифметика, должны использовать
|
||||
decimal helpers, а не арифметику с плавающей точкой.
|
||||
|
||||
Portfolio response shape:
|
||||
Форма ответа портфеля:
|
||||
|
||||
```typescript
|
||||
type BrokerPortfolio = {
|
||||
@ -236,7 +241,7 @@ type BrokerPortfolio = {
|
||||
};
|
||||
```
|
||||
|
||||
Operation response shape:
|
||||
Форма ответа операций:
|
||||
|
||||
```typescript
|
||||
type BrokerOperationsPage = {
|
||||
@ -248,137 +253,142 @@ type BrokerOperationsPage = {
|
||||
};
|
||||
```
|
||||
|
||||
Each operation keeps the original T-Bank enum in `type`, plus a MoexVibe display category:
|
||||
Каждая операция хранит исходный enum T-Bank в поле `type`, а также отображаемую категорию MoexVibe:
|
||||
|
||||
| Category | T-Bank operation examples |
|
||||
| Категория | Примеры операций T-Bank |
|
||||
| --- | --- |
|
||||
| `trade` | `OPERATION_TYPE_BUY`, `OPERATION_TYPE_SELL`, margin/delivery variants |
|
||||
| `income` | `OPERATION_TYPE_DIVIDEND`, `OPERATION_TYPE_COUPON`, repayments, overnight income |
|
||||
| `tax` | `OPERATION_TYPE_TAX`, dividend tax, bond tax, progressive tax variants |
|
||||
| `fee` | broker fee, service fee, margin fee, success fee, cash/out/advice/other fees |
|
||||
| `transfer` | input, output, securities transfer, SWIFT/acquiring/multi transfers |
|
||||
| `other` | unspecified and T-Bank operation types not yet categorized |
|
||||
| `trade` | `OPERATION_TYPE_BUY`, `OPERATION_TYPE_SELL`, маржинальные/поставочные варианты |
|
||||
| `income` | `OPERATION_TYPE_DIVIDEND`, `OPERATION_TYPE_COUPON`, погашения, overnight income |
|
||||
| `tax` | `OPERATION_TYPE_TAX`, налог на дивиденды, налог на облигации, прогрессивные налоги |
|
||||
| `fee` | брокерская комиссия, сервисная комиссия, маржинальная комиссия, success fee, прочие комиссии |
|
||||
| `transfer` | пополнение, вывод, перевод бумаг, SWIFT/acquiring/multi transfers |
|
||||
| `other` | unspecified и еще не категоризованные типы операций T-Bank |
|
||||
|
||||
Never drop unknown operation types. Store and display them as `other` with the original enum.
|
||||
Нельзя отбрасывать неизвестные типы операций. Их нужно хранить и показывать как `other` с исходным
|
||||
enum-значением.
|
||||
|
||||
## Operation Sync Strategy
|
||||
## Стратегия синхронизации операций
|
||||
|
||||
Phase 1 can read operations directly from T-Bank with a cache. Phase 2 should persist normalized
|
||||
operations locally because operation history is the audit trail and should not depend on repeatedly
|
||||
walking the same remote pages.
|
||||
Фаза 1 может читать операции напрямую из T-Bank с кешированием. Фаза 2 должна сохранять
|
||||
нормализованные операции локально, потому что история операций — это audit trail, и она не должна
|
||||
зависеть от повторного обхода одних и тех же удаленных страниц.
|
||||
|
||||
Durable sync design:
|
||||
Дизайн устойчивой синхронизации:
|
||||
|
||||
- Store one row per T-Bank operation item keyed by `(accountId, cursor)` when cursor is present.
|
||||
- Also keep `operationId`, `parentOperationId`, `date`, `type`, `state`, `instrumentUid`, `figi`,
|
||||
`ticker`, `classCode`, `payment`, `price`, `commission`, `yield`, `accruedInt`, `quantity`,
|
||||
`quantityDone`, `raw`.
|
||||
- Store sync state per account: last successful range, last cursor, last synced timestamp.
|
||||
- Backfill historical data by date windows, for example one calendar year per run.
|
||||
- Refresh a moving recent window, for example last 3 days, because broker operation IDs and parent
|
||||
IDs can change according to the proto comments.
|
||||
- Keep raw operation payload JSON for audit/debug while exposing normalized DTOs.
|
||||
- Хранить одну строку на каждый элемент операции T-Bank с ключом `(accountId, cursor)`, если cursor
|
||||
присутствует.
|
||||
- Также хранить `operationId`, `parentOperationId`, `date`, `type`, `state`, `instrumentUid`,
|
||||
`figi`, `ticker`, `classCode`, `payment`, `price`, `commission`, `yield`, `accruedInt`,
|
||||
`quantity`, `quantityDone`, `raw`.
|
||||
- Хранить состояние синхронизации по каждому счету: последний успешный диапазон, последний cursor, timestamp
|
||||
последней синхронизации.
|
||||
- Делать backfill исторических данных окнами по датам, например по одному календарному году за запуск.
|
||||
- Обновлять скользящее недавнее окно, например последние 3 дня, потому что operation IDs и parent
|
||||
IDs могут меняться согласно комментариям в proto.
|
||||
- Сохранять сырой JSON payload операции для аудита/отладки и отдавать наружу нормализованные DTO.
|
||||
|
||||
## Cache Strategy
|
||||
## Стратегия кеширования
|
||||
|
||||
Use existing backend `CacheService` for short-lived reads and add T-Bank-specific key prefixes.
|
||||
Frontend TanStack Query should use matching or shorter stale times.
|
||||
Использовать существующий backend `CacheService` для короткоживущих чтений и добавить key prefixes
|
||||
для T-Bank. Frontend TanStack Query должен использовать совпадающие или более короткие stale times.
|
||||
|
||||
| Data | Backend TTL | Reason |
|
||||
| Данные | TTL бэкенда | Причина |
|
||||
| --- | --- | --- |
|
||||
| Accounts | 1 hour | Account list changes rarely. |
|
||||
| Portfolio totals and positions | 30-60 seconds | User-facing current view, should feel fresh but not spam T-Bank. |
|
||||
| Cash/withdraw limits | 30-60 seconds | Similar freshness to positions. |
|
||||
| Instrument metadata | 24 hours | Instrument name, lot, ISIN, UID are stable. |
|
||||
| Operation page, recent window | 60-300 seconds | Useful before durable sync exists. |
|
||||
| Operation historical pages | 24 hours or DB only | History is mostly immutable outside recent correction window. |
|
||||
| Счета | 1 час | Список счетов меняется редко. |
|
||||
| Итоги портфеля и позиции | 30-60 секунд | Текущий пользовательский вид должен ощущаться свежим, но не спамить T-Bank. |
|
||||
| Деньги/лимиты вывода | 30-60 секунд | Такая же свежесть, как у позиций. |
|
||||
| Метаданные инструмента | 24 часа | Название инструмента, лот, ISIN, UID стабильны. |
|
||||
| Страница операций, недавнее окно | 60-300 секунд | Полезно до появления устойчивой синхронизации. |
|
||||
| Исторические страницы операций | 24 часа или только БД | История в основном неизменна вне недавнего окна корректировок. |
|
||||
|
||||
Add environment variables:
|
||||
Добавить переменные окружения:
|
||||
|
||||
| Variable | Default | Description |
|
||||
| Переменная | Значение по умолчанию | Описание |
|
||||
| --- | --- | --- |
|
||||
| `T_BANK_TOKEN` | none | Server-side T-Bank Invest token. Required when integration is enabled. |
|
||||
| `T_BANK_TOKEN` | нет | Серверный T-Bank Invest token. Обязателен, когда интеграция включена. |
|
||||
| `T_BANK_BASE_URL` | `invest-public-api.tbank.ru:443` | gRPC endpoint. |
|
||||
| `T_BANK_APP_NAME` | `ksv741.moex-vibe` | Optional gRPC metadata. |
|
||||
| `T_BANK_RATE_LIMIT_PER_SECOND` | `5` | Conservative local limiter across T-Bank calls. |
|
||||
| `CACHE_TBANK_ACCOUNTS_TTL` | `3600` | Accounts cache TTL. |
|
||||
| `CACHE_TBANK_PORTFOLIO_TTL` | `60` | Portfolio and positions cache TTL. |
|
||||
| `CACHE_TBANK_OPERATIONS_TTL` | `300` | Recent operation page cache TTL. |
|
||||
| `CACHE_TBANK_INSTRUMENT_TTL` | `86400` | Instrument metadata cache TTL. |
|
||||
| `T_BANK_APP_NAME` | `ksv741.moex-vibe` | Опциональная gRPC metadata. |
|
||||
| `T_BANK_RATE_LIMIT_PER_SECOND` | `5` | Консервативный локальный limiter для всех вызовов T-Bank. |
|
||||
| `CACHE_TBANK_ACCOUNTS_TTL` | `3600` | TTL кеша счетов. |
|
||||
| `CACHE_TBANK_PORTFOLIO_TTL` | `60` | TTL кеша портфеля и позиций. |
|
||||
| `CACHE_TBANK_OPERATIONS_TTL` | `300` | TTL кеша страницы недавних операций. |
|
||||
| `CACHE_TBANK_INSTRUMENT_TTL` | `86400` | TTL кеша метаданных инструмента. |
|
||||
|
||||
The rate limiter must stay below documented public limits. On 429 or exhausted rate-limit metadata,
|
||||
back off until reset when metadata is available.
|
||||
Rate limiter должен оставаться ниже документированных публичных лимитов. При 429 или исчерпанных
|
||||
метаданных лимита нужно отступать до reset, если метаданные доступны.
|
||||
|
||||
## Error Handling
|
||||
## Обработка ошибок
|
||||
|
||||
- Missing `T_BANK_TOKEN`: return a clear 503-style application error and mark integration
|
||||
unavailable; do not crash the whole backend in development.
|
||||
- Auth error from T-Bank: return 502/503 with a generic message; never echo token or full metadata.
|
||||
- Account not found or excluded by type/status: return 404 from MoexVibe endpoints.
|
||||
- T-Bank 429: return 429 or 503 with retry metadata when available.
|
||||
- T-Bank transient gRPC errors: retry with small exponential backoff, then surface a typed upstream
|
||||
error with `trackingId` if present.
|
||||
- Mapping unknown enum values: preserve the original value and categorize as `other`.
|
||||
- Отсутствует `T_BANK_TOKEN`: вернуть понятную application error в стиле 503 и пометить интеграцию
|
||||
недоступной; в development не ронять весь бэкенд.
|
||||
- Ошибка авторизации от T-Bank: вернуть 502/503 с общим сообщением; никогда не отдавать token или
|
||||
полные метаданные наружу.
|
||||
- Счет не найден или исключен по type/status: вернуть 404 из endpoints MoexVibe.
|
||||
- T-Bank 429: вернуть 429 или 503 с retry metadata, если она доступна.
|
||||
- Временные gRPC-ошибки T-Bank: повторные попытки с небольшим exponential backoff, затем типизированная
|
||||
upstream error с `trackingId`, если он присутствует.
|
||||
- Неизвестные enum values при mapping: сохранить исходное значение и категоризовать как `other`.
|
||||
|
||||
## Security
|
||||
## Безопасность
|
||||
|
||||
- `T_BANK_TOKEN` must never be logged, returned in API responses, or committed.
|
||||
- gRPC metadata logging must redact `Authorization`.
|
||||
- The initial deployment is single-user by design. Before enabling access for multiple MoexVibe
|
||||
users, add per-user encrypted token storage and authorization rules that bind each broker account
|
||||
to its owner.
|
||||
- The integration is read-only. Do not include order, stop-order, transfer, or pay-in clients in the
|
||||
first module.
|
||||
- `T_BANK_TOKEN` нельзя логировать, возвращать в API responses или коммитить.
|
||||
- Логирование gRPC metadata должно редактировать `Authorization`.
|
||||
- Первый deployment по дизайну single-user. Перед включением доступа для нескольких пользователей
|
||||
MoexVibe нужно добавить encrypted token storage на уровне пользователя и authorization rules, которые связывают
|
||||
каждый брокерский счет с владельцем.
|
||||
- Интеграция только для чтения. В первый модуль нельзя включать clients для orders, stop-orders, transfers
|
||||
или pay-in.
|
||||
|
||||
## Frontend Product Shape
|
||||
## Вид продукта на фронтенде
|
||||
|
||||
Add a broker portfolios area separate from manually managed virtual portfolios:
|
||||
Добавить раздел брокерских портфелей отдельно от вручную управляемых виртуальных портфелей:
|
||||
|
||||
- List page: account cards for brokerage and IIS accounts, with total value, cash, daily change, and
|
||||
last refresh time.
|
||||
- Account detail page: tabs for `Позиции`, `Операции`, and later `Аналитика`.
|
||||
- Positions table: instrument name, ticker, type, quantity, current price, current value, expected
|
||||
yield, daily yield, blocked quantity.
|
||||
- Cash section: available and blocked money by currency.
|
||||
- Operations table: date, type/category, instrument, quantity, payment, commission, tax/income
|
||||
indicators, status, expandable trade details.
|
||||
- Страница списка: карточки счетов для брокерских счетов и ИИС, с полной стоимостью, деньгами,
|
||||
дневным изменением и временем последнего обновления.
|
||||
- Страница счета: вкладки `Позиции`, `Операции` и позже `Аналитика`.
|
||||
- Таблица позиций: название инструмента, тикер, тип, количество, текущая цена, текущая стоимость,
|
||||
ожидаемая доходность, дневная доходность, заблокированное количество.
|
||||
- Секция денег: доступные и заблокированные деньги по валютам.
|
||||
- Таблица операций: дата, type/category, инструмент, количество, платеж, комиссия,
|
||||
признаки налога/дохода, статус, раскрываемые детали сделки.
|
||||
|
||||
Do not merge real broker accounts into the existing manual `Portfolio` model. Keep them separate in
|
||||
UI and backend API. Later, MoexVibe can add comparison views or import flows from broker operations
|
||||
into virtual portfolios.
|
||||
Не смешивать реальные брокерские счета с существующей ручной моделью `Portfolio`. Держать их отдельно
|
||||
в UI и backend API. Позже MoexVibe сможет добавить представления сравнения или import flows из брокерских
|
||||
операций в виртуальные портфели.
|
||||
|
||||
## Documentation Deliverables
|
||||
## Документация
|
||||
|
||||
Publish durable documentation in `apps/docs` during implementation:
|
||||
Во время реализации опубликовать устойчивую документацию в `apps/docs`:
|
||||
|
||||
- `apps/docs/docs/backend/tbank-invest.md`: module architecture, API methods, env vars, error
|
||||
handling, and read-only scope.
|
||||
- Update `apps/docs/docs/backend/modules.md`: add `TBankInvestModule`.
|
||||
- Update `apps/docs/docs/backend/configuration.md`: document `T_BANK_*` and cache TTL variables.
|
||||
- Update `apps/docs/docs/backend/caching.md`: add T-Bank cache strategy.
|
||||
- Update `apps/docs/docs/backend/portfolio.md`: distinguish manual portfolios from broker
|
||||
portfolios.
|
||||
- Add ADR `apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md`: record gRPC over REST/SDK decision.
|
||||
- Update `apps/docs/sidebars.ts` to include the new backend page and ADR.
|
||||
- `apps/docs/docs/backend/tbank-invest.md`: архитектура модуля, методы API, переменные окружения,
|
||||
обработка ошибок и область только для чтения.
|
||||
- Обновить `apps/docs/docs/backend/modules.md`: добавить `TBankInvestModule`.
|
||||
- Обновить `apps/docs/docs/backend/configuration.md`: задокументировать `T_BANK_*` и переменные
|
||||
TTL кеша.
|
||||
- Обновить `apps/docs/docs/backend/caching.md`: добавить стратегию кеширования для T-Bank.
|
||||
- Обновить `apps/docs/docs/backend/portfolio.md`: разделить ручные портфели и брокерские портфели.
|
||||
- Добавить ADR `apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md`: зафиксировать решение gRPC over
|
||||
REST/SDK.
|
||||
- Обновить `apps/docs/sidebars.ts`, чтобы включить новую страницу бэкенда и ADR.
|
||||
|
||||
## Acceptance Criteria
|
||||
## Критерии приемки
|
||||
|
||||
- Backend lists only open `ACCOUNT_TYPE_TINKOFF` and `ACCOUNT_TYPE_TINKOFF_IIS` accounts.
|
||||
- Backend exposes current account portfolio with positions and cash without exposing raw token data.
|
||||
- Backend exposes paginated operation history and includes buy, sell, tax, fee, coupon, dividend,
|
||||
deposit, withdrawal, and unknown operation categories.
|
||||
- T-Bank calls use a conservative rate limiter and cache strategy.
|
||||
- Missing or invalid token produces a clear integration-unavailable error.
|
||||
- Published docs in `apps/docs` describe architecture, configuration, caching, and the gRPC decision.
|
||||
- Tests cover account filtering, money mapping, operation categorization, query validation, cache
|
||||
keys, and upstream error mapping.
|
||||
- Бэкенд показывает только открытые счета `ACCOUNT_TYPE_TINKOFF` и `ACCOUNT_TYPE_TINKOFF_IIS`.
|
||||
- Бэкенд отдает текущий портфель счета с позициями и деньгами, не раскрывая сырые данные токена.
|
||||
- Бэкенд отдает историю операций с пагинацией и включает категории buy, sell, tax, fee, coupon,
|
||||
dividend, deposit, withdrawal и unknown operations.
|
||||
- Вызовы T-Bank используют консервативный rate limiter и стратегию кеширования.
|
||||
- Отсутствующий или некорректный token дает понятную ошибку integration-unavailable.
|
||||
- Опубликованная документация в `apps/docs` описывает архитектуру, конфигурацию, кеширование и
|
||||
решение по gRPC.
|
||||
- Тесты покрывают фильтрацию счетов, mapping денег, категоризацию операций, валидацию query,
|
||||
cache keys и mapping upstream-ошибок.
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
1. Backend gRPC foundation and DTO mappers.
|
||||
2. Read-only account and portfolio endpoints.
|
||||
3. Operation history endpoint with cursor pagination.
|
||||
4. Frontend broker account list and account detail pages.
|
||||
5. Durable operation sync in database.
|
||||
6. Optional stream workers for near-real-time refresh after unary sync is stable.
|
||||
## Этапы реализации
|
||||
|
||||
1. Базовая gRPC-инфраструктура бэкенда и DTO mappers.
|
||||
2. Endpoints только для чтения для счетов и портфеля.
|
||||
3. Endpoint истории операций с курсорной пагинацией.
|
||||
4. Список брокерских счетов и detail pages счета на фронтенде.
|
||||
5. Устойчивая синхронизация операций в базе данных.
|
||||
6. Опциональные stream workers для near-real-time refresh после стабилизации unary sync.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user