asset-management について
asset-managementは、米ドル建ての株式・ETFを追跡する個人投資家向けコマンドラインツールです。取引ログから監査可能な数値を算出し、AIによる説明を計算から分離して、ルール名付きの提案を示します。
主な機能
- 01ドローダウン重視のポートフォリオ概要
- 02ルールに基づく売買提案
- 03制約付きの任意AIナレーション
- 04ベンチマークとのバックテスト
- 05GhostfolioのCSV・JSONをインポート
- 06オフライン価格キャッシュ
活用シーン
- 01ポートフォリオの下落と回復の確認
- 02ルール付きリバランス案の作成
- 03ベンチマークに対する配分の検証
- 04Ghostfolioから保有データをインポート
MCP 対応
Claude Desktop向けの読み取り専用MCPアドオンを備え、同じ検証済みコアを使ってポートフォリオについて対話できます。アドオンはローカルの価格キャッシュを利用できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
asset-management
要約: uvx --from git+https://github.com/disin7c9/asset-management asset-management --demo — 同梱のサンプルポートフォリオを使った、ドローダウンを重視したポートフォリオ概要を、セットアップ不要で1つのコマンドで表示します。USD のみ、ロングのみの株式/ETF に対応しています。取引履歴はご自身で管理してください。
個人の株式/ETF ポートフォリオを追跡し、監査可能な提案を受け取れます。すべての数値は Python が計算します。オプションの AI は内容を説明しますが、数値を受け取ることはありません。また、AI が引用する値はすべて、計算結果から置き換えられます。
🔢 数値の保護
多くの AI 金融ツールでは、モデルが数値を生成します。ここでは、モデルができるのは {{token}} プレースホルダーを配置することだけです。決定論的なレンダラーが、検証済みのコアから数値を置き換えます。さらに、モデルが自分で数字を入力した場合、ナレーション全体を拒否します。あらゆる Unicode 数値カテゴリが対象となるため、3.2 と同様に ½ も拒否されます:
the model wrote │ the reader gets
─────────────────────────────────────────────┼─────────────────────────────────────────────
"Your deepest stretch fell │ "Your deepest stretch fell -9.84% from
{{max_drawdown}} from its peak, and an │ its peak, and an ulcer index of 2.38%
ulcer index of {{ulcer}} says the ride │ says the ride stayed shallow."
stayed shallow." │ ✔ every figure substituted from the
│ validated core
─────────────────────────────────────────────┼─────────────────────────────────────────────
"Your portfolio fell 12% but recovered │ (no summary at all)
nicely — don't worry." │ ✘ REFUSED — one model-typed numeral voids
│ the entire note; the brief prints
│ without it
両方の処理を実際に実行してみてください。API キーは不要で、本番環境で使われる数値保護機構を実際に動かします:uv run python scripts/demo_fence.py
🎁 得られるもの
- 実際の保有資産をドローダウン優先でまとめた概要 — ピークからどれだけ下落したか、どれだけ長く含み損の状態だったか、どれだけ回復したかを確認できます。サンプリングされた各リスク統計にはブートストラップ信頼区間が付きます(リターンは会計上の恒等式であるため、正直に信頼区間は付きません)。
- 選択した目標に向けた決定論的な買い/売りの提案。各行には、それを導いた名前付きルールが併記されるため、ボットを信頼するのではなく、ルールを学べます。
- オプションで保護機構付きのAI ナレーションを利用できるほか、同じ検証済みコアを使った読み取り専用のClaude Desktop アドオン(「ポートフォリオとチャット」)も利用できます。
保有資産は追記専用の取引履歴(各行に日付、ティッカー、アクション、数量、価格、手数料を記録)から導出され、保存されることはありません。そのため、同じ入力からは常に同じ出力が得られます。価格はプロバイダーのフォールバック機能を使って取得され(Yahoo Finance が主、Tiingo が副。Tiingo では無料の API キーを使用)、ディスク上にキャッシュされます。表示されるすべての数値は出典を追跡できます。また、正直に計算できない数値(期間が短すぎる、実際に解が存在しないなど)は、でっち上げるのではなく n/a と表示します。株式分割は自動調整されます(株数を分割調整後の価格履歴と照合するため、保有期間中の分割でリターンが歪むことはありません)。分割情報フィードが利用できない場合は、不一致検出機能が 2:1 以上の比率を検出します。
📑 目次
- 🚀 まずはこちら
- 🔍 数値を信頼できる理由
- 💬 ポートフォリオについて相談する — Claude Desktop アドオン(読み取り専用 MCP)
- 📖 リファレンス
- 🔧 プロジェクト
- 🔒 プライバシーポリシー
- 📜 ライセンス
🚀 まずはこちら
⚡ 60 秒で試す(同梱の架空ポートフォリオ、セットアップ不要)
uvx --from git+https://github.com/disin7c9/asset-management asset-management --demo
または、クローンしたディレクトリから実行します:uv sync && uv run python -m app --demo(Python 3.12 と uv が必要です)。
全機能を試す(同梱のポートフォリオを使い、オンラインで約 1 分)— 以下の 2 つのコマンドで、このツールの特徴的な機能を最初から最後まで確認できます。プリセットの目標が提案され、既知の 60-40 基準と照らして検証されます。検証には、直近の期間を検証用に分離した判定が含まれます。また、しきい値帯に基づくリバランスの提案が、各項目の根拠となるルールとファンドごとの情報とともに示されます。
uvx --from git+https://github.com/disin7c9/asset-management asset-management --demo --allocate moderate --allocate-out demo_target.csv
uvx --from git+https://github.com/disin7c9/asset-management asset-management --demo --backtest --target demo_target.csv --benchmark 60-40 --rebalance bands --metadata
このツールが述べないことにも注目してください。根拠が乏しい場合、判定は「60-40 と比較して下落率に明確な差はありません。ペア・ブートストラップでも差は確認できません」のような表現になります。「ベンチマークを上回ります」とは決して述べません。出力を信頼できると感じたら、以下の 4 つのステップで自分のお金を対象に使い始めましょう。
💵 自分のお金で使う — 4 つのステップ
ステップ 1 — ブック(入力)
すべては1つの取引ログから生成されます。CSV 形式はGhostfolio 独自の CSV インポートスキーマです(ここに保存しているブックを、そのまま Ghostfolio にもインポートできます)。列は Date, Code, DataSource, Currency, Price, Quantity, Action, Fee, Note です。Action は buy、sell、dividend、fee、interest、deposit、withdraw のいずれかです。キャッシュフロー(deposit/withdraw)には CASH コードを使い、金額を Price 列に入力します。このツールはUSD のみ(ロングのみの株式/ETF)に対応しています。Currency は USD にする必要があります。USD 以外の行は、行番号を含むエラーを表示して拒否され、暗黙に1:1でドル換算されることはありません。数値列の空欄はゼロとして扱われます。ISO 形式以外の日付は、明確なエラーとともに拒否されます。UTF-8 BOM は許容されます。付属のサンプル(data/sample_data/transactions.csv)には、すべての行タイプが含まれています。
すでに Ghostfolio をお使いですか? 入力先に Ghostfolio のJSONエクスポート(Portfolio → Activities → ⋯ → Export)を直接指定してください。ローダーが形式を検出して直接読み込むため、変換は不要です。アクティビティを読み込みます(配当の現金額は quantity × unitPrice で計算され、Ghostfolio の UTC タイムスタンプはローカル日付に戻して丸められます)。また、USD 以外、暗号資産、証券以外(ITEM/LIABILITY)の行は警告を表示してスキップします(現時点では USD のみ、ロングのみの株式/ETFに対応)。Ghostfolio のネイティブアカウントに対応していないブローカーでは、まず Export-To-Ghostfolio などのコミュニティ製コンバーターを実行して、Ghostfolio JSON に変換できます(26のブローカーに対応)。
uv run python -m app --book your.csv # or ghostfolio-export.json — auto-detected
uv run python -m app --book your.csv --dry-run # preview an import BEFORE trusting it:
# format, events, skipped rows with reasons,
# derived holdings — fetches nothing
デフォルトは一度だけ設定してください。リポジトリのルートにある Git 管理対象外の .env に ASSET_BOOK=path/to/your.csv(必要に応じて ASSET_TARGET=path/to/target.csv)を設定すると、引数なしの python -m app で概要を表示できます。明示的なフラグは常に優先されます。暗黙に適用される組み込みのデフォルトはありません。--book または ASSET_BOOK を指定せずにブックが必要なアクションを実行するとエラーになり、引数なしで実行するとヒントが表示されます。付属のサンプルは --demo を指定した場合にのみ使用され、自動的に選択されることはありません。
ステップ 2 — キャッシュを準備する(一度だけ)
コア機能はオフライン優先です。価格はディスク上のキャッシュから取得され、オンラインで実行したときに更新されます。新しくクローンした後、最初に一度キャッシュを作成してください。
uv run python -m app --book your.csv --warm # your tickers + the benchmark references
uv run python -m app --book your.csv --warm full # + the ~375-ETF discovery universe (slow) —
# only needed for offline --discover
以後、--offline での実行と Claude Desktop アドオンは、このキャッシュだけで動作します(--cache-dir/ASSET_CACHE_DIR で場所を変更できます)。
任意ですが推奨: 無料の Tiingo API キーを .env に追加し、TIINGO_API_KEY=... を設定すると、2つ目の価格ソースを利用できます。Yahoo Finance はときどき短時間に大量のリクエストを受けると制限をかけます。API キーがあれば、取得先は Tiingo にフォールバックするため、ティッカーが見つからないという報告を避けられます。API キーがない場合、価格は Yahoo からのみ取得されます。
ステップ 3 — ターゲットを選ぶ
判断に関わる機能(--rebalance、--backtest、保留データによるチェック)の多くは、ターゲット配分を基準に動作します。ターゲット配分は、あなた自身が管理・編集する小さな CSV(Ticker,Weight)です。出発点に応じて、次の 3 つの方法で作成できます。
- すでにポートフォリオを保有している場合 →
--dump-target target.csvを実行すると、現在の配分が書き出されます。希望する構成になるように編集してください。(ターゲットは完全な仕様です。売却の扱いについては リバランスモード を参照してください。) - リスクへの向き合い方を起点にする場合 →
--onboardを実行すると、ターミナルで簡単な質問が 3 つ表示され(運用期間/損失への対応/現金バッファー)、回答に合ったプリセットが作成されます。または、--allocate conservative|moderate|aggressiveで自分で選ぶこともできます。--allocate-out target.csvで保存できます。どちらも保有資産がまったくない状態で実行でき、最初の取引を行う前でも、各役割がキュレーション済みユニバースから補完されます。これらは、方針に応じて株式/債券/分散投資対象の配分を分けた、戦略的な役割バケットテンプレートです(各バケット内ではコア・サテライト構成)。すでに保有している役割は、その役割に該当するあなたの最大のファンドに割り当てられ、保有していない役割には、キュレーション済みユニバースから適切な ETF が割り当てられます。 - 自分で調べて作成する場合 →
--discoverを実行すると、保有割合の少ない役割に向けた、スクリーニング済み ETF が提案されます。--screen QQQM,SCHDを実行すると、候補をあなたのポートフォリオに照らして評価できます(コスト、流動性、運用期間、重複度、および実際にあなたの最悪のドローダウンを分散できたかどうか)。その後、CSV を手動で作成してください。
よりシンプルなリバランスルールも 2 つあります。--allocate equal_weight(1/N。堅牢なベースライン)と、--allocate inverse_vol(各保有銘柄が同じ金額ではなく、ほぼ同じリスクを負担する方式。--allocate-cap 0.30 で単一銘柄のウェイトに上限を設定できます)です。意図的に含めていないのは、リターン予測を使うオプティマイザー(平均分散法/最大シャープレシオ法)です。過剰適合するため、今後のバージョンでエッジのゲートの先に置いています。
ターゲットに従う前に検証してください。
uv run python -m app --backtest --target target.csv --benchmark 60-40 # or all-weather | permanent
ターゲットとベンチマークの共通期間を使って、想定元本 1 万ドル、ドローダウンを優先した損益推移と信頼区間でシミュレーションし、さらに直近の保留期間に対する判定(shallower/deeper/inconclusive/insufficient)を追加します。「上回る」という判定はしません。この判定はアーチャー指数(全期間のドローダウンによる痛み。深さと継続期間の両方を評価します)に基づき、CDaR(最悪のテールの平均値)がそれに反しないことを条件とし、ペア・ブートストラップの信頼区間で確認します。最大ドローダウンは判断基準ではなく、参考情報として報告されます。最悪のイベント 1 件だけでは、短い期間の履歴に基づいて何かを判断するにはノイズが大きすぎるためです。判定できない場合は inconclusive と表示し、どのゲートで阻まれたのかを正確に示します。
提案、シミュレーション、実行は、強制的に別々のステップとして扱われます。 --allocate/--onboard は提案のみを行い(必要に応じてファイルに書き出し)、同じコマンドで --backtest/--rebalance と組み合わせることはできません。まずファイルを確認し、次にシミュレーションを行い、その後、注文を要求します。それぞれ別のコマンドで実行します。戦略が、いつの間にか取引に変わることはありません。
Step 4 — 週次ブリーフ
ステータスブリーフ、バンド超過時の推奨アクション、バックテストとベンチマークの判定、ファンド情報を、1つのコマンドで定期的に確認できます。
uv run python -m app --book your.csv --backtest --target target.csv --benchmark permanent --rebalance bands --metadata # + --narrate --save --send to taste
--benchmarkには、ご自身の運用姿勢に最も近い参照先(60-40/all-weather/permanent)を選びます。bandsは、いずれかの保有銘柄がバンドを超えて乖離したとき(「5/25ルール」)にのみ実行され、ポートフォリオ全体をリバランスします。入金する週で売却したくない場合は、代わりに--rebalance cash_flow_only --new-cash 500を指定します(アンダーウェイトの銘柄にのみ投資するため、税務上有利です)。- パネルは組み合わせ可能です。フラグを指定するたびにパネルが追加され、積み重なります(リファレンスを参照)。
同じレポートは、1回の生成で3つの形式で出力されます。標準出力へのプレーンテキスト(常時)、reports/<asof>.md への Markdown(--save)、HTML メール(--send。.env に RESEND_API_KEY と REPORT_TO を設定)。出力先への書き込みに失敗しても実行はクラッシュせず、ブリーフは引き続き出力され、失敗が記録されます。ただし、指定した出力先への書き込みに失敗した場合は、スケジューラーが検知できるよう、プロセスはゼロ以外の終了コードで終了します。同じルールは、処理を実行できなかった場合にも適用されます。価格を取得できた保有銘柄が1つもない場合(プロバイダーに接続できない、またはキャッシュが空でオフラインの場合)も、台帳からブリーフは出力されますが、終了コードはゼロ以外になり、修正方法を示す1行が出力されます。ティッカーが1つだけ見つからない場合は partial 実行となり、終了コードは 0 です。毎週月曜日のブリーフは、これを cron で実行するだけです。
# 08:00 every Monday — email the brief and archive the markdown.
# cron's PATH is minimal and won't find uv — point PATH at it (see `which uv`):
PATH=/home/you/.local/bin:/usr/bin:/bin
0 8 * * 1 cd /path/to/asset-management && uv run python -m app --book your.csv --backtest --target target.csv --benchmark permanent --rebalance bands --metadata --save --send >> reports/cron.log 2>&1
(cron が実行されるのは、その時点でマシンの電源が入っている場合だけです。WSL では、wsl.exe を実行する Windows タスク スケジューラが、確実に実行できる代替手段です。)ツールが読み取るすべてのキーは、設定に記載されています。
WSL を使わない Windows 環境でも、上記の手順はそのまま使えます(uv が独自の Python を用意します。irm https://astral.sh/uv/install.ps1 | iex でインストールし、クローンを作成して .env を記述し、実行してください)。異なるのはスケジューラーだけです。
# the Windows counterpart of the cron line above, from your clone.
# -StartWhenAvailable: a Monday the machine was asleep runs on wake instead of skipping.
Register-ScheduledTask -TaskName "asset-brief" `
-Trigger (New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At 8:00am) `
-Settings (New-ScheduledTaskSettingsSet -StartWhenAvailable) `
-Action (New-ScheduledTaskAction -Execute "$env:USERPROFILE\.local\bin\uv.exe" `
-Argument "run python -m app --book your.csv --backtest --target target.csv --benchmark permanent --rebalance bands --metadata --save --send" `
-WorkingDirectory "C:\path\to\asset-management")
🔍 数値を信頼できる理由
🚫 出力の構成自体が金融アドバイスではないことを示します。細かい注意書きではありません
出力の構成によって、これはアドバイスではなく説明となっています。
- すべての提案には、実行された名前付きルール(「5/25 バンド超過 — X を一部売却」)が必ず併記され、単に「X を買う」とだけ示すことはありません。
- サンプリングされたリスク指標にはすべて信頼区間が付与され、データが少なすぎる場合は、その旨(
inconclusive、insufficient、n/a)を示し、そうでないかのように装うことはありません。 - ベンチマークの判定で使うのは、
shallower/deeper/inconclusive/insufficientだけです。「上回る」という語彙はありません。 - 優位性を主張するものはすべて、提案に表示される前にアウト・オブ・サンプルのゲートを通過する必要があります。イン・サンプルのみの数値は、設計上、使用されません。
- ツールは読み取り専用です。取引を行うことはなく、AI が台帳に書き込むこともありません。
これはブリーフの構成についての説明です。オプションの AI による説明は文章です。安全策によって、その数値がコアから取得されることは保証されますが、表現は制約されません。アドバイスや予測のような文が出力されても、阻止されることはありません。app/narrate.py のモジュール docstring に、何が適用され、何が適用されないかが正確に記載されています。
✅ 正確性はご自身で確認できます
- ゲートは、
mainへのプッシュとすべての PR で、テストスイート全体、mypy --strict、ruff を実行します。ページ上部のバッジは、このゲートの現在の状態を示しています。 - 保有銘柄、時価、損益は Ghostfolio とセント単位で照合し、シャープレシオ/ソルティノレシオ/ドローダウンは quantstats と小数点以下4桁まで照合しました。これは 2026-06-02 に記録されたスナップショットであり、ライブ CI のゲートではありません → reconcile/RESULTS.md
- すべての数式は MATH.md に記載されています。
- 数値の安全策は、ご自身で試せるスクリプトです:
scripts/demo_fence.py。
🔬 検証 — 独立した2つのツールとの照合
数値は、独立した2つのツール(reconcile/ 内のハーネス)と照合しました。記録された実行結果は、11件のイベントを含むブックを対象とした2026-06-02時点のスナップショットです。現金の追跡機能で CASH 行が追加される前の、同梱のサンプルを対象としています。そのため、CI が毎回再実行するゲートではなく、ある時点での確認結果としてご覧ください。
- ghostfolio は同じ取引ログから保有資産、時価、損益を再構築し、セント単位まで一致します。
- quantstats はリターン系列からシャープレシオ、ソルティノレシオ、最大ドローダウンを独立して算出し、小数点以下4桁まで一致します。
ツールが計算するすべての数式 — リターン、ドローダウン関連指標(Ulcer / CDaR)、リスク調整後比率、ブートストラップ信頼区間、配分/スクリーニングの計算 — は、MATH.md に一元的に定義されています。
これらを合わせて、パイプライン全体を検証します。ghostfolio は保有資産と時価の再構築を確認し、quantstats はリスク/リターンの数式を確認します。比較の詳細は reconcile/RESULTS.md をご覧ください。
uv run --with quantstats python reconcile/reconcile_quantstats.py # metric cross-check
# end-to-end (ghostfolio): see reconcile/RESULTS.md
💬 ポートフォリオとチャットする — Claude Desktop アドオン(読み取り専用 MCP)
ポートフォリオを AI アシスタント(Claude Desktop、Claude Code、…)に読み取り専用ツールとして公開し、呼び出せるようにします。これにより、CLI と同じ決定論的なコアが計算するすべての数値をツールの結果として確認しながら、「ポートフォリオとチャット」できます。境界にはご注意ください。アシスタント自身の文章は制約されません。モデルがチャットに独自の数値を入力するのを防ぐことはできないため、ツールの結果に明確にたどれない数値は未検証として扱ってください。サーバーも portfolio://guarantees リソースでまさにこの点を説明しています。(CLI の --narrate 経路は異なり、数値を機械的に置き換え、モデルが入力した数字を含む説明文は拒否します。)サーバーは読み取り専用で、ASSET_BOOK のブックに紐付けられます(ファイルパス引数はありません)。オフラインで動作しますが、範囲を限定したオプトアウト可能な取得が2つあります。キャッシュが空の場合、コアセット(保有銘柄とベンチマーク参照)を一度だけ自動でキャッシュに取り込みます(約30~60秒)。また、スクリーニングや提案でキャッシュにない銘柄を指定すると、必要に応じて取得します。ASSET_MCP_OFFLINE=1 を設定すると、両方を無効にできます(すでに温まったキャッシュが対象です。空のキャッシュを指定した場合は、正直に n/a を返すだけです)。ツールは8つあります。
portfolio_summary— 保有資産、損益、年率換算リターンを表示します。risk_report— ドローダウンを重視したリスクレポートです。最大ドローダウン(深さ/日付/回復)、Ulcer、CDaR、シャープレシオ/ソルティノレシオ/カルマーレシオを、すべてブートストラップ信頼区間とともに表示します。rebalance_check—ASSET_TARGETに近づけるための売買/保有提案を表示します(提案のみで、取引は行いません。部分的にしかキャッシュされていないブックでは、数量の算出を拒否します)。securities_facts— 各保有資産の公表済みファンド情報(経費率、AUM、出来高、運用年数、カテゴリー)を表示します。discover_gaps— 保有比率が3%以下の役割を補う、新しい ETF を提案します。任意のrole/flavor引数で、特定の分類を詳しく調べられます(提案のみ。候補の評価にはscreen_candidateを使用してください)。screen_candidate— 新しい候補銘柄をポートフォリオに照らして評価します(分散効果/コスト/流動性/運用年数/重複。それぞれ理由を表示します)。propose_allocation— ポートフォリオと銘柄ユニバースを対象に、投資姿勢(conservative/moderate/aggressive)に応じた戦略的な目標配分を提案します。直近の除外期間を用いた Ulcer 優先の判定基準で参照値と比較して検証します。提案のみで、数値はコアから取得し、推奨は行いません。starter_allocation— 使うのが初めてですか?リスクに関する簡単な質問3つに答えると、初期の投資姿勢と検証済みの配分案を提示します(propose_allocationへの導入経路です)。
インストール(Claude Desktop): Settings → Developer → Edit Config を開き、以下を追加します(claude_desktop_config.json をまだ編集したことがない場合は、置き換えるだけでも構いません)。
{
"mcpServers": {
"asset-management": {
"command": "uvx",
"args": ["--from",
"https://github.com/disin7c9/asset-management/releases/download/v2.12.2/asset_management-2.12.2-py3-none-any.whl",
"asset-management-mcp"],
"env": {
"ASSET_BOOK": "C:\\path\\to\\your\\transactions.csv",
"ASSET_TARGET": "C:\\path\\to\\your\\target.csv",
"TIINGO_API_KEY": "your-free-tiingo-key(optional_secondary_source)"
}
}
}
}
Claude Desktop を再起動します。必要なのは PATH 上の uv だけです。ほかには何も必要ありません。uvx が Python とロック済みの依存関係を自動的に解決します。初回の起動には時間がかかります(pandas/NumPy/PyArrow を含む約 500 MB の環境を構築します)。1 分ほどお待ちください。2 回目以降は数秒で起動します。最初のツール呼び出しでは、価格キャッシュのウォームアップに一度だけ約 30〜60 秒かかります。このキャッシュはホームフォルダーに保存されるため、再インストール後も保持されます。無料プランで利用できます。URL でリリースを固定しているため、起動のたびに動作が変わることはありません。アップグレードするには、両方のバージョン番号を最新リリースに置き換えます。
env の各項目はすべて省略可能です。使わないものは削除してください。 ASSET_BOOK を削除すると、まずは架空のデータで同梱のデモポートフォリオを試せます。ASSET_TARGET は、rebalance_check が保有資産を照合する対象です。設定しない場合、このツールはヒントを示してエラーになります。TIINGO_API_KEY(無料アカウント)を設定すると、Yahoo のリクエストが制限されたときに使える 2 つ目の価格ソースが追加されます。使用頻度の低い設定として、ASSET_CACHE_DIR(価格キャッシュの保存先を変更)と ASSET_MCP_OFFLINE=1(価格を取得しない。キャッシュがすでにウォームアップされている場合に使用)があります。LLM キーはここには設定しません。これらのツールを読み取るアシスタントがナレーターです。サーバー自体がモデルを呼び出すことはありません。
.mcpb バンドルは、現時点では Claude Code 専用です。 uv run python scripts/build_mcpb.py を実行して dist/asset-management-<version>.mcpb を作成し、Settings → Extensions からワンクリックでインストールします。このツールは Claude Code で動作します。Claude Desktop のチャットウィンドウでは動作しません。Desktop は、サイドロードされた拡張機能のツールをモデルに提供しません。ツールメニューには表示され、権限の切り替え設定も維持されますが、呼び出されることはありません。ディレクトリからインストールした拡張機能と、設定に登録したサーバーはどちらも動作します。そのため、チャットでは上記の設定方法を使用してください。
チャットでは: + メニューを開くと、すぐに使えるスターターが表示されます。ポートフォリオのチェックアップ、ドローダウンはどのくらいですか?、リバランスすべきですか?、不足分を補う、初期配分を見つける、投資方針を提案するが用意されており、それぞれ数値のみを使う形式があらかじめ読み込まれます。サーバーは portfolio://guarantees(コードに同梱され、バージョン管理された、適用必須の 4 つの保証)も公開します。同じ + メニューからこれを添付してください。また、モデルがリソースを自分で読み取れるクライアントでは、「これらの数値は信頼できますか?」と尋ねるだけで、即興で回答する代わりにマニフェストを参照して答えます。
🛟 問題が発生した場合
- Claude にツールが 1 つしか見えない、または何も見えないと言われます。 Settings → Connectors → asset-management に移動し、ツールをオンにしてください。新しく登録されたサーバーのツールは ブロックされた状態で追加され、権限を求める画面で 1 つを承認しても有効になるのはそのツールだけです。Claude は 渡された短いリストをそのまま正直に報告します。ほかのツールが存在することを知る方法がないため、サーバーには「ツールが 1 つしかない」と 言います。実際には 8 つあります。
- メニューにツールは表示されますが、Claude が呼び出しません。
.mcpbバンドルをインストールしました。Claude Desktop のチャットウィンドウでは、サイドロードされた拡張機能のツールをモデルに提供しません。メニューと権限の切り替え項目は拡張機能自体から表示されるため、すべて 接続されているように見えても、モデルにはツールの存在が一度も伝わりません。拡張機能を削除して、上記の設定方法を使ってください。 - サーバーがまったく表示されません。 Claude Desktop は PATH 上で
"command"を解決しますが、uvxが見つかりません。uv をインストールして Desktop を再起動するか、"command"に絶対パスを指定してください (Windows の場合:C:\Users\<you>\.local\bin\uvx.exe)。 - 初回起動時に切断状態のままになります。 起動時に環境を構築しています(約 500 MB)。 1 分ほど待ってから Claude Desktop を再起動してください。2 回目以降の起動は数秒で完了します。
- 初回起動時に
os error 32(「別のプロセスがファイルを使用しています」)で終了します。 Windows の場合:uv が環境を構築している間、別のプロセス(通常はウイルス対策ソフトのスキャナー)が uv のキャッシュ内に新しく書き込まれたファイルを一時的に保持し、インストールに失敗することがあります。設定にあるuvxコマンドをターミナルで一度実行してください(再び失敗した場合は、もう一度実行してください。再試行しても問題ありません)。何も表示されなくなったら環境の構築は完了です。Ctrl-C を押して Desktop を再起動してください。以降の起動では構築済みの環境が再利用されるため、競合は発生しません。 - すべての数値が
n/aになります。 価格キャッシュが空で、ASSET_MCP_OFFLINE=1によってキャッシュを温めるための取得が禁止されています。この変数を削除するか、CLI から一度キャッシュを温めてください:uvx --from git+https://github.com/disin7c9/asset-management asset-management --book your.csv --warm。 - (
.mcpbのみ)No MCP config found for extension … skippingと表示されます。 Configure フォームを保存するまで Desktop はサーバーを起動しません。また、何かを変更するまで Save は無効のままです。任意のフィールドを切り替えて保存し、再起動してください。
または、サーバーを直接実行するか、Claude Code に登録してください:
uv run python -m app.mcp_server # serve over stdio (set ASSET_BOOK in .env)
asset-management-mcp # the same server as an installed entry point
claude mcp add asset-management -- uv run python -m app.mcp_server # register, then /mcp to use it
サーバー自体は LLM を実行しません。アシスタントが呼び出して使用するものであり、これは金融アドバイスではありません。
📖 リファレンス
レポートは組み合わせ可能なパネルであり、排他的なモードではありません。フラグを組み合わせると、パネルが積み重なります。各アクションに必要なものは次のとおりです。
| アクション | 必要なもの | 内容 |
|---|---|---|
| ステータス概要(デフォルト) | --book | 保有資産、リターン、ドローダウン/リスク |
--rebalance MODE | --book + --target | 目標に向けた買い/売りの提案(--new-cash で入金額を指定) |
--allocate RULE | プリセットには不要。リウェイトルールには --book | 目標を提案します。保有資産をリウェイトする(equal_weight/inverse_vol。ブックが必要)か、戦略的な役割テンプレートを作成します(conservative/moderate/aggressive。ブックなしで利用可能)。--allocate-out で書き出します |
--onboard | 不要(ブックがある場合は、現在の保有資産を基に役割を設定します) | 新規ユーザー向けのステップ 0 です。ターミナルで簡単なリスクに関する質問 3 つに回答すると、該当する投資姿勢に合わせて --allocate プリセットを自動作成します(提案のみ。--allocate-out で保存) |
--dry-run | --book(または --demo) | インポートを信頼する前にプレビューします。検出された形式、解析されたイベント、スキップ/フラグ付けされた行とその理由、およびそれらから導かれる保有資産を表示します。データの取得も概要の計算もしません |
--metadata | --book | 保有資産ごとの公開ファンド情報(経費率、AUM、出来高、運用年数、カテゴリー)。7 日間キャッシュされます |
--screen TICKERS | --book + 価格 | 新規候補をブックと比較して評価します。分散効果(自身の下落日と最大ドローダウンを含む)、コスト、流動性、運用年数、集中度、レバレッジ型/インバース型の自動却下、保有銘柄との重複排除を確認し、各判定に理由を示します。--target を追加すると、保有対象外の役割チェックを実行します。5% のスリーブによって、保有対象外の期間におけるドローダウンの痛みが軽減されたか(Ulcer、CDaR チェックを含む)を確認します。「判定不能」の場合は、判定を妨げた条件を示します。提案のみ。PASS は「妥当で、低コストで、流動性があり、実質的に異なる」という意味であり、予測ではありません |
--discover [roles] | --book + 価格 | 保有比率が 3% 以下の役割について、新しい ETF を提案し、同じスクリーニングを実行します。提案のみ(ディスカバリーを参照) |
--backtest | --target | リバランスとバイ・アンド・ホールドの名目上の比較。--book は不要で、シミュレーションのみを出力します |
--backtest --benchmark REF | --target | 目標を標準的な参照ポートフォリオ(60-40/all-weather/permanent)と比較して検証します。ドローダウン優先の構成要素と、保有対象外の直近期間を使った Ulcer 優先の判定を行います |
--narrate | --book + LLM キー | 概要の先頭に、わかりやすい言葉でサマリーを表示します。モデルが文章だけを書き、すべての数値はコアから置換・検証されます(オプトイン、デフォルトではオフ。ナレーションを参照) |
全フラグの一覧(分類別):
# input & cache
uv run python -m app --demo # zero-setup test drive on the bundled example book
uv run python -m app --book your.csv # your book (CSV or Ghostfolio JSON, auto-detected)
uv run python -m app --book your.csv --dry-run # preview an import — fetches nothing
uv run python -m app --book your.csv --warm # ONE-TIME: fill the offline cache (add `full` for the universe)
uv run python -m app --book your.csv --offline # serve from the on-disk cache; no network
uv run python -m app --book your.csv --no-prices # holdings + realized P&L only (no network)
uv run python -m app --book your.csv --cache-dir /some/dir # override the cache location
# panels (stack freely)
uv run python -m app --book your.csv --no-risk # skip the holdings drawdown/risk panel
uv run python -m app --book your.csv --metadata # + SECURITIES panel (fund facts)
uv run python -m app --book your.csv --screen QQQM,SCHD # judge NEW candidates
uv run python -m app --book your.csv --screen SCHD --target t.csv # + the held-out role check
uv run python -m app --book your.csv --discover # + DISCOVERY panel (gap-filling ETFs)
uv run python -m app --book your.csv --narrate # + the fenced plain-language SUMMARY
# targets (propose-only)
uv run python -m app --book your.csv --dump-target target.csv # write your CURRENT allocation
uv run python -m app --book your.csv --allocate moderate --allocate-out t.csv # strategic preset
uv run python -m app --book your.csv --allocate inverse_vol --allocate-out t.csv # re-weight holdings
uv run python -m app --demo --onboard # 3-question quiz → a preset
# act & validate (separate commands, by design)
uv run python -m app --book your.csv --rebalance to_total --target target.csv # suggestions toward the target
uv run python -m app --book your.csv --rebalance cash_flow_only --target target.csv --new-cash 1000
uv run python -m app --backtest --target target.csv # notional backtest — target-only, no --book
uv run python -m app --backtest --target target.csv --benchmark 60-40 # + the reference comparison & verdict
uv run python -m app --book your.csv --rebalance bands --backtest --target target.csv # panels stack
# delivery
uv run python -m app --book your.csv --save # also write reports/<asof>.md (markdown)
uv run python -m app --book your.csv --save --reports-dir /elsewhere # ...somewhere other than reports/
uv run python -m app --book your.csv --send # also email the brief as HTML via Resend
target.csv は --dump-target/--allocate-out で作成します(または data/sample_data/target.csv を使用します)。--allocate は提案のみであり、--rebalance/--backtest とは併用できません。
💻 コア概要
出力例 — 完全な --demo 概要(ドローダウン、比率、リターン、保有資産)
$ uv run python -m app --demo
=== DRAWDOWN (investment, time-weighted) ===
Max drawdown: -9.84% (95% CI -17.16% .. -6.44%)
peak 2025-02-19 → trough 2025-04-08 → 2025-06-10 (111 days)
Ulcer index: 2.40% (95% CI 1.60% .. 6.32%)
CDaR (worst 5%): 6.67% (95% CI 4.51% .. 14.09%)
You've spent 81% of this period below a previous high.
Gains given back: -$1,240 — peak profit $3,336 (2025-02-19) → $2,096 (2025-04-08) → 2025-06-10
(dollars of profit given back from a peak; flow-neutral — funding & transfers don't distort it)
=== RISK-ADJUSTED (annualized, 252-day basis, risk-free 0%, ± bootstrap CI) ===
Sharpe: +1.55 (95% CI +0.52 .. +2.49)
Sortino: +2.33 (95% CI +0.73 .. +3.85)
Calmar: +1.73 (95% CI +0.49 .. +3.56)
=== RETURNS (annualized, 252-day basis) ===
Period: 2023-01-05 → 2026-07-30 (1302 days, ~3.56y)
Time-weighted (true TWR): +17.04%
Money-weighted (IRR): +15.92%
Modified Dietz (approx TWR): +15.65%
(point figures: accounting identities over your cash flows, not sampled statistics → no band; see RISK-ADJUSTED for bootstrapped CIs)
=== HOLDINGS ===
ticker shares avg cost price mkt value unreal realized
-------------------------------------------------------------------------
BND 50.000 72.26 72.50 3624.75 +11.75 +45.10
IAU 25.000 37.23 75.34 1883.50 +952.88 +220.62
VEA 15.000 40.27 69.01 1035.22 +431.22 +0.00
VOO 12.000 375.13 675.28 8103.36 +3601.76 +295.80
-------------------------------------------------------------------------
Total cost basis (held): $9,649.23
Market value (priced): $14,646.84
Unrealized P&L: $+4,997.61
Realized P&L (sells+div): $+549.37
Fees paid (informational): $33.00
Net P&L (unrealized + realized): $+5,546.99
Prices: 4 cache (age: 6m .. 6m old as of 2026-07-29 15:20 UTC)
Generated by asset-management. Figures are computed by a deterministic core; this is not financial advice.
信頼区間は移動ブロック・ブートストラップによるものです。ドローダウンは口座残高ではなく、投資の(時間加重)ドローダウンです。このパネルでは取り戻せなかった利益も報告します。これは累積市場利益のうち、金額ベースで最も大きな下落(実感としての「増えた分がどれだけ消えていくのを見たか」)です。フローに左右されないため、入金、出金、振替による歪みはありません。実行ごとに、構造化 JSON ログ行(run_summary)も stderr に出力されます。内容は {date, source, n_events_replayed, n_prices_fetched, n_prices_missing, n_series_fetched, n_series_missing, fallbacks_used, status, report_saved, email_sent, rebalance, backtest, allocate, dump_target, metadata, screen, narrate, discover, discover_narrate, benchmark_narrate, warm, onboard, dry_run} です(該当する場合は email_detail/error も含まれます)。
🔁 リバランスモード
ターゲットは完全な仕様です(--target path、列は Ticker,Weight。ウェイトは相対値で、正規化されます)。保有している銘柄のうち、記載されていないものは決済対象とみなされ、$0 まで売却されます。そのため、--rebalance には --target が必須です(暗黙のデフォルトはありません)。実行時には、ターゲットに含まれていない保有銘柄を列挙して警告します。意図的にポジションを決済するには、ウェイトを 0 にします。これは明示的な警告なしの決済です。省略した場合も同様に決済されますが、安全のための警告が表示されます(ツールは「忘れた」のか「そのつもりだった」のかを判別できません)。モードは次のとおりです。
to_total— ターゲットに正確に合わせるために売買します(キャッシュ中立。--new-cashも投入します)cash_flow_only— アンダーウェイトの銘柄に--new-cashを投資します。売却はしません(税制面で有利です)fixed_dca— ドリフトを無視して、--new-cashでターゲットの構成比どおりに購入しますbands—to_totalと同じですが、ドリフトのトリガーを条件とします。いずれの銘柄もバンドを外れていなければ売買せず、いずれかの銘柄が外れていれば、すべてのポジションをターゲットに戻します(--new-cashも含みます)。バンドを外れた銘柄だけを売買すると、相殺するドリフトがバンド内の銘柄に残るため、売却するだけで買いが発生しません。バンド幅には、絶対値で指定する--band(デフォルトは 5pp)と、銘柄のターゲットウェイトに--band-relを掛けた値(「5/25 ルール」、デフォルトは 25%)のうち、小さい方が適用されます。これにより、小さな構成枠にその何倍もの幅のバンドが割り当てられることを防ぎます。ターゲットが 0%の場合、バンド幅も 0 となり、常に決済されます。
出力例 — リバランスの提案(デモ用ポートフォリオ、同梱のターゲット VOO 45 / VEA 20 / BND 25 / IAU 10)
以下の両ブロックは、data/sample_data/target.csv に対して、記載されたコマンドを実行した出力そのままです。価格は変動するため、数値はある時点のものですが、出力の構成は変わりません。
to_total — ターゲットに正確に合わせるために売買します。
$ uv run python -m app --demo --rebalance to_total --target data/sample_data/target.csv
=== SUGGESTED ACTIONS (rebalance to target) ===
ticker cur% tgt% action $ shares why
------------------------------------------------------------------------------
VEA 7.1 20.0 BUY +1894.14 +27.445 7.1% vs 20.0% target
VOO 55.3 45.0 SELL -1512.28 -2.239 55.3% vs 45.0% target
IAU 12.9 10.0 SELL -418.82 -5.559 12.9% vs 10.0% target
BND 24.7 25.0 BUY +36.96 +0.510 24.7% vs 25.0% target
Buy $1,931.10 · Sell $1,931.10 · net +$0.00 (cash-neutral)
bands(5/25 ルール)— 同じターゲットです。バンドはトリガーです。VEA はバンドの外側に 12.9pp あるため、ポートフォリオ全体をターゲットに戻します。BND は自身のバンド内にありますが、相殺するドリフトがあるため売買します。そのことは行に記載されています。
$ uv run python -m app --demo --rebalance bands --target data/sample_data/target.csv
=== SUGGESTED ACTIONS (threshold-band rebalance) ===
ticker cur% tgt% action $ shares why
------------------------------------------------------------------------------
VEA 7.1 20.0 BUY +1894.14 +27.445 drift -12.9pp exceeds 5.00pp band
VOO 55.3 45.0 SELL -1512.28 -2.239 drift +10.3pp exceeds 5.00pp band
IAU 12.9 10.0 SELL -418.82 -5.559 drift +2.9pp exceeds 2.50pp band
BND 24.7 25.0 BUY +36.96 +0.510 24.7% vs 25.0% target (rebalance triggered)
Buy $1,931.10 · Sell $1,931.10 · net +$0.00 (cash-neutral)
バンドの外側にある銘柄がなければ、売買は一切行われません。提案のみで、売買は実行されません。各行には、発動したルールが記載されています。
--backtest --target T.csv を実行すると、その対象について 名目 10,000 ドルの過去のシミュレーションを実行し、BACKTEST パネルを表示します。パネルでは、リバランスあり(スケジュールは --rebalance-every {monthly,quarterly,annually} で指定。デフォルトは四半期ごと)と バイ・アンド・ホールドを比較します。ドローダウン優先の指標(最大ドローダウン、Ulcer、CDaR。それぞれブートストラップ信頼区間付き)のほか、シャープレシオ/ソルティノレシオとリターンも表示します。これは名目上のシミュレーションです。全ティッカーの価格がそろう最も早い日付に、対象のウェイトで 10,000 ドルを新たに投資した状態から開始します(--backtest-start で開始日を変更できます)。そのため、実際に購入したタイミングに左右されず、戦略を検証できます。過去のシミュレーションであり、予測ではありませんと明記しています。
固定のリバランスポリシーではパラメーターを調整しないため、全期間がアウト・オブ・サンプルとして扱われ、過剰適合する要素はありません。ウォークフォワードの訓練/テストによる選定の仕組みは、戦略が探索(パラメーターの調整や候補の選択。オプティマイザーや、エッジを狙うタイミング戦略など)を行う場合にのみ必要となるため、意図的に後回しにしています。規律とエッジのゲートにより、将来エッジ戦略が提案に表示されるには、ウォークフォワード・バックテストに合格する必要があります。現在のリバランス方式はいずれも規律に該当するため、自由に提案できます。
--benchmark を指定すると、ホールドアウト期間の判定は、名前付きの 3 つのゲートを通じて行われます。Ulcer の改善幅がノイズのマージンを超える必要があります。CDaR(最悪テールの平均)は同等でもかまいませんが、改善の方向に反してはなりません。さらに、対応のある移動ブロック・ブートストラップ信頼区間で改善が確認される必要があります。条件を満たさない場合は inconclusive となり、理由には判定を妨げたゲートが示されます。最大ドローダウンとボラティリティは参考情報として報告するだけで、判定には使いません。単一の最悪イベントは極値統計量であり、短い期間ではノイズが大きすぎて、判断材料にはならないためです。
出力例 — 60-40 に対するホールドアウト期間の判定
$ uv run python -m app --backtest --target data/sample_data/target.csv --benchmark 60-40
=== BENCHMARK (preset vs 60-40 · 2023-01-06 → 2026-07-29 — propose-only) ===
preset 60-40
--------------------------------------------------------------------------
Max drawdown -10.40% -11.22%
95% CI -16.5% .. -5.7% -17.5% .. -5.6%
Ulcer index 2.20% 2.24%
95% CI 1.5% .. 5.5% 1.4% .. 6.9%
CDaR (worst 5%) 6.43% 6.59%
95% CI 4.1% .. 13.0% 4.1% .. 14.7%
Sharpe (252-basis) +1.54 +1.45
95% CI +0.57 .. +2.51 +0.46 .. +2.45
Sortino +2.30 +2.16
95% CI +0.81 .. +4.04 +0.65 .. +3.82
Annualized return +16.47% +14.06%
Final value $17,144 $15,924
prices: cache · oldest fetch 2026-07-29
Held-out recent-window: OOS (2025-07-08→2026-07-29, 266d): Ulcer 1.90% vs 1.33% 60-40; CDaR 6.06% vs 4.08%; max DD -8.0% vs -5.8% (context); return +16.3% vs +12.6%/yr (+3.7pp, context) — no clear drawdown difference from 60-40; 95% CI of the Ulcer gain [-2.36pp, +0.27pp] (the paired bootstrap does not confirm the gap)
ここでは Ulcer の差がノイズのマージンを超えているため、判定はブートストラップまで進みます。しかし、信頼区間がゼロをまたぐため、結果は inconclusive のままで、判定を止めたゲートが示されます。5% のスリーブでスクリーニングされた候補はその逆です。効果がはるかに小さく、通常はゲート 1 だけで判定が決まります(MATH.md §12.7)。
この結果が述べていないことにも注目してください。プリセットは 60-40 よりもドローダウンによる負担がわずかに少なく(Ulcer は 5.57% 対 5.74%)、リターンもわずかに低くなっています(年率 +9.42% 対 +9.81%)。これはトレードオフであり、勝利ではありません。また、ドローダウン面の優位性さえホールドアウト期間では確認できませんでした。Ulcer の改善幅がノイズのマージン内にあるため、判定は**「明確なドローダウンの差なし」**です。ホールドアウト期間の行には、ドローダウンの改善とともにリターンの代償も表示されるため、負担の少ない値動きが、その代償を隠したまま提示されることはありません。
--discover は保有資産を役割(米国大型株、新興国市場、TIPS、REIT など)に対応付け、保有割合が ≤3% の役割を見つけ、それぞれから キュレーションされたユニバース(同梱の app/data/universe.csv、低コスト ETF 約375本)にある最大の コア ファンドを取り出し、--screen と同じスクリーニングを実行します。DISCOVERY パネルを表示し、各候補の判定(PASS/WARN/FAIL)と理由を示します。--discover reit,tips で対象の役割を絞り込めます。--narrate を追加すると、役割への適合度に基づいて候補を順位付けした、フェンス付きの注記を表示します。PASS は「健全で、低コストかつ流動性があり、保有資産とは実質的に異なる」という意味であり、予測ではありません。
パネルが行き過ぎた解釈をしないよう、4つの誠実さに関するルールを設けています。ギャップは専用ファンドがないことを意味します。保有する幅広いファンドに、その役割がすでに時価総額に応じて含まれている場合があります(全市場ファンドには中小型株がすでに含まれ、総合債券ファンドには国債や投資適格社債がすでに含まれています)。そのため、パネルは不足を示唆するのではなく、その点を明示します。候補は、互いに近い代替候補を棚ごとにまとめて表示します。ユニバースの各行には機械可読な flavor(その「棚」。国債のデュレーション、セクター、REIT の地域など)があり、各メニューには1つの棚から比較可能なファンド(3本以上で、実質的な選択肢)を表示します。同時に、役割にある他の棚も隠したり順位付けしたりせず、件数とともに名称を示します。デフォルトの棚はプリセットですでに購入対象となっているものなので、新たな見解を加えることはありません。それ以外の棚も、フラグを1つ指定するだけで確認できます(--discover treasury:long)。コアファンドを先に表示します。core フラグ(単純なブレンド型/分散型/投資適格。Growth/Value スタイル、単一地域、または高利回り傾斜型ではないもの)により、AUM の偶然の大きさでジャンク債ファンドが「社債のギャップ」を埋めることを防ぎます。コアのない棚は、指定されるまでインデックス行のままです(--discover corporate-bond:high-yield は、ジャンク債と明記したうえで表示することへの同意です)。そして、セクター/テーマ型の棚はギャップとして表示しません。テックへの投資を保有していないのは、欠落ではなく投資方針です。--discover sector-equity は棚のマップ(テック · 半導体 · クリーンエネルギー · …)を表示し、どのセクターを選ぶかは利用者自身の判断であるため、セクターは選びません。棚を指定すると、そのファンドをスクリーニングできます。新しい列のないカスタムユニバースは、通常の動作にそのまま戻ります。
出力例 — 主要な棚、サテライトのインデックス、棚の指定
コマンドが表示するパネルから各役割を1つずつ抜粋した、出力そのままの例です(完全なパネルにはすべてのギャップが含まれます)。
デフォルト(--demo --discover) — 役割にある他の棚の名称を示し、順位付けはせず、主要な棚の比較可能なファンドを表示します。
corporate-bond — you currently hold 0%
· investment-grade — the standard sleeve
VCIT PASS Vanguard Intermediate-Term Corp (0 fail / 0 warn / 6 pass / 2 n-a)
Corporate Bond ETF.
LQD PASS iShares iBoxx $ Investment Grad (0 fail / 0 warn / 8 pass / 0 n-a)
Corporate Bond ETF.
IGIB PASS iShares 5-10 Year Investment Gr (0 fail / 0 warn / 6 pass / 2 n-a)
Corporate Bond ETF.
also here: high-yield — junk — equity-like drawdowns (19) — drill with --discover corporate-bond:<shelf>
サテライト(--demo --discover sector-equity) — ツールは棚をマッピングしますが、どれか1つを選ぶことはしません。
sector-equity — you currently hold 0%
a tactical bet, not a hole — picking the sector/theme is the decision here, and it is yours. This maps the shelves; it won't rank them.
tech (6) · semis (3) · software (1) · ai (2) · cyber (1) · innovation (3) · quantum (1) · health (5) · biotech (4) · devices (1) · financial (6) · banks (3) · bdc (1) · brokers (1) · utilities (5) · energy (6) · explorers (1) · oil-services (1) · miners (4) · nat-resources (2) · materials (3) · uranium (3) · battery-metals (1) · water (2) · infrastructure (5) · grid (1) · clean-energy (2)
name one to see its screened funds, e.g. --discover sector-equity:tech
棚を1つ指定(--demo --discover treasury:long) — 棚のラベルでリスクをあらかじめ明示します。
treasury — you currently hold 0%
· long — 20y+ — rate-sensitive, equity-scale drawdowns
TLT PASS iShares 20+ Year Treasury Bond (0 fail / 0 warn / 6 pass / 2 n-a)
Long Government ETF.
VGLT PASS Vanguard Long-Term Treasury ETF (0 fail / 0 warn / 6 pass / 2 n-a)
Long Government ETF.
TLH PASS iShares 10-20 Year Treasury Bon (0 fail / 0 warn / 6 pass / 2 n-a)
Long Government ETF.
SPTL PASS State Street SPDR Portfolio Lon (0 fail / 0 warn / 6 pass / 2 n-a)
Long Government ETF.
GOVI PASS Invesco Equal Weight 0-30 Year (0 fail / 0 warn / 6 pass / 2 n-a)
Long Government ETF.
XTEN PASS BondBloxx Bloomberg Ten Year Ta (0 fail / 0 warn / 6 pass / 2 n-a)
Long Government ETF.
also here: intermediate — the standard sleeve (~3-10y) (6) · short — 1-3y — cash-like (4) — drill with --discover treasury:<shelf>
ユニバースは自動構築され、更新もできます。手動での保守は不要です。
uv run python scripts/build_universe.py --auto --out app/data/universe.csv
スクリーナーから資産クラスのカテゴリーごとに、米国の最大規模の ETF を取り込みます(過去のリターンではなくファンドの規模に基づきます。パフォーマンスを追いかけることこそ、これが避ける落とし穴です)。レバレッジ型/インバース型を除外し、スクリーナーでは絞り込めない少数のカテゴリーについては、厳選したセットを保持します。.env で ASSET_UNIVERSE=path/to/universe.csv を指定すると、独自のリストを --discover に渡せます。
--target(または .env の ASSET_TARGET)を指定すると、検出された各候補に対して保有外の役割チェックも実行します。候補を5%の配分として再現し、スクリーニングの判定に使っていない直近の期間で評価します。実行モードは、そうしないと2つのモードの見た目が同じになるため、パネルに明記します。候補4本あたり、およそ1秒かかります。
🔍 候補をスクリーニングする
--screen TICKER は、コスト、流動性、運用開始からの期間、集中度、保有中の銘柄との重複、過去のドローダウンをどれだけ分散できたか、そしてその銘柄自身の最大ドローダウンを、それぞれ理由とともに評価し、新しい銘柄を保有銘柄群と照らし合わせます。あくまで提案のみであり、PASS は「健全で、低コストで、流動性が高く、本当に異なる」という意味で、予測ではありません。
「自身のドローダウン」の行で比較するのは、ポートフォリオ全体の最大下落ではなく、すでに保有しているファンドのうち最も大きく下落したものです。組み合わせると構成要素単体より下落幅が小さくなるのは当然なので、「保有銘柄群より下落が深い」という基準は、ほとんどの単一株式ファンドがクリアします。同梱の例では、保有銘柄群の4ファンドのうち3つが該当します。比較対象の基準をクリアすることは、「すでに許容している範囲より悪くない」という意味です。また、株式の規模に基づく固定基準もあり、-30%を下回ると、いずれにせよ警告が表示されます。
--target を追加すると、保有期間外での役割チェックも実行します。候補にポートフォリオの5%を割り当ててリプレイし、判定時に参照していない直近の期間だけを使って評価します。この判定について、知っておくべき点が2つあります。測定するのはリターンではなく、ドローダウンによる痛みです。無相関のファンドは、リターンがなくても値動きを滑らかにするため、ドローダウンの改善と並べてリターンへの影響を表示し、両方を考慮できるようにしています。また、役割チェックを行う場合、スクリーニングのリターンに基づくチェック(相関、ドローダウン期間中のリターン、自身のドローダウン)はインサンプル期間のみで計算され、[in-sample through …] と表示されます。これにより、候補の選定に、チェックで後から保留する期間が使われることはありません。構造的なチェック(コスト、流動性、運用開始からの期間、重複)はリターンの情報を含まないため、引き続き全期間の履歴を参照します。詳細は MATH.md §12.3 をご覧ください。
出力例 — 同じ役割、異なるリスク(IEF と TLT)
どちらも米国債の役割を果たし、相関の基準もクリアしますが、own-drawdown を見ると、同じリスクではないことがわかります。
$ uv run python -m app --demo --screen IEF,TLT
IEF — PASS (0 fail / 0 warn / 6 pass / 2 n-a)
[pass] diversifier: ρ=+0.22 vs your book; red-day ρ=+0.06; during your worst drawdown (2025-02-19→2025-04-08, -9.8%) it returned +2.9%
[pass] own-drawdown: worst fall -10.2% (2023-05-03→2023-10-19) in 3.6y; shallower than IAU, which you already hold (-26.4%)
TLT — PASS (0 fail / 0 warn / 6 pass / 2 n-a)
[pass] diversifier: ρ=+0.25 vs your book; red-day ρ=+0.09; during your worst drawdown (2025-02-19→2025-04-08, -9.8%) it returned +1.0%
[pass] own-drawdown: worst fall -22.4% (2023-04-06→2023-10-19) in 3.6y; shallower than IAU, which you already hold (-26.4%)
この例では、どちらも比較対象の基準をクリアしています。この保有銘柄群には、-26.4% 下落した IAU がすでに含まれているため、基準は抽象的な閾値ではなく、すでに許容している下落幅に基づいています。それでも違いは明らかです。TLT の最大下落幅は IEF の2倍を超えており、最も大きく下落した保有銘柄でも下落幅が22%未満の保有銘柄群であれば、同じ TLT の行に警告が表示されますが、IEF には表示されません。相関だけでは、どちらも同じように通過してしまいます。
--narrate を指定すると、ブリーフの先頭に平易な英語で短い SUMMARY が追加され、ドローダウン、リスク、リターンに何が起きたかを文章で説明します。これはオプトイン方式で、デフォルトでは無効です。また、LLM がブリーフに誤った数値を記載できない仕組みになっています。モデルが書くのは {{placeholder}} トークンを含む文章だけで、ツールが自身のコアで検証済みの数値を差し込みます。モデルが数値を直接記載しようとした場合や、存在しない数値に言及した場合は、要約全体を却下します。表現はモデルが作成しますが、数値はすべてツールが提供し、ブロックには生成したモデルの名前が表示されます。同じ安全策は、検出パネル(--discover --narrate:モデルがスクリーニング済みの候補を役割への適合度で順位付けして説明し、予測は行いません)と、ベンチマークの評価結果(--backtest --benchmark … --narrate)にも適用されます。これは説明であり、金融上の助言ではありません。
.env にご自身の LLM キーを設定してください。この機能を有効にしない限り、データが外部に送信されることはありません。
ASSET_NARRATE_PROVIDER=anthropic # or: openai (any OpenAI-compatible endpoint)
ASSET_NARRATE_MODEL=claude-haiku-4-5
ASSET_NARRATE_KEY=sk-...
# for provider=openai, also set the endpoint (https required; http only for localhost):
ASSET_NARRATE_BASE_URL=https://api.groq.com/openai/v1
ASSET_NARRATE_TIER=paid # free | paid | local — privacy dial (see below); default: free
プライバシー設定。 tier で、マシンの外部に送信される情報を制御します。free(デフォルト。入力内容が学習に使われる可能性のあるプロバイダーの無料プラン向け)では、「中程度」「堅調」といった大まかな定性的区分だけが送信されます。正確な金額、リターン、日付はマシン内にとどまり、ローカルで差し込まれます。paid(契約上、データを学習に使用しないプロバイダー向け)では、より詳細な表現を生成するため、正確な数値が送信されます。3つ目の区分である**localは、ご自身のマシン上で動作するモデル向けです(Ollama / llama.cpp を http://localhost で実行する場合。::1 または host.docker.internal も指定できます)。マシンの外部に送信されないため、正確な数値も送信されます。この設定が有効になるのは、実際のローカルエンドポイントに対してのみです。それ以外の場合は free に切り替わります。この設定は安全側に動作**します。正確な値が送信されるのは、paid または local が明示的に指定された場合だけです。未入力または誤記された tier は free のままになり、free では、送信内容がプロバイダーの学習に使われる可能性があることをツールが1行のリマインダーとして記録します。ナレーションが設定されていない場合やモデル呼び出しに失敗した場合は、SUMMARY を付けずにブリーフを出力します。
🔑 設定 — すべてのキーを1か所に
設定はすべて環境変数で行います。チェックアウトから実行する場合は、リポジトリのルートにある gitignore 対象の .env に一度設定してください。uvx で実行する場合(.env を読み込めるチェックアウトがない場合)は、同じ名前で実際の環境変数を設定するか、Claude Desktop の設定の env ブロックに設定してください。明示的に指定したフラグが常に優先されます。
# your book + target (Steps 1 & 3)
ASSET_BOOK=data/my_data/transactions.csv
# ASSET_CSV=... # legacy alias for ASSET_BOOK, still honored
ASSET_TARGET=data/my_data/target.csv
# prices — optional free second source for when Yahoo throttles (Step 2)
TIINGO_API_KEY=...
# email delivery for --send (Step 4)
RESEND_API_KEY=...
[email protected]
# [email protected] # optional; defaults to Resend's onboarding sender
# narration for --narrate (optional — your own LLM key; see Narration above)
ASSET_NARRATE_PROVIDER=anthropic # or: openai (any OpenAI-compatible endpoint)
ASSET_NARRATE_MODEL=claude-haiku-4-5
ASSET_NARRATE_KEY=sk-...
# ASSET_NARRATE_BASE_URL=https://... # provider=openai only
# ASSET_NARRATE_TIER=free # free (default: only coarse bands leave) | paid (exact figures) | local
# MCP addon only
# ASSET_MCP_OFFLINE=1 # never fetch — for an already-warmed cache
# rarely needed
# ASSET_CACHE_DIR=/somewhere/else # move the price cache
# ASSET_UNIVERSE=path/to/universe.csv # your own discovery universe
どの設定も必須ではありません。キーが必要な機能は、呼び出し時にその旨を通知し、キーがなくても問題なく動作を継続します。
🔧 プロジェクト
🧪 開発
uv run pytest # unit + property-based + regression tests
uv run mypy app/ # strict type-checking
uv run ruff check app/ tests/ # lint
uv run python scripts/build_mcpb.py # package the Claude-Desktop addon → dist/asset-management-<v>.mcpb
📁 ディレクトリ構成
asset-management/
├── app/
│ ├── events.py CSV / Ghostfolio-JSON → typed event list
│ ├── derive.py events → holdings + cost basis + realized P&L
│ ├── corporate_actions.py split-adjust raw share counts (stock splits)
│ ├── prices.py multi-source price fetch (latest + history + splits) with provenance + cache
│ ├── metadata.py published fund facts (expense ratio, AUM, volume, age, holdings) + cache
│ ├── returns.py events + prices → equity curve, true TWR, MWR (XIRR), Modified Dietz
│ ├── risk.py drawdown family (max-DD / Ulcer / CDaR) + Sharpe/Sortino/Calmar with bootstrap CIs
│ ├── strategy.py holdings + target → named buy/sell suggestions (rebalance modes) + edge gate
│ ├── allocate.py choose a target: equal_weight / inverse_vol + risk-posture presets + per-asset caps
│ ├── onboard.py step-0 risk quiz (pure): 3 questions → a conservative / moderate / aggressive posture
│ ├── screen.py judge NEW candidate tickers (diversifier / cost / liquidity / age / overlap)
│ ├── backtest.py notional simulation + the held-out role/benchmark verdicts (Ulcer-first)
│ ├── pipeline.py the shared book→prices→returns→risk bundle (cli + mcp_server) + cache warm + the --demo book
│ ├── report.py suggestions + backtest + state + prices + returns + risk → ReportData → text/markdown/HTML
│ ├── narrate.py fenced narration: validated figures → {{token}} prose → SUMMARY (pure; no number can be the model's)
│ ├── llm.py optional narrator backend (OpenAI-compatible + Anthropic); fail-closed, opt-in
│ ├── email.py send the HTML brief via Resend (--send)
│ ├── cli.py argparse + entry composition + delivery routing + structured run log
│ ├── mcp_server.py read-only stdio MCP server: 8 tools over the core (offline; one-time cold-call auto-warm, ASSET_MCP_OFFLINE=1 opts out)
│ ├── universe.py curated ETF universe loader (Candidate + roles); app/data/universe.csv, auto-built
│ ├── discover.py book → role gaps → shelf menus: lead-shelf default / full menu / index / drill (--discover, propose-only)
│ ├── log_config.py logging setup
│ ├── http_safe.py the one no-redirect URL opener — a 3xx must not carry an API key to another host
│ ├── __main__.py python -m app
│ └── __init__.py
├── tests/ automated suite (unit, property, regression) — offline, run on every change
├── reconcile/ manual cross-validation against external tools (ghostfolio, quantstats)
├── scripts/ build_universe.py (refresh the ETF universe) · build_mcpb.py (Claude-Desktop bundle) · demo_fence.py (poke the fence)
├── .github/ the gate: ruff + mypy --strict + pytest on every push to main + every PR
├── pyproject.toml dependencies, mypy/ruff config
└── README.md
🔒 プライバシーポリシー
このツールはすべてお使いのマシン上で動作します。バックエンドも、アカウントも、テレメトリーもありません — 作者が何かを受け取ることは一切ありません。
- 収集する情報。 何もありません。取引ログ、そこから算出された保有資産、価格キャッシュは、お使いのコンピューター上の、指定したパス(
--book、--cache-dir、またはASSET_BOOK/ASSET_CACHE_DIR)でのみ読み書きされます。何もアップロードされず、利用データ、クラッシュレポート、分析データも送信されません。 - お使いのマシンから外部に送信される情報は、以下のみです。 (1)価格およびファンドデータのリクエスト — 株式ティッカーシンボルは、株価、履歴、株式分割、公開されているファンド情報を取得するために、Yahoo Finance に送信されます。また、
TIINGO_API_KEYを設定している場合は、Tiingo にも送信されます。送信されるのはティッカーシンボルのみで、保有数量、取得原価、残高は決して送信されません。(2)ナレーション、--narrateはデフォルトでオフです。有効にしてご自身の LLM キーを指定すると、ポートフォリオの数値が、選択したプロバイダー(OpenAI 互換または Anthropic)に送信され、文章としてまとめられます。ASSET_NARRATE_TIERの設定で送信する詳細の量を制御できます。localを選ぶと、お使いのマシン内にとどまります。オフにすると、LLM には一切接続されません。(3)メール送信、--sendはデフォルトでオフです。有効にしてご自身の Resend API キーを指定すると、保有資産、保有株数、取得原価、損益を含むレンダリング済み HTML レポートが、指定した宛先に配信されるよう Resend に POST されます。それ以外の情報は送信されません。また、--sendを指定しない限り、Resend に接続されることはありません。 - 保存と保持期間。 クローンから実行する場合、価格キャッシュは
data/pricesに保存されます。ツールをパッケージとしてインストールした場合は、ホームフォルダー内の.asset-management/pricesに保存されます(どちらもASSET_CACHE_DIRで変更できます)。削除するまで保持されます。2つのディレクトリーは別々です。クローンからキャッシュを作成しても、wheel としてインストールされた Desktop アドオンが読み取るキャッシュには反映されません。--saveで保存したレポートはreports/に保存されます。すべてご自身が所有する通常のファイルです。フォルダーを削除すれば、データもなくなります。作者はコピーを保持しておらず、保持することもできません。 - 第三者との共有。 作者は何も共有せず、共有するために何かを受け取ることもありません。関与する第三者は、上記で利用を選択した Yahoo、Tiingo、選択した LLM プロバイダー、Resend のみです。それぞれ独自のプライバシーポリシーに従います。
- MCP サーバー。 台帳への書き込みや取引は一切行いません。設定済みのブックに固定されており、ファイルパスを引数に取らず、書き込みツールも公開しません。独自の価格キャッシュ(ブックが設定されていない場合はデモ用ブックも)を
ASSET_CACHE_DIRの下に書き込みます。回数を制限したネットワーク取得を、オプトアウト可能な形で2回実行します(初回のキャッシュ作成時に一度、および未キャッシュのティッカーをスクリーニングする際のオンデマンド取得時)。ASSET_MCP_OFFLINE=1を設定すると、どちらも無効になります。それ以外の場所には何も送信しません。 - お問い合わせ。 github.com/disin7c9/asset-management/issues で Issue を作成してください。
📜 ライセンス
AGPL-3.0-or-later。商用利用を含め、無料で使用、変更、セルフホストできます。条件は1つです。変更版を配布する場合、または変更版をネットワークサービスとして他者に提供する場合は、変更したソースコードを同じライセンスで公開する必要があります。(Ghostfolio が使用しているものと同じライセンスです。)
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。