docs: document tbank invest integration

This commit is contained in:
Sergey Krylov 2026-06-17 06:49:09 +03:00
parent 3fe4e7a4ec
commit b10a2cfb0d
8 changed files with 163 additions and 2 deletions

View File

@ -0,0 +1,30 @@
# ADR-011: Интеграция с T-Bank Invest использует gRPC
**Статус:** Accepted
**Дата:** 2026-06-16
## Контекст
MoexVibe нужна read-only интеграция с T-Bank Invest для брокерских счетов и ИИС: текущие позиции,
деньги на счёте и история операций. T-Bank предоставляет gRPC, REST-прокси, WebSocket и официальный
JS SDK.
## Решение
Использовать тонкую backend gRPC-интеграцию на основе официальных proto-контрактов. REST оставить
для ручной диагностики, а официальный JS SDK не делать прямой зависимостью первого варианта.
## Обоснование
- gRPC — основной протокол T-Bank Invest API.
- Unary methods покрывают счета, портфель, позиции, операции и инструменты.
- Stream methods можно добавить позже без изменения публичного MoexVibe API.
- Собственный транспортный слой позволяет контролировать rate limiting, cache TTL, redaction
metadata, test doubles и будущий переход от одного server token к per-user token storage.
## Последствия
- Backend хранит vendored proto-контракты T-Bank Invest.
- Backend владеет T-Bank-specific rate limits и cache TTL.
- Интеграция остаётся read-only, пока отдельный ADR не разрешит торговые операции и заявки.

View File

@ -12,5 +12,6 @@
| [ADR-008](ADR-008-auth-system) | Accepted | Authentication и Authorization |
| [ADR-009](ADR-009-portfolio-domain) | Accepted | Доменная модель портфеля |
| [ADR-010](ADR-010-backend-price-computation) | Accepted | Расчёт цен на backend |
| [ADR-011](ADR-011-tbank-invest-grpc) | Accepted | Интеграция с T-Bank Invest через gRPC |
Все опубликованные ADR находятся в `apps/docs/docs/adr/` и отображаются в этом Docusaurus-разделе.

View File

