mcpField
P
portfolio-optimization

portfolio-optimization

github.com/nvidia数据与数据库3.8k110 次安装今天更新
代码仓库

关于 portfolio-optimization

此 Skill 使用 `portfolio_optimization` 和 NVIDIA cuOpt 构建、分析、回测及再平衡量化投资组合,适用于 Mean-CVaR 与 Mean-Variance 配置任务,并提供 GPU 求解及有效前沿工作流。

主要功能

  1. 01Mean-CVaR 投资组合优化
  2. 02Mean-Variance SOCP 配置
  3. 03GPU KDE 情景生成
  4. 04有效前沿分析
  5. 05投资组合回测与再平衡

适用场景

  1. 01控制股票配置的下行风险
  2. 02设置方差或波动率上限
  3. 03比较不同风险厌恶值下的配置
  4. 04回测或定期再平衡持仓

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nvidia/skills --skill portfolio-optimization

安装后在 Claude Code 中输入/portfolio-optimization确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

BENCHMARK.md
skill-card.md
skill.oms.sig
evals/
EVAL.md
evals-full.json
evals.json
references/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,
)

指令

执行前简要说明将采用的默认设置,然后遵循以下防护措施:

  1. 加载 data/stock_data/sp500.csv;如果文件缺失,请先询问,再使用 portfolio_optimization.utils.download_data 下载 sp500。不要使用 glob 查找文件、替换数据或伪造价格数据。
  2. 求解前验证用户提供的 CSV:要求索引或第一列为日期格式,股票代码列为数值型,日期筛选后至少有 60 行,并且至少包含一个请求的股票代码。如果用户提供了开始/结束日期,请在计算收益前对价格 DataFrame 切片,并报告保留的日期范围。在计算收益前,先在价格 DataFrame 中筛选股票代码。regime_dict 不接受股票代码字段。
  3. 使用 utils.calculate_returns(...) 计算对数收益率。
  4. 对于 Mean-CVaR 任务,使用 cvar_utils.generate_cvar_data(...)、KDE 和 KDESettings(device="GPU") 生成情景。对于 Mean-Variance SOCP 方差上限任务,不要生成 CVaR 情景;计算对数收益率后直接使用 returns_dict。
  5. 对于常规 Mean-CVaR 投资组合请求,定义包含明确 w_min 和 w_max 的 CvarParameters,并设置 c_min=0.0 和 c_max=0.0,以确保结果为全额投资,而非 100% 现金。
  6. 对于方差上限、波动率上限、Markowitz、SOCP 或 QCQP 请求,定义 MeanVarianceParameters,将 var_limit 设为正方差上限,将 c_min=0.0、c_max=0.0,并将 L_tar=1.0 设为仅做多且全额投资的配置。如果用户提供波动率上限,先将其平方,再赋给 var_limit。
  7. 对于 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"))。
  8. 只能使用 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,确认 cuopt Python 包可导入,并使用 api="cuopt_python" 调用优化器;cuOpt 会为二次约束自动选择 barrier 方法。绝不要回退到 CLARABEL、SCS、ECOS 或其他 CPU 求解器。如果缺少 cuOpt,请完成验证和设置,并报告缺少 GPU/cuOpt 运行时,而不是伪造 CPU 求解结果。
  9. 对于自定义约束,将用户请求映射到相应的参数模型:CVaR 风险控制使用 CvarParameters;方差或波动率上限使用 MeanVarianceParameters.var_limit;权重上限使用 w_min/w_max;风险偏好使用 risk_aversion;置信水平使用 confidence;现金额度使用 c_max。除非该包明确支持混合整数锥优化,否则将基数约束与 SOCP 视为不支持。
  10. 如果用户在回测中未指定基准,使用相同股票代码构建等权投资组合。如果用户未指定约束,保留默认值表中的值,并在求解前简要重述重要假设。
  11. 按配置比例排序并提供权重、现金权重、预期收益、求解器标签(cuOpt GPU)以及所用风险指标:Mean-CVaR 使用 CVaR,SOCP 使用已实现方差和 var_limit。包括用户要求的有效前沿图、权重表、回测指标或再平衡计划。对于表格,按列或按行列出股票代码,并包含小数权重和百分比;对于图表,保留软件包返回的图,不要从头重新绘制。
  12. 对于要求达到报告级别的回答,提供证据证明确实运行了所请求的工作流。对于有效前沿,说明 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-cu13 26.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 行数据。

评论

支持 Markdown