No description
  • TypeScript 95.8%
  • CSS 3.4%
  • JavaScript 0.5%
  • HTML 0.3%
Find a file
bap 0b8ff4a77a
Some checks failed
Deploy to GitHub Pages / deploy (push) Has been cancelled
Merge pull request 'add notes' (#3) from feature/notes into main
Reviewed-on: #3
2026-03-29 02:16:07 +03:00
.devcontainer fix forgejo dns 2026-03-28 21:47:39 +00:00
.github/workflows GithubPages 2026-03-05 07:59:27 +00:00
public new icon 2026-03-05 08:22:07 +00:00
src add notes 2026-03-28 23:15:43 +00:00
.gitignore gitignore 2026-03-05 08:06:20 +00:00
bun.lock Структура проекта часть 1 2026-03-02 11:21:46 +00:00
components.json Начальная настройка среды разработки 2026-03-02 00:20:22 +00:00
eslint.config.js Начальная настройка среды разработки 2026-03-02 00:20:22 +00:00
index.html new icon 2026-03-05 08:22:07 +00:00
package.json Структура проекта часть 1 2026-03-02 11:21:46 +00:00
README.md 1. Добавлена новая функциональность позволяющая скопировать текущий процент ценной бумаги в плановый 2026-03-07 00:26:23 +00:00
tsconfig.app.json fix deploy error 2026-03-05 08:02:08 +00:00
tsconfig.json Структура проекта часть 1 2026-03-02 11:21:46 +00:00
tsconfig.node.json Начальная настройка среды разработки 2026-03-02 00:20:22 +00:00
vite.config.ts GithubPages 2026-03-05 07:59:27 +00:00

Portfolio Rebalancer

Одностраничное веб-приложение для управления и ребалансировки инвестиционного портфеля. Работает полностью в браузере, развёртывается на GitHub Pages как статический сайт без бэкенда.

Все данные хранятся только у пользователя на компьютере.

🔗 Live Demo


Возможности

  • Управление позициями — акции и кэш в произвольных валютах
  • Актуальные котировки — через Finnhub.io (бесплатный API)
  • Выбор типа цены — цена закрытия предыдущего дня или цена последней сделки
  • Конвертация валют — через frankfurter.app (официальные курсы ЕЦБ)
  • Ребалансировка — расчёт сколько акций купить/продать и сколько кэша добавить/вывести
  • Порог шума — мелкие сделки (< 0.1% от стоимости портфеля) автоматически игнорируются
  • Portfolio Drift — интегральная оценка отклонения от целевых весов
  • Быстрое выравнивание — клик по «Факт %» копирует текущую долю позиции в целевой процент
  • Контроль суммы — бейдж показывает суммарный целевой % и отклонение от 100%; блок ребалансировки предупреждает, если сумма некорректна
  • Портфель в файле — сохранение/загрузка JSON; API-ключ и настройки сохраняются вместе с портфелем
  • Двуязычный интерфейс — русский и английский
  • Светлая и тёмная тема
  • Встроенная справка — инструкция по использованию, описание алгоритма, форматы тикеров

Стек

Инструмент Версия Назначение
Bun latest Runtime, пакетный менеджер, сборка
React 19 UI
TypeScript ~5.9 Типизация
Vite 7 Dev-сервер и бандлер
Tailwind CSS v4 Стили
shadcn/ui latest UI-компоненты (Neutral theme)
Zustand 5 Управление состоянием
lucide-react latest Иконки

Источники данных

Сервис Данные Ограничения
Finnhub.io Котировки акций 60 запросов/мин, требует бесплатный API-ключ
frankfurter.app Курсы валют (ЕЦБ) ~33 валюты, RUB не поддерживается с 2022

Настройка API-ключа Finnhub

Вариант 1 — через интерфейс (рекомендуется):

  1. Зарегистрироваться на finnhub.io
  2. Скопировать API Key из Dashboard
  3. Открыть приложение → кнопка ⚙ → вставить ключ
  4. Сохранить портфель — ключ запишется в JSON-файл и будет восстановлен при следующей загрузке

Вариант 2 — через переменную окружения (для локальной разработки):

# .env
VITE_FINNHUB_API_KEY=your_key_here

Режим цены акции

Настраивается в ⚙ → «Цена акции». Сохраняется в JSON-файл.

Режим Поле Finnhub Описание
Цена закрытия pc Официальная цена закрытия предыдущего торгового дня. Стабильна весь день
Последняя сделка c Цена последней совершённой сделки. Актуальна при высокой волатильности

При недоступности основного поля (рынок закрыт, значение = 0) — автоматический fallback на альтернативное поле.


Формат портфеля (JSON)

{
  "baseCurrency": "USD",
  "positions": [
    { "ticker": "AAPL", "quantity": 10, "targetPercent": 25 },
    { "ticker": "BMW.DE", "quantity": 5, "targetPercent": 20 }
  ],
  "cash": [
    { "currency": "USD", "amount": 5000, "targetPercent": 30 },
    { "currency": "EUR", "amount": 2000, "targetPercent": 25 }
  ],
  "finnhubApiKey": "your_key_here",
  "priceMode": "previousClose"
}

Сумма всех targetPercent (акции + кэш) должна равняться 100%.

Поля finnhubApiKey и priceMode опциональны — сохраняются автоматически при сохранении портфеля.


Формат тикеров

Тикер Биржа Валюта
AAPL, MSFT NASDAQ / NYSE USD
BMW.DE XETRA EUR
HSBA.L LSE GBP
7203.T TSE (Tokyo) JPY
0700.HK HKEX HKD
CBA.AX ASX AUD
RY.TO TSX CAD

Алгоритм ребалансировки

Расчёт текущего состояния

currentValue[i]   = price[i] × quantity[i]          (в базовой валюте)
totalValue        = Σ currentValue[i]                (акции + кэш)
currentPercent[i] = currentValue[i] / totalValue × 100
delta[i]          = targetPercent[i]  currentPercent[i]

Рекомендации по акциям

targetQuantity[i] = floor(targetPercent[i] / 100 × totalValue / priceBase[i])
diff[i]           = targetQuantity[i]  currentQuantity[i]

Используется Math.floor — не выходить за бюджет целевой аллокации.

Порог шума

Сделка игнорируется (показывается «Держать»), если:

|tradeValue[i]| / totalValue < 0.1%

Это устраняет ложные рекомендации, возникающие из-за округления целевых процентов.

Остаток распределяется в кэш

После выделения средств под акции оставшаяся сумма распределяется между кэш-позициями пропорционально их targetPercent.

Portfolio Drift

Drift = 0.5 × Σ |currentPercent[i]  targetPercent[i]|

Показывает, какую долю портфеля нужно переложить для достижения цели.

Drift Интерпретация
< 5% В норме, ребалансировка не нужна
510% Стоит рассмотреть ребалансировку
> 10% Ребалансировка оправдана

Разработка

Требования

Запуск

# 1. Клонировать репозиторий, открыть в VSCode
# 2. Command Palette → "Reopen in Container"
# 3. В терминале контейнера:
bun run dev

Приложение доступно на http://localhost:5173.

Сборка

bun run build

Структура проекта

src/
├── components/
│   ├── ui/                  # shadcn/ui (сгенерировано)
│   ├── Header.tsx           # шапка: навигация, настройки, тема, язык
│   ├── HelpModal.tsx        # встроенная справка (RU/EN)
│   ├── PortfolioTable.tsx   # таблица позиций с inline-редактированием
│   ├── RebalanceTable.tsx   # таблица рекомендаций по ребалансировке
│   └── PortfolioChart.tsx   # круговая диаграмма портфеля (SVG)
├── store/
│   ├── portfolioStore.ts    # Zustand: портфель, котировки, ребалансировка
│   └── settingsStore.ts     # Zustand: тема, язык, режим цены
├── services/
│   ├── finnhub.ts           # котировки акций (Finnhub.io)
│   └── frankfurter.ts       # курсы валют (ECB / frankfurter.app)
├── lib/
│   ├── rebalance.ts         # логика ребалансировки + Portfolio Drift
│   ├── i18n.ts              # переводы RU/EN
│   ├── types.ts             # TypeScript типы
│   └── utils.ts             # shadcn утилиты (cn)
├── App.tsx
└── main.tsx

Deploy pipeline

Forgejo (self-hosted) → GitHub (mirror) → GitHub Pages

Push в main автоматически запускает GitHub Actions workflow (.github/workflows/deploy.yml): bun installbun run build → deploy dist/ на GitHub Pages.


Лицензия

MIT