portfolio-optimization について
`portfolio_optimization` と NVIDIA cuOpt を使い、定量ポートフォリオの構築、分析、バックテスト、リバランスを行うスキルです。Mean-CVaR と Mean-Variance の配分に対応し、GPU最適化や効率的フロンティアの作成を扱います。
主な機能
- 01Mean-CVaRポートフォリオ最適化
- 02Mean-Variance SOCP配分
- 03GPU KDEシナリオ生成
- 04効率的フロンティア分析
- 05ポートフォリオのバックテストとリバランス
活用シーン
- 01銘柄配分の下方リスク管理
- 02分散またはボラティリティ上限の設定
- 03リスク回避度別の配分比較
- 04保有資産のバックテストや定期リバランス
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nvidia/ skills --skill portfolio-optimizationインストール後、Claude Code で/portfolio-optimizationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
BENCHMARK.mdskill-card.mdskill.oms.sigevals/EVAL.mdevals-full.jsonevals.jsonreferences/workflows/agent_recipes.md詳細
NVIDIA cuOpt によるポートフォリオ最適化
目的
NVIDIA によって高速化された Mean-CVaR および Mean-Variance 最適化を使用して、定量的なポートフォリオを構築・分析します。portfolio_optimization パッケージを使ってリターンを計算し、CVaR 用の KDE シナリオを生成し、cuOpt GPU ソルバーで分散上限付き Markowitz 配分を SOCP/QCQP 問題として解き、効率的フロンティアを描画し、ポートフォリオをバックテストし、価格データからリバランスのワークフローを実行します。
使用する場面
次のようなタスクでは、このスキルを使用してください。
- 株価から Mean-CVaR ポートフォリオを構築または最適化する。
- 下方 CVaR リスクを抑えながら、ティッカー間のウェイトを配分する。
- cuOpt の SOCP/QCQP サポートを使い、厳格な分散またはボラティリティ上限付きの Mean-Variance または Markowitz 配分を解く。
- ポートフォリオの対象銘柄群について、効率的フロンティアを描画または確認する。
- リスク回避度別のウェイト表を作成する。
- 最適化されたポートフォリオをベンチマークと比較してバックテストする。
- スケジュールまたは乖離トリガーに基づいてポートフォリオをリバランスする。
- S&P 500、S&P 100、Dow 30、またはユーザー提供の価格データセットでワークフローを実行する。
よくある起動フレーズには、「ポートフォリオを最適化して」「CVaR ポートフォリオを構築して」「これらのティッカーを cuOpt で最適化して」「cuOpt で解いて」「効率的フロンティアを描画して」「リスク回避度別のウェイトを表示して」「この配分をバックテストして」「毎月リバランスして」「保有銘柄を CVaR で分析して」「配分を比較して」「下方リスクを軽減して」「配分を構築して」「配分の選択肢を評価して」「保有銘柄をストレステストして」「下方リスクへのエクスポージャーを評価して」「ウェイト上限のもとで保有銘柄を見直して」「ベンチマークポートフォリオを比較して」「CVaR シナリオをシミュレーションして」「ポートフォリオリスクをスクリーニングして」「制約下で保有銘柄を最適化して」「分散上限付きポートフォリオを解いて」「SOCP を使って」「ボラティリティ上限を設定して」「より低リスクの配分を見つけて」などがあります。
一般的な金融の概要、価格予測、ニューラルネットワークのトレーニング、車両経路問題、またはポートフォリオ以外の最適化には使用しないでください。
前提条件
portfolio_optimizationパッケージがインストールされた Python 環境。- cuOpt と cuML がインストールされた NVIDIA GPU ランタイム。Mean-Variance SOCP ワークフローには、26.06 系以降など、QCQP/SOCP をサポートする cuOpt ビルドが必要です。
- ホスト環境とワークフローに適合する CUDA エクストラ:CUDA 12 で cuOpt/cuML 26.06 をフルに使用する場合は
uv sync --extra cuda12、現在の CUDA 13 スタックをフルに使用する場合はuv sync --extra cuda13、cuOpt 26.06 による CUDA 13 の SOCP のみの検証にはuv sync --extra cuda13-socp。 cp.CUOPTを提供するcvxpy。- デフォルトの価格 CSV をダウンロードする必要がある場合は、初回実行時にネットワークアクセスが必要です。
セットアップ
このスキルは、インストール済みの portfolio_optimization パッケージを使用します。環境は、Brev の起動可能な環境から、または適合する CUDA エクストラをインストールした NVIDIA-AI-Blueprints/portfolio-optimization リポジトリから用意できます。
パッケージ化されたエージェント/評価用サンドボックスでは、portfolio_optimization は個別に公開された wheel ではなく、PYTHONPATH 経由で利用できる場合があります。見つからないと判断する前に、python -c "import portfolio_optimization" でローカルパッケージを確認してください。pip install portfolio_optimization は実行しないでください。サンプルのワークフローを一から再実装したり、パッケージの API を汎用的な pandas/scipy/cvxpy のポートフォリオコードに置き換えたりしないでください。
具体的な実装の詳細については、信頼できる情報源として references/workflows/agent_recipes.md を参照してください。価格の読み込み、リターンの準備、cuOpt での解法、25 点のフロンティアの構築、等ウェイトとの比較によるバックテスト、リバランサーの呼び出しについて、正確で動作する形式が記載されています。
デフォルトのデータセットは data/stock_data/sp500.csv です。これは gitignore 対象です。初回ダウンロードの前に、パッケージの yfinance データヘルパーを介して公開市場データを取得することをユーザーに伝え、確認を求めてください。
import cvxpy as cp
from portfolio_optimization.cvar_parameters import CvarParameters
from portfolio_optimization.utils import download_data
download_data("data/stock_data", datasets=["sp500"])
CVAR_SOLVER_SETTINGS = {"solver": cp.CUOPT, "verbose": False, "solver_method": "PDLP"}
cvar_params = CvarParameters(
w_min=0.0, w_max=1.0,
c_min=0.0, c_max=0.0,
risk_aversion=1.0, confidence=0.95,
)
手順
実行前に、適用するデフォルト設定を簡潔に説明し、その後、次のガードレールに従ってください。
data/stock_data/sp500.csvを読み込みます。存在しない場合は、portfolio_optimization.utils.download_dataでsp500をダウンロードしてよいか確認してください。価格データを glob したり、代替したり、捏造したりしないでください。- 求解の前にユーザーの CSV を検証します。日付形式のインデックスまたは先頭の日付列、数値のティッカー列、日付フィルタ後に少なくとも 60 行、および要求されたティッカーが少なくとも 1 つ必要です。ユーザーが開始日/終了日を指定した場合は、リターン計算の前に価格 DataFrame をスライスし、保持された日付範囲を報告してください。ティッカーのフィルタは、リターンを計算する前に価格 DataFrame に対して行います。
regime_dictはティッカーフィールドを取りません。 utils.calculate_returns(...)で LOG リターンを計算します。- Mean-CVaR タスクでは、
cvar_utils.generate_cvar_data(...)、KDE、およびKDESettings(device="GPU")でシナリオを生成します。Mean-Variance SOCP の分散上限タスクでは、CVaR シナリオを生成せず、LOG リターン計算後のreturns_dictを直接使用してください。 - 通常の Mean-CVaR ポートフォリオ要求では、明示的な
w_minとw_maxを指定してCvarParametersを定義し、c_min=0.0およびc_max=0.0を設定して、結果が 100 パーセント現金ではなく完全投資になるようにします。 - 分散上限、ボラティリティ上限、Markowitz、SOCP、または QCQP の要求では、正の分散上限を
var_limitに設定し、c_min=0.0、c_max=0.0、およびロングオンリーで完全投資の配分にはL_tar=1.0を指定してMeanVarianceParametersを定義します。ユーザーがボラティリティ上限を指定した場合は、var_limitに代入する前に二乗してください。 - Mean-CVaR タスクでは
cvar_optimizer.CVaR(returns_dict, cvar_params)を構築します。直接 cuOpt の Mean-Variance SOCP タスクではmean_variance_optimizer.MeanVariance(returns_dict, mean_variance_params, api_settings=ApiSettings(api="cuopt_python"))を構築します。 - NVIDIA cuOpt のみで求解します。CVaR では、
hasattr(cp, "CUOPT")およびstr(cp.CUOPT) in {str(s) for s in cp.installed_solvers()}を確認し、単発の求解またはループによるフロンティア求解のたびにCVAR_SOLVER_SETTINGSを渡してください。直接 Mean-Variance SOCP では、cuoptPython パッケージがインポート可能であることを確認し、api="cuopt_python"でオプティマイザーを呼び出します。cuOpt は二次制約に対してバリア法を自動選択します。CLARABEL、SCS、ECOS、またはその他の CPU ソルバーにフォールバックしないでください。cuOpt が存在しない場合は、検証/セットアップを完了し、CPU の結果を捏造するのではなく、GPU/cuOpt ランタイムが存在しないことを報告してください。 - カスタム制約については、ユーザーの要求を適切なパラメーターモデルにマッピングします。CVaR のリスク制御は
CvarParametersに、分散またはボラティリティの上限はMeanVarianceParameters.var_limitに、ウェイト上限はw_min/w_maxに、リスク選好度はrisk_aversionに、信頼水準はconfidenceに、現金許容額はc_maxにマッピングします。パッケージが明示的な混合整数錐計画のサポートを公開していない限り、カーディナリティと SOCP の組み合わせは未サポートとして扱ってください。 - ユーザーがバックテスト用のベンチマークを省略した場合は、同じティッカーによる等ウェイトポートフォリオを使用します。ユーザーが制約を省略した場合は、デフォルト表の値を保持し、求解前に重要となる前提を簡潔に再確認してください。
- 配分順にソートしたウェイト、現金ウェイト、期待リターン、ソルバーラベル(
cuOpt GPU)、および使用したリスク指標を提示します。リスク指標は、Mean-CVaR では CVaR、SOCP では実現分散とvar_limitです。要求されたフロンティア図、ウェイト表、バックテスト指標、またはリバランススケジュールを含めてください。表では、ティッカーを列または行として小数のウェイトとパーセンテージを含めます。プロットでは、パッケージが返した図をそのまま保持し、ゼロから描き直さないでください。 - レポート品質の回答では、要求されたワークフローが実際に実行された証拠を含めます。効率的フロンティアでは、
len(results_df)を明記し、要求されたra_numを使用します(ユーザーが別途指定しない限り 25)。分散上限 SOCP 求解では、result_row["solver"]、実現分散、要求されたvar_limitを報告し、実現分散が上限以下であることを確認します。ウェイト表では、results_df["weights"]をティッカー列に展開し、cashとrisk_aversionを含めます。バックテストでは、最適化ポートフォリオとベンチマークポートフォリオの両方についてmean portfolio return、sharpe、sortino、max drawdownを含めます。リバランスでは、results_dataframe、re_optimize_dates、およびcumulative_portfolio_valueの末尾を含めます。
標準ワークフローのひな形
該当するポートフォリオ最適化タスクは、この形式から始め、要求された出力に合わせて必要な箇所だけを調整します。コピーしてすぐに使える関数については、独自のコードを書く前に references/workflows/agent_recipes.md を参照してください。
平均-CVaR ワークフロー
import cvxpy as cp
import pandas as pd
from portfolio_optimization import backtest, cvar_optimizer, cvar_utils, rebalance, utils
from portfolio_optimization.cvar_parameters import CvarParameters
from portfolio_optimization.portfolio import Portfolio
from portfolio_optimization.settings import KDESettings, ReturnsComputeSettings, ScenarioGenerationSettings
if not hasattr(cp, "CUOPT") or str(cp.CUOPT) not in {str(s) for s in cp.installed_solvers()}:
raise RuntimeError("cuOpt GPU solver is required; do not substitute a CPU solver.")
CVAR_SOLVER_SETTINGS = {"solver": cp.CUOPT, "verbose": False, "solver_method": "PDLP"}
prices = utils.get_input_data("data/stock_data/sp500.csv")
returns_dict = utils.calculate_returns(
prices,
regime_dict=None,
returns_compute_settings=ReturnsComputeSettings(return_type="LOG"),
)
returns_dict = cvar_utils.generate_cvar_data(
returns_dict,
ScenarioGenerationSettings(
fit_type="kde",
kde_settings=KDESettings(device="GPU"),
),
)
cvar_params = CvarParameters(
w_min=0.0,
w_max=1.0,
c_min=0.0,
c_max=0.0,
risk_aversion=1.0,
confidence=0.95,
)
optimizer = cvar_optimizer.CVaR(returns_dict, cvar_params)
result, optimal_portfolio = optimizer.solve_optimization_problem(
solver_settings=CVAR_SOLVER_SETTINGS,
print_results=False,
)
平均-分散 SOCP ワークフロー
import importlib.util
import numpy as np
from portfolio_optimization import mean_variance_optimizer, utils
from portfolio_optimization.mean_variance_parameters import MeanVarianceParameters
from portfolio_optimization.settings import ApiSettings, ReturnsComputeSettings
if importlib.util.find_spec("cuopt") is None:
raise RuntimeError("cuOpt Python API is required; do not substitute a CPU solver.")
prices = utils.get_input_data("data/stock_data/sp500.csv")
returns_dict = utils.calculate_returns(
prices,
regime_dict=None,
returns_compute_settings=ReturnsComputeSettings(return_type="LOG"),
)
weights = np.ones(len(returns_dict["tickers"])) / len(returns_dict["tickers"])
var_limit = float(weights @ returns_dict["covariance"] @ weights) * 1.05
mean_variance_params = MeanVarianceParameters(
w_min=0.0,
w_max=1.0,
c_min=0.0,
c_max=0.0,
L_tar=1.0,
var_limit=var_limit,
)
optimizer = mean_variance_optimizer.MeanVariance(
returns_dict,
mean_variance_params,
api_settings=ApiSettings(api="cuopt_python"),
)
result, optimal_portfolio = optimizer.solve_optimization_problem(print_results=False)
realized_variance = float(
optimal_portfolio.weights @ returns_dict["covariance"] @ optimal_portfolio.weights
)
効率的フロンティアまたはウェイトの表を作成するには、次を呼び出します。
results_df, fig, ax = cvar_utils.create_efficient_frontier(
returns_dict,
cvar_params,
CVAR_SOLVER_SETTINGS,
ra_num=25,
show_plot=False,
show_discretized_portfolios=False,
benchmark_portfolios=False,
print_portfolio_results=False,
)
weights_table = pd.DataFrame(results_df["weights"].tolist(), index=results_df.index)
ベンチマークのバックテストでは、最適化された配分を Portfolio(name="cuOpt Optimal", tickers=returns_dict["tickers"], weights=optimal_portfolio.weights, cash=optimal_portfolio.cash) でラップし、同じ returns_dict["tickers"] を使って均等加重の Portfolio を作成します。その後、backtest.portfolio_backtester(..., test_method="historical").backtest_against_benchmarks(...) を使います。バックテスターは (backtest_results, ax) を返します。
月次リバランスでは、まず価格の DataFrame を CSV パスに書き込みます。re_optimize_criteria={"type": "drift_from_optimal", "threshold": 0, "norm": 1} を指定して rebalance.rebalance_portfolio(dataset_directory=<csv_path>, ...) をインスタンス化し、re_optimize(transaction_cost_factor=..., plot_title="Monthly Rebalancing") を呼び出します。リバランサーは (results_dataframe, re_optimize_dates, cumulative_portfolio_value) を返します。
データとデフォルト設定
| 設定 | デフォルト |
|---|---|
| データセット | data/stock_data/sp500.csv |
| 期間 | 利用可能な全期間 |
| ポートフォリオの種類 | ロングオンリー |
| 最大ウェイト | 指定がない場合はなし |
| リスク回避度 | 1.0 |
| 信頼度 | 0.95 |
| シナリオ手法 | GPU 上の KDE |
| ソルバー | CVaR:PDLP を使用する cuOpt GPU、平均-分散 SOCP:バリア法が自動選択される cuOpt Python API の直接呼び出し |
| リバランス | 要求がない場合はなし |
デフォルトの S&P 500 ファイルは過去時点のスナップショットであり、現在の構成銘柄が含まれていない場合があります。ユーザー提供の CSV は、utils.get_input_data と互換性のある、ティッカーを列とする日付インデックス付きの価格テーブルにしてください。要求されたティッカーが存在しない場合は、それらを除外して、除外したものを報告し、ユーザーが他のデータの取得を明示的に求めていない限り、利用可能な列を使って続行します。
主要な API
ポートフォリオの計算やシミュレーションループを再実装せず、パッケージの API を使用してください。portfolio_optimization ヘルパーはフラットなオブジェクトを返します。returns_dict には returns、mean、covariance、tickers などのキーが含まれます。returns_dict["regime_1"] のようにインデックスを指定しないでください。solve_optimization_problem(...) が返すのは (result_row, portfolio) であり、入れ子になった結果の辞書ではありません。
- リターン:
utils.calculate_returns(input_dataset, regime_dict, returns_compute_settings)。 - レジームフィルター:
regime_dictはNone、または{"name": "...", "range": ("YYYY-MM-DD", "YYYY-MM-DD")}です。レジーム名をキーとする辞書ではなく、ティッカーも含みません。 - シナリオ:Mean-CVaR の場合に限り、
cvar_utils.generate_cvar_data(returns_dict, scenario_generation_settings)を使用します。 - CVaR オプティマイザー:
cvar_optimizer.CVaR(returns_dict, cvar_params)。 - Mean-Variance SOCP オプティマイザー:
mean_variance_optimizer.MeanVariance(returns_dict, mean_variance_params, api_settings=ApiSettings(api="cuopt_python"))。 - CVaR の求解:
result_row, portfolio = cvar_problem.solve_optimization_problem(solver_settings=CVAR_SOLVER_SETTINGS, print_results=False)。 - SOCP の求解:
result_row, portfolio = mean_variance_problem.solve_optimization_problem(print_results=False)。 - 効率的フロンティア:
cvar_utils.create_efficient_frontier(returns_dict, cvar_params, solver_settings=CVAR_SOLVER_SETTINGS, ra_num=25)。返されるresults_dfには、指標、weightsの辞書列、cashが含まれます。 - ポートフォリオ:
Portfolio(name="", tickers=None, weights=None, cash=0.0, time_range=None)。ティッカーと、それらに対応するフラットな配列形式のweightsを渡します。 - バックテスト:最適化後の配分と各ベンチマークについて、
portfolio.Portfolioオブジェクトを作成します。均等加重ベンチマークには、重み1 / len(tickers)とcash=0.0を使用します。その後、backtest.portfolio_backtester(test_portfolio, returns_dict, risk_free_rate=0.0, test_method="historical", benchmark_portfolios=[...]).backtest_against_benchmarks(...)を呼び出します。 - リバランス:
rebalance.rebalance_portfolio(...)のdataset_directoryには、DataFrame ではなく CSV のパスが必要です。re_optimize(...)を呼び出します。これは(results_dataframe, re_optimize_dates, cumulative_portfolio_value)を返します。 - 設定モデル:
ReturnsComputeSettings、ScenarioGenerationSettings、KDESettings、ApiSettings、CvarParameters、MeanVarianceParameters。
使用例
- 「S&P 500 から最適なポートフォリオを構築する」:価格データを読み込み、LOG リターンを計算し、GPU KDE シナリオを生成して、ロングオンリーかつ完全投資の
CvarParametersを設定し、cuOpt で解き、分散された重みとリターン/CVaR を報告します。 - 「SOCP で分散上限のあるポートフォリオを解く」:価格データを読み込み、LOG リターンを計算し、
MeanVarianceParameters(var_limit=...)を設定して、api="cuopt_python"を直接指定して解き、期待リターン、実現分散、var_limit、重みを報告します。 - 「効率的フロンティアをプロットする」:
create_efficient_frontier(...)を呼び出してresults_dfを返し、リクエストに応じて図を表示または保存します。 - 「リスク回避度別の重みを表示する」:
results_df["weights"]を展開して、資産ごとのテーブルを作成します。 - 「均等加重と比較してバックテストする」:最適化後の
Portfolioオブジェクトと均等加重のPortfolioオブジェクトを作成し、パッケージのバックテスト機能を使用してシャープレシオ、ソルティノレシオ、最大ドローダウンを報告します。 - 「月次リバランスをバックテストする」:前述のドリフトトリガーを指定して
rebalance_portfolioを設定し、re_optimize(transaction_cost_factor=...)を実行します。
制限事項
- cuOpt と cuML を備えた NVIDIA GPU が必要です。CPU ソルバーは意図的に許可されていません。
- Mean-Variance SOCP の分散上限には、cuOpt の QCQP/SOCP サポートが必要です。CUDA エクストラをインストールする場合は、26.06 系以降を使用してください。
cuda13-socpは、cuml-cu1326.06 がまだ公開されていないため、cuML を含めずに cuOpt をインストールします。直接的な SOCP/QCQP の検証には使用できますが、GPU KDE CVaR ワークフローには使用しないでください。- Cardinality と SOCP の組み合わせは、パッケージが明示的な混合整数錐サポートを提供していない限り、未サポートとして扱われます。
- CPU のみの評価用コンテナでも、ルーティング、データ処理、レポート動作は検証できますが、有効な cuOpt ソルブは実行できません。その場合は、GPU/cuOpt ランタイムが不足していることを明示してください。
- デフォルトの価格データは過去のスナップショットであり、現在の構成銘柄が含まれていない場合があります。
- 初回のデータセットダウンロードには、ユーザーが CSV を提供しない限り、ネットワークアクセスが必要です。
トラブルシューティング
- デフォルト CSV が見つからない、または
FileNotFoundErrorが発生する場合:パッケージがdownload_data("data/stock_data", datasets=["sp500"])で公開市場データを取得することを説明してください。実行するのは、ユーザーの確認後に限ってください。 SolverErrorまたはcp.CUOPTが見つからない場合:ホストに適合する CUDA エクストラをインストールし、python -c "import cvxpy as cp; print(hasattr(cp, 'CUOPT'), cp.installed_solvers())"で確認してください。cumlのImportErrorまたは GPU KDE の失敗が発生する場合:python -c "import cuml"で cuML が存在することを確認し、KDESettings(device="GPU")を維持してください。cuda13-socpを使用している場合、CVaR/KDE ではこれは想定どおりです。cuML を使用するワークフローにはcuda12またはcuda13に切り替えてください。- SOCP の設定がソルブ前に失敗する場合:インストール済みの
cuoptパッケージが 26.06 系以降であること、およびMeanVarianceParameters.var_limitが正であることを確認してください。 - 通常の最適化で現金のみが返される場合:
CvarParametersでc_max=0.0を設定してください。 - ソルバーが実行不可能または解なしと報告する場合:矛盾する境界条件、要求された上限/cardinality に対してティッカー数が少なすぎること、またはデータが少なすぎる日付フィルターがないか確認してください。要求を実行可能にするために必要な、最小限の制約変更を報告してください。
- 要求されたティッカーがデフォルト CSV に存在しない場合:該当するティッカーを報告し、残りの要求されたティッカーで続行してください。
- ユーザーの CSV が検証に失敗する場合:日付をインデックスとする価格テーブル、または第 1 列が日付で残りの列が数値のティッカー価格である CSV を求めてください。フィルター後に最低 60 行が必要であることも伝えてください。