@ -37,7 +37,7 @@ flowchart LR
```typescript
getOrFetch<T>(
keyPrefix: string, // 'marketdata' | 'history' | 'candles' | 'search' | 'bond' | 'dividends'
keyPrefix: string, // 'marketdata' | 'history' | 'candles' | 'search' | 'bond' | 'dividends' | 'tbank:*'
keyParts: string[], // ['shares', 'SBER'] | ['SBER', '2026-06-13', '2026-06-14']
fetchFn: () => Promise<T>,
ttlConfigKey: string, // 'marketDataTtl' | 'historyTtl' | etc.
@ -78,3 +78,14 @@ export class CacheModule {}
| Спецификация инструмента | `securityTtl` | 86400s (24 ч) | `CACHE_SECURITY_TTL` |
| Поиск | `searchTtl` | 3600s (1 ч) | `CACHE_SEARCH_TTL` |
| Дивиденды | `dividendsTtl` | 86400s (24 ч) | `CACHE_DIVIDENDS_TTL` |
| Счета T-Bank | `tbankAccountsTtl` | 3600s (1 ч) | `CACHE_TBANK_ACCOUNTS_TTL` |
| Портфель T-Bank | `tbankPortfolioTtl` | 60s (1 мин) | `CACHE_TBANK_PORTFOLIO_TTL` |
| Операции T-Bank | `tbankOperationsTtl` | 300s (5 мин) | `CACHE_TBANK_OPERATIONS_TTL` |
| Инструменты T-Bank | `tbankInstrumentTtl` | 86400s (24 ч) | `CACHE_TBANK_INSTRUMENT_TTL` |
## T-Bank cache
`TBankModule` использует те же механики `CacheService`, но с отдельными key prefixes
`tbank:accounts`, `tbank:portfolio`, `tbank:positions`, `tbank:operations` и `tbank:instrument`.
Это позволяет держать агрессивно короткий TTL для текущего портфеля и более длинный TTL для
справочных данных инструментов.

View File

@ -11,12 +11,21 @@
| `MOEX_RATE_LIMIT` | `10` | Максимум запросов в секунду к MOEX |
| `MOEX_CIRCUIT_BREAKER_THRESHOLD` | `5` | Количество ошибок до открытия circuit breaker |
| `MOEX_CIRCUIT_BREAKER_RESET_SECONDS` | `30` | Время в секундах до сброса circuit breaker |
| `T_BANK_TOKEN` | `''` | Server-side токен T-Bank Invest |
| `T_BANK_BASE_URL` | `invest-public-api.tbank.ru:443` | gRPC endpoint T-Bank Invest |
| `T_BANK_APP_NAME` | `ksv741.moex-vibe` | Metadata приложения для T-Bank |
| `T_BANK_RATE_LIMIT_PER_SECOND` | `5` | Локальный rate limiter для T-Bank |
| `T_BANK_REQUEST_TIMEOUT_MS` | `10000` | Deadline gRPC-запроса (мс) |
| `CACHE_MARKET_DATA_TTL` | `900` | TTL рыночных данных (секунды) |
| `CACHE_HISTORY_TTL` | `3600` | TTL истории торгов (секунды) |
| `CACHE_CANDLES_TTL` | `3600` | TTL свечей (секунды) |
| `CACHE_SECURITY_TTL` | `86400` | TTL спецификации инструмента (секунды) |
| `CACHE_SEARCH_TTL` | `3600` | TTL результатов поиска (секунды) |
| `CACHE_DIVIDENDS_TTL` | `86400` | TTL дивидендов (секунды) |
| `CACHE_TBANK_ACCOUNTS_TTL` | `3600` | TTL списка брокерских счетов T-Bank (секунды) |
| `CACHE_TBANK_PORTFOLIO_TTL` | `60` | TTL брокерского портфеля T-Bank (секунды) |
| `CACHE_TBANK_OPERATIONS_TTL` | `300` | TTL страницы операций T-Bank (секунды) |
| `CACHE_TBANK_INSTRUMENT_TTL` | `86400` | TTL метаданных инструментов T-Bank (секунды) |
## Файл конфигурации
@ -33,6 +42,13 @@ registerAs('app', () => ({
process.env.MOEX_CIRCUIT_BREAKER_RESET_SECONDS || '30', 10,
),
},
tbank: {
token: process.env.T_BANK_TOKEN || '',
baseUrl: process.env.T_BANK_BASE_URL || 'invest-public-api.tbank.ru:443',
appName: process.env.T_BANK_APP_NAME || 'ksv741.moex-vibe',
rateLimitPerSecond: parseInt(process.env.T_BANK_RATE_LIMIT_PER_SECOND || '5', 10),
requestTimeoutMs: parseInt(process.env.T_BANK_REQUEST_TIMEOUT_MS || '10000', 10),
},
cache: {
marketDataTtl: parseInt(process.env.CACHE_MARKET_DATA_TTL || '900', 10),
historyTtl: parseInt(process.env.CACHE_HISTORY_TTL || '3600', 10),
@ -40,6 +56,10 @@ registerAs('app', () => ({
securityTtl: parseInt(process.env.CACHE_SECURITY_TTL || '86400', 10),
searchTtl: parseInt(process.env.CACHE_SEARCH_TTL || '3600', 10),
dividendsTtl: parseInt(process.env.CACHE_DIVIDENDS_TTL || '86400', 10),
tbankAccountsTtl: parseInt(process.env.CACHE_TBANK_ACCOUNTS_TTL || '3600', 10),
tbankPortfolioTtl: parseInt(process.env.CACHE_TBANK_PORTFOLIO_TTL || '60', 10),
tbankOperationsTtl: parseInt(process.env.CACHE_TBANK_OPERATIONS_TTL || '300', 10),
tbankInstrumentTtl: parseInt(process.env.CACHE_TBANK_INSTRUMENT_TTL || '86400', 10),
},
}));
```

View File

