Skip to main content
Glama
openportfolio

openportfolio

by seonglae

openportfolio

すべての口座を一冊の帳簿に。すべての予測を記録に。

オープンソース・セルフホスト型のポートフォリオトラッカー。あらゆる証券会社、年金、ウォレット、銀行口座を単一の純資産に統合し、価格の背後にある投資家の資金フローを保存し、事前に登録した予測をBrierスコアで評価します。プロバイダーのAPIキーはどこにも必要ありません。

License npm TypeScript Stars

ウェブサイト · ドキュメント · ライブデモ

Deploy with Vercel

ダッシュボードとバックエンドをワンクリックで。同期ワーカーは設計上、お使いのマシン上で動作します。デプロイを参照してください。

スクリーンショットはデモの帳簿を示しています。そこに含まれる数字はすべて架空のものです。

ステータス: プレリリース。 動作し、以下のセットアップも機能します。インターフェースは今後変更される可能性があります。

これは取引ボットではありません。 バックエンドには注文を発注する機能はなく、同梱のアダプターは canPlaceOrders: false を宣言しており、PlaceOrderRequest にはデフォルトのない OrderConfirmation が必要です。このツールが行うのは、集約、監視、そしてスコアリングです。

なぜ

無関係に見えて実は同じ問題である、二つの問題。

ポートフォリオは構造的に分散しています。 ここに証券会社、あそこに年金、ISA、取引所口座、銀行残高、そしてどのAPIも返してくれない保有資産。それらのアプリはそれぞれ数字を表示しますが、どれもあなたの本当の数字を表示しません。だから実際に意思決定を左右する数字、つまり合計、一つの銘柄への集中度、使わない通貨で保有されている割合は、誰も持っていない数字なのです。それらは推定され、その推定は意思決定を避ける方向に甘くなります。

市場コメントは説明責任を負いません。 そして、モデルが何を尋ねても自信満々の方向性のある見解を出すようになった瞬間から、その傾向はさらに強まりました。問題は見解が間違っていることではありません。間違ってもコストがかからず痕跡も残らないため、読む価値のある予測者と、ただ流暢なだけの予測者が、外部からも内部からも区別できないことです。

どちらも簿記の失敗なので、openportfolioはそれらを簿記として扱います。

一つの純資産

口座は会場アダプターを通じて単一の基準通貨に統合され、為替レートは換算した行に保存されます。そのためスナップショットは、今日のレートが示す価値ではなく、当時の帳簿の価値を記録します。3つの場所に保有されているポジションは、一つのエクスポージャーです。

価格だけでなくフロー

価格は、誰が買い、誰が売らされたかの結果です。投資家タイプ別の純買い、売買回転率、日付付きの将来イベントのカレンダーは、誰かが思い出したときに導出されるのではなく、第一級の時系列として保存されます。強制売り手はスケジュールに従っており、そのスケジュールは公開されています。参加者クラス別のポジションは、CFTCの週次Commitments of Tradersからキーなしで取得されます。

スコア化された実績

予測は、確率、期間、そしてそれを確定させる条件とともに事前に登録されます。期間が経過すると、機械で解決可能なものは自動的に解決され、Brierスコアが付けられます。信頼度ダイアグラムが成果物です:あなたが言ったこと、実際に起きたこと、そしてその間のギャップを示します。

4つ目のテーブルが、ただ一つの理由で存在します。「発表を待ってから決める」という形の推奨は、口に出した瞬間に消えてしまいます。decisions はそのようなもののキューであり、それぞれにトリガー条件と結果があり、いずれかが変化するまでボードに残り続けます。

Related MCP server: Financial Context Engine

プロバイダーAPIキーは不要

帳簿を監視することは、実際に何かが監視している場合にのみ有用です:取引終了後の照合、期間が経過した日に予測を確定すること、3週間前に期限が来ていた先送りされた決定に気づくこと。

従量課金の推論はその用途には不適切です。実行ごとにトークン単位で課金される場合、自律的なチェックはすべて購入になり、運用者のお金を無断で使う製品は、まず許可を求めるか、バッチ処理するか、配給する必要があります。その3つすべてが、自分自身を監視するポートフォリオを、見るために許可を求めるポートフォリオに変えてしまいます。

