docs: состояние фазы 2 после аллокации, API и экранов портфеля
Плюс два ограничения, которые стоили времени: AssetClass нельзя выставлять наружу енумом из-за значения index, и сектор не заполнен ни у одного инструмента — его нет в ответе GetInstrumentBy, нужны потиповые Shares/Bonds.
This commit is contained in:
@@ -49,7 +49,10 @@ just revision "msg" # новая alembic-миграция из изме
|
|||||||
- **Аналитика читает только `event.status = confirmed`.** Snapshot-таблицы — для сверки.
|
- **Аналитика читает только `event.status = confirmed`.** Snapshot-таблицы — для сверки.
|
||||||
- **Сырые данные (`raw_*`) append-only и идемпотентны** по стабильному id источника.
|
- **Сырые данные (`raw_*`) append-only и идемпотентны** по стабильному id источника.
|
||||||
- `openapi/openapi.json` и `app/packages/api_client` — генерируются, но коммитятся;
|
- `openapi/openapi.json` и `app/packages/api_client` — генерируются, но коммитятся;
|
||||||
после изменения роутов запускай `just gen-client`.
|
после изменения роутов запускай `just gen-client`. В схемах API не выставляй наружу
|
||||||
|
`AssetClass`: одно из его значений — `index`, а Dart-енум не может иметь члена с таким
|
||||||
|
именем (конфликт с `Enum.index`), и сгенерированный клиент перестаёт компилироваться.
|
||||||
|
Стабильные ключи (`asset_class`, `price_status`, `bucket`, `period`) идут строками.
|
||||||
- Секреты только в `.env` (в `.gitignore`). Реальные отчёты брокеров — в
|
- Секреты только в `.env` (в `.gitignore`). Реальные отчёты брокеров — в
|
||||||
`backend/tests/fixtures/reports/raw/` (в `.gitignore`); в git только обезличенные фикстуры.
|
`backend/tests/fixtures/reports/raw/` (в `.gitignore`); в git только обезличенные фикстуры.
|
||||||
- Коммиты — Conventional Commits на русском (`feat(ledger): …`), автор — только пользователь.
|
- Коммиты — Conventional Commits на русском (`feat(ledger): …`), автор — только пользователь.
|
||||||
@@ -68,9 +71,14 @@ just revision "msg" # новая alembic-миграция из изме
|
|||||||
- `analytics/valuation` — дневная серия стоимости и холдинги по scope (`all`, `account:<id>`,
|
- `analytics/valuation` — дневная серия стоимости и холдинги по scope (`all`, `account:<id>`,
|
||||||
`portfolio:<id>`), сверка со снапшотами брокера; `analytics/returns` — XIRR и TWR по
|
`portfolio:<id>`), сверка со снапшотами брокера; `analytics/returns` — XIRR и TWR по
|
||||||
периодам (`1m 3m 6m ytd 1y 3y all`) плюс XIRR на инструмент; `pricing/prices.py` — цена на
|
периодам (`1m 3m 6m ytd 1y 3y all`) плюс XIRR на инструмент; `pricing/prices.py` — цена на
|
||||||
дату с протяжкой вперёд и порогом устаревания 10 дн.
|
дату с протяжкой вперёд и порогом устаревания 10 дн.;
|
||||||
|
- `analytics/allocation` — разрезы по классу актива, сектору, стране и валюте; каждый
|
||||||
|
покрывает один и тот же итог (бумаги + кэш), поэтому веса любого из них дают единицу;
|
||||||
|
- API `/analytics/*`, `/events`, `/instruments/{id}` и экраны «Портфель» (позиции +
|
||||||
|
аллокация) и карточка инструмента.
|
||||||
|
|
||||||
Осталось: `analytics/allocation`, корпоративные действия, API и экраны позиций.
|
Осталось: `ledger/matching.py` + `account_link`, корпоративные действия, бэкфилл истории
|
||||||
|
цен MOEX.
|
||||||
|
|
||||||
Известные расхождения derived vs брокерский снапшот по позициям (5 шт, не ошибки разбора):
|
Известные расхождения derived vs брокерский снапшот по позициям (5 шт, не ошибки разбора):
|
||||||
редомициляция FIVE→X5, MGNT, HEAD, внебиржевая SIBN6P4. Без цен два инструмента — `SIBN6P4`
|
редомициляция FIVE→X5, MGNT, HEAD, внебиржевая SIBN6P4. Без цен два инструмента — `SIBN6P4`
|
||||||
@@ -78,4 +86,6 @@ just revision "msg" # новая alembic-миграция из изме
|
|||||||
счетах 46, 48 и 52 (+6313, +56447, +146 ₽) — это неполнота леджера T-Invest, а не оценки:
|
счетах 46, 48 и 52 (+6313, +56447, +146 ₽) — это неполнота леджера T-Invest, а не оценки:
|
||||||
счёт 51 сходится точно. История цен тонкая (большинство облигаций — только с апреля 2026,
|
счёт 51 сходится точно. История цен тонкая (большинство облигаций — только с апреля 2026,
|
||||||
TBRU@ — с 14.09.2026), поэтому TWR пропускает 32 дня и сообщает об этом в
|
TBRU@ — с 14.09.2026), поэтому TWR пропускает 32 дня и сообщает об этом в
|
||||||
`metric_data_quality`. План фаз — в `docs/ai/plan.md`.
|
`metric_data_quality`. Сектор не заполнен ни у одного инструмента: `sources/tinvest` читает
|
||||||
|
`GetInstrumentBy`, в ответе которого этого поля нет — нужны потиповые `Shares`/`Bonds`.
|
||||||
|
План фаз — в `docs/ai/plan.md`.
|
||||||
|
|||||||
+11
-4
@@ -50,8 +50,8 @@
|
|||||||
|
|
||||||
## Аналитика (`analytics/`, `pricing/{fx,prices}.py`, `metrics/refresh.py`)
|
## Аналитика (`analytics/`, `pricing/{fx,prices}.py`, `metrics/refresh.py`)
|
||||||
Шаги регистрируются в `analytics/__init__.py` и выполняются `refresh_all` по порядку:
|
Шаги регистрируются в `analytics/__init__.py` и выполняются `refresh_all` по порядку:
|
||||||
`fx → classify → lots → valuation → returns → networth → cashflow → spending → runway →
|
`fx → classify → lots → valuation → returns → allocation → networth → cashflow → spending →
|
||||||
quality` (фаза 2 вставит income и allocation после returns). Запуск:
|
runway → quality` (фаза 4 вставит income и rebalance после allocation). Запуск:
|
||||||
worker после синка с `changed=True`, `fintracker metrics refresh`, `POST /metrics/refresh`,
|
worker после синка с `changed=True`, `fintracker metrics refresh`, `POST /metrics/refresh`,
|
||||||
`POST /rules/apply`. Каждая `metric_*` таблица пересобирается целиком. Net worth считается
|
`POST /rules/apply`. Каждая `metric_*` таблица пересобирается целиком. Net worth считается
|
||||||
от текущего `account.balance` назад по транзакциям; конвертация — `FxTable` по дате
|
от текущего `account.balance` назад по транзакциям; конвертация — `FxTable` по дате
|
||||||
@@ -63,7 +63,10 @@ worker после синка с `changed=True`, `fintracker metrics refresh`, `P
|
|||||||
`portfolio:<id>`. `returns` читает только `metric_portfolio_value_daily`: XIRR по внешним
|
`portfolio:<id>`. `returns` читает только `metric_portfolio_value_daily`: XIRR по внешним
|
||||||
потокам и терминальной стоимости, TWR цепочкой `V_t / (V_{t-1} + F_t)`. Покупка бумаги без
|
потокам и терминальной стоимости, TWR цепочкой `V_t / (V_{t-1} + F_t)`. Покупка бумаги без
|
||||||
цены трактуется как вывод из оцениваемого портфеля (`unvalued_flow_rub`), иначе дыра в
|
цены трактуется как вывод из оцениваемого портфеля (`unvalued_flow_rub`), иначе дыра в
|
||||||
данных читалась бы как обвал.
|
данных читалась бы как обвал. `allocation` режет тот же итог четырьмя способами (класс,
|
||||||
|
сектор, страна, валюта), включая кэш в каждый разрез: четыре диаграммы одного портфеля
|
||||||
|
обязаны быть одного размера. Бакет — стабильный ключ (`cash`, `unknown`, код страны), язык
|
||||||
|
живёт в клиенте.
|
||||||
|
|
||||||
Деньги в JSON — `Money` (`api/schemas/common.py`), всегда позиционная строка.
|
Деньги в JSON — `Money` (`api/schemas/common.py`), всегда позиционная строка.
|
||||||
|
|
||||||
@@ -73,7 +76,11 @@ worker после синка с `changed=True`, `fintracker metrics refresh`, `P
|
|||||||
- Ошибки — RFC 7807 (`application/problem+json`), см. `api/errors.py`.
|
- Ошибки — RFC 7807 (`application/problem+json`), см. `api/errors.py`.
|
||||||
- Auth: `POST /auth/login` → access JWT (1 ч) + refresh (30 д, хранится хэшем, ротируется);
|
- Auth: `POST /auth/login` → access JWT (1 ч) + refresh (30 д, хранится хэшем, ротируется);
|
||||||
login rate-limited в памяти (`api/ratelimit.py`).
|
login rate-limited в памяти (`api/ratelimit.py`).
|
||||||
- Деньги в JSON — строки.
|
- Деньги в JSON — строки; енумы моделей наружу отдаются не всегда: `asset_class` идёт
|
||||||
|
строкой, потому что значение `index` ломает генератор Dart-клиента.
|
||||||
|
- Аналитика инвестиций читает только `metric_*` — на запрос ничего не считается.
|
||||||
|
Параметр `scope` (`all | account:<id> | portfolio:<id>`) резолвится через
|
||||||
|
`analytics/valuation.account_scopes`, неизвестный scope — 404, а не пустой график.
|
||||||
|
|
||||||
## Клиент (`app/`)
|
## Клиент (`app/`)
|
||||||
Flutter, Riverpod, go_router, fl_chart; клиент `app/packages/api_client` генерируется
|
Flutter, Riverpod, go_router, fl_chart; клиент `app/packages/api_client` генерируется
|
||||||
|
|||||||
Reference in New Issue
Block a user