openportfolio
openportfolio
Каждый счёт — одна книга. Каждый прогноз — на записи.
Открытый, самостоятельно размещаемый трекер портфеля. Он сводит все брокерские счета, пенсионные накопления, кошельки и банковские счета в единый показатель чистого состояния, хранит потоки инвестора за ценой и оценивает по Брайеру прогнозы, которые вы зарегистрировали до того, как они сбылись. Ни одного ключа API провайдера — нигде в нём.
Веб-сайт · Документация · Живое демо
Панель управления и бэкенд — в один клик. Синхронизирующий воркер работает на вашей машине, по замыслу: см. Развёртывание.
Скриншоты показывают демонстрационную книгу. Все цифры в них вымышлены.
Статус: предрелизный. Он работает, и описанная ниже настройка работает. Интерфейсы ещё будут меняться.
Это не торговый бот. В бэкенде нет ни одной функции, которая размещает ордер, поставляемые адаптеры объявляют canPlaceOrders: false, а PlaceOrderRequest требует OrderConfirmation, у которого нет значения по умолчанию. Что он делает — агрегирует, наблюдает и ведёт счёт.
Зачем
Две проблемы, которые выглядят несвязанными, а на деле — одна и та же.
Портфель разрознен по своей конструкции. Брокер здесь, пенсия там, ISA, биржевой счёт, банковский баланс, актив, который не вернёт ни один API. Каждое из этих приложений показывает вам число, и ни одно не показывает ваше. Поэтому цифры, которые на самом деле решают, — итог, концентрация в одном имени, доля книги в валюте, которой вы не тратите, — это цифры, которых нет ни у кого. Их оценивают на глаз, и оценка щедра в ту сторону, которая позволяет избежать решения.
Рыночные комментарии безответственны — и стали ещё более безответственными с того момента, как модель стала выдавать уверенный направленный прогноз на любой ваш вопрос. Проблема не в том, что прогнозы ошибочны. Проблема в том, что ошибиться ничего не стоит и не оставляет следов, поэтому прогнозист, которого стоит читать, и прогнозист, который просто красноречив, неотличимы снаружи — и изнутри тоже.
Обе проблемы — это провалы учёта, поэтому openportfolio относится к ним как к учёту.
Одно чистое состояние | Счета, подтянутые через адаптеры площадок в единую базовую валюту, с курсом, сохранённым на той строке, которую он конвертировал, так что снимок фиксирует, сколько стоила книга тогда, а не сколько говорят сегодняшние курсы. Позиции, удерживаемые в трёх местах, — это одна экспозиция. |
Потоки, а не только цены | Цена — это результат того, кто покупал, а кого заставили продавать. Чистые покупки по типу инвестора, оборачиваемость и календарь датированных будущих событий хранятся как первоклассные ряды, а не вычисляются, когда кто-то вспомнит спросить. Вынужденный продавец — по расписанию, и расписание публично. Позиционирование по классам участников приходит без ключей из еженедельного отчёта CFTC Commitments of Traders. |
Оценённый послужной список | Прогноз регистрируется до факта с вероятностью, горизонтом и условием, которое его разрешает. Когда горизонт проходит, те прогнозы, что разрешаются машинно, разрешаются сами и получают оценку по Брайеру. Диаграмма надёжности — это и есть продукт: она показывает, что вы сказали, что произошло и каков разрыв между ними. |
Есть и четвёртая таблица, которая существует по одной причине. Рекомендация, сформулированная как «дождитесь публикации, потом решайте», испаряется в момент, когда её произносят вслух. decisions — это очередь таких решений, каждое с условием-триггером и исходом, и они остаются на доске, пока одно из них не изменится.
Related MCP server: FinChat
Ни одного ключа API провайдера
Наблюдение за книгой полезно только если кто-то действительно наблюдает: сверяет после закрытия, разрешает прогноз в день, когда истекает его горизонт, замечает, что отложенное решение созрело три недели назад.
Тарифицируемый по объёму вывод — неправильная форма для этого. Когда каждый запуск списывает деньги за токены, каждая автономная проверка становится покупкой, а продукт, который тратит деньги оператора без запроса, должен сначала спрашивать, либо пакетировать, либо нормировать. Все три варианта превращают портфель, который следит за собой, в портфель, который просит разрешения посмотреть.
Поэтому каждый вызов модели вместо этого направляется в агентный CLI, в который вы уже вошли: codex, antigravity или claude, с порядком резервного переключения по задачам. В этом репозитории нет ключа провайдера и нет поля, куда его можно вставить. Это не делает запуск бесплатным: у подписочных планов есть лимиты скорости, и цепочка резервирования существует отчасти потому, что один провайдер исчерпывается раньше других. Меняется тип лимита. Работа агента ограничена квотой и настенными часами, а не расходами, поэтому её не нужно оправдывать по одному вызову за раз.
Следствие: openportfolio по замыслу размещается самостоятельно. Ваше развёртывание выполняет ваши синхронизации на вашей машине под вашими логинами, против ваших собственных счетов.
Что он делает
Поверхность | |
Чистое состояние | счета, балансы, разбивка по площадкам и классам активов, снимки в одной базовой валюте, FX без ключей |
Площадки | контракт адаптера с объявленными возможностями; котировочные адаптеры без ключей для листингуемых инструментов и для монет, плюс ручной |
Потоки | чистые покупки и оборачиваемость по типу инвестора за сессию, по рынку или по символу |
Прогнозы | вероятность, горизонт и критерий разрешения; авторазрешение по истечении горизонта; балл Брайера и корзины надёжности |
Решения | очередь отложенных решений, с условиями-триггерами и исходами |
Катализаторы | датированные будущие события и активы, которых они касаются |
Аудит | добавляемая-только запись каждой изменяющей состояние мутации, включая то, что крон сделал без присмотра |
MCP | 25 инструментов, чтобы |
Мультитенантность | каждая таблица ограничена тенантом, каждый индекс начинается с него, один сервисный ключ на тенанта |
Быстрый старт
Node 22+, pnpm и аккаунт Convex. Бесплатного тарифа достаточно.
git clone https://github.com/seonglae/openportfolio.git
cd openportfolio
pnpm install
cp .env.example .env.local
npx convex dev --once # creates the deployment
# create the first book
npx convex env set OPENPORTFOLIO_DEV_TENANT home
npx convex run tenants:create '{"slug":"home","name":"Home","baseCurrency":"GBP"}'
# the UI, then the sync loop
pnpm --filter openportfolio-browser dev # http://localhost:6101
npx tsx sync-worker.mts --onceПока ничего не подключено, он регистрирует площадки, которые может обслуживать, и фиксирует чистое состояние в ноль, что корректно. Добавьте файл ручных активов, чтобы получить настоящее:
[
{ "accountKey": "isa", "symbol": "VWRL", "assetClass": "etf", "qty": 40, "price": 118.2, "currency": "GBP" },
{ "accountKey": "wallet", "symbol": "BTC", "assetClass": "crypto", "qty": 0.15, "price": 0, "currency": "USD" }
]export OPENPORTFOLIO_MANUAL_HOLDINGS=$PWD/holdings.json
npx convex run accounts:link '{"accountKey":"isa","venue":"manual","kind":"brokerage","label":"ISA","currency":"GBP"}'
npx convex run accounts:link '{"accountKey":"wallet","venue":"manual","kind":"wallet","label":"Wallet","currency":"USD"}'
npx tsx sync-worker.mts --onceЦены в файле — отправная точка, а не запись: воркер заново котирует каждую строку, которую может, через источник без ключей, маршрутизируя по классу активов, конвертирует их в GBP и записывает один итог. Акции, ETF и фонды идут в Yahoo, монеты — в CoinGecko. Строка, чья записанная цена и есть запись, — пенсия или недвижимость — получает класс other, о котором ни один источник котировок не спрашивается.
Полное руководство: openportfolio.app/docs/quickstart
Или разверните его
Этот поток клонирует этот репозиторий в ваш собственный Git-аккаунт, устанавливает интеграцию Convex из Vercel Marketplace и выделяет проект Convex под вашей собственной командой Convex, запрашивает у вас одно значение и собирает обе половины одной командой:
npx convex deploy --cmd-url-env-var-name VITE_CONVEX_URL --cmd 'pnpm --filter openportfolio-browser build'Шаг Marketplace — единственная причина, по которой это один клик, а не два: Vercel может создать бэкенд во время импорта, вместо того чтобы отправлять вас создавать его сначала, и передаёт сборке ключ развёртывания. vercel.json защищает команду условием CONVEX_DEPLOY_KEY и откатывается к обычной браузерной сборке, так что тот же файл покрывает и развёртывание, которое вы подготовили сами, а теперь хотите размещённую страницу. Без защиты этот случай провалил бы сборку.
Он не спрашивает у вас ничего. Вход не требует ключа, потому что он работает внутри только что созданного развёртывания: аутентификация — это Convex Auth с провайдером пароля, так что ваше развёртывание само выпускает и проверяет свои токены, и вход никогда его не покидает. Никакой компании-аутентификатора на пути, никакого аккаунта, который нужно создавать где-то ещё. Convex Auth находится в бете у вышестоящего проекта, что и есть честная цена этого выбора.
Затем одна команда на стороне Convex для генерации ключей подписи развертывания и создания первого портфеля. Первый зарегистрировавшийся становится его владельцем:
npx @convex-dev/auth
npx convex run tenants:create '{"slug":"home","name":"Home","baseCurrency":"GBP"}'Регистрации затем закрываются сами: вызывающий, не принадлежащий ни одному тенанту, может создать только самый первый портфель, поэтому публичный URL не становится чьим-то чужим бэкендом. OPENPORTFOLIO_OPEN_SIGNUP=1 снова открывает её.
Воркер синхронизации не является частью этого и не может быть. Он читает ваши счета через адаптеры и отправляет модельную работу в CLI агента, в который вы вошли, а внутри бессерверной функции нет вошедшего CLI. Развернутая половина по-прежнему вычисляет прогнозы и оценивает их на собственных кронах Convex; запускайте воркер, когда хотите обновить балансы. Подробности: Развертывание.
Кнопки Cloudflare нет: эта кнопка поддерживает только Workers, и её режим монорепозитория требует полной изоляции приложения в его подкаталоге, чем browser/ не является.
Перед тем как открыть доступ
Две вещи открыты на localhost и должны быть закрыты, прежде чем развертывание станет доступным из интернета.
Dev-тенант. Пока установлен
OPENPORTFOLIO_DEV_TENANT, любой неаутентифицированный вызывающий ограничен этим тенантом. Сбросьте его. Вход уже есть и не требует настройки.Сервисные ключи. У Workers и MCP-сервера нет браузерной сессии, поэтому они предъявляют ключ. Сгенерируйте его локально и отправьте только его хэш.
npx @convex-dev/auth # once, generates this deployment's signing keys
npx convex env unset OPENPORTFOLIO_DEV_TENANT
KEY="$(openssl rand -hex 32)"
npx convex run tenants:issueServiceKey "{\"key\":\"$KEY\",\"label\":\"sync-worker\",\"role\":\"member\"}"
echo "OPENPORTFOLIO_SERVICE_KEY=$KEY" >> .env.localМультитенантность
Одно развертывание содержит много портфелей. Инвариант: вызывающий никогда не сообщает, какой он тенант.
tenantId выводится из строк членства вызывающего или из собственной строки сервисного ключа, поэтому нет аргумента, который клиент может установить, чтобы получить доступ к другому портфелю. Публичный API принимает tenantSlug, и только как дискриминатор для вызывающего, принадлежащего нескольким тенантам; членство по-прежнему решает. Идентификатор документа, принадлежащий другому тенанту, читается как отсутствующий, а не запрещённый, потому что «запрещено» подтверждает существование строки, что само по себе является межтенантным чтением.
Каждый индекс начинается с tenantId, поэтому запрос, забывающий область, вообще не может использовать индекс. Одно исключение намеренно и отмечено: крон резолвера просматривает просроченные вызовы каждого портфеля через индекс без тенанта и является internalMutation именно по этой причине. Он недоступен ни одному клиенту.
Подробности: openportfolio.app/docs/multi-tenancy
Адаптеры площадок
Адаптер объявляет, что он умеет, и реализует только это:
type VenueAdapter = {
venue: string;
kind: AccountKind;
capabilities: { canReadBalances: boolean; canReadQuotes: boolean; canPlaceOrders: boolean };
readBalances(request: ReadBalancesRequest): Promise<AdapterBalance[]>;
readQuote(request: ReadQuoteRequest): Promise<AdapterQuote>;
placeOrder?(request: PlaceOrderRequest): Promise<OrderReceipt>;
};Поставляются четыре, ни один из них не требует ключа. yahoo оценивает всё, что где-либо котируется, в той валюте, в которой торгуется листинг, поэтому портфель из акций США, ETF LSE и наименований KRX остаётся актуальным без счёта ни в одном из них. coingecko оценивает монеты. Оба отказываются от балансов, потому что источник цен не знает, что у вас есть, и возврат пустого списка читался бы как «у вас ничего нет». manual читает JSON-файл, который вы ведёте, — так пенсия или некотируемая позиция попадает в итог, а не остаётся за его пределами. csv читает собственный экспорт брокера: укажите OPENPORTFOLIO_CSV_DIR на папку и поместите в неё <accountKey>.csv. Столбцы сопоставляются по имени, поэтому большинство экспортов работают без правок, и он покрывает счета, у которых вообще никогда не будет API.
Какой источник оценивает какую строку, решает уже указанный на ней класс актива, и ничто не переключается на другой. Оба отвечают на неправильный инструмент с HTTP 200: спросите Yahoo про BTC — и он вернёт траст Grayscale около $30 вместо биткоина около $68 000, а у CoinGecko есть токен с id aapl стоимостью около 18 центов. Неправильное число в итоге хуже, чем отсутствующее, поэтому класс, о котором не следует спрашивать ни один источник, просто не переоценивается.
Ни один адаптер брокера с ключом не поставляется. Добавление такого означает написание модуля в packages/node/src/adapters/, получение его учётных данных из окружения воркера и регистрацию в defaultRegistry(). Держите учётные данные в процессе воркера: бэкенд их никогда не видит, и этот репозиторий тоже.
Подробности: openportfolio.app/docs/adapters
Требования
Node 22+, pnpm
Учётная запись Convex (бесплатного тарифа достаточно)
Как минимум один вошедший CLI агента, если вы хотите агентский воркер:
codex,antigravity(agy) илиclaudeБольше ничего. Аутентификация — это Convex Auth, работающий в вашем собственном развертывании, поэтому нет поставщика удостоверений, для которого нужно регистрироваться.
Разработка
pnpm typecheck # every workspace, src and test alike
pnpm test # vitest across packages, convex handlers, browser helpers
# the demo build used for the screenshots and the hosted demo
pnpm --filter openportfolio-browser exec vite build --config vite.demo.config.ts
# the marketing site and docs are static; regenerate the docs pages after editing
python3 site/build-docs.pyСоглашения, полный инвариант тенанта и заметки для агентских CLI, работающих в этом репозитории, находятся в AGENTS.md.
Лицензия
Apache-2.0. См. LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to manage and analyze personal investment portfolios, including fund and stock holdings, net value tracking, XIRR calculations, penetration analysis, and backtesting.Apache 2.0
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to manage personal finances through MCP tools for transaction management, spending analytics, and goal tracking.1
- FlicenseNot gradedqualityBmaintenanceSelf-hosted household finance app for shared expenses, budgets, investments, loans, and zakat, exposing MCP tools for AI agents to manage finances via natural language.3
- FlicenseNot gradedqualityCmaintenanceAn MCP server that exposes personal financial data — transaction ledger, portfolio holdings, live/historical market prices, and quantitative risk metrics — as standardized tools, resources, and prompts, enabling natural language reasoning over real computed numbers.
Related MCP Connectors
Real SEC, 13F, insider, congress & macro data your AI agent can cite. Hosted MCP, 24 tools.
The financial MCP for AI agents - 90+ financial tables, SEC filings, signals, alt-data.
Shared long-term memory vault for AI agents with 20 MCP tools.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/seonglae/openportfolio'
If you have feedback or need assistance with the MCP directory API, please join our Discord server