关于 cufolio
cuFOLIO 是面向量化投资组合构建与分析的技能,使用 NVIDIA GPU 加速。它通过 cuFOLIO 包和 cuOpt 求解器支持 Mean-CVaR 优化、有效前沿分析、回测及再平衡。
主要功能
- 01Mean-CVaR 投资组合优化
- 02GPU KDE 情景生成
- 03有效前沿分析
- 04投资组合回测
- 05按计划或漂移触发再平衡
适用场景
- 01按下行风险优化股票代码配置
- 02比较不同风险厌恶度下的权重
- 03将配置与基准进行回测
- 04模拟投资组合再平衡
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nvidia/ skills --skill cufolio安装后在 Claude Code 中输入/cufolio确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
BENCHMARK.mdskill-card.mdskill.oms.sigevals/EVAL.mdevals-full.jsonevals.jsonreferences/workflows/agent_recipes.md详细介绍
cuFOLIO 技能
用途
使用 NVIDIA 加速的 Mean-CVaR 优化来构建和分析量化投资组合。使用 cuFOLIO 计算收益、生成 KDE 情景、通过 cuOpt GPU 求解器求解资产配置、绘制有效前沿、回测投资组合,并根据价格数据运行再平衡工作流。
适用场景
在以下任务中使用此技能:
- 根据股票价格构建或优化 Mean-CVaR 投资组合。
- 在控制下行 CVaR 风险的同时,为多个股票代码分配权重。
- 绘制或检查某个投资组合资产范围的有效前沿。
- 生成按风险厌恶程度划分的权重表。
- 根据基准回测优化后的投资组合。
- 按计划或漂移触发条件对投资组合进行再平衡。
- 根据 S&P 500、S&P 100、Dow 30 或用户提供的价格数据集运行工作流。
常见触发短语包括“优化我的投资组合”“构建 CVaR 投资组合”“对这些股票代码使用 cuFOLIO”“使用 cuOpt 求解”“绘制有效前沿”“显示按风险厌恶程度划分的权重”“回测此资产配置”“每月再平衡”“使用 CVaR 分析我的持仓”“比较资产配置”“降低下行风险”“构建资产配置”“评估资产配置方案”“对我的持仓进行压力测试”“评估下行风险敞口”“根据权重上限检查我的持仓”“比较基准投资组合”“模拟 CVaR 情景”“筛查投资组合风险”“在约束条件下优化持仓”以及“寻找风险更低的资产配置”。
不要将其用于通用金融摘要、价格预测、神经网络训练、车辆路径规划或非投资组合优化。
前提条件
- 已安装
cufolio包的 Python 环境。 - 已安装 cuOpt 和 cuML 的 NVIDIA GPU 运行时。
- 与主机匹配的 CUDA 附加项,例如
uv sync --extra cuda12或uv sync --extra cuda13。 cvxpy提供cp.CUOPT。- 如果必须下载默认价格 CSV,首次运行时需要网络访问。
设置
此技能调用已安装的 cufolio 包。可通过 Brev launchable 或在 NVIDIA-AI-Blueprints/cuFOLIO 中安装匹配的 CUDA 附加项来准备环境。
在打包的 Agent/评估沙箱中,cufolio 可能通过 PYTHONPATH 提供,而不是作为单独发布的 wheel 提供。声明缺少该包之前,先运行 python -c "import cufolio" 验证本地包。不要运行 pip install cufolio,不要从头重新实现 cuFOLIO 工作流,也不要用通用 pandas/scipy/cvxpy 投资组合代码替代包 API。
具体实现细节请以 references/workflows/agent_recipes.md 为准。该文件包含加载价格、准备收益、使用 cuOpt 求解、构建包含 25 个点的有效前沿、根据等权重基准进行回测,以及调用再平衡器的精确可用示例。
默认数据集为 data/stock_data/sp500.csv。此文件已被 git 忽略。首次下载之前,请告知用户:此操作会通过 cuFOLIO/yfinance 数据辅助工具获取公开市场数据,并请用户确认:
import cvxpy as cp
from cufolio.cvar_parameters import CvarParameters
from cufolio.utils import download_data
download_data("data/stock_data", datasets=["sp500"])
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;如果文件缺失,先征求用户同意,再使用cufolio.utils.download_data下载sp500。不得使用 glob 查找、替换或伪造价格数据。 - 求解前验证用户提供的 CSV:要求有类似日期的索引或第一列日期列、数值型 ticker 列、日期筛选后至少保留 60 行,并且至少包含一个用户请求的 ticker。如果用户提供了起止日期,先对价格 DataFrame 切片,再计算收益率,并报告保留的日期范围。计算收益率前,先在价格 DataFrame 中筛选 ticker。
regime_dict不接受 ticker 字段。 - 使用
utils.calculate_returns(...)计算对数收益率。 - 使用
cvar_utils.generate_cvar_data(...)、KDE 和KDESettings(device="GPU")生成情景。 - 显式设置
w_min和w_max来定义CvarParameters。对于一般的“构建最优投资组合”请求,将c_min=0.0和c_max=0.0,确保资金全部投入,而不是 100% 持有现金。 - 直接从该收益率字典构建
cvar_optimizer.CVaR(returns_dict, cvar_params);保持 ticker、情景数组、均值和协方差的形状与 cuFOLIO 辅助函数返回的形状一致。 - 仅使用 NVIDIA cuOpt 求解。求解前,验证
hasattr(cp, "CUOPT")和str(cp.CUOPT) in {str(s) for s in cp.installed_solvers()}。对每次单次求解或循环式有效前沿求解,都传入SOLVER_SETTINGS。绝不退回使用 CLARABEL、SCS、ECOS 或其他 CPU 求解器。如果没有 cuOpt,则完成验证和设置,并报告缺少 GPU/cuOpt 运行时,而不是伪造 CPU 求解结果。 - 对于自定义约束,将用户请求映射到
CvarParameters:权重上限映射到w_min/w_max,风险偏好映射到risk_aversion,置信水平映射到confidence,现金额度映射到c_max;只有当软件包为该工作流提供显式资产数量约束时,才支持基数约束。如果约束之间存在冲突(例如,最大权重过低,无法覆盖请求的 ticker 数量),应解释冲突并询问用户要放宽哪项约束,不得自行猜测。 - 如果用户未指定回测基准,则使用相同 ticker 构建等权投资组合。如果用户未指定约束,则保留默认设置表中的值,并在求解前简要重申会产生显著影响的假设。
- 按配置权重排序提供权重、现金权重、预期收益率、CVaR、求解器标签(
cuOpt GPU),以及用户要求的有效前沿图、权重表、回测指标或再平衡计划。对于表格,将 ticker 作为列或行,并同时列出小数权重和百分比;对于图表,保留 cuFOLIO 返回的图表,不要从头重新绘制。 - 对需要用于报告的回答,提供证据证明实际运行了所请求的工作流。对于有效前沿,注明
len(results_df),并使用用户指定的ra_num(默认值为 25,除非用户另有指定)。对于权重表,将results_df["weights"]展开为 ticker 列,并包含cash和risk_aversion。对于回测,优化投资组合和基准投资组合均须包含mean portfolio return、sharpe、sortino和max drawdown。对于再平衡,须包含results_dataframe、re_optimize_dates和cumulative_portfolio_value的末尾部分。
标准工作流骨架
从此结构开始处理正向 cuFOLIO 任务,并且只调整所请求的输出。编写自定义代码前,请先阅读 references/workflows/agent_recipes.md,其中包含可完整复制的函数。
import cvxpy as cp
import pandas as pd
from cufolio import backtest, cvar_optimizer, cvar_utils, rebalance, utils
from cufolio.cvar_parameters import CvarParameters
from cufolio.portfolio import Portfolio
from cufolio.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.")
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=SOLVER_SETTINGS,
print_results=False,
)
要生成有效前沿或权重表,请调用:
results_df, fig, ax = cvar_utils.create_efficient_frontier(
returns_dict,
cvar_params,
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 |
| 日期范围 | 完整可用范围 |
| 投资组合类型 | 仅做多 |
| 最大权重 | 除非另有指定,否则为 None |
| 风险厌恶度 | 1.0 |
| 置信度 | 0.95 |
| 情景方法 | GPU 上的 KDE |
| 求解器 | 使用 PDLP 的 cuOpt GPU |
| 再平衡 | 除非另有请求,否则不进行 |
默认的 S&P 500 文件是历史快照,可能不包含当前成分股。用户提供的 CSV 应为以日期为索引、以股票代码为列的价格表,并且与 utils.get_input_data 兼容。如果请求的股票代码缺失,则删除这些代码、报告缺失项,并使用可用列继续,除非用户明确要求获取其他数据。
关键 API
使用软件包 API,而不是重新实现投资组合数学计算或模拟循环。cuFOLIO 辅助函数返回扁平对象: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")};它不是按市场状态名称索引的,也不包含股票代码。 - 情景:
cvar_utils.generate_cvar_data(returns_dict, scenario_generation_settings)。 - 优化器:
cvar_optimizer.CVaR(returns_dict, cvar_params)。 - 求解:
result_row, portfolio = cvar_problem.solve_optimization_problem(solver_settings=SOLVER_SETTINGS, print_results=False)。 - 有效前沿:
cvar_utils.create_efficient_frontier(returns_dict, cvar_params, solver_settings=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。
示例
- “根据 S&P 500 构建最优投资组合”:加载价格数据,计算对数收益率,生成 GPU KDE 情景,设置仅做多且满仓的
CvarParameters,使用 cuOpt 求解,并报告分散化的权重以及收益率/CVaR。 - “绘制有效前沿”:调用
create_efficient_frontier(...),返回results_df,并按要求显示或保存图表。 - “按风险厌恶程度给出权重”:将
results_df["weights"]展开为逐资产表格。 - “与等权投资组合进行回测”:构建优化后的
Portfolio对象和等权Portfolio对象,然后使用 cuFOLIO 回测器,并报告 Sharpe、Sortino 和最大回撤。 - “回测每月再平衡”:使用上述漂移触发条件配置
rebalance_portfolio,然后运行re_optimize(transaction_cost_factor=...)。
局限
- 需要配备 cuOpt 和 cuML 的 NVIDIA GPU;不允许使用 CPU 求解器。
- 仅有 CPU 的评估容器仍可验证路由、数据处理和报告行为,但无法生成有效的 cuOpt 求解结果。在这种情况下,请明确说明缺少 GPU/cuOpt 运行环境。
- 默认价格数据是历史快照,可能不包含当前成分股。
- 首次运行时,下载数据集需要网络连接,除非用户提供 CSV 文件。
故障排除
- 缺少默认 CSV 或出现
FileNotFoundError:说明 cuFOLIO 会通过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 失败:确认已安装 cuML(运行python -c "import cuml"),并保留KDESettings(device="GPU")。 - 普通优化返回全现金:在
CvarParameters中设置c_max=0.0。 - 求解器报告不可行或无解:检查边界条件是否矛盾、股票代码数量是否不足以满足指定的上限/基数,或日期筛选后数据是否过少;报告使请求可行所需的最小约束调整。
- 默认 CSV 中缺少请求的股票代码:报告缺少的代码,并继续处理其余请求的代码。
- 用户 CSV 未通过验证:请用户提供带日期索引的价格表,或首列为日期、其余列为数值型股票价格的 CSV;说明筛选后至少需要 60 行。