pypfopt.efficient_frontier.efficient_cvar 源代码

"""
The ``efficient_cvar`` submodule houses the EfficientCVaR class, which
generates portfolios along the mean-CVaR frontier.
"""

import warnings

import cvxpy as cp
import numpy as np

from .. import objective_functions
from .efficient_frontier import EfficientFrontier


[文档] class EfficientCVaR(EfficientFrontier): """ EfficientCVaR 类允许沿平均 CVaR 前沿进行优化,使用R ockafellar and Ursayev(2001)的表述。 可用变量: - 输入: - ``n_assets`` - int - ``tickers`` - str list - ``bounds`` - float tuple OR (float tuple) list - ``returns`` - pd.DataFrame - ``expected_returns`` - np.ndarray - ``solver`` - str - ``solver_options`` - {str: str} dict - 输出: ``weights`` - np.ndarray 公共方法: - ``min_cvar()`` 使 CVaR 最小化 - ``efficient_risk()`` 在给定的 CVaR 的情况下,使收益最大化。 - ``efficient_return()`` 在给定的目标收益率下,使 CVaR 最小。 - ``add_objective()`` 为优化问题添加一个(凸)目标 - ``add_constraint()`` 在优化问题中加入一个约束条件。 - ``portfolio_performance()`` 计算投资组合的期望收益和 CVaR。 - ``set_weights()`` 从 weights dict 创建 self.weights (np.ndarray)。 - ``clean_weights()`` 对权重进行四舍五入,并剪掉接近零的部分。 - ``save_weights_to_file()`` 将权重保存为 csv、json 或 txt。 """ def __init__( self, expected_returns, returns, beta=0.95, weight_bounds=(0, 1), solver=None, verbose=False, solver_options=None, ): """ :param expected_returns: expected returns for each asset. Can be None if optimising for conditional value at risk only. :type expected_returns: pd.Series, list, np.ndarray :param returns: (historic) returns for all your assets (no NaNs). See ``expected_returns.returns_from_prices``. :type returns: pd.DataFrame or np.array :param beta: confidence level, defauls to 0.95 (i.e expected loss on the worst (1-beta) days). :param weight_bounds: minimum and maximum weight of each asset OR single min/max pair if all identical, defaults to (0, 1). Must be changed to (-1, 1) for portfolios with shorting. :type weight_bounds: tuple OR tuple list, optional :param solver: name of solver. list available solvers with: `cvxpy.installed_solvers()` :type solver: str :param verbose: whether performance and debugging info should be printed, defaults to False :type verbose: bool, optional :param solver_options: parameters for the given solver :type solver_options: dict, optional :raises TypeError: if ``expected_returns`` is not a series, list or array """ super().__init__( expected_returns=expected_returns, cov_matrix=np.zeros((returns.shape[1],) * 2), # dummy weight_bounds=weight_bounds, solver=solver, verbose=verbose, solver_options=solver_options, ) self.returns = self._validate_returns(returns) self._beta = self._validate_beta(beta) self._alpha = cp.Variable() self._u = cp.Variable(len(self.returns))
[文档] def set_weights(self, input_weights): raise NotImplementedError("Method not available in EfficientCVaR.")
@staticmethod def _validate_beta(beta): if not (0 <= beta < 1): raise ValueError("beta must be between 0 and 1") if beta <= 0.2: warnings.warn( "Warning: beta is the confidence-level, not the quantile. Typical values are 80%, 90%, 95%.", UserWarning, ) return beta def min_volatility(self): raise NotImplementedError("Please use min_cvar instead.") def max_sharpe(self, risk_free_rate=0.02): raise NotImplementedError("Method not available in EfficientCVaR.") def max_quadratic_utility(self, risk_aversion=1, market_neutral=False): raise NotImplementedError("Method not available in EfficientCVaR.")
[文档] def min_cvar(self, market_neutral=False): """ 尽量减少投资组合的 CVaR(进一步解释见文件)。 :param market_neutral: whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound. :param market_neutral: bool, optional :return: asset weights for the volatility-minimising portfolio :rtype: OrderedDict """ self._objective = self._alpha + 1.0 / ( len(self.returns) * (1 - self._beta) ) * cp.sum(self._u) for obj in self._additional_objectives: self._objective += obj self.add_constraint(lambda _: self._u >= 0.0) self.add_constraint( lambda w: self.returns.values @ w + self._alpha + self._u >= 0.0 ) self._make_weight_sum_constraint(market_neutral) return self._solve_cvxpy_opt_problem()
[文档] def efficient_return(self, target_return, market_neutral=False): """ 在给定的目标收益下,使 CVaR 最小化。 :param target_return: the desired return of the resulting portfolio. :type target_return: float :param market_neutral: whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound. :type market_neutral: bool, optional :raises ValueError: if ``target_return`` is not a positive float :raises ValueError: if no portfolio can be found with return equal to ``target_return`` :return: asset weights for the optimal portfolio :rtype: OrderedDict """ update_existing_parameter = self.is_parameter_defined("target_return") if update_existing_parameter: self._validate_market_neutral(market_neutral) self.update_parameter_value("target_return", target_return) else: self._objective = self._alpha + 1.0 / ( len(self.returns) * (1 - self._beta) ) * cp.sum(self._u) for obj in self._additional_objectives: self._objective += obj self.add_constraint(lambda _: self._u >= 0.0) self.add_constraint( lambda w: self.returns.values @ w + self._alpha + self._u >= 0.0 ) ret = self.expected_returns.T @ self._w target_return_par = cp.Parameter(name="target_return", value=target_return) self.add_constraint(lambda _: ret >= target_return_par) self._make_weight_sum_constraint(market_neutral) return self._solve_cvxpy_opt_problem()
[文档] def efficient_risk(self, target_cvar, market_neutral=False): """ 使目标 CVaR 的收益最大化。最终的投资组合的 CVaR 将低于目标值(但不保证相等)。 :param target_cvar: the desired conditional value at risk of the resulting portfolio. :type target_cvar: float :param market_neutral: whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound. :param market_neutral: bool, optional :return: asset weights for the efficient risk portfolio :rtype: OrderedDict """ update_existing_parameter = self.is_parameter_defined("target_cvar") if update_existing_parameter: self._validate_market_neutral(market_neutral) self.update_parameter_value("target_cvar", target_cvar) else: self._objective = objective_functions.portfolio_return( self._w, self.expected_returns ) for obj in self._additional_objectives: self._objective += obj cvar = self._alpha + 1.0 / (len(self.returns) * (1 - self._beta)) * cp.sum( self._u ) target_cvar_par = cp.Parameter( value=target_cvar, name="target_cvar", nonneg=True ) self.add_constraint(lambda _: cvar <= target_cvar_par) self.add_constraint(lambda _: self._u >= 0.0) self.add_constraint( lambda w: self.returns.values @ w + self._alpha + self._u >= 0.0 ) self._make_weight_sum_constraint(market_neutral) return self._solve_cvxpy_opt_problem()
[文档] def portfolio_performance(self, verbose=False): """ 优化后,计算(并可选择打印)最优投资组合的表现,具体是:期望收益率、CVaR :param verbose: whether performance should be printed, defaults to False :type verbose: bool, optional :raises ValueError: if weights have not been calculated yet :return: expected return, CVaR. :rtype: (float, float) """ mu = objective_functions.portfolio_return( self.weights, self.expected_returns, negative=False ) cvar = self._alpha + 1.0 / (len(self.returns) * (1 - self._beta)) * cp.sum( self._u ) cvar_val = cvar.value if verbose: print("Expected annual return: {:.1f}%".format(100 * mu)) print("Conditional Value at Risk: {:.2f}%".format(100 * cvar_val)) return mu, cvar_val