DRRK/CLAUDE.md
Andrey Epifancev b4d49bdab8 docs: описать проект в CLAUDE.md и ARCHITECTURE.md
- CLAUDE.md: что это, стек и ограничения, архитектура, команды, тесты
- ARCHITECTURE.md: карта модулей, границы импортов, открытые вопросы
- .claude/code-paths: app/, alembic/, tests/ для хука require-plan
- .gitignore: .claude/worktrees/

Refs #1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:51:22 +04:00

7.8 KiB
Raw Blame History

DRRK

Что это

Streamlit-сервис «Портфели ДРРК» (BIQ-38115) для ДРРК банка: заменяет два Excel-файла с VBA-макросами, в которых ведутся портфели облигаций UW (андеррайтинг) и SALES (продажи), плюс сводный отчёт по обоим. Главный сценарий: пользователь ведёт справочник бумаг, подтягивает котировки MOEX и курс CNY ЦБ, загружает выгрузку сделок QUIK (.xlsx) в портфель и вводит вручную то, чего нет в QUIK (купоны, амортизация, комиссия UW, корректировки). Расчёт финрезультата, фондирование и отчёты — следующие итерации (TODO(BIQ-38115) в коде).

Подробности: README.md (потоки, таблицы, переменные окружения), docs/project.md (решения D…, открытые вопросы Q…), docs/legacy_excel.md и docs/vba/ (как устроены текущие Excel-файлы).

Стек и ограничения

  • Python 3 (CI-образ streamlit-app-base:2.1.0), Streamlit 1.41, async SQLAlchemy 2.0 + asyncpg (прод, PostgreSQL) / aiosqlite (локально и в тестах), Alembic, pydantic-settings, requests, openpyxl. Тесты — pytest + pytest-asyncio.
  • Платформа — Раиса (k8s), авторизация Keycloak через raisa-streamlit-oauth. Этот пакет есть только во внутреннем зеркале: вне контура ставить requirements-dev.txt (без него), при LOCATION=local он не нужен.
  • Внешние данные (MOEX ISS, ЦБ) — только через шлюз Gravitee, у MOEX и ЦБ разные ключи. Внешние API вызываются только по кнопке или джобой; экраны читают БД.
  • Streamlit синхронный: любой доступ к БД из UI и джобы — только через run_db(coro) (app/db/base.py), движок с NullPool. Свой event loop/пул не заводить.
  • Деньги, цены, курсы — Decimal / Numeric, никогда float.
  • Ключи шлюза и заголовки запросов не логируются никогда (кроме X-Gravitee-Transaction-Id, D18).
  • Файлы заказчика (оригинал выгрузки QUIK, docs/excel/) — банковская тайна и ПДн: только локально, в git не добавлять. Тесты работают на обезличенной копии tests/fixtures/quik_sample_300326_anon.xlsx.
  • Где бизнес-решения нет — TODO(BIQ-38115) с номером вопроса Q… из docs/project.md; решения из спеки/обсуждения записывать туда как D….
  • Соглашения (конфиг, БД, Alembic, auth, линтеры, стиль тестов) повторяют проект SFM-parser (../sfm).

Архитектура

  • app/integrations/ — HTTP-клиенты к шлюзу (gateway.py: ключ, таймауты, повторы, журнал запросов) и разбор ответов MOEX/ЦБ в dataclass'ы. Без БД.
  • app/imports/quik.py — разбор выгрузки сделок QUIK (.xlsx). Чистая логика, без БД и сети.
  • app/db/ — движок, run_db(), типы колонок, ORM-модели (models/, реэкспорт в __init__.py).
  • app/repositories/ — async-доступ к таблицам, upsert через ON CONFLICT.
  • app/services/ — сценарии: бумаги, рыночные данные, курсы, загрузка QUIK, ручные операции, даты.
  • app/ui/pages/ — страницы Streamlit; main.py — auth + st.navigation.
  • app/jobs/update_market.py — джоба котировок и курса для расписания.
  • app/auth/, app/core/ — Keycloak/mock-авторизация, настройки, логирование.
  • Карта модулей и границы импортов — ARCHITECTURE.md.

Команды

  • pytest — весь набор (unit + smoke UI через AppTest), без сети и Postgres. Зелёным считается только весь набор.
  • ruff check . && black --check . && isort --check-only --profile black . — линтеры (они же в pre-commit).
  • python run_local.py — локальный запуск: LOCATION=local, миграции SQLite, streamlit run main.py.
  • alembic upgrade head / alembic revision -m "..." — миграции (стиль — alembic/versions/).
  • python -m app.jobs.update_market [--date YYYY-MM-DD] — джоба котировок и курса CNY.
  • DRRK_SMOKE_GATEWAY=1 pytest tests/smoke/test_gateway_smoke.py -v — smoke против реального шлюза (нужны ключи и доступ к тестовому контуру).
  • Окружение: python -m venv .venv && .venv/bin/pip install -r requirements-dev.txt. На macOS arm64 SQLAlchemy не тянет greenlet — доставить pip install greenlet, иначе тесты с БД падают с «the greenlet library is required».

Тесты

  • tests/unit/ — без сети и без Postgres; БД — временный SQLite со схемой из миграций (фикстура session_local в tests/conftest.py), внешние клиенты — фейки из tests/unit/fakes.py.
  • tests/smoke/ — UI через streamlit.testing.v1.AppTest; реальный шлюз — только по флагу DRRK_SMOKE_GATEWAY=1.
  • tests/fixtures/ — реальные ответы MOEX ISS / ЦБ байт в байт (см. .gitattributes) и обезличенная выгрузка QUIK. Новые ответы внешних API сохранять туда же, а не собирать руками.
  • test_anonymized_fixture_matches_original пропускается без локального оригинала выгрузки — это нормально.

Дальше

Следующие итерации — расчёт финрезультата, фондирование, сводный отчёт (см. TODO(BIQ-38115) и открытые вопросы Q… в docs/project.md). Многие из них упираются в неотвеченные Q — перед brainstorming сверяться со списком.

OpenSpec и superpowers

  • Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать.
  • Когда brainstorming классифицировал задачу как architectural, структуру change создать через openspec new change <id> (или /opsx:propose <id>), а содержимое proposal.md, specs/ и design.md написать по итогам brainstorming. tasks.md из OpenSpec не заполнять — задачи живут в плане superpowers.
  • /opsx:apply не использовать: исполнение идёт через writing-plans и SDD/executing-plans.
  • openspec archive <id> — после merge PR, как часть чеклиста закрытия.
  • Хук require-plan защищает каталоги из .claude/code-paths (app/, alembic/, tests/). Файлы в корне (main.py, run_local.py) хук не защищает — правки в них всё равно только по плану или с .bounded.