moex-vibe/docs/superpowers/specs/2026-06-13-moex-vibe-design.md

16 KiB
Raw Permalink Blame History

MoexVibe — MVP Design Specification

Date: 2026-06-13 Status: Draft Author: AI Assistant (Staff+ Architect)


1. Product Requirements Document (PRD)

1.1 Product Vision

Веб-приложение для анализа ценных бумаг Московской биржи (MOEX). Позволяет искать акции и облигации, просматривать их текущие параметры, доходность, дивиденды/купоны, историю торгов и графики цены.

1.2 Target Audience

Частные инвесторы, интересующиеся российским фондовым рынком. B2C, read-only сервис без аутентификации.

1.3 MVP Scope

In Scope Out of Scope
Поиск по инструментам (акции + облигации) Аутентификация / пользователи
Карточка акции (цена, капитализация, дивиденды, график) Портфели и избранное
Карточка облигации (ISIN, купон, НКД, YTM, дюрация, график) Сравнение инструментов
Часовые и дневные свечи (1 год истории) Финансовая отчётность (МСФО/РСБУ)
Docker-ready деплой Фьючерсы, опционы, валютный рынок
Документация (ADR, API, архитектура) Real-time данные (WebSocket)
Экспорт данных
Мобильные приложения

1.4 User Stories

  • US-001: Пользователь вводит текст в поиск и видит подходящие акции и облигации
  • US-002: Пользователь переходит на карточку акции, видит текущую цену, изменение, капитализацию
  • US-003: Пользователь видит историю дивидендных выплат по акции
  • US-004: Пользователь видит график цены (дневные и часовые свечи) за последний год
  • US-005: Пользователь переходит на карточку облигации, видит ISIN, номинал, купон, дату погашения
  • US-006: Пользователь видит НКД, доходность к погашению, дюрацию
  • US-007: Пользователь видит график цены облигации за последний год

1.5 Non-Functional Requirements

  • Максимальное время ответа API: < 500ms (p95) при попадании в кеш
  • Доступность: бэкенд stateless, готов к масштабированию
  • Задержка данных: 15 минут (бесплатный MOEX ISS)
  • Все ответы API кешируются на бэкенде

2. Domain Model

Security (abstract base)
├── secid: string          — "SBER"
├── isin: string           — "RU0009029540"
├── name: string           — полное наименование
├── shortName: string      — краткое наименование
├── latName: string | null
├── listLevel: 1 | 2 | 3   — уровень листинга
├── issueSize: number       — объём выпуска
├── faceValue: number       — номинал
├── faceUnit: string        — "RUB" / "USD" / "SUR"
├── issueDate: string       — ISO date
├── isQualifiedInvestors: boolean
├── morningSession: boolean
├── eveningSession: boolean
│
├── Stock
│   ├── type: "common_share" | "preferred_share"
│   ├── marketData: StockMarketData
│   │   ├── price: number
│   │   ├── change: number
│   │   ├── changePercent: number
│   │   ├── open: number
│   │   ├── high: number
│   │   ├── low: number
│   │   ├── volume: number
│   │   ├── value: number
│   │   └── issueCapitalization: number
│   └── dividends: Dividend[]
│       ├── registryCloseDate: string (ISO date)
│       ├── value: number (RUB per share)
│       └── currency: string
│
└── Bond
    ├── matDate: string            — дата погашения
    ├── couponValue: number         — размер купона (RUB)
    ├── couponPercent: number|null  — ставка купона (%)
    ├── couponPeriod: number        — дней между купонами
    ├── nextCoupon: string (ISO date)
    ├── accruedInt: number          — НКД
    ├── bondType: string            — "Фикс" / "Флоатер" / "Линкер" / etc
    ├── bondSubType: string         — "До погашения" / "До оферты"
    ├── offerDate: string | null
    ├── buybackDate: string | null
    ├── marketData: BondMarketData
    │   ├── price: number           — % от номинала
    │   ├── yieldToMaturity: number | null
    │   ├── duration: number | null
    │   ├── open: number
    │   ├── high: number | null
    │   ├── low: number | null
    │   └── volume: number
    └── history: BondHistoryEntry[]
        ├── date: string
        ├── closePrice: number
        ├── yieldClose: number
        └── duration: number

