[2026-06-25] taiga-vault: family/how-to/htpc-emulators-setup.md family/how-to/htpc-gaming-plans.md family/how-to/kraken-access.md family/how-to/openmediavault-rpi5.md family/how-to/time-machine.md family/how-to/wireguard-vpn.md personal/documents/todo-list.md personal/plans/extract-stable-prompt-blocks.md personal/plans/hermes-whale-system-prompt.md personal/plans/thread-scoped-memory.md
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
# Импорт банковских выписок за последний год
|
||||
|
||||
**Создано:** 2026-06-23
|
||||
**Цель:** Получить все транзакции за последний год (середина 2025 — июнь 2026) в Budget App, автоматизируя импорт банковских выписок как можно полнее.
|
||||
|
||||
## Проблема
|
||||
|
||||
В `Budget.xlsx` данные заканчиваются в **середине 2025** (май-июнь 2025, в зависимости от счёта). Последние ~12 месяцев транзакций не внесены в Excel. Вручную вспомнить каждую трату за год — нереалистично.
|
||||
|
||||
## Существующий инструмент: budget-bank-statement-converter
|
||||
|
||||
**Путь:** `~/Developer/budget-bank-statement-converter/`
|
||||
**Язык:** Swift (macOS command-line tool)
|
||||
**Формат вывода:** CSV с колонками `[дата, сумма, дебет, кредит, категория, комментарий, курс]` — совпадает с форматом Excel.
|
||||
|
||||
### Поддерживаемые банки
|
||||
|
||||
| Банк | Формат входа | Конфиг | Статус |
|
||||
|------|-------------|--------|--------|
|
||||
| Demir | CSV (из PDF → Adobe Extract → CSV) | `demir-config.json` | ✅ Работает |
|
||||
| Сбер | CSV (выгрузка из СберБизнес) | `sber-config.json` | ✅ Работает |
|
||||
| Тинькофф | CSV (выгрузка из Тинькофф) | `tinkoff-config.json` | ✅ Работает |
|
||||
| ВТБ | CSV (из PDF → Adobe Extract → CSV) | `vtb-config.json` | ✅ Работает |
|
||||
| Альфа | — | — | ❌ `fatalError("Alfa not implemented")` |
|
||||
|
||||
### Как работает
|
||||
|
||||
1. **PDF → CSV**: использует Adobe PDF Extract API (`pdfservices-api-credentials.json`) — загружает PDF, получает ZIP с CSV-таблицами.
|
||||
2. **CSV → формат App**: разбирает CSV, маппит категории через regex-конфиг, нормализует double-entry (дебет/кредит/конверсии).
|
||||
3. Если нужно — автоматически конкатенирует `fileoutpart0001.csv`… файлы.
|
||||
4. Использует OpenAI GPT-3.5-turbo для AI-категоризации (закомментировано, `aiMaxTokens = 80`).
|
||||
|
||||
### Что нужно для использования
|
||||
|
||||
- Xcode (для сборки Swift-проекта)
|
||||
- `OPENAI_API_KEY` в env (не обязательно, выключено)
|
||||
- `pdfservices-api-credentials.json` для Adobe Extract
|
||||
- JSON config для каждого банка: `Сбер config`, `Tinkoff config`, `Demir config`, `VTB config`
|
||||
|
||||
## План импорта
|
||||
|
||||
### Шаг 1: Получить выписки из банков
|
||||
|
||||
**Что нужно выгрузить за июнь 2025 — июнь 2026:**
|
||||
|
||||
| Счёт | Банк | Как получить выписку |
|
||||
|------|------|---------------------|
|
||||
| Нал RUB | Наличные | Ручной ввод (см. ниже про наличные) |
|
||||
| Нал KGS | Наличные | Ручной ввод |
|
||||
| Нал USD | Наличные | Ручной ввод |
|
||||
| Нал KZT | Наличные | Ручной ввод |
|
||||
| Demir ИП | Demir | CSV (интернет-банк/моб. приложение) |
|
||||
| Demir ИП USD | Demir | CSV (интернет-банк/моб. приложение) |
|
||||
| Demir KGS | Demir | CSV (интернет-банк/моб. приложение) |
|
||||
| Demir USD | Demir | CSV (интернет-банк/моб. приложение) |
|
||||
| Тинькофф Black | Тинькофф | CSV (выгрузка из Тинькофф) |
|
||||
| Тинькофф Кредитка | Тинькофф | CSV (выгрузка из Тинькофф) |
|
||||
| Сбер | Сбер | CSV (СберБизнес / PDF) |
|
||||
| Сбер Кредитка | Сбер | CSV (СберБизнес / PDF) |
|
||||
| Альфа | Альфа | CSV — но конвертер Альфу не поддерживает |
|
||||
| Альфа Кредитка | Альфа | — |
|
||||
| ВТБ | ВТБ | PDF → Adobe Extract → CSV |
|
||||
| ВТБ Кредитка | ВТБ | PDF → Adobe Extract → CSV |
|
||||
|
||||
### Шаг 2: Конвертировать выписки в CSV формата App
|
||||
|
||||
Запуск для каждого банка:
|
||||
```bash
|
||||
./budget-bank-statement-converter --bank <bank> --account <account> <input.csv>
|
||||
```
|
||||
|
||||
Выход: `_processed.csv` с колонками `дата, сумма, дебет, кредит, категория, комментарий, курс`.
|
||||
|
||||
### Шаг 3: Написать Python импортёр CSV → Budget App DB
|
||||
|
||||
Существующий `xlsx_import.py` читает из Excel. Нужен новый: `csv_bank_import.py`, который:
|
||||
|
||||
- Читает CSV в формате App (колонки из `csvHeaders` в `Common.swift`)
|
||||
- Привязывает `дебет`/`кредит` к существующим счетам в БД (по имени)
|
||||
- Маппит категории из CSV на существующие категории в БД (по имени подкатегории)
|
||||
- Игнорирует дубликаты (hash по `date + amount + source + dest + comment`)
|
||||
- Поддерживает несколько CSV-файлов за раз (много выписок)
|
||||
- Выводит отчёт: сколько добавлено, сколько пропущено (дубликаты), какие категории не найдены
|
||||
|
||||
### Шаг 4: Импортировать в Budget App
|
||||
|
||||
```bash
|
||||
cd ~/Developer/budget-app
|
||||
uv run python src/budget/importers/csv_bank_import.py <output1.csv> <output2.csv> ...
|
||||
```
|
||||
|
||||
### Шаг 5: Дописать недостающее в Swift-конвертере
|
||||
|
||||
- **AlfaToCSV**: реализовать парсер для Альфа-банка (CSV выгрузка из моб. банка/СберБизнес)
|
||||
- **AI-категоризация**: раскомментировать и обновить (GPT-3.5 → DeepSeek/local LLM?)
|
||||
|
||||
## Наличные расходы — проблема и решение
|
||||
|
||||
### Проблема
|
||||
Наличные траты не трекались последний год. У нас есть конечный остаток налички на руках сейчас, но нет истории по категориям.
|
||||
|
||||
### Подходы
|
||||
|
||||
#### A. Снять остаток наличных сейчас → счёт в БД (простой)
|
||||
- Посчитать физическую наличку сейчас → записать как `initial_balance` для `Нал RUB`, `Нал KGS`, `Нал USD`, `Нал KZT`.
|
||||
- Все траты наличными за год никогда не будут зафиксированы.
|
||||
- **Минус:** дыра в данных большого объёма (вероятно значительная часть расходов).
|
||||
|
||||
#### B. Экстраполяция по историческим трендам (средний)
|
||||
- Взять помесячные тренды наличных трат по категориям за 2023–первую половину 2025.
|
||||
- Экстраполировать на июнь 2025 — июнь 2026 с учётом сезонности.
|
||||
- Создать транзакции-плейсхолдеры с пометкой `import_id = 'cash_estimate'`.
|
||||
- **Минус:** неточность, может не отражать реальные изменения.
|
||||
|
||||
#### C. Ретроспектива через месяц-два + экстраполяция (предпочтительный)
|
||||
- **Сейчас:** начать трекать наличные расходы (вручную или через мобильный интерфейс Budget App).
|
||||
- **Через 1–2 месяца:** по собранным данным наличных трат вычислить реальные помесячные паттерны.
|
||||
- Экстраполировать на пропущенный год с этими паттернами.
|
||||
- **Плюс:** база для экстраполяции будет основана на реальных данных, а не на исторических.
|
||||
|
||||
#### D. None of the above — принять дыру
|
||||
- Сделать только безналичный импорт. Наличные начинаем трекать с сегодня.
|
||||
- В аналитике отмечать периоды как "без наличных".
|
||||
- **Плюс:** не надо ничего выдумывать.
|
||||
|
||||
### Рекомендация: C+D combined
|
||||
1. Трекать наличку вручную через UI Budget App начиная с сегодня.
|
||||
2. Через 2 месяца посчитать реальные тренды и решить, стоит ли экстраполировать на прошлый год.
|
||||
3. Если нет — просто принять дыру и жить с хорошей аналитикой начиная с 2026-06.
|
||||
|
||||
## Что уже реализовано в Budget App для импорта
|
||||
|
||||
- ✅ `xlsx_import.py` — полный импорт из Budget.xlsx (34k строк)
|
||||
- ✅ Все счета, категории, курсы, транзакции — в БД
|
||||
- ✅ Идемпотентный UPSERT для счетов и курсов
|
||||
- ✅ Транзакции добавляются обычным insert (без import_hash после фикса)
|
||||
|
||||
## Реализованный скрипт: bank_scraper
|
||||
|
||||
**Путь:** `~/Developer/budget-app/scripts/bank_scraper/`
|
||||
|
||||
Структура:
|
||||
```
|
||||
scripts/bank_scraper/
|
||||
├── __init__.py
|
||||
├── .gitignore # config.yaml + data/imports/ не коммитятся
|
||||
├── config.example.yaml # шаблон для копирования в config.yaml
|
||||
├── base_driver.py # base class BankDriver + load_config()
|
||||
├── orchestrator.py # entry point (н.п.)
|
||||
└── drivers/
|
||||
└── demir.py # Demir IB драйвер (н.п.)
|
||||
```
|
||||
|
||||
**Статус:** ✅ base + Demir driver написаны, Playwright установлен. **НО — Demir требует QR-логин через мобильное приложение**, не логин/пароль на сайте.
|
||||
|
||||
## Реальность Demir IB
|
||||
|
||||
Сайт `93.171.215.109` (и `apps.demirbank.kg/ib/`) — **Flutter web SPA** с QR-аутентификацией. Нет формы логина с паролем — нужно сканировать QR мобильным приложением Demir.
|
||||
|
||||
**Варианты решения:**
|
||||
|
||||
### A. Продолжить с Playwright + session persistence
|
||||
- Один раз залогиниться руками (QR → моб. приложение)
|
||||
- Сохранить session cookies/storage в persistent context
|
||||
- Дальше переиспользовать сессию для выгрузок (пока не протухнет)
|
||||
- **Плюс:** минимум кода
|
||||
- **Минус:** сессия рано или поздно протухнет, нужен ручной ре-логин
|
||||
|
||||
### B. Appium / ADB — эмуляция мобильного приложения
|
||||
- Демонстратор Android/iOS эмулятора с мобильным приложением Demir
|
||||
- Appium для UI automation внутри приложения
|
||||
- **Плюс:** полный контроль
|
||||
- **Минус:** сложно, накладно
|
||||
|
||||
### C. Заменить Demir на первый банк с логином/паролем
|
||||
- Тинькофф имеет API для разработчиков (OAuth)
|
||||
- Сбер — есть API SberBusinessAPI (хотя для юрлиц)
|
||||
- Можно начать с Тинькофф: Tinkoff API → выписка без браузера
|
||||
- **Плюс:** самый простой tech-wise
|
||||
- **Минус:** Demir пока под вопросом
|
||||
|
||||
### D. Парсить CSV выписки, которые уже есть в mobile/email
|
||||
- Возможно Demir присылает выписки на email
|
||||
- Или можно скачать через мобильное приложение → экспорт → AirDrop/email себе
|
||||
- Это полу-ручной подход (но быстрее чем QR scraping)
|
||||
|
||||
## Решение
|
||||
|
||||
**Рекомендация: A + D**
|
||||
1. Самый ценный банк — **Тинькофф** (есть API) — начинаем с него
|
||||
2. Demir — разово выгрузить через мобильное приложение (Export CSV/email)
|
||||
3. Если сессия Demir долго живёт — Playwright persistent context отработает
|
||||
|
||||
### Новый порядок разработки
|
||||
|
||||
1. ✅ Demir driver (написан, но упирается в QR)
|
||||
2. **Tinkoff API driver** — следующий приоритет (без браузера, REST API)
|
||||
3. **Сбер / ВТБ / Альфа** — Playwright или Tinkoff-style API
|
||||
4. **Parse Demir CSV** — Python-версия DemirToCSV для уже скачанных файлов
|
||||
|
||||
## Файл вывода Swift-конвертера
|
||||
|
||||
```
|
||||
csvHeaders = ["дата", "сумма", "дебет", "кредит", "категория", "комментарий", "курс"]
|
||||
```
|
||||
|
||||
- `дата` — `dd.MM.yyyy HH:mm` (формат EUR)
|
||||
- `сумма` — строка с суммой (±знак)
|
||||
- `дебет` — имя счёта-источника (пусто = доход извне)
|
||||
- `кредит` — имя счёта-получателя (пусто = расход вовне)
|
||||
- `категория` — имя подкатегории
|
||||
- `комментарий` — очищенный текст
|
||||
- `курс` — кросс-курс при внутреннем переводе между валютами
|
||||
|
||||
## Автоматизация выгрузки выписок из банков
|
||||
|
||||
### 1. Browser automation libraries (CV-driven)
|
||||
|
||||
| Библиотека | Язык | Браузеры | CV | 2FA/SMS |
|
||||
|-----------|------|----------|----|---------|
|
||||
| **Playwright** (MS) | Python, JS, Java, .NET ⭐ | Chromium, Firefox, WebKit | Есть (locator screenshots) | `page.wait_for_selector` на поле ввода кода |
|
||||
| **Puppeteer** (Google) | JS (Python через pyppeteer) | Chromium | Есть | — |
|
||||
| **Selenium** | Python, Java, JS и др. | Все major | Через сторонние утилиты | — |
|
||||
|
||||
**Рекомендация: Playwright Python** — де-факто стандарт в 2025, cross-browser, async, видит элементы даже в SPA, встроенные ожидания. Подходит и для РФ-банков (Сбер, Тинькофф, Альфа-клик — все на SPA).
|
||||
|
||||
### 2. Готовые решения на GitHub
|
||||
|
||||
**AploBankParsers** ([github.com/Zaurrex1/AploBankParsers](https://github.com/Zaurrex1/AploBankParsers)):
|
||||
- Парсер выписок **СберБизнес** (production-ready) — читает xlsx/сsv из уже выгруженного файла
|
||||
- Заглушки для Альфа, ВТБ, Тинькофф
|
||||
- Это парсер **уже скачанных файлов**, не скрапер
|
||||
|
||||
**bank_scrapers** ([github.com/eebette/bank_scrapers](https://github.com/eebette/bank_scrapers)):
|
||||
- Playwright-based для scraping bank websites
|
||||
- Generic, не специфичен под РФ-банки
|
||||
|
||||
**Sber API** — официальный REST API Сбера:
|
||||
- `developers.sber.ru/docs/ru/sber-api/specifications/statement/transactions`
|
||||
- Получение выписки по счёту за 5 лет
|
||||
- **Требует** корпоративного доступа (SberBusinessAPI / ДБО), не подойдёт для личного СберБанк
|
||||
|
||||
**Готового решения "под ключ" для РФ-банков** (Playwright → bank login → 2FA → CSV выписка) **нет** в открытом доступе. Каждый банк — свой уникальный UI и flow. Придётся писать самим.
|
||||
|
||||
### 3. Архитектура скрипта
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ Telegram Bot (Hermes/кит) │ ← запрашивает SMS-код
|
||||
├─────────────────────────────────┤
|
||||
│ Orchestrator (Python) │ ← запускает по крону / кнопке
|
||||
│ ┌─────────────────────────┐ │
|
||||
│ │ Playwright browser │ │ ← drives bank login page
|
||||
│ │ - headless=false │ │ (visible для отладки)
|
||||
│ │ - persistent context │ │ (сессия не слетает)
|
||||
│ └─────────────────────────┘ │
|
||||
│ ┌─────────────────────────┐ │
|
||||
│ │ Bank drivers: │ │
|
||||
│ │ - tinkoff.py │ │
|
||||
│ │ - sber.py │ │
|
||||
│ │ - alfa.py │ │
|
||||
│ │ - demir.py │ │
|
||||
│ │ - vtb.py │ │
|
||||
│ └─────────────────────────┘ │
|
||||
│ ┌─────────────────────────┐ │
|
||||
│ │ Output: CSV в формате │ │
|
||||
│ │ budget-bank-statement- │ │
|
||||
│ │ converter │ │
|
||||
│ └─────────────────────────┘ │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4. Flow для каждого банка
|
||||
|
||||
```
|
||||
1. Запустить headless Playwright (или visible=False для отладки)
|
||||
2. Открыть страницу логина банка
|
||||
3. Ввести credentials (из конфига, НЕ скрипта)
|
||||
4. Если запрошен SMS-код:
|
||||
→ отправить в Telegram: "Код из смс для {bank}:"
|
||||
→ ждать ответа (polling/async)
|
||||
→ ввести полученный код
|
||||
5. Дождаться загрузки дашборда
|
||||
6. Перейти на страницу выписок/истории
|
||||
7. Указать период: 2025-06-01 — 2026-06-23
|
||||
8. Скачать CSV/Excel
|
||||
9. Сохранить в ~/Developer/budget-app/data/imports/{bank}/{date}.csv
|
||||
10. Конвертировать через budget-bank-statement-converter (или Python-версию)
|
||||
11. Импортировать в БД
|
||||
12. Закрыть браузер
|
||||
```
|
||||
|
||||
### 5. Обработка SMS-кодов (Telegram)
|
||||
|
||||
Скрипт не должен хранить сессию банка, каждый запуск — новая авторизация.
|
||||
|
||||
**Варианты:**
|
||||
1. **Telegram Bot (inline keyboard)**: скрипт ждёт сообщение, когда нужен код — присылает кнопку "Отправить код для {bank}", пользователь вводит → скрипт вставляет
|
||||
2. **Hermes-агент**: крон-джоб спрашивает в Telegram нужный код, ждёт ответа через webhook
|
||||
3. **Простой stdin**: скрипт пишет "Введите код для Тинькофф:" и ждёт ввод (если запуск из терминала)
|
||||
|
||||
**Рекомендация: вариант 1** — TG bot минимальная зависимость, полный контроль.
|
||||
|
||||
Для реализации: существующий Hermes/Zulip может служить relay. Или простой скрипт на Python + python-telegram-bot с `await incoming_message`.
|
||||
|
||||
### 6. Чувствительность данных — ограничения
|
||||
|
||||
Скрипт будет:
|
||||
- Знать **логины/пароли** банков (хранятся в локальном конфиге, НЕ в коде)
|
||||
- Открывать **браузер на машине Алекса** (никаких VPN/прокси)
|
||||
- Передавать только SMS-коды через TG — пароли не передаются
|
||||
- Работать **локально**, без LLM/агентов в browser automation
|
||||
|
||||
Код пишем так, чтобы ни одна строка credentials не была в скрипте:
|
||||
```python
|
||||
# config.yaml (chmod 600)
|
||||
banks:
|
||||
tinkoff:
|
||||
login: "7999..."
|
||||
password: "..."
|
||||
phone: "7999..."
|
||||
sber:
|
||||
login: "..."
|
||||
password: "..."
|
||||
```
|
||||
|
||||
### 7. Альтернатива: API банков (без browser)
|
||||
|
||||
| Банк | REST API для личных счетов | Комментарий |
|
||||
|------|---------------------------|-------------|
|
||||
| Тинькофф | Есть (Tinkoff API для разработчиков) | Требует регистрации приложения, OAuth |
|
||||
| Сбер | Sber API для юрлиц, нет для личных | Не подходит |
|
||||
| Альфа | Альфа-Бизнес API (юрлица) | Не подходит |
|
||||
| Demir | Нет публичного API | — |
|
||||
| ВТБ | Нет публичного API | — |
|
||||
|
||||
Тинькофф — единственный из списка, у кого есть адекватный API для физлиц (Tinkoff API / Tinkoff Invest API). Можно получить выписку через API, без browser. Остальные — только SPA scraping.
|
||||
|
||||
**Код:** 10 swift-файлов, ~2 400 строк.
|
||||
|
||||
**Что хорошо:**
|
||||
- Хорошая архитектура: каждый банк = отдельный struct с чётким интерфейсом
|
||||
- Конфиги вынесены из кода (JSON)
|
||||
- Regex-маппинг категорий гибкий
|
||||
- Умеет объединять multi-part CSV и извлекать из PDF через Adobe API
|
||||
- Формат вывода совпадает со структурой Excel/Budget App
|
||||
|
||||
**Чего не хватает:**
|
||||
- Парсер Альфа-банка (только заглушка)
|
||||
- AI-категоризация закомментирована (GPT-3.5, устарела)
|
||||
- Нет интеграции с Budget App (только → CSV, не → БД)
|
||||
- Нет обработки для Demir ИП USD / Demir USD / Demir KGS отдельно (DemirToCSV один конфиг на все)
|
||||
- PDF-парсер привязан к Adobe PDF Extract API (платный сервис, credentials нужны)
|
||||
- Нет обработки для Сбер Кредитка как отдельного счёта (SberToCSV один конфиг)
|
||||
- Нет автоматического определения новых форматов CSV от банков
|
||||
@@ -0,0 +1,70 @@
|
||||
# Остатки счетов из Excel (Budget.xlsx)
|
||||
|
||||
Файл: `~/Downloads/Budget.xlsx`
|
||||
Лист: `транзакции`
|
||||
|
||||
Балансы **совпадают** с API после фиксов (2026-06-23).
|
||||
|
||||
| Счёт | Баланс | Валюта | Последняя операция |
|
||||
|---|---|---|---|
|
||||
| Нал RUB | 815 555.70 | RUB | 2025-05-16 (R6029, 60 000 deb) |
|
||||
| Нал KGS | 1 794.00 | KGS | 2025-06-17 (R6042, deb) |
|
||||
| Нал KZT | -22 820.00 | KZT | 2024-12-15 (R5505, deb) |
|
||||
| Нал USD | 1.00 | USD | 2025-02-04 (R5976, deb) |
|
||||
| Нал AED | 0.00 | AED | 2023-11-16 (R2335, deb) |
|
||||
| Нал EUR | — | EUR | нет операций |
|
||||
| Нал UZS | 18 000.35 | UZS | 2024-03-31 (R3337, deb) |
|
||||
| Demir ИП | 192 308.54 | KGS | 2025-01-31 (R5943, deb) |
|
||||
| Demir ИП USD | 284 842.96 | USD | 2026-05-29 (R6053, cred) |
|
||||
| Demir KGS | 96 013.12 | KGS | 2025-01-31 (R5947, deb) |
|
||||
| Demir USD | 3 394.29 | USD | 2025-01-31 (R5950, cred) |
|
||||
| Тинькофф Black | 521 263.65 | RUB | 2025-01-31 (R5959, deb) |
|
||||
| Тинькофф Кредитка | 7 170.17 | RUB | 2025-01-28 (R5901, deb) |
|
||||
| Сбер | 266 905.98 | RUB | 2025-01-28 (R5898, deb) |
|
||||
| Сбер Кредитка | 3 059.00 | RUB | 2025-01-31 (R5946, deb) |
|
||||
| Альфа | 78 030.00 | RUB | 2025-01-28 (R5925, cred) |
|
||||
| Альфа Кредитка | 0.50 | RUB | 2025-01-28 (R5924, deb) |
|
||||
| ВТБ | 265 178.95 | RUB | 2025-01-27 (R5887, deb) |
|
||||
| ВТБ Кредитка | 297 189.00 | RUB | 2025-01-31 (R5952, deb) |
|
||||
| Райффайзен | — | RUB | нет операций |
|
||||
|
||||
## Формула API
|
||||
|
||||
`balance = incoming_transfer + incoming_income - outgoing + initial_balance`
|
||||
|
||||
Где:
|
||||
- `incoming_transfer` — `SUM(cross_rate * amount)` где source IS NOT NULL (cross_rate конвертирует валюту source в валюту dest)
|
||||
- `incoming_income` — `SUM(amount)` где source IS NULL (amount уже в валюте счёта, cross_rate — только для отчёта)
|
||||
- `outgoing` — `SUM(amount)` (всегда в валюте счёта-источника)
|
||||
|
||||
## Фиксы (2026-06-23)
|
||||
|
||||
### 1. incoming для income и transfer — разный расчёт
|
||||
|
||||
Было: `incoming = SUM(COALESCE(cross_rate, 1.0) * amount)` — cross_rate применялся ко всем включая income (где amount уже в валюте счёта). Для KGS-счетов с income-пополнениями (например "Конвертация USD по курсу 87") incoming умножался на 87, давая баланс ×87.
|
||||
|
||||
Стало: incoming разделён на две части:
|
||||
- `incoming_transfer (source IS NOT NULL)`: cross_rate применяется (конвертирует валюту source→dest)
|
||||
- `incoming_income (source IS NULL)`: просто amount (cross_rate — только для отчётной валюты)
|
||||
|
||||
### 2. import_hash и on_conflict_do_nothing — удалены
|
||||
|
||||
Было: дедупликация по `import_hash = SHA256(date|amount|source|dest|comment)`. В Excel есть 141 дублирующаяся строка с одинаковыми этими полями (реальные повторные списания). `on_conflict_do_nothing` молча пропускал их, но `tx_count` врал что импортировал.
|
||||
|
||||
Стало: обычный `session.add(tx)`. Колонка `import_hash` и уникальный индекс дропнуты из таблицы. Функция `_make_import_hash` удалена.
|
||||
|
||||
### 3. Accounts — идемпотентность
|
||||
|
||||
Было: каждый запуск импорта создавал 20 новых аккаунтов (session.add). После нескольких запусков — 40+ аккаунтов с разными UUID, транзакции привязаны к разным наборам.
|
||||
|
||||
Стало: UPSERT — ищет существующий account по `name + user_id`, переиспользует.
|
||||
|
||||
### 4. UserSettings — UPSERT
|
||||
|
||||
Было: `session.add(UserSettings(...))` — падало с UniqueViolation при повторном запуске.
|
||||
|
||||
Стало: `pg_insert(...).on_conflict_do_update(...)`.
|
||||
|
||||
### 5. Нюанс Excel col I/L
|
||||
|
||||
Для некоторых строк Excel не кеширует вычисленные значения col I (остаток деб) — показывает None. Это не баг, а особенность data_only=True — если Excel не пересчитал формулы перед сохранением, кеш пуст. В таких случаях последний корректный баланс берётся из предыдущей строки минус amount.
|
||||
@@ -0,0 +1,264 @@
|
||||
---
|
||||
aliases:
|
||||
- Freedom Strategy
|
||||
- Budget app FIRE
|
||||
- Budget app strategy
|
||||
related:
|
||||
- '[[personal/projects/budget-app/index]]'
|
||||
- '[[personal/projects/budget-app/fire-investment-strategies-2026]]'
|
||||
tags:
|
||||
- personal
|
||||
- budget-app
|
||||
- finance
|
||||
- FIRE
|
||||
- freedom
|
||||
- roadmap
|
||||
title: Финансовая стратегия + FIRE-адаптация для Budget App
|
||||
updated: '2026-06-23T00:00:00.000Z'
|
||||
---
|
||||
# Финансовая стратегия + FIRE-адаптация для Budget App
|
||||
|
||||
**Обновлено:** 2026-06-23
|
||||
**Основание:** анализ данных budget-app + личные вводные Alex
|
||||
|
||||
---
|
||||
|
||||
## 1. Текущая позиция (June 2026)
|
||||
|
||||
### 1.1 Балансы счетов
|
||||
|
||||
| Счёт | Валюта | Баланс | Статус |
|
||||
|------|--------|--------|--------|
|
||||
| Нал USD | USD | $925,511 | **Накопления** |
|
||||
| Demir ИП USD | USD | $284,843 | **Накопления** |
|
||||
| Demir USD | USD | $3,394 | Остаток |
|
||||
| Нал KGS | KGS | 1,794 | Остаток (текущие) |
|
||||
| Demir ИП | KGS | -359,861 | На расход |
|
||||
| Demir KGS | KGS | -3,542,775 | Ушёл в минус |
|
||||
| RUB счета (Альфа, Сбер, ВТБ, Тинькофф, Нал) | RUB | ~-29.5M | Кредитки + овердрафты |
|
||||
| Нал RUB | RUB | -12,709,478 | Долг |
|
||||
| Альфа | RUB | -18,187,976 | Долг |
|
||||
|
||||
**Итого накопления:** ~$1,210,000 USD ≈ **112.5M KGS** (по курсу 93)
|
||||
**Реально свободные:** ~$100k (нал USD), остальное — предпринимательские счета + оборотка
|
||||
|
||||
### 1.2 Расходы (из БД, 2024 — июнь 2025, 17 мес)
|
||||
|
||||
| Показатель | Значение |
|
||||
|------------|----------|
|
||||
| Средние расходы/мес | ~1,075,000 KGS |
|
||||
| Средний доход/мес | ~851,000 KGS |
|
||||
| Норма сбережений (по БД) | -26% (данные неполные — часть трат не проведена) |
|
||||
|
||||
Категоризация в БД сломана: все расходы свалены в "📉 Расходы" (11.4M), остальное — депозиты, кэшбэк, аренда (~378k). Данные после июня 2025 не вносились, последняя транзакция май 2026 — видимо разовый импорт.
|
||||
|
||||
### 1.3 Внешние активы и доходы
|
||||
|
||||
| Статья | Цифра |
|
||||
|--------|-------|
|
||||
| **Накопления (ликвид)** | ~$100k (Нал USD) |
|
||||
| **Аренда 2 квартир** | 60-80k KGS/мес |
|
||||
| **Образование старшей** | $8-10k/год = 65-77k KGS/мес |
|
||||
| **Младшая (дистант)** | Запуск в этом году — точные цифры появятся |
|
||||
| **Частный дом** | Текущее содержание |
|
||||
| **Indie dev** | Проекта пока нет |
|
||||
|
||||
---
|
||||
|
||||
## 2. Стратегия (Freedom, не FIRE)
|
||||
|
||||
Классический FIRE (накопить 25x и сидеть без дела) **тебе не подходит** по нескольким причинам:
|
||||
|
||||
1. **Валютный риск** — живёшь в KGS, доход в RUB/USD. KGS волатильна. FIRE-расчёт в KGS ненадёжен
|
||||
2. **Образование детей** — крупный обязательный платёж на ~10 лет вперёд. Это не "сократить", это фиксированная статья
|
||||
3. **Ты не хочешь "не работать"** — инди-дев показывает что хочешь заниматься проектами, а не сидеть на пляже
|
||||
4. **Две квартиры** — актив, который уже почти покрывает образование старшей
|
||||
|
||||
### 2.1 Твоя цель: "Freedom Gap"
|
||||
|
||||
Не FIRE number, а **Freedom Gap** — разница между расходами и пассивным/полупассивным доходом, которую нужно закрыть капиталом.
|
||||
|
||||
```
|
||||
Freedom Gap = (расходы/мес) − (аренда + дивиденды + проектный доход)
|
||||
```
|
||||
|
||||
| Сценарий | Расходы/мес | Аренда | Проект | Gap/мес | Капитал для 4% |
|
||||
|----------|------------|--------|--------|---------|----------------|
|
||||
| **Сейчас** | ~900k KGS | 70k | 0 | **830k KGS** | **249M KGS** ($2.7M) |
|
||||
| **Аренда → образование** | 900k | 70k→на образование | 0 | 830k | 249M KGS ($2.7M) |
|
||||
| **+Проект $2k/мес** | 900k | 70k | 186k | **644k KGS** | **193M KGS** ($2.1M) |
|
||||
| **+Проект $5k/мес** | 900k | 70k | 465k | **365k KGS** | **110M KGS** ($1.2M) |
|
||||
|
||||
### 2.2 Реалистичные вехи
|
||||
|
||||
| Веха | Условие | Цифра |
|
||||
|------|---------|-------|
|
||||
| **🟢 1ая: аренда = образование** | Аренда 80k покрывает старшую ~77k | ✅ **УЖЕ почти** |
|
||||
| **🟢 2ая: проект = жизнь** | Проект $3k/мес покрывает ~280k KGS | Нужен работающий проект |
|
||||
| **🟡 3ая: капитал + аренда = всё** | Накопить 110M KGS ($1.2M) при проекте $5k | 10-15 лет |
|
||||
| **🔴 4ая: полная свобода** | Накопить 249M KGS ($2.7M) | Долгий горизонт |
|
||||
|
||||
---
|
||||
|
||||
## 3. Инвестиционная стратегия для Alex
|
||||
|
||||
### 3.1 Принципы
|
||||
|
||||
1. **Core-Satellite** — 70% широкий рынок (VT / VWRA), 30% дивиденды + защита
|
||||
2. **Мультивалютность** — портфель в USD (защита от девальвации KGS)
|
||||
3. **Дивиденды как income stream** — снижают sequence-of-returns risk, частично покрывают расходы
|
||||
4. **Буфер 12 мес** — в USD, не трогать
|
||||
5. **Ребаланс раз в год** — не дёргаться
|
||||
|
||||
### 3.2 Рекомендуемая аллокация
|
||||
|
||||
| Класс | % | Инструмент | Назначение |
|
||||
|-------|---|-----------|------------|
|
||||
| **Global Equity ETF** | 60% | VWRA (VT) — IRSH / LSE | Рост капитала, мультивалютная диверсификация |
|
||||
| **Dividend ETF** | 15% | VIG, SCHD, или HDV | Стабильный дивидендный поток |
|
||||
| **Bonds (TIPS)** | 10% | TIP (iShares TIPS) | Защита от инфляции |
|
||||
| **Cash USD** | 10% | HYSA / money market | Буфер, ~12 мес расходов |
|
||||
| **Real Estate (REIT)** | 5% | VNQ / O | Доход от недвижимости без управления |
|
||||
|
||||
**Почему не BND:** для тебя облигации в USD не дают премии, а TIPS защищают от инфляции которая в KGS выше номинальной.
|
||||
|
||||
### 3.3 Ребаланс
|
||||
|
||||
- **Раз в год** в декабре
|
||||
- **Автоматический триггер:** любая позиция отклонилась >5% от цели
|
||||
- **Новые деньги:** направляются в самый отстающий класс (автоматический buy-low)
|
||||
|
||||
---
|
||||
|
||||
## 4. Что внедрить в budget-app (Phase 6 — Roadmap)
|
||||
|
||||
### 4.1 Приоритеты (по ценности)
|
||||
|
||||
| # | Фича | Зачем | Оценка сложности | Статус |
|
||||
|---|------|-------|-----------------|--------|
|
||||
| **P0** | **Savings Rate Dashboard** | Увидеть реальную норму сбережений. Сейчас -26% — надо понять куда уходят деньги | Medium | ❌ Не начато |
|
||||
| **P0** | **Multi-Currency Net Worth** | Общий капитал в USD: наличка + счета + квартиры + пассивы | Low | ❌ Не начато |
|
||||
| **P1** | **Investment Snapshots** | Раз в месяц вбить "сколько на Нал USD / IBKR / крипте" — увидеть динамику | Low | ❌ Не начато |
|
||||
| **P1** | **Freedom Gap Dashboard** | Доход (аренда+дивиденды+проект) - расходы = gap | Medium | ❌ Не начато |
|
||||
| **P1** | **Passive Income Tracker** | Сколько приносят аренда, дивиденды, депозиты в месяц | Low | ❌ Не начато |
|
||||
| **P2** | **Projection Engine** | "Если докладываю X/мес и проекты дают Y/мес — через N лет свобода" | Medium+ | ❌ Не начато |
|
||||
| **P2** | **What-if Simulator** | "Что если аренда упадёт / курс изменится / проект взлетит" | Medium+ | ❌ Не начато |
|
||||
| **P3** | **Monte Carlo FI Calculator** | Классический 4% vs 3.5% vs variable, probability of success | High | ❌ Не начато |
|
||||
| **P3** | **Withdrawal Strategy Planner** | Бакетная / guardrails / dividend | High | ❌ Не начато |
|
||||
|
||||
### 4.2 Схема расширения БД
|
||||
|
||||
```sql
|
||||
-- Инвестиционные снапшоты (ручной ввод раз в месяц)
|
||||
CREATE TABLE investment_snapshot (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||
account_id UUID REFERENCES account(id), -- NULL для внешних брокеров
|
||||
name VARCHAR(128) NOT NULL, -- "Нал USD", "Interactive Brokers", "Крипта"
|
||||
portfolio_value NUMERIC(16,2) NOT NULL,
|
||||
currency_code VARCHAR(5) NOT NULL REFERENCES currency(code),
|
||||
snapshot_date DATE NOT NULL,
|
||||
asset_class VARCHAR(32), -- cash, bonds, stocks, real_estate, crypto, business
|
||||
notes TEXT,
|
||||
UNIQUE(user_id, name, snapshot_date)
|
||||
);
|
||||
|
||||
-- Цели свободы
|
||||
CREATE TABLE freedom_goal (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||
name VARCHAR(128) NOT NULL, -- "Свобода", "Образование детей", "Ремонт дома"
|
||||
target_amount NUMERIC(16,2) NOT NULL,
|
||||
target_currency_code VARCHAR(5) NOT NULL REFERENCES currency(code),
|
||||
current_amount NUMERIC(16,2) DEFAULT 0, -- ручной или вычисляемый
|
||||
category VARCHAR(32), -- freedom, education, major_purchase
|
||||
monthly_contribution NUMERIC(16,2), -- план пополнения
|
||||
target_date DATE,
|
||||
expected_return_rate NUMERIC(5,4), -- 0.07 = 7%
|
||||
is_active BOOLEAN DEFAULT true
|
||||
);
|
||||
```
|
||||
|
||||
### 4.3 UI макет (какие страницы добавить)
|
||||
|
||||
```
|
||||
/dashboard
|
||||
├── Savings Rate (график за 12 мес + норма)
|
||||
├── Net Worth (USD, KGS — два числа)
|
||||
├── Freedom Gap (прогресс-бар: 0% → 100%)
|
||||
└── Cash Reserve (мес жизни / норма 12)
|
||||
|
||||
/freedom
|
||||
├── Goals (список целей с прогресс-барами)
|
||||
├── Investment Snapshots (таблица + график)
|
||||
├── Projection (график: сегодня → свобода)
|
||||
└── What-if (слайдеры: аренда, проект, курс)
|
||||
|
||||
/finances
|
||||
├── Passive Income (аренда, дивиденды, депозиты за месяц)
|
||||
└── Allocations (pie chart портфеля)
|
||||
```
|
||||
|
||||
### 4.4 API эндпоинты (новые)
|
||||
|
||||
```
|
||||
GET /api/freedom/goals — список целей свободы
|
||||
POST /api/freedom/goals — создать цель
|
||||
PUT /api/freedom/goals/:id — обновить
|
||||
GET /api/freedom/goals/:id/projection — проекция к цели
|
||||
|
||||
GET /api/investments/snapshots — список снапшотов (с пагинацией)
|
||||
POST /api/investments/snapshots — добавить снапшот
|
||||
GET /api/investments/snapshots/latest — последний по каждому инструменту
|
||||
|
||||
GET /api/dashboard/net-worth — общий капитал (USD + KGS)
|
||||
GET /api/dashboard/freedom-gap — gap месяца
|
||||
GET /api/dashboard/passive-income — аренда + дивиденды за период
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Первый шаг (что сделать прямо сейчас)
|
||||
|
||||
### Step 0: Заполнить данные в budget-app
|
||||
Без актуальных расходов любой расчёт — гадание. Нужно:
|
||||
1. Импортировать выписки за июнь 2025 — июнь 2026 (Альфа, Сбер, Demir)
|
||||
2. Разнести по категориям (хотя бы крупные статьи: дети, дом, стройка, еда)
|
||||
3. Вбить балансы квартир и других активов как investment_snapshot
|
||||
|
||||
### Step 1: Net Worth Dashboard
|
||||
Показать общий капитал в USD без лишних действий.
|
||||
Уже можно сделать — данные по счетам есть в БД, квартиры добавляются как manual entry.
|
||||
|
||||
### Step 2: Investment Snapshots
|
||||
Форма: дата, инструмент, сумма, валюта → график роста капитала с проекцией.
|
||||
Без автоматизации — раз в месяц ввести руками.
|
||||
|
||||
### Step 3: Freedom Goal
|
||||
После того как есть:
|
||||
- реальные расходы (не -26%)
|
||||
- актуальный капитал с квартирами
|
||||
- доход от проекта (хоть какой-то)
|
||||
|
||||
→ построить Freedom Gap Dashboard с what-if сценариями.
|
||||
|
||||
---
|
||||
|
||||
## 6. Что не надо делать (анти-приоритеты)
|
||||
|
||||
| Не надо | Почему |
|
||||
|---------|--------|
|
||||
| Monte Carlo Simulation | Сложно, данных мало, толку для твоего случая 0 |
|
||||
| Withdrawal Strategy Planner | Ты не на пенсии, не нужен |
|
||||
| Roth Conversion Ladder | Не применимо (не US resident) |
|
||||
| FIRE Number Calculator в классике | Тебе нужен Freedom Gap, не 25x |
|
||||
| Авто-импорт курсов валют | Не влияет на решение — достаточно раз в месяц |
|
||||
| Сложные прогнозы в KGS | Курс KGS непредсказуем, считай в USD |
|
||||
|
||||
---
|
||||
|
||||
## 7. Связанные заметки
|
||||
|
||||
- [[personal/projects/budget-app/index|Budget App — главный документ]]
|
||||
- [[personal/projects/budget-app/fire-investment-strategies-2026|Исходное исследование FIRE + инвестстратегий]]
|
||||
- [[personal/documents/budget-app-features|Список всех идей фич]]
|
||||
@@ -0,0 +1,246 @@
|
||||
---
|
||||
title: FIRE и инвестиционные стратегии для Budget App
|
||||
tags:
|
||||
- personal
|
||||
- budget-app
|
||||
- finance
|
||||
- FIRE
|
||||
- investments
|
||||
related: '[[personal/projects/budget-app/index]]'
|
||||
updated: '2026-06-23T00:00:00.000Z'
|
||||
---
|
||||
# FIRE и инвестиционные стратегии для Budget App
|
||||
|
||||
**Создано:** 2026-06-23
|
||||
**Источник:** исследование whale (FIRE guides, Goldman Sachs, PIMCO, Bogleheads, 2026)
|
||||
|
||||
---
|
||||
|
||||
## 1. FIRE-типы и целевые показатели
|
||||
|
||||
### 1.1 Виды FIRE
|
||||
|
||||
| Тип | Годовые расходы | Целевой портфель | Суть |
|
||||
|-----|----------------|-------------------|------|
|
||||
| **Lean FIRE** | < $40,000 | < $1,000,000 | Минимализм, быстрый выход |
|
||||
| **Chubby FIRE** | $40k–$100k | $1M–$2.5M | Комфорт без излишеств |
|
||||
| **Fat FIRE** | $100k+ | $2.5M–$6M+ | Высокий уровень жизни |
|
||||
| **Barista FIRE** | Переменные | 50–80% от полного | Частичная занятость покрывает часть |
|
||||
| **Coast FIRE** | Любые | Достаточно чтобы дорасти | Перестать пополнять, дать сложному % работать |
|
||||
|
||||
### 1.2 FIRE Number
|
||||
|
||||
**Формула:** `FIRE Number = Annual Expenses × 25` (4% rule)
|
||||
|
||||
На 2026 год рекомендуется **3–3.5%** вместо 4%:
|
||||
- Ранний выход = 40–60 лет на пенсии (Trinity Study считала 30 лет)
|
||||
- Повышенные оценки рынка (Shiller CAPE выше исторической нормы)
|
||||
- Низкая доходность облигаций
|
||||
|
||||
| Норма сбережений | Годовые расходы | FIRE (4%) | FIRE (3.5%) |
|
||||
|------------------|----------------|-----------|-------------|
|
||||
| $30,000 | $30,000 | $750,000 | $857,000 |
|
||||
| $40,000 | $40,000 | $1,000,000 | $1,143,000 |
|
||||
| $50,000 | $50,000 | $1,250,000 | $1,429,000 |
|
||||
| $75,000 | $75,000 | $1,875,000 | $2,143,000 |
|
||||
| $100,000 | $100,000 | $2,500,000 | $2,857,000 |
|
||||
|
||||
### 1.3 Скорость до FIRE от нормы сбережений
|
||||
|
||||
| Норма сбережений | Лет до FIRE |
|
||||
|-----------------|-------------|
|
||||
| 10% | 51 лет |
|
||||
| 20% | 37 лет |
|
||||
| 30% | 28 лет |
|
||||
| 40% | 22 года |
|
||||
| 50% | 17 лет |
|
||||
| 60% | 12.5 лет |
|
||||
| 70% | 8.5 лет |
|
||||
| 80% | 5.5 лет |
|
||||
|
||||
*Assumes 5% real returns, starting from zero*
|
||||
|
||||
---
|
||||
|
||||
## 2. Стратегии вывода (Withdrawal Strategies)
|
||||
|
||||
### 2.1 4% Rule (Trinity Study)
|
||||
- 95% success rate для 30 лет с 60/40 портфелем
|
||||
- **Для FIRE (40+ лет):** риск sequence-of-returns выше → используй 3.5%
|
||||
|
||||
### 2.2 Flexible Spending (Variable Withdrawal)
|
||||
- В плохие годы режешь дискреционные траты на 10–20%
|
||||
- В хорошие — тратишь больше
|
||||
- **Guardrails:** увеличиваешь withdrawal когда портфель выше цели, уменьшаешь когда ниже
|
||||
|
||||
### 2.3 Bucket Strategy
|
||||
- **Bucket 1 (1–2 года):** кэш/деньги — текущие расходы
|
||||
- **Bucket 2 (3–7 лет):** облигации
|
||||
- **Bucket 3 (8+ лет):** акции
|
||||
- Ребалансируешь только когда акции переросли
|
||||
|
||||
### 2.4 Dividend Investing
|
||||
- Покрываешь расходы дивидендами (не продавая акции)
|
||||
- Ниже общая доходность, но выше стабильность
|
||||
|
||||
### 2.5 Roth Conversion Ladder
|
||||
- Конвертируешь traditional IRA → Roth IRA ежегодно (1 год расходов)
|
||||
- Через 5 лет конвертированные средства доступны без штрафа
|
||||
- Нужен 5-летний bridge из taxable счетов
|
||||
|
||||
---
|
||||
|
||||
## 3. Инвестиционные стратегии на 2026
|
||||
|
||||
### 3.1 Strategic Asset Allocation (классика)
|
||||
- Фиксированные цели, ребаланс раз в квартал/год
|
||||
- **Типичный model:** 60% equities / 30% fixed income / 10% alternatives
|
||||
- **Историческая доходность 60/40:** ~6.5% annualized (2014–2024)
|
||||
- **Плюс:** убирает эмоции, дисциплина правил
|
||||
- **Минус:** не адаптируется к среде
|
||||
|
||||
### 3.2 Three-Fund Portfolio (Bogleheads)
|
||||
- US Total Stock Market (VTI/VTSAX)
|
||||
- International Total Stock Market (VXUS/VTIAX)
|
||||
- US Total Bond Market (BND/VBTLX)
|
||||
- **Allocation:** 60/20/20 (или агрессивнее для молодых)
|
||||
- **Expense ratios:** 0.03–0.05%
|
||||
- **Разница в комиссиях:** 0.03% vs 1.0% на $500k за 20 лет = ~$100k+
|
||||
|
||||
### 3.3 Tactical Asset Allocation (2026 context)
|
||||
- Отклонения 5–20% от стратегической аллокации на основе макро
|
||||
- **Overweight:** энергия (commodities supercycle), floating-rate securities, инфраструктура
|
||||
- **Underweight:** long-duration bonds, перегретые US tech
|
||||
- **Добавить:** emerging market debt, inflation-protected assets
|
||||
- **Alpha:** ~2.1% annually above static (multi-asset, 2024 study)
|
||||
|
||||
### 3.4 Enhanced Passive (Goldman Sachs 2026)
|
||||
- **Alpha Enhanced:** tracking error 50–200 bps, чуть выше комиссии
|
||||
- **Зачем 2026:** снижение ожидаемой рыночной доходности, концентрация индексов, неопределённость
|
||||
- **Systematic factor tilts** — небольшие ставки на value, momentum, quality
|
||||
|
||||
### 3.5 Insured / Dynamic Allocation
|
||||
- **Insured:** автоматический переход в консерватив при drawdown > X%
|
||||
- **Dynamic:** 7.8% annualized vs static с меньшими просадками (2024)
|
||||
- **Рекомендация 2026:** infrastructure, private credit, low-duration fixed income
|
||||
|
||||
### 3.6 Goldman Sachs 2026: активные ETF + alternatives
|
||||
- **Active ETFs:** AUM растёт 46% CAGR с 2020
|
||||
- **Derivative-income ETFs:** $47B inflows (Q1–Q3 2025)
|
||||
- **Private assets:** Millennials держат ~20%, Boomers ~6% — поколенческий сдвиг
|
||||
- **Tail-risk hedging:** нужен более широкий набор инструментов (не только bonds/USD)
|
||||
|
||||
---
|
||||
|
||||
## 4. Тактики для нашего контекста (Alex, мультивалютный, KGS-based)
|
||||
|
||||
### 4.1 Особенности
|
||||
- **Базовая валюта:** KGS (высокая волатильность, зависимость от переводов РФ)
|
||||
- **Счета:** RUB, USD, EUR, KGS, KZT, UZS, AED, CNY
|
||||
- **Доход:** в основном RUB/USD
|
||||
- **Расходы:** KGS (жизнь), USD (крупные/накопления), RUB (регулярные)
|
||||
|
||||
### 4.2 Что адаптировать в budget-app
|
||||
|
||||
#### Phase 6 — что строить (приоритеты):
|
||||
|
||||
1. **FIRE Number Calculator**
|
||||
- Поле: годовые расходы (берутся из фактических транзакций за 12 мес)
|
||||
- Поле: текущий инвестированный капитал
|
||||
- Поле: expected return (4–7%)
|
||||
- Вывод: FIRE number (25x / 28.6x / 33x), years to FIRE
|
||||
- Вывод: сколько нужно докладывать в месяц для выхода через N лет
|
||||
|
||||
2. **Savings Rate Dashboard**
|
||||
- `Норма сбережений = (Доходы - Расходы) / Доходы`
|
||||
- График: savings rate по месяцам
|
||||
- График: накопленный капитал vs FIRE trajectory (projection line)
|
||||
|
||||
3. **FireGoal модель**
|
||||
- `fire_goal` таблица: user_id, target_amount, target_currency_id, expected_return_rate, monthly_contribution, target_date
|
||||
- Progress bar: сколько % от цели накоплено
|
||||
- What-if: "что если увеличу savings rate на 5%?"
|
||||
|
||||
4. **Multi-currency FIRE number**
|
||||
- Пересчёт цели в base currency (KGS) и в USD
|
||||
- Проблема: валютный риск при пенсии в KGS — нужно показывать и USD-эквивалент
|
||||
|
||||
5. **Investment Tracking**
|
||||
- `investment` / `portfolio` таблицы: date, account_id, value, currency_id
|
||||
- Ввод: ручные snapshots (когда обновляешь брокерский счёт)
|
||||
- График: капитал по месяцам + проекция 7% CAGR
|
||||
- Автоматический импорт: нет (ручной ввод раз в месяц)
|
||||
|
||||
6. **Withdrawal Simulator (+FI Calc)**
|
||||
- Monte Carlo simulation (по историческим данным)
|
||||
- Поля: начальный капитал, годовые расходы, asset allocation, withdrawal rate
|
||||
- Результат: probability of success (не остаться без денег)
|
||||
- Модель 4% vs 3.5% vs variable
|
||||
|
||||
7. **"Буфер" / Cash Reserve Dashboard**
|
||||
- Сколько месяцев расходов в кэше (по счетам типа cash/debit)
|
||||
- Целевой буфер: 6–12 месяцев расходов
|
||||
- Trigger: < 3 мес → alert
|
||||
|
||||
### 4.3 Архитектурные решения для budget-app
|
||||
|
||||
```
|
||||
investment_account (таблица):
|
||||
id, user_id, account_id FK → account,
|
||||
portfolio_value, currency_id, snapshot_date,
|
||||
asset_class {cash, bonds, stocks, real_estate, crypto, other},
|
||||
notes
|
||||
|
||||
fire_goal (уже есть в схеме):
|
||||
id, user_id, target_amount, target_currency_id,
|
||||
expected_return_rate (decimal, 0.07 = 7%),
|
||||
monthly_contribution (decimal),
|
||||
target_date NULL,
|
||||
current_portfolio_value (вычисляется из investment_account snapshot)
|
||||
|
||||
fire_projection:
|
||||
computed view: по месяцам от текущей даты
|
||||
columns: month, contribution, return, portfolio_value, is_fire (bool если >= target)
|
||||
```
|
||||
|
||||
### 4.4 Какие FIRE-варианты реалистичны для Alex
|
||||
|
||||
| FIRE-тип | Расходы/мес | Год | FIRE Number (4%) | Годовая норма сбережений | Лет* |
|
||||
|----------|-------------|-----|------------------|-------------------------|------|
|
||||
| Lean FIRE | Минимальные | ??? | ??? | ??? | ??? |
|
||||
| Chubby FIRE | Текущие | ??? | ??? | ??? | ??? |
|
||||
| Barista FIRE | С part-time | ??? | ??? | ??? | ??? |
|
||||
|
||||
*\* — нужно подставить фактические цифры из budget-app*
|
||||
|
||||
**Рекомендуемая стратегия для Alex:**
|
||||
- Core: Three-Fund Portfolio (VT + BNDW) — глобальная диверсификация, не привязана к KGS
|
||||
- Satellite: TIPS / real assets (инфляция в KGS выше чем в USD)
|
||||
- Дивидендная составляющая: покрывает часть расходов (снижает sequence-of-returns risk)
|
||||
- Буфер: 12+ месяцев расходов в USD (не KGS — защита от девальвации)
|
||||
- Ребаланс: раз в год, или при drift > 5%
|
||||
|
||||
---
|
||||
|
||||
## 5. Ключевые метрики на дашборд
|
||||
|
||||
| Метрика | Откуда берётся | Формула |
|
||||
|---------|---------------|---------|
|
||||
| **Норма сбережений** | Транзакции за 12 мес | (Доходы - Расходы) / Доходы |
|
||||
| **FIRE Number** | Средние расходы × 25 | (AvgExpenses × 12) × 25 |
|
||||
| **FIRE Progress** | Инвестиционный капитал | PortfolioValue / FIRE_Number × 100% |
|
||||
| **Years to FIRE** | Калькулятор | (ln(FIRE/R) - ln(FIRE/(R - P×12))) / ln(1+r) — сложно |
|
||||
| **Safe Withdrawal Amount** | Портфель × 4% | PortfolioValue × 0.04 |
|
||||
| **Runway (мес)** | Кэш / расходы в месяц | CashBalance / MonthlyExpenses |
|
||||
| **Dollar-Cost Avg Equity** | Инвестиции / куплено единиц | — |
|
||||
|
||||
---
|
||||
|
||||
## 6. Связанные ресурсы
|
||||
|
||||
- [[personal/projects/budget-app/index|Budget App — главный документ]]
|
||||
- [[personal/documents/budget-app-features|Исходный список фич]]
|
||||
- [WealthVieu FIRE Guide 2026](https://wealthvieu.com/retirement/fire/)
|
||||
- [Goldman Sachs — Portfolio Construction 2026](https://am.gs.com/en-us/advisors/insights/article/investment-outlook/portfolio-construction-2026)
|
||||
- [Bogleheads Safe Withdrawal Rates](https://www.bogleheads.org/wiki/Safe_withdrawal_rates)
|
||||
- [PIMCO — Investment Ideas for 2026](https://www.pimco.com/us/en/insights/charting-the-year-ahead-investment-ideas-for-2026)
|
||||
@@ -81,7 +81,7 @@
|
||||
- Категоризация — ручная.
|
||||
- Невозможно нормально работать с мобильного.
|
||||
- Анализ ограничен сводными в Excel: нет нормальных графиков по подкатегориям, кросс-фильтрации, прогнозов.
|
||||
- Нет ничего про FIRE: проекций пенсии, моделирования инвестиций, целевых процентов нормы сбережений.
|
||||
- Нет ничего про Freedom Gap / инвестиции: нет трекинга портфеля, нормы сбережений, проекции "когда аренда + проекты покроют расходы".
|
||||
- История курсов хранится в строках листа `курсы` — это не нормализованная таблица.
|
||||
- Нет API: нельзя интегрировать с банковскими экспортами, нельзя автоматически тянуть в дашборд.
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
2. Считает аналитику и сводки **на лету** через SQL, а не через `VLOOKUP`.
|
||||
3. Импортирует банковские выписки (Альфа, Сбер, Demir, …) автоматически + AI-резолвер для неоднозначных случаев.
|
||||
4. Даёт мобильный UI для быстрого ввода трат на ходу.
|
||||
5. Моделирует **FIRE-сценарии** (отдельная фаза после готового бюджетирования).
|
||||
5. **Freedom Gap** — разница между расходами и пассивным/полупассивным доходом (аренда, дивиденды, проекты). График прогресса к нулевому gap. What-if сценарии: "если проект даёт X, аренда Y — через N лет свобода".
|
||||
6. Прогнозирует траты на N месяцев вперёд по сезонной модели (категории `🧱 Стройка`, `🏖️ Отдых` цикличны).
|
||||
|
||||
### Доменная модель (первая итерация)
|
||||
@@ -144,13 +144,34 @@ Base currency не хардкодится — выбирается в `Settings`
|
||||
|
||||
1. **Phase 0 — Discovery & schema**. Полностью разобрать Excel, утвердить доменную модель. *(в процессе — этот док)*
|
||||
2. **Phase 1 — Skeleton + import**. Создать `~/Developer/budget-app/` (git init), FastAPI + Vue в Docker Compose в `~/docker/budget-app/` с подключением к существующему Postgres-кластеру на хосте (новая БД `budget_app`). Миграция xlsx → БД. Read-only viewer транзакций + остатков + годовые отчёты. Внешний доступ через `budget.qentra.top` с auth (FastAPI Users + JWT).
|
||||
3. **Phase 2 — Курсы и настройки**. Cron-джоб для НБ КР с back-fill. Базовая валюта в настройках. Все пересчёты привязаны к ней.
|
||||
4. **Phase 3 — Ручной ввод**. Форма добавления транзакции, CRUD категорий/счетов, ручная правка курсов.
|
||||
5. **Phase 4 — Импорт банков + AI-резолвер**. Автофетч/парсеры (Альфа, Сбер, Demir). LLM-резолвер. TG-бот для ручных подтверждений.
|
||||
6. **Phase 5 — Аналитика**. Дашборды: расход по категориям, динамика, бёрндаун по бюджету. Прогнозы.
|
||||
7. **Phase 6 — Mobile / PWA**. Быстрый ввод с телефона.
|
||||
8. **Phase 7 — MCP HTTP**. Экспонировать MCP-эндпоинт для Hermes.
|
||||
9. **Phase 8 — Инвестпортфели + FIRE**. Тикеры/цены/дивы. Калькулятор FIRE. *(отдельное планирование когда бюджетирование готово.)*
|
||||
3. **Phase 2 — Полноценный Viewer + CRUD**. Довести до уровня Excel по функциональности:
|
||||
- Валюта у счетов (символ)
|
||||
- Категории с иерархией (группа → подкатегория)
|
||||
- Свёртка по годам/месяцам (drill-down как в Excel)
|
||||
- Dynamic scrolling (infinite scroll вместо кнопок пагинации)
|
||||
- CRUD транзакций: добавление, редактирование, удаление
|
||||
- CRUD категорий
|
||||
- CRUD счетов
|
||||
- Ручная правка курсов
|
||||
- Дашборды: расход по категориям, динамика, бёрндаун по бюджету
|
||||
- Прогнозы (сезонная модель по категориям)
|
||||
- Multi-currency: отображение балансов в валюте счёта + в base currency
|
||||
- Сводная таблица по годам (как лист `сводная` в Excel)
|
||||
- Налоговый учёт (как лист `налоги 22-24` в Excel)
|
||||
4. **Phase 3 — Импорт банков + AI-резолвер**. Автофетч/парсеры (Альфа, Сбер, Demir). LLM-резолвер. TG-бот для ручных подтверждений.
|
||||
5. **Phase 4 — Mobile / PWA**. Быстрый ввод с телефона.
|
||||
6. **Phase 5 — MCP HTTP**. Экспонировать MCP-эндпоинт для Hermes.
|
||||
7. **Phase 6 — Freedom / Инвестиции**. Замена классического FIRE на Freedom Gap — разница между расходами и пассивным доходом.
|
||||
- P0: Savings Rate Dashboard (норма сбережений из транзакций)
|
||||
- P0: Multi-Currency Net Worth (общий капитал USD/KGS)
|
||||
- P1: Investment Snapshots (ручной ввод раз в месяц, таблица + график)
|
||||
- P1: Freedom Gap Dashboard (доход-расход-аренда-проект = gap)
|
||||
- P1: Passive Income Tracker (аренда + дивиденды + депозиты)
|
||||
- P2: Freedom Goal with Projection Engine (what-if сценарии)
|
||||
- P3: What-if Simulator (слайдеры: курс, аренда, проект, норма сбережений)
|
||||
- Новые таблицы: `investment_snapshot`, `freedom_goal`
|
||||
- Новые страницы: `/freedom`, `/finances`
|
||||
- *Не делать:* Monte Carlo, Withdrawal Strategy Planner, Roth Conversion Ladder — не применимы
|
||||
|
||||
### Multi-tenant readiness (для будущего public SaaS)
|
||||
|
||||
@@ -340,6 +361,12 @@ CLOUDFLARE_TUNNEL_TOKEN=...
|
||||
- `echarts` (скаффолд под Phase 5, не используем активно)
|
||||
- dev: `vite`, `typescript`, `eslint`, `prettier`
|
||||
|
||||
### Правила работы
|
||||
|
||||
1. **Тесты — обязательны** для каждого нового API-роута или изменения. Если код не покрыт тестом — он не готов.
|
||||
2. **Обновление доку** — после каждой завершённой задачи обновлять таблицу прогресса и Acceptance criteria в этом доке.
|
||||
3. **Комит** — после каждой логически завершённой задачи (не раз в 10 шагов).
|
||||
|
||||
### Acceptance criteria Phase 1
|
||||
|
||||
- [ ] `https://budget.qentra.top` открывается, login работает.
|
||||
@@ -364,4 +391,156 @@ CLOUDFLARE_TUNNEL_TOKEN=...
|
||||
---
|
||||
|
||||
**Создано:** 2026-06-21
|
||||
**Статус:** Phase 0 закрыт, Phase 1 спланирован
|
||||
**Статус:** Phase 1 — в работе
|
||||
|
||||
## Phase 1 progress
|
||||
|
||||
| Шаг | Статус | Кем |
|
||||
| -------------------------------------- | ------ | ---- |
|
||||
| Init репо | ✅ | Орёл |
|
||||
| Backend skeleton | ✅ | Орёл |
|
||||
| Postgres bootstrap — роль + БД | ✅ | Кит |
|
||||
| Alembic initial migration | ✅ | Кит |
|
||||
| FastAPI Users + auth routes | ✅ | Кит |
|
||||
| Bootstrap первого юзера | ✅ | Кит |
|
||||
| XLSX импортёр (6 045 транзакций) | ✅ | Кит |
|
||||
| Read-only API (12 тестов) | ✅ | Кит |
|
||||
| Frontend skeleton + read-only страницы | ✅ | Кит |
|
||||
| Docker Compose (2 сервиса, работает) | ✅ | Кит |
|
||||
| Cloudflare Tunnel budget.qentra.top | ✅ | Alex |
|
||||
| Сверка данных | ✅ | Кит |
|
||||
| | | |
|
||||
|
||||
## Disaster recovery: CASCADE data loss
|
||||
|
||||
**Сценарий:** Удалён пользователь (A-click → user delete). Из-за `ON DELETE CASCADE` на `transaction_user_id_fkey` все транзакции этого пользователя удалены мгновенно (6 045 строк). Балансы обнулены.
|
||||
|
||||
### Recovery шаги (на будущее)
|
||||
|
||||
```bash
|
||||
# 1. Пересоздать пользователя с тем же email (admin123)
|
||||
curl -XPOST .../api/auth/register -H... -d'{"email":"alex@qentra.top","password":"admin123"}'
|
||||
|
||||
# 2. Переимпортировать транзакции из xlsx
|
||||
cd ~/Developer/budget-app
|
||||
uv run python src/budget/importers/__init__.py
|
||||
|
||||
# 3. Проверить балансы — все 18 счетов должны совпасть с excel-balances.md
|
||||
# 4. Пересобрать и передеплоить backend (дата формат) + frontend (любые изменения)
|
||||
docker-compose build backend && docker-compose up -d backend
|
||||
cd frontend && npm run build && cd .. && docker-compose build --no-cache frontend && docker-compose up -d frontend
|
||||
```
|
||||
|
||||
### Формат даты на фронте (актуальный)
|
||||
|
||||
API возвращает `t.date.isoformat()` → `2026-05-29T17:00:00`.
|
||||
Фронт режет: `{{ t.date.slice(0, 10) }} {{ t.date.slice(11, 16) }}` → `2026-05-29 17:00`.
|
||||
|
||||
Если время = `00:00` — в Excel не было времени для этой транзакции. Это корректно.
|
||||
|
||||
### Символы валют вместо колонки
|
||||
|
||||
Колонка "Валюта" убрана из таблиц Transactions, Accounts, TaxRecords.
|
||||
Вместо неё символ валюты показывается непосредственно перед суммой (Transactions, TaxRecords) или в ячейке (Accounts).
|
||||
|
||||
Маппинг на фронте (постоянный, не из БД):
|
||||
|
||||
| Код | Символ | Валюта |
|
||||
|-----|--------|--------|
|
||||
| USD | $ | Доллар |
|
||||
| EUR | € | Евро |
|
||||
| RUB | ₽ | Рубль |
|
||||
| KGS | С̲ | Сом (с с нижней чертой) |
|
||||
| KZT | ₸ | Тенге (уже есть в Unicode) |
|
||||
|
||||
Файлы: `frontend/src/pages/Transactions.vue`, `Accounts.vue`, `TaxRecords.vue` — каждая содержит `CURRENCY_SYMBOLS` маппинг и функцию `getCurrencySymbol`/`currencySymbol`.
|
||||
|
||||
### Пароль
|
||||
|
||||
- `admin123` — совпадает с `ADMIN_PASSWORD` в `.env`
|
||||
|
||||
## Phase 2 progress
|
||||
|
||||
| Шаг | Статус | Кем |
|
||||
| ---------------------------------------------------------------------------- | ------ | -------- |
|
||||
| Валюта у счетов (символ) | ✅ | Кит |
|
||||
| Категории с иерархией (API + фронт) | ✅ | Кит |
|
||||
| Фронт: формы CRUD (транзакции, категории, счета) | ✅ | Кит |
|
||||
| Свёртка по годам/месяцам (drill-down) | ✅ | Кит |
|
||||
| Dynamic scrolling (infinite scroll) | ✅ | Кит |
|
||||
| CRUD транзакций (API + тесты) | ✅ | Кит |
|
||||
| CRUD категорий (API + тесты) | ✅ | Кит |
|
||||
| CRUD счетов (API + тесты) | ✅ | Кит |
|
||||
| Ручная правка курсов + API | ✅ | Кит |
|
||||
| Дашборды (расход по категориям, динамика, сводная) | ✅ | Кит |
|
||||
| Multi-currency отображение (баланс в валюте счёта + base currency) | ✅ | Кит |
|
||||
| Сводная по годам | ✅ | Кит |
|
||||
| Налоговый учёт | ✅ | Кит |
|
||||
| DateTime в транзакциях (date → DateTime, datetime-local на фронте, миграция) | ✅ | Кит |
|
||||
| Символ валюты вместо колонки (Transactions, Accounts, TaxRecords) | ✅ | Кит |
|
||||
|
||||
|
||||
### Phase 2 — что сделано (подробно)
|
||||
|
||||
**API (новые эндпоинты):**
|
||||
- `GET /api/reports/monthly?year=` — помесячная разбивка доходов/расходов
|
||||
- `GET /api/reports/summary` — сводная по годам
|
||||
- `GET /api/reports/category-breakdown?year=&month=` — расходы по группам категорий (данные для дашборда)
|
||||
- `GET/POST/PUT/DELETE /api/exchange-rates` — CRUD курсов валют
|
||||
- `GET /api/exchange-rates/summary` — группировка по типу налога
|
||||
- `GET/POST/PUT/DELETE /api/tax-records` — CRUD налоговых записей
|
||||
- Accounts API теперь возвращает `balance_in_base` и `base_currency` (мультивалютность)
|
||||
|
||||
**БД:**
|
||||
- Новая таблица `tax_record` (alembic migration)
|
||||
|
||||
**Фронтенд (новые страницы):**
|
||||
- `/dashboard` — дашборд с помесячной динамикой (CSS-chart), расходами по категориям, сводной по годам
|
||||
- `/exchange-rates` — таблица курсов с фильтрами, CRUD через модалку
|
||||
- `/tax-records` — таблица налогов с фильтрами и сводкой по типам
|
||||
|
||||
**Фронтенд (доработки):**
|
||||
- `/transactions` — infinite scroll вместо пагинации (scroll-based)
|
||||
- `/accounts` — отображение баланса в валюте счёта + в базовой валюте
|
||||
- Навигация обновлена — добавлены ссылки на Дашборд, Курсы, Налоги
|
||||
|
||||
## Тесты
|
||||
|
||||
Запуск всех тестов одной командой (из `backend/`):
|
||||
|
||||
```bash
|
||||
cd backend && uv run pytest
|
||||
```
|
||||
|
||||
Verbose: `cd backend && uv run pytest -v`
|
||||
|
||||
### Фикстуры
|
||||
|
||||
Общий `conftest.py` в `tests/` предоставляет:
|
||||
- `engine` — Postgres test DB (`budget_app_test`) с `create_all`/`drop_all` на каждый тест + seed валют
|
||||
- `client` — ASGI клиент с зарегистрированным тестовым юзером
|
||||
- `auth_headers` — JWT Bearer token
|
||||
|
||||
Все тесты используют **Postgres** (не sqlite). Настройка через `settings.test_database_url`.
|
||||
|
||||
### Покрытие
|
||||
|
||||
**37 тестов + 1 skipped**:
|
||||
|
||||
| Файл | Тестов | Что проверяет |
|
||||
|------|--------|---------------|
|
||||
| `test_health.py` | 1 | Health endpoint |
|
||||
| `test_models.py` | 3 | Импорт моделей, метаданные, create_all в sqlite |
|
||||
| `test_auth.py` | 2 | Auth flow (register→login→me), unauthorized |
|
||||
| `test_api.py` | 6 | Транзакции (list, filter, search), accounts, reports, unauthorized |
|
||||
| `test_crud.py` | 6 | CRUD транзакций |
|
||||
| `test_categories.py` | 8 | CRUD групп и категорий |
|
||||
| `test_accounts.py` | 6 | CRUD счетов, удаление с транзакциями |
|
||||
| `test_account_balances.py` | 6 | **Баланс: доход+расход, переводы, cross_rate, initial_balance** |
|
||||
|
||||
### Вычисление баланса
|
||||
|
||||
`balance = initial_balance + incoming - outgoing`
|
||||
|
||||
- `incoming` = SUM(amount) if cross_rate IS NULL, SUM(amount * cross_rate) если перевод между валютами (для dest_account)
|
||||
- `outgoing` = SUM(amount) — всегда в валюте источника
|
||||
|
||||
Reference in New Issue
Block a user