そこで、すべてのモデル呼び出しは、代わりにあなたがすでにサインインしているエージェントCLI、codex、antigravity、または claude にタスクごとのフォールバック順序でディスパッチされます。このリポジトリにはプロバイダーキーはなく、それを入れるフィールドもありません。それでも実行が無料になるわけではありません:サブスクリプションプランにはレート制限があり、フォールバックチェーンが存在する理由の一部は、あるプロバイダーが他のプロバイダーより先に枯渇するからです。変わるのは制限の種類です。エージェントの作業は支出ではなくクォータと実時間によって制限されるため、1回の呼び出しごとに正当化する必要がありません。

その結果、openportfolioは設計上セルフホストです。あなたのデプロイは、あなたのマシン上で、あなた自身のログインで、あなた自身の口座に対して同期を実行します。

機能

サーフェス

純資産

口座、残高、会場別・資産クラス別の内訳、単一基準通貨でのスナップショット、キーなしFX

会場

宣言された機能を持つアダプター契約; 上場証券とコイン用のキーなしクォートアダプター、および手動アダプター

フロー

セッションごと、市場ごと、またはシンボルごとの投資家タイプ別の純買いと売買回転率

予測

確率、期間、解決基準; 期間満了時の自動解決; Brierスコアと信頼度バケット

決定

先送りされた決定のキュー、トリガー条件と結果付き

カタリスト

日付付きの将来イベントとそれが影響する資産

監査

すべての状態変更ミューテーションの追記専用記録、cronが無人で行ったものも含む

MCP

25のツールにより、codex / antigravity / claude が帳簿を直接読み書き可能

マルチテナンシー

すべてのテーブルがテナントにスコープされ、すべてのインデックスがテナントを先頭に、テナントごとに1つのサービスキー

クイックスタート

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

またはデプロイする

Deploy with Vercel

このフローは、このリポジトリをあなた自身のGitアカウントにクローンし、Vercel MarketplaceからConvexインテグレーションをインストールし、あなた自身のConvexチームの下にConvexプロジェクトをプロビジョニングし、1つの値だけを尋ね、両方の半分を単一のコマンドでビルドします:

npx convex deploy --cmd-url-env-var-name VITE_CONVEX_URL --cmd 'pnpm --filter openportfolio-browser build'

Marketplaceのステップが、これが2クリックではなく1クリックである唯一の理由です:Vercelはインポート中にバックエンドを作成できるため、先に作成しに行く必要がなく、ビルドにデプロイキーを渡します。vercel.json は CONVEX_DEPLOY_KEY でコマンドをガードし、プレーンなブラウザビルドにフォールバックするため、同じファイルが、自分でプロビジョニングしてホストされたページを欲しいというデプロイもカバーします。ガードがなければ、そのケースはビルドに失敗します。

何も尋ねません。サインインにキーは不要です。なぜなら、作成されたばかりのデプロイメント内で実行されるからです:認証はパスワードプロバイダーを使用したConvex Authであり、あなたのデプロイメントが独自のトークンを発行・検証し、サインインがデプロイメントの外に出ることはありません。経路に認証会社はなく、他の場所でアカウントを作成する必要もありません。Convex Authは上流でベータ版であり、それがこの選択の正直なコストです。

次に、Convex側で1つのコマンドを実行して、デプロイメントの署名鍵を生成し、最初のブックを作成します。最初のサインアップがそれを所有します:

npx @convex-dev/auth
npx convex run tenants:create '{"slug":"home","name":"Home","baseCurrency":"GBP"}'

サインアップはその後自動的に閉じられます。どのテナントにも属さない呼び出し元は、最初のブックしか作成できないため、公開URLが他人のバックエンドになることはありません。OPENPORTFOLIO_OPEN_SIGNUP=1で再び開きます。

同期ワーカーはこの一部ではなく、そうなることはできません。アダプターを通じてあなたのアカウントを読み取り、モデル作業をあなたがサインインしているエージェントCLIにディスパッチしますが、サーバーレス関数内にはサインイン済みのCLIはありません。デプロイされた側は依然として予測を解決し、Convex自身のcronでスコアリングします。残高を更新したいときにワーカーを実行してください。詳細:Deploying。