Candle
├── open: number
├── high: number
├── low: number
├── close: number
├── volume: number
├── value: number
├── begin: string (ISO datetime)
└── end: string (ISO datetime)

SearchResult
├── secid: string
├── isin: string
├── shortName: string
├── type: "share" | "bond"
├── listLevel: number
├── currency: string | null
└── price: number | null

3. Architecture

┌──────────────┐     ┌─────────────────────────────────────┐     ┌──────────────┐
│   Browser    │────▶│          NestJS Backend              │────▶│   MOEX ISS   │
│  (React SPA) │◀────│  (1 instance, stateless)             │◀────│   (HTTP)     │
└──────────────┘     │                                     │     └──────────────┘
                     │  ┌─────────────────────────────────┐ │
                     │  │  Core Modules                   │ │
                     │  │  ┌──────────┐ ┌───────────────┐ │ │
                     │  │  │ Search   │ │  SharesModule │ │ │
                     │  │  │ Module   │ │  (stocks)     │ │ │
                     │  │  └──────────┘ └───────────────┘ │ │
                     │  │  ┌──────────┐ ┌───────────────┐ │ │
                     │  │  │ Bonds    │ │  Candles      │ │ │
                     │  │  │ Module   │ │  Module       │ │ │
                     │  │  └──────────┘ └───────────────┘ │ │
                     │  └─────────────────────────────────┘ │
                     │  ┌─────────────────────────────────┐ │
                     │  │  Shared Infrastructure          │ │
                     │  │  ┌──────────┐ ┌───────────────┐ │ │
                     │  │  │ MOEX    │ │  Cache        │ │ │
                     │  │  │ Client  │ │  Manager      │ │ │
                     │  │  │(rate-ltd│ │  (in-memory)  │ │ │
                     │  │  │ circuit │ │               │ │ │
                     │  │  │breaker) │ │               │ │ │
                     │  │  └──────────┘ └───────────────┘ │ │
                     │  └─────────────────────────────────┘ │
                     └─────────────────────────────────────┘

3.1 Caching Strategy

Data Type Backend TTL Frontend staleTime Notes
MarketData 900s (15m) 900s Совпадает с задержкой MOEX
History 3600s (1h) 3600s Обновляется раз в день после торгов
Candles 3600s (1h) 3600s Дневные свечи не меняются intraday
Security spec 86400s (1d) 86400s Редко меняется
Search results 3600s (1h) 3600s
Dividends 86400s (1d) 86400s

3.2 Error Handling Strategy

  • Все MOEX-ошибки маппятся в нормализованный ErrorResponse
  • При пустых данных (выходные, праздники) — 200 с null значениями, не 404
  • Circuit breaker: при 5+ последовательных ошибках MOEX — пауза 30s
  • Graceful degradation: если MOEX недоступен, возвращать последние кешированные данные

3.3 Rate Limiting

  • MOEX Client: очередь запросов ~10 req/s (конфигурируется)
  • При превышении — автоматическое ожидание в очереди
  • Отсутствие внешнего rate limiter на уровне NestJS (приложение публичное, read-only)

4. API Endpoints

Method Path Description
GET /api/v1/health Healthcheck
GET /api/v1/securities/search Поиск по инструментам
GET /api/v1/securities/shares/:secid Спецификация акции
GET /api/v1/securities/shares/:secid/marketdata Рыночные данные акции
GET /api/v1/securities/shares/:secid/candles Свечи (1h/24h)
GET /api/v1/securities/shares/:secid/history Дневная история
GET /api/v1/securities/shares/:secid/dividends Дивиденды
GET /api/v1/securities/bonds/:secid Спецификация облигации
GET /api/v1/securities/bonds/:secid/marketdata Рыночные данные облигации
GET /api/v1/securities/bonds/:secid/candles Свечи (1h/24h)
GET /api/v1/securities/bonds/:secid/history Дневная история

5. Repo Structure

