第 1 章 · 02 安装与第一个组合


文档摘要

第 1 章 · 02 安装与第一个组合 本节摘要:本节带你从零跑出第一个最优组合——把抽象的「四层架构」落到可执行代码上。先讲清安装方式:pip 一行命令,源码安装的备用方案,以及 Windows/macOS 上的 C++ 编译依赖。再讲清输入数据的规范——价格 DataFrame 的长宽格式、索引为日期、列为资产代码、值为复权收盘价。最后用一个最小可运行的端到端示例,把 + + 串成一条线,输出权重并打印预期年化收益、波动率与夏普比率。读完本节,你的电脑上应当能复现一份最优组合权重表。 内容来源:原项目源码 快速示例、 安装说明、 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 用 pip 完成 PyPortfolioOpt 的安装,知道源码安装的备用方式。

第 1 章 · 02 安装与第一个组合

本节摘要:本节带你从零跑出第一个最优组合——把抽象的「四层架构」落到可执行代码上。先讲清安装方式:pip 一行命令,源码安装的备用方案,以及 Windows/macOS 上的 C++ 编译依赖。再讲清输入数据的规范——价格 DataFrame 的长宽格式、索引为日期、列为资产代码、值为复权收盘价。最后用一个最小可运行的端到端示例,把 mean_historical_return + sample_cov + EfficientFrontier.max_sharpe 串成一条线,输出权重并打印预期年化收益、波动率与夏普比率。读完本节,你的电脑上应当能复现一份最优组合权重表。

内容来源:原项目源码 README.md 快速示例、docs/index.rst 安装说明、cookbook/2-Mean-Variance-Optimisation.ipynb,汉化并套用体系化模板。

学习目标

阅读完本节,你应当能够:

  1. pip 完成 PyPortfolioOpt 的安装,知道源码安装的备用方式。
  2. 准备符合规范的价格 DataFrame(日期索引、资产列、复权价)。
  3. 跑通 mean_historical_return + sample_cov + max_sharpe 三步走。
  4. clean_weights 清理权重,用 portfolio_performance 打印表现。
  5. 把权重存盘为 csv/json/txt

一、安装

1.1 用 pip(推荐)

最简单的方式是 pip:

pip install pyportfolioopt

PyPortfolioOpt 的核心依赖是 numpypandasscipycvxpyscikit-learn。pip 会自动装好。其中 cvxpy 是底层的凸优化建模语言,所有 EfficientFrontier 的求解都委托给它。

⚠️ Windows 用户注意:cvxpy 依赖 C++ 编译环境。Windows 上若装不上,需先装 Visual Studio Build Tools;macOS 上需先装 XCode Command Line Tools(xcode-select --install)。具体见 cvxpy 官方安装文档。

1.2 从源码安装

若想用最新未发布版本,或想修改源码,可克隆仓库后本地安装:

git clone https://github.com/PyPortfolio/PyPortfolioOpt.git cd PyPortfolioOpt pip install .

若既要改源码又想全局 from pypfopt import x,用「可编辑模式」:

pip install -e git+https://github.com/pyportfolio/pyportfolioopt.git

1.3 用 poetry(可选)

作者推荐用依赖管理器。在 poetry 项目里:

poetry add pyportfolioopt

依赖管理器能锁定版本,避免日后环境漂移。生产环境强烈建议用这种方式。

二、准备价格数据

PyPortfolioOpt 的所有收益/风险函数默认吃价格 DataFrame:

  • 行索引:日期或时间戳。
  • :每只资产的代码(ticker)。
  • :复权收盘价(adjusted close),不复权会有除权除息跳空,扭曲收益。

理想数据长这样(截自官方测试集 tests/resources/stock_prices.csv):

date XOM RRC BBY MA PFE JPM 2010-01-04 54.068794 51.300568 32.524055 22.062426 13.940202 35.175220 2010-01-05 54.279907 51.993038 33.349487 21.997149 13.741367 35.856571 2010-01-06 54.749043 51.690697 33.090542 22.081820 13.697187 36.053574 2010-01-07 54.577045 51.593170 33.616547 21.937523 13.645634 36.767757 2010-01-08 54.358093 52.597733 32.297466 21.945297 13.756095 36.677460

读取时记得把日期列解析成 index:

import pandas as pd df = pd.read_csv( "tests/resources/stock_prices.csv", parse_dates=True, index_col="date", )