Cloudflareボタンはありません。そのボタンはWorkersのみをサポートしており、そのモノレポモードはアプリがサブディレクトリに完全に分離されていることを要求しますが、browser/はそうではありません。

公開する前に

localhostで開いている2つのものがあり、デプロイメントがインターネットから到達可能になる前に閉じる必要があります。

  1. 開発テナント。 OPENPORTFOLIO_DEV_TENANTが設定されている間、認証されていない呼び出し元はそのテナントにスコープされます。それを設定解除してください。サインインはすでにあり、設定は不要です。

  2. サービスキー。 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

マルチテナンシー

1つのデプロイメントが多くのブックを保持します。不変条件は、呼び出し元が自分がどのテナントかを決して言わないことです。

tenantIdは呼び出し元のメンバーシップ行またはサービスキー自身の行から導出されるため、クライアントが別のブックに到達するために設定できる引数はありません。公開APIはtenantSlugを受け入れますが、それは複数のテナントに属する呼び出し元の曖昧さを解消するためだけです。メンバーシップが依然として決定します。別のテナントに属するドキュメントIDは、禁止ではなく欠落として読み取られます。「禁止」は行の存在を確認することになり、それ自体がクロステナント読み取りになるためです。

すべてのインデックスはtenantIdで始まるため、スコープを忘れたクエリはインデックスをまったく使用できません。1つの例外は意図的でマークされています。リゾルバーcronは、テナントなしのインデックスを通じてすべてのブックの期限切れコールをスイープし、まさにその理由から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>;
};

4つが同梱されており、いずれもキーは不要です。yahooは、上場されている場所ならどこでも、その上場が取引される通貨で価格を付けます。そのため、米国株、LSE ETF、KRX銘柄のブックは、いずれのアカウントも持たずに最新の状態を保てます。coingeckoはコインの価格を付けます。両方とも残高を拒否します。価格ソースはあなたが何を保有しているかを知らないため、空のリストを返すと「何も保有していない」と読まれるからです。manualはあなたが管理するJSONファイルを読み取ります。これにより、年金や非上場の保有が合計から除外されるのではなく、組み込まれます。csvはブローカー自身のエクスポートを読み取ります。OPENPORTFOLIO_CSV_DIRをフォルダーに向けて、<accountKey>.csvをそこに置きます。列は名前で照合されるため、ほとんどのエクスポートは編集なしで機能し、APIが決してないであろうアカウントもカバーします。

どのソースがどの行の価格を付けるかは、すでにその行にあるアセットクラスによって決定され、どちらかにフォールバックすることはありません。両方とも、間違った証券に対してHTTP 200で応答します。YahooにBTCを尋ねると、約68,000ドルのビットコインではなく、約30ドルのGrayscaleトラストが返され、CoinGeckoには約18セントの価値があるaaplというIDのトークンがあります。合計の間違った数字は、欠落した数字よりも悪いため、どちらのソースにも尋ねるべきでないクラスは、単に再価格設定されません。

キー付きブローカーアダプターは同梱されていません。追加するには、packages/node/src/adapters/にモジュールを書き、ワーカーの環境から資格情報を取得し、defaultRegistry()に登録します。資格情報はワーカープロセスに保持してください。バックエンドもこのリポジトリもそれを見ることはありません。

詳細:openportfolio.app/docs/adapters

要件

  • Node 22+、pnpm

  • Convexアカウント(無料ティアで十分)

  • エージェントワーカーを使用する場合は、サインイン済みのエージェントCLIが少なくとも1つ: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を参照してください。

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables 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
  • F
    license
    Not graded
    quality
    D
    maintenance
    An 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.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to operate a self-hosted financial platform over MCP, managing books, expenses, revenues, settlements, payments, accounts, categories, transfers, positions, and cash flow with idempotent and audited writes.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants and agents to securely query, reconcile, analyze, and manage personal finances in Actual Budget through MCP tools and APIs.
    1,036 npm
    MIT