Source code for czsc.utils.portfolio

# -*- coding: utf-8 -*-
"""
author: zengbin93
email: zeng_bin8888@163.com
create_dt: 2024/7/26 15:46
describe: 资产组合研究相关的工具函数
"""
import loguru


[docs]def max_sharp(df, weight_bounds=(0, 1), **kwargs): """最大夏普比例组合 依赖 PyPortfolioOpt 库,需要安装 pip install PyPortfolioOpt :param df: pd.DataFrame, 包含多个资产的日收益率数据,index 为日期 :param weight_bounds: tuple, 权重范围, 默认 (0, 1), 参考 EfficientFrontier 的 weight_bounds 参数 如果需要设置不同的权重范围,可以传入 dict,如 {"asset1": (0, 0.5), "asset2": (0.1, 0.5)} :param kwargs: 其他参数 - logger: loguru.logger, 默认 None, 日志记录器 - rounding: int, 默认 4, 权重四舍五入的小数位数 :return: dict, 资产权重 """ from pypfopt import EfficientFrontier from pypfopt import expected_returns from pypfopt import risk_models from czsc.utils.stats import daily_performance logger = kwargs.get("logger", loguru.logger) df = df.copy() if "dt" in df.columns: df = df.set_index("dt") mu = expected_returns.mean_historical_return(df, returns_data=True) S = risk_models.risk_matrix(df, returns_data=True, method="sample_cov") if isinstance(weight_bounds, dict): weight_bounds = [ [weight_bounds.get(asset, (0, 1))[0] for asset in df.columns], [weight_bounds.get(asset, (0, 1))[1] for asset in df.columns], ] # Optimize for maximal Sharpe ratio ef = EfficientFrontier(mu, S, weight_bounds=weight_bounds) _ = ef.max_sharpe() weights = ef.clean_weights(cutoff=1e-4, rounding=kwargs.get("rounding", 4)) logger.info(f"资产权重:{weights}") portfolio = df.apply(lambda x: x * weights[x.name], axis=0) stats = daily_performance(portfolio.sum(axis=1).to_list()) logger.info(f"组合表现:{stats}") return weights