关于 portfolio-optimization
此 Skill 使用 `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 工作流要求 cuOpt 版本支持 QCQP/SOCP,例如 26.06 系列或更新版本。
- 与主机和工作流匹配的 CUDA 附加项:在 CUDA 12 上使用
uv sync --extra cuda12安装完整的 cuOpt/cuML 26.06,在当前完整 CUDA 13 技术栈上使用uv sync --extra cuda13,或使用uv sync --extra cuda13-socp通过 cuOpt 26.06 对 CUDA 13 SOCP 进行单独验证。 cvxpy提供cp.CUOPT。- 如果必须下载默认价格 CSV,首次运行时需要网络连接。
设置
此技能驱动已安装的 portfolio_optimization 包。可通过 Brev launchable 环境,或通过安装匹配的 CUDA 附加项后使用 NVIDIA-AI-Blueprints/portfolio-optimization 仓库来准备就绪的环境。
在打包的 Agent/评测沙盒中,portfolio_optimization 可能通过 PYTHONPATH 提供,而不是作为单独发布的 wheel。宣称缺少该包之前,请使用 python -c "import portfolio_optimization" 验证本地包。不要执行 pip install portfolio_optimization;不要从头重新实现示例工作流,也不要用通用的 pandas/scipy/cvxpy 投资组合代码替换该包的 API。
具体实现细节请以 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 行,并且至少包含一个请求的股票代码。如果用户提供了开始/结束日期,请在计算收益前对价格 DataFrame 切片,并报告保留的日期范围。在计算收益前,先在价格 DataFrame 中筛选股票代码。
regime_dict不接受股票代码字段。 - 使用
utils.calculate_returns(...)计算对数收益率。 - 对于 Mean-CVaR 任务,使用
cvar_utils.generate_cvar_data(...)、KDE 和KDESettings(device="GPU")生成情景。对于 Mean-Variance SOCP 方差上限任务,不要生成 CVaR 情景;计算对数收益率后直接使用returns_dict。 - 对于常规 Mean-CVaR 投资组合请求,定义包含明确
w_min和w_max的CvarParameters,并设置c_min=0.0和c_max=0.0,以确保结果为全额投资,而非 100% 现金。 - 对于方差上限、波动率上限、Markowitz、SOCP 或 QCQP 请求,定义
MeanVarianceParameters,将var_limit设为正方差上限,将c_min=0.0、c_max=0.0,并将L_tar=1.0设为仅做多且全额投资的配置。如果用户提供波动率上限,先将其平方,再赋给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。对于直接使用 cuOpt 求解的 Mean-Variance SOCP,确认cuoptPython 包可导入,并使用api="cuopt_python"调用优化器;cuOpt 会为二次约束自动选择 barrier 方法。绝不要回退到 CLARABEL、SCS、ECOS 或其他 CPU 求解器。如果缺少 cuOpt,请完成验证和设置,并报告缺少 GPU/cuOpt 运行时,而不是伪造 CPU 求解结果。 - 对于自定义约束,将用户请求映射到相应的参数模型: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。
Mean-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,
)
Mean-Variance 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;Mean-Variance SOCP:直接使用 cuOpt Python API,并自动选择 barrier |
| 再平衡 | 除非另有要求,否则不进行 |
默认的 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为 CSV 路径,而不是 DataFrame。调用re_optimize(...);它会返回(results_dataframe, re_optimize_dates, cumulative_portfolio_value)。 - 设置模型:
ReturnsComputeSettings、ScenarioGenerationSettings、KDESettings、ApiSettings、CvarParameters和MeanVarianceParameters。
示例
- “从标普 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对象,然后使用软件包的回测工具,报告 Sharpe、Sortino 和最大回撤。 - “回测月度再平衡”:使用上文的漂移触发条件配置
rebalance_portfolio,然后运行re_optimize(transaction_cost_factor=...)。
限制
- 需要配备 cuOpt 和 cuML 的 NVIDIA GPU;有意禁止使用 CPU 求解器。
- Mean-Variance SOCP 方差上限需要 cuOpt QCQP/SOCP 支持。安装 CUDA 扩展时,请使用 26.06 版本线或更高版本。
cuda13-socp有意安装不含 cuML 的 cuOpt,因为目前尚未发布cuml-cu1326.06;请将其用于直接验证 SOCP/QCQP,而不要用于 GPU KDE CVaR 工作流。- 除非软件包提供明确的混合整数锥规划支持,否则视为不支持同时使用基数约束和 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())"验证。 - 出现
ImportError,提示缺少cuml,或 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。 - 求解器报告问题不可行或无解:检查是否存在相互矛盾的约束、股票代码数量是否不足以满足所需的上限/基数约束,或日期筛选是否导致数据过少;报告能使请求变为可行的最小约束变更。
- 默认 CSV 中缺少请求的股票代码:报告缺少的股票代码,并继续使用其余请求的股票代码。
- 用户 CSV 未通过验证:请用户提供按日期索引的价格表,或首列为日期、其余列为数值型股票价格的 CSV;说明筛选后至少需要 60 行数据。