moex-vibe/
├── apps/
│   ├── backend/
│   │   ├── src/
│   │   │   ├── main.ts
│   │   │   ├── app.module.ts
│   │   │   ├── common/
│   │   │   │   ├── dto/
│   │   │   │   │   ├── api-response.dto.ts
│   │   │   │   │   └── pagination.dto.ts
│   │   │   │   ├── filters/
│   │   │   │   │   └── http-exception.filter.ts
│   │   │   │   ├── interceptors/
│   │   │   │   │   ├── logging.interceptor.ts
│   │   │   │   │   └── transform.interceptor.ts
│   │   │   │   └── middleware/
│   │   │   │       └── request-logging.middleware.ts
│   │   │   ├── config/
│   │   │   │   └── configuration.ts
│   │   │   └── modules/
│   │   │       ├── moex-client/
│   │   │       ├── cache/
│   │   │       ├── securities/
│   │   │       ├── shares/
│   │   │       ├── bonds/
│   │   │       └── health/
│   │   ├── test/
│   │   └── package.json
│   └── frontend/
│       ├── src/
│       │   ├── api/         # openapi-typescript generated
│       │   │   ├── types.ts
│       │   │   └── client.ts
│       │   ├── hooks/
│       │   │   ├── useStock.ts
│       │   │   ├── useBond.ts
│       │   │   ├── useSearch.ts
│       │   │   ├── useCandles.ts
│       │   │   └── useDividends.ts
│       │   ├── pages/
│       │   │   ├── HomePage.tsx
│       │   │   ├── StockPage.tsx
│       │   │   └── BondPage.tsx
│       │   ├── components/
│       │   │   ├── Layout/
│       │   │   ├── SearchBar/
│       │   │   ├── SecurityCard/
│       │   │   ├── PriceChart/
│       │   │   ├── StockDetails/
│       │   │   └── BondDetails/
│       │   ├── routes.tsx
│       │   └── main.tsx
│       └── package.json
├── docs/
│   ├── superpowers/specs/
│   ├── architecture/
│   │   ├── adr/
│   │   ├── diagrams/
│   │   └── domain-model.md
│   ├── openapi/
│   │   └── openapi.yaml
│   └── website/        (Docusaurus — post-MVP)
├── package.json
├── tsconfig.base.json
└── .gitignore

6. Sprint Plan

Sprint 1 — Backend Foundation

  • NestJS project init + npm workspaces
  • ConfigurationModule, Logging, Global filters
  • MoexClientModule (rate-limited HTTP client)
  • CacheModule (cache-manager in-memory)
  • HealthController
  • ESLint, Prettier, tsconfig

Sprint 2 — Securities API

  • SecuritiesModule (search)
  • SharesModule (spec + marketdata + dividends)
  • OpenAPI decorators
  • Unit tests

Sprint 3 — Bonds + History

  • BondsModule (spec + marketdata)
  • CandlesModule (shares + bonds)
  • HistoryModule
  • OpenAPI decorators
  • Unit tests

Sprint 4 — Frontend Foundation

  • Vite + React + TypeScript init
  • openapi-typescript codegen
  • TanStack Query + React Router
  • Layout, SearchBar, HomePage

Sprint 5 — Frontend Details

  • StockPage (price block, dividends table, chart)
  • BondPage (bond details, chart)
  • PriceChart component (lightweight-charts)
  • Loading/error states

Sprint 6 — Docs + Infrastructure

  • ADRs, architecture docs
  • OpenAPI spec
  • Dockerfile + docker-compose
  • README

7. Risks

Risk Impact Mitigation
MOEX ISS API changes High MoexClient abstraction layer
Rate limiting by MOEX Medium p-queue + circuit breaker
Empty data on holidays/weekends Low Graceful null handling
Large search result sets Low Server-side limit + frontend debounce

8. Post-MVP Roadmap

  1. Финансовая отчётность (MOEX CCI — IFRS/RAS)
  2. Аутентификация, портфели, избранное
  3. Сравнение инструментов (multi-chart)
  4. Фьючерсы и опционы
  5. Экспорт (CSV, PDF)
  6. WebSocket для real-time данных
  7. Redis для масштабирования