diff --git a/apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md b/apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md new file mode 100644 index 0000000..f202a87 --- /dev/null +++ b/apps/docs/docs/adr/ADR-011-tbank-invest-grpc.md @@ -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 не разрешит торговые операции и заявки. diff --git a/apps/docs/docs/adr/index.md b/apps/docs/docs/adr/index.md index 7fea488..58d9185 100644 --- a/apps/docs/docs/adr/index.md +++ b/apps/docs/docs/adr/index.md @@ -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-разделе. diff --git a/apps/docs/docs/backend/caching.md b/apps/docs/docs/backend/caching.md index db9337b..0072435 100644 --- a/apps/docs/docs/backend/caching.md +++ b/apps/docs/docs/backend/caching.md @@ -37,7 +37,7 @@ flowchart LR ```typescript getOrFetch( - 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, 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 для +справочных данных инструментов. diff --git a/apps/docs/docs/backend/configuration.md b/apps/docs/docs/backend/configuration.md index d5a517c..d4b723d 100644 --- a/apps/docs/docs/backend/configuration.md +++ b/apps/docs/docs/backend/configuration.md @@ -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), }, })); ``` diff --git a/apps/docs/docs/backend/modules.md b/apps/docs/docs/backend/modules.md index 4a76b31..457153c 100644 --- a/apps/docs/docs/backend/modules.md +++ b/apps/docs/docs/backend/modules.md @@ -8,7 +8,7 @@ flowchart TB AppModule["AppModule"] GlobalModules["Глобальные модули
ConfigModule
PrismaModule
CacheModule
MoexClientModule"] - FeatureModules["Feature-модули
HealthModule
AuthModule
PortfolioModule
SecuritiesModule
SharesModule
BondsModule
CandlesModule"] + FeatureModules["Feature-модули
HealthModule
AuthModule
PortfolioModule
SecuritiesModule
SharesModule
BondsModule
CandlesModule
TBankModule"] AppModule --> GlobalModules AppModule --> FeatureModules @@ -21,10 +21,12 @@ flowchart TB AuthModule["AuthModule"] PortfolioModule["PortfolioModule"] MarketModules["SecuritiesModule
SharesModule
BondsModule
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` diff --git a/apps/docs/docs/backend/portfolio.md b/apps/docs/docs/backend/portfolio.md index 8953965..a84803c 100644 --- a/apps/docs/docs/backend/portfolio.md +++ b/apps/docs/docs/backend/portfolio.md @@ -3,6 +3,12 @@ `PortfolioModule` позволяет пользователям создавать и вести виртуальные инвестиционные портфели для аналитики и отслеживания позиций. +## Ручные портфели и брокерские портфели + +`PortfolioModule` остаётся доменом ручных виртуальных портфелей. Брокерские счета T-Bank Invest +экспортируются отдельным `TBankModule` под `/api/v1/broker/*` и не сохраняются как записи +`Portfolio`. + ## Обзор - **Backend:** `PortfolioModule` (`apps/backend/src/modules/portfolio/`) diff --git a/apps/docs/docs/backend/tbank-invest.md b/apps/docs/docs/backend/tbank-invest.md new file mode 100644 index 0000000..a332568 --- /dev/null +++ b/apps/docs/docs/backend/tbank-invest.md @@ -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 +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 токенов и привязку каждого брокерского счёта к владельцу. diff --git a/apps/docs/sidebars.ts b/apps/docs/sidebars.ts index 2d11a82..41c75ae 100644 --- a/apps/docs/sidebars.ts +++ b/apps/docs/sidebars.ts @@ -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', ], }, ],