💡 数据获取:官方 cookbook 用 yfinance 库直接从雅虎财经下载,无需 API key:yf.download(tickers, period="max")["Close"]。教程示例可用它快速搭起数据。生产环境建议用 wind/tushare/聚宽等付费源,保证除权处理与数据质量。

三、三行代码出第一个组合

数据就绪后,从估收益、估风险、求最优三步走。完整的最小示例:

import pandas as pd from pypfopt import EfficientFrontier, risk_models, expected_returns # 1. 读数据 df = pd.read_csv("tests/resources/stock_prices.csv", parse_dates=True, index_col="date") # 2. 估收益与风险 mu = expected_returns.mean_historical_return(df) # 均值历史收益 S = risk_models.sample_cov(df) # 样本协方差 # 3. 优化:最大夏普 ef = EfficientFrontier(mu, S) raw_weights = ef.max_sharpe() cleaned_weights = ef.clean_weights() # 4. 打印 for name, value in cleaned_weights.items(): print(f"{name}: {value:.4f}")

输出示例(实际数值随数据集变化):

GOOG: 0.0458 AAPL: 0.0674 FB: 0.2008 ... MA: 0.3287 PFE: 0.2039 SBUX: 0.0173

💡 clean_weights 干了啥:优化器输出的 raw_weightsOrderedDict,值带浮点误差。clean_weights() 会把绝对值小于 cutoff(默认 1e-4)的权重置零,剩余保留 rounding(默认 5)位小数。这让结果可读,也方便后续离散分配。设 clean_weights(cutoff=1e-4, rounding=5) 可调。

四、查看组合表现

求出权重后,用 portfolio_performance 查看预期表现:

exp_return, volatility, sharpe = ef.portfolio_performance(verbose=True)

会打印:

Expected annual return: 29.9% Annual volatility: 21.8% Sharpe Ratio: 1.38

三个量的含义:

指标 公式 含义
预期年化收益 w @ mu 组合的期望年化收益(mu 已年化)
年化波动率 sqrt(wᵀ Σ w) 组合收益的年化标准差,即风险
夏普比率 (R_p - R_f) / σ 单位风险的超额收益,越大越好

⚠️ 这是样本内表现:portfolio_performance 用的是与优化相同的 mu、Σ,因此是样本内(in-sample)估计,几乎一定好看。它不能代表未来真实表现。务必做样本外回测再下结论。

五、权重存盘与离散分配

5.1 存盘

save_weights_to_file 把权重写入文件,支持三种格式:

ef.save_weights_to_file("weights.csv") # pd.Series.to_csv ef.save_weights_to_file("weights.json") # json.dump ef.save_weights_to_file("weights.txt") # str(dict)

5.2 离散分配(预告)

连续权重(如 MA=0.3287)还要换算成「买几股」才算可下单的分配。PyPortfolioOpt 提供 DiscreteAllocation 把权重 + 最新价 + 总资金 转成整数股数:

from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices latest_prices = get_latest_prices(df) da = DiscreteAllocation(cleaned_weights, latest_prices, total_portfolio_value=10000) allocation, leftover = da.greedy_portfolio() for name, value in allocation.items(): print(f"{name}: {value}") print(f"Funds remaining: ${leftover:.2f}")

输出:

MA: 19 PFE: 57 FB: 12 ... Funds remaining: $17.46

这是真实下单的依据。离散分配的两种算法(greedy_portfoliolp_portfolio)会在第 9 章详讲。

本节要点回顾

  1. 安装:pip install pyportfolioopt;Windows 装 VS Build Tools、macOS 装 XCode CLT 才能装上 cvxpy。
  2. 输入格式:价格 DataFrame——日期索引、资产列、复权收盘价。
  3. 三步走:mean_historical_return(df)sample_cov(df)EfficientFrontier(mu, S).max_sharpe()
  4. 清理与查看:clean_weights() 把极小值置零并四舍五入;portfolio_performance(verbose=True) 打印年化收益/波动率/夏普。
  5. 样本内警告:表现数字好看不等于真能赚,务必做样本外回测。
  6. 存盘与离散分配:save_weights_to_file 存三格式;DiscreteAllocation 把权重换算成买几股。

下一节,我们把这套流程抽象成贯穿全书的「四层架构」——收益模型、风险模型、目标函数、优化器,看它们如何可替换地组装。


发布者: 作者: 灏天文库 转发
评论区 (0)
U