3.4 KiB
Raw Blame History

Portfolio Enricher Optimization

Date: 2026-06-14 Status: Approved Author: AI Agent

Problem

GET /api/v1/portfolios/1 с 104 позициями (90 облигаций + 14 акций) выполняется ~29 секунд из-за 298 последовательных HTTP-запросов к MOEX ISS через rate limiter (10 req/s).

Root Cause

Per-position enrichment в PortfolioService.enrichPositions() генерирует:

Шаг Вызовов Метод
shortName 104 getSecurityDescriptionизбыточно
Акции (14) 14 getShareMarketData
Облигации (90) 90 getBondData
Облигации (90) 90 getBondMarketDataдублирует endpoint
Total 298

Две ключевые проблемы:

  1. getBondData и getBondMarketData вызывают один и тот же MOEX endpoint, но парсят разные таблицы ответа
  2. getSecurityDescription для shortName — избыточен: shortName уже доступен в market data ответах
  3. Каждый secid запрашивается отдельно, хотя MOEX ISS поддерживает batch через ?securities= параметр

Solution

1. Merge bond data calls

Новый метод getBondDataCombined(secid) делает один запрос к MOEX и парсит обе таблицы (securities + marketdata), возвращая объединённый результат.

Profit: 180 → 90 запросов для bonds

2. Remove redundant getSecurityDescription

  • getShareMarketData response уже содержит SHORTNAME в securities таблице — добавим поле shortName в тип MoexShareMarketData
  • getBondDataCombined уже возвращает shortName из securities таблицы

Profit: 104 → 0 запросов

3. Batch requests by market

Группируем secid по типу (share/bond) и делаем 2 batch-запроса вместо индивидуальных:

  • GET /engines/stock/markets/shares/securities.json?securities=SBER,VTBR,...&boards=TQBR
  • GET /engines/stock/markets/bonds/securities.json?securities=RU000A...,SU26240...&boards=TQCB

Новые методы: getShareMarketDataBatch(secids), getBondMarketDataBatch(secids).

Profit: 104 → 2 запроса

Metrics

Scenario API calls Est. time (10 req/s)
Before 298 ~29.8s
After 2 ~0.3s

Files Changed

File Change
apps/backend/src/modules/moex-client/moex-client.types.ts Add shortName to MoexShareMarketData
apps/backend/src/modules/moex-client/moex-client.service.ts +getShareMarketDataBatch, +getBondMarketDataBatch, merge bond methods, add shortName to share response
apps/backend/src/modules/portfolio/portfolio.service.ts Rewrite enrichPositions — batch, no redundant calls

Risks and Mitigations

  • MOEX ISS rate limiting: Batch reduces requests, lowering risk. Circuit breaker stays intact.
  • Cache invalidation: Batch results cached per market, not per secid. TTL unchanged (900s market data).
  • Empty batches: If portfolio has no shares or no bonds, skip market entirely. No unnecessary calls.
  • Long secid lists: URL length may exceed limits. Mitigation: split batches if secids > 50 per call (monitor and split if needed).