docs: document tbank invest integration
This commit is contained in:
parent
3fe4e7a4ec
commit
b10a2cfb0d
30
apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md
Normal file
30
apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md
Normal 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 не разрешит торговые операции и заявки.
|
||||||
@ -12,5 +12,6 @@
|
|||||||
| [ADR-008](ADR-008-auth-system) | Accepted | Authentication и Authorization |
|
| [ADR-008](ADR-008-auth-system) | Accepted | Authentication и Authorization |
|
||||||
| [ADR-009](ADR-009-portfolio-domain) | Accepted | Доменная модель портфеля |
|
| [ADR-009](ADR-009-portfolio-domain) | Accepted | Доменная модель портфеля |
|
||||||
| [ADR-010](ADR-010-backend-price-computation) | Accepted | Расчёт цен на backend |
|
| [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-разделе.
|
Все опубликованные ADR находятся в `apps/docs/docs/adr/` и отображаются в этом Docusaurus-разделе.
|
||||||
|
|||||||
@ -37,7 +37,7 @@ flowchart LR
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
getOrFetch<T>(
|
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']
|
keyParts: string[], // ['shares', 'SBER'] | ['SBER', '2026-06-13', '2026-06-14']
|
||||||
fetchFn: () => Promise<T>,
|
fetchFn: () => Promise<T>,
|
||||||
ttlConfigKey: string, // 'marketDataTtl' | 'historyTtl' | etc.
|
ttlConfigKey: string, // 'marketDataTtl' | 'historyTtl' | etc.
|
||||||
@ -78,3 +78,14 @@ export class CacheModule {}
|
|||||||
| Спецификация инструмента | `securityTtl` | 86400s (24 ч) | `CACHE_SECURITY_TTL` |
|
| Спецификация инструмента | `securityTtl` | 86400s (24 ч) | `CACHE_SECURITY_TTL` |
|
||||||
| Поиск | `searchTtl` | 3600s (1 ч) | `CACHE_SEARCH_TTL` |
|
| Поиск | `searchTtl` | 3600s (1 ч) | `CACHE_SEARCH_TTL` |
|
||||||
| Дивиденды | `dividendsTtl` | 86400s (24 ч) | `CACHE_DIVIDENDS_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 для
|
||||||
|
справочных данных инструментов.
|
||||||
|
|||||||
@ -11,12 +11,21 @@
|
|||||||
| `MOEX_RATE_LIMIT` | `10` | Максимум запросов в секунду к MOEX |
|
| `MOEX_RATE_LIMIT` | `10` | Максимум запросов в секунду к MOEX |
|
||||||
| `MOEX_CIRCUIT_BREAKER_THRESHOLD` | `5` | Количество ошибок до открытия circuit breaker |
|
| `MOEX_CIRCUIT_BREAKER_THRESHOLD` | `5` | Количество ошибок до открытия circuit breaker |
|
||||||
| `MOEX_CIRCUIT_BREAKER_RESET_SECONDS` | `30` | Время в секундах до сброса 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_MARKET_DATA_TTL` | `900` | TTL рыночных данных (секунды) |
|
||||||
| `CACHE_HISTORY_TTL` | `3600` | TTL истории торгов (секунды) |
|
| `CACHE_HISTORY_TTL` | `3600` | TTL истории торгов (секунды) |
|
||||||
| `CACHE_CANDLES_TTL` | `3600` | TTL свечей (секунды) |
|
| `CACHE_CANDLES_TTL` | `3600` | TTL свечей (секунды) |
|
||||||
| `CACHE_SECURITY_TTL` | `86400` | TTL спецификации инструмента (секунды) |
|
| `CACHE_SECURITY_TTL` | `86400` | TTL спецификации инструмента (секунды) |
|
||||||
| `CACHE_SEARCH_TTL` | `3600` | TTL результатов поиска (секунды) |
|
| `CACHE_SEARCH_TTL` | `3600` | TTL результатов поиска (секунды) |
|
||||||
| `CACHE_DIVIDENDS_TTL` | `86400` | 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,
|
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: {
|
cache: {
|
||||||
marketDataTtl: parseInt(process.env.CACHE_MARKET_DATA_TTL || '900', 10),
|
marketDataTtl: parseInt(process.env.CACHE_MARKET_DATA_TTL || '900', 10),
|
||||||
historyTtl: parseInt(process.env.CACHE_HISTORY_TTL || '3600', 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),
|
securityTtl: parseInt(process.env.CACHE_SECURITY_TTL || '86400', 10),
|
||||||
searchTtl: parseInt(process.env.CACHE_SEARCH_TTL || '3600', 10),
|
searchTtl: parseInt(process.env.CACHE_SEARCH_TTL || '3600', 10),
|
||||||
dividendsTtl: parseInt(process.env.CACHE_DIVIDENDS_TTL || '86400', 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),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
```
|
```
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
flowchart TB
|
flowchart TB
|
||||||
AppModule["AppModule"]
|
AppModule["AppModule"]
|
||||||
GlobalModules["Глобальные модули<br/>ConfigModule<br/>PrismaModule<br/>CacheModule<br/>MoexClientModule"]
|
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 --> GlobalModules
|
||||||
AppModule --> FeatureModules
|
AppModule --> FeatureModules
|
||||||
@ -21,10 +21,12 @@ flowchart TB
|
|||||||
AuthModule["AuthModule"]
|
AuthModule["AuthModule"]
|
||||||
PortfolioModule["PortfolioModule"]
|
PortfolioModule["PortfolioModule"]
|
||||||
MarketModules["SecuritiesModule<br/>SharesModule<br/>BondsModule<br/>CandlesModule"]
|
MarketModules["SecuritiesModule<br/>SharesModule<br/>BondsModule<br/>CandlesModule"]
|
||||||
|
TBankModule["TBankModule"]
|
||||||
|
|
||||||
PrismaService["PrismaService"]
|
PrismaService["PrismaService"]
|
||||||
CacheService["CacheService"]
|
CacheService["CacheService"]
|
||||||
MoexClientService["MoexClientService"]
|
MoexClientService["MoexClientService"]
|
||||||
|
TBankClientService["TBankClientService"]
|
||||||
|
|
||||||
PrismaModule --> PrismaService
|
PrismaModule --> PrismaService
|
||||||
CacheModule --> CacheService
|
CacheModule --> CacheService
|
||||||
@ -37,6 +39,10 @@ flowchart TB
|
|||||||
|
|
||||||
MarketModules --> CacheService
|
MarketModules --> CacheService
|
||||||
MarketModules --> MoexClientService
|
MarketModules --> MoexClientService
|
||||||
|
|
||||||
|
TBankModule --> CacheService
|
||||||
|
TBankModule --> PrismaService
|
||||||
|
TBankModule --> TBankClientService
|
||||||
```
|
```
|
||||||
|
|
||||||
## Список модулей
|
## Список модулей
|
||||||
@ -53,6 +59,7 @@ flowchart TB
|
|||||||
| `BondsModule` | Нет | `modules/bonds/` | Облигации |
|
| `BondsModule` | Нет | `modules/bonds/` | Облигации |
|
||||||
| `CandlesModule` | Нет | `modules/candles/` | Свечи OHLCV |
|
| `CandlesModule` | Нет | `modules/candles/` | Свечи OHLCV |
|
||||||
| `PortfolioModule` | Нет | `modules/portfolio/` | Пользовательские портфели и аналитика |
|
| `PortfolioModule` | Нет | `modules/portfolio/` | Пользовательские портфели и аналитика |
|
||||||
|
| `TBankModule` | Нет | `modules/tbank/` | Read-only брокерские портфели T-Bank Invest |
|
||||||
|
|
||||||
### PrismaModule
|
### PrismaModule
|
||||||
|
|
||||||
@ -131,3 +138,16 @@ flowchart TB
|
|||||||
- Обогащение позиций текущими ценами из MOEX
|
- Обогащение позиций текущими ценами из MOEX
|
||||||
- Расчёт summary, PnL и долей портфеля
|
- Расчёт summary, PnL и долей портфеля
|
||||||
- Все endpoints защищены JWT
|
- Все 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`
|
||||||
|
|||||||
@ -3,6 +3,12 @@
|
|||||||
`PortfolioModule` позволяет пользователям создавать и вести виртуальные инвестиционные портфели для
|
`PortfolioModule` позволяет пользователям создавать и вести виртуальные инвестиционные портфели для
|
||||||
аналитики и отслеживания позиций.
|
аналитики и отслеживания позиций.
|
||||||
|
|
||||||
|
## Ручные портфели и брокерские портфели
|
||||||
|
|
||||||
|
`PortfolioModule` остаётся доменом ручных виртуальных портфелей. Брокерские счета T-Bank Invest
|
||||||
|
экспортируются отдельным `TBankModule` под `/api/v1/broker/*` и не сохраняются как записи
|
||||||
|
`Portfolio`.
|
||||||
|
|
||||||
## Обзор
|
## Обзор
|
||||||
|
|
||||||
- **Backend:** `PortfolioModule` (`apps/backend/src/modules/portfolio/`)
|
- **Backend:** `PortfolioModule` (`apps/backend/src/modules/portfolio/`)
|
||||||
|
|||||||
71
apps/docs/docs/backend/tbank-invest.md
Normal file
71
apps/docs/docs/backend/tbank-invest.md
Normal 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 токенов и привязку каждого брокерского счёта к владельцу.
|
||||||
@ -17,6 +17,7 @@ const sidebars: SidebarsConfig = {
|
|||||||
'backend/configuration',
|
'backend/configuration',
|
||||||
'backend/caching',
|
'backend/caching',
|
||||||
'backend/moex-client',
|
'backend/moex-client',
|
||||||
|
'backend/tbank-invest',
|
||||||
'backend/securities',
|
'backend/securities',
|
||||||
'backend/portfolio',
|
'backend/portfolio',
|
||||||
],
|
],
|
||||||
@ -63,6 +64,7 @@ const sidebars: SidebarsConfig = {
|
|||||||
'adr/ADR-008-auth-system',
|
'adr/ADR-008-auth-system',
|
||||||
'adr/ADR-009-portfolio-domain',
|
'adr/ADR-009-portfolio-domain',
|
||||||
'adr/ADR-010-backend-price-computation',
|
'adr/ADR-010-backend-price-computation',
|
||||||
|
'adr/ADR-011-tbank-invest-grpc',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user