@ -8,7 +8,7 @@
flowchart TB
AppModule["AppModule"]
GlobalModules["Глобальные модули<br/>ConfigModule<br/>PrismaModule<br/>CacheModule<br/>MoexClientModule"]
FeatureModules["Feature-модули<br/>HealthModule<br/>AuthModule<br/>PortfolioModule<br/>SecuritiesModule<br/>SharesModule<br/>BondsModule<br/>CandlesModule"]
FeatureModules["Feature-модули<br/>HealthModule<br/>AuthModule<br/>PortfolioModule<br/>SecuritiesModule<br/>SharesModule<br/>BondsModule<br/>CandlesModule<br/>TBankModule"]
AppModule --> GlobalModules
AppModule --> FeatureModules
@ -21,10 +21,12 @@ flowchart TB
AuthModule["AuthModule"]
PortfolioModule["PortfolioModule"]
MarketModules["SecuritiesModule<br/>SharesModule<br/>BondsModule<br/>CandlesModule"]
TBankModule["TBankModule"]
PrismaService["PrismaService"]
CacheService["CacheService"]
MoexClientService["MoexClientService"]
TBankClientService["TBankClientService"]
PrismaModule --> PrismaService
CacheModule --> CacheService
@ -37,6 +39,10 @@ flowchart TB
MarketModules --> CacheService
MarketModules --> MoexClientService
TBankModule --> CacheService
TBankModule --> PrismaService
TBankModule --> TBankClientService
```
## Список модулей
@ -53,6 +59,7 @@ flowchart TB
| `BondsModule` | Нет | `modules/bonds/` | Облигации |
| `CandlesModule` | Нет | `modules/candles/` | Свечи OHLCV |
| `PortfolioModule` | Нет | `modules/portfolio/` | Пользовательские портфели и аналитика |
| `TBankModule` | Нет | `modules/tbank/` | Read-only брокерские портфели T-Bank Invest |
### PrismaModule
@ -131,3 +138,16 @@ flowchart TB
- Обогащение позиций текущими ценами из MOEX
- Расчёт summary, PnL и долей портфеля
- Все endpoints защищены JWT
### TBankModule
Read-only интеграция с T-Bank Invest для брокерских счетов и ИИС.
- `TBankClientService` создаёт gRPC clients по vendored proto-контрактам и добавляет metadata
`Authorization` + `x-app-name`
- `BrokerAccountsService` фильтрует только открытые брокерские счета и ИИС
- `BrokerPortfolioService` объединяет портфель, позиции, cash и метаданные инструментов
- `BrokerOperationsService` отдаёт cursor-paginated историю операций
- `BrokerOperationSyncService` сохраняет историю операций в отдельные Prisma-таблицы
- Direct-read endpoints используют `CacheService` с T-Bank TTL и не записывают данные в ручной
`PortfolioModule`

View File

@ -3,6 +3,12 @@
`PortfolioModule` позволяет пользователям создавать и вести виртуальные инвестиционные портфели для
аналитики и отслеживания позиций.
## Ручные портфели и брокерские портфели
`PortfolioModule` остаётся доменом ручных виртуальных портфелей. Брокерские счета T-Bank Invest
экспортируются отдельным `TBankModule` под `/api/v1/broker/*` и не сохраняются как записи
`Portfolio`.
## Обзор
- **Backend:** `PortfolioModule` (`apps/backend/src/modules/portfolio/`)

View File

@ -0,0 +1,71 @@
# T-Bank Invest
`TBankModule` — read-only интеграция backend с T-Bank Invest API. Frontend не обращается к
T-Bank напрямую: он использует endpoints MoexVibe под `/api/v1/broker`.
## Область поддержки
Первая версия показывает только открытые брокерские счета и ИИС:
- `ACCOUNT_TYPE_TINKOFF`
- `ACCOUNT_TYPE_TINKOFF_IIS`
Инвесткопилка, счета ЦФА, дебетовые счета, накопительные счета и счета фондов денежного рынка
игнорируются.
## Протокол
MoexVibe использует gRPC endpoint `invest-public-api.tbank.ru:443`. REST-прокси T-Bank считается
инструментом для ручной диагностики, а не основным протоколом интеграции.
Backend отправляет metadata:
```text
Authorization: Bearer <T_BANK_TOKEN>
x-app-name: ksv741.moex-vibe
```
Токен читается из env-переменных backend и не возвращается во frontend, Swagger responses или логи.
## Backend endpoints
Все endpoints защищены JWT и возвращают стандартную оболочку `{ data, meta }`.
| Endpoint | Описание |
|---|---|
| `GET /api/v1/broker/accounts` | Открытые брокерские счета и ИИС |
| `GET /api/v1/broker/accounts/:accountId/portfolio` | Итоги портфеля, позиции, деньги и заблокированные деньги |
| `GET /api/v1/broker/accounts/:accountId/operations` | История операций с cursor pagination |
## Методы T-Bank
| Задача | Метод T-Bank |
|---|---|
| Счета | `UsersService/GetAccounts` |
| Итоги портфеля | `OperationsService/GetPortfolio` |
| Деньги и settled-позиции | `OperationsService/GetPositions` |
| История операций | `OperationsService/GetOperationsByCursor` |
| Метаданные инструментов | `InstrumentsService/GetInstrumentBy` |
## Кеширование и синхронизация
Direct-read endpoints используют короткий in-memory cache, чтобы не спамить T-Bank API при
переключении страниц и повторных запросах:
- счета: `CACHE_TBANK_ACCOUNTS_TTL`
- портфель: `CACHE_TBANK_PORTFOLIO_TTL`
- страницы операций: `CACHE_TBANK_OPERATIONS_TTL`
- метаданные инструментов: `CACHE_TBANK_INSTRUMENT_TTL`
Для долговременной истории операций есть отдельные таблицы Prisma:
- `BrokerOperation` — нормализованная операция и сырой JSON payload;
- `BrokerOperationSyncState` — состояние последней синхронизации по брокерскому счёту.
Эти таблицы не связаны с ручными портфелями `Portfolio` и `Position`.
## Безопасность
Текущая версия рассчитана на single-user/admin сценарий: используется один server-side
`T_BANK_TOKEN`. Перед multi-user режимом нужно добавить зашифрованное хранение пользовательских
T-Bank токенов и привязку каждого брокерского счёта к владельцу.

View File

@ -17,6 +17,7 @@ const sidebars: SidebarsConfig = {
'backend/configuration',
'backend/caching',
'backend/moex-client',
'backend/tbank-invest',
'backend/securities',
'backend/portfolio',
],
@ -63,6 +64,7 @@ const sidebars: SidebarsConfig = {
'adr/ADR-008-auth-system',
'adr/ADR-009-portfolio-domain',
'adr/ADR-010-backend-price-computation',
'adr/ADR-011-tbank-invest-grpc',
],
},
],