EfficientFrontier#
- class amplpyfinance.EfficientFrontier(expected_returns, cov_matrix, weight_bounds=(0, 1), tickers=None, solver='gurobi', solver_options='', verbose=False)[source]#
An EfficientFrontier object contains multiple optimization methods that can be called (corresponding to different objective functions) with various parameters.
AMPL version of
pypfopt.EfficientFrontierwith similar interface. This class is also available under the aliasamplpyfinance.EfficientFrontierWithAMPLin order to distinguish frompypfopt.EfficientFrontierif used together.Instance variables:
Inputs:
n_assets- inttickers- str listbounds- float tuple OR (float tuple) listcov_matrix- np.ndarrayexpected_returns- np.ndarraysolver- strsolver_options- str
Output:
weights- np.ndarray
Public methods:
min_volatility()optimizes for minimum volatilitymax_sharpe()optimizes for maximal Sharpe ratio (a.k.a the tangency portfolio)max_quadratic_utility()maximizes the quadratic utility, given some risk aversion.efficient_risk()maximizes return for a given target riskefficient_return()minimizes risk for a given target returnsportfolio_performance()calculates the expected return, volatility and Sharpe ratio for the optimized portfolio.clean_weights()rounds the weights and clips near-zeros.save_weights_to_file()saves the weights to csv, json, or txt.
- __init__(expected_returns, cov_matrix, weight_bounds=(0, 1), tickers=None, solver='gurobi', solver_options='', verbose=False)[source]#
Corresponding AMPL code:
set A ordered; param S{A, A}; param mu{A} default 0; param lb default 0; param ub default 1; var w{A} >= lb <= ub;
ampl.set["A"] = tickers ampl.param["S"] = pd.DataFrame( cov_matrix, index=tickers, columns=tickers ).unstack(level=0) ampl.param["mu"] = expected_returns ampl.param["lb"] = weight_bounds[0] ampl.param["ub"] = weight_bounds[1]
- Parameters:
expected_returns (pd.Series, list, np.ndarray) – expected returns for each asset. Can be None if optimizing for volatility only (but not recommended).
cov_matrix (pd.DataFrame or np.array) – covariance of returns for each asset. This must be positive semidefinite, otherwise optimization will fail.
weight_bounds (tuple OR tuple list, optional) – 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.
tickers (str list, optional) – asset labels.
solver (str) – name of the AMPL solver to use.
solver_options (str) – options for the given solver
verbose (bool, optional) – whether performance and debugging info should be printed, defaults to False
- Raises:
TypeError – if
expected_returnsis not a series, list or arrayTypeError – if
cov_matrixis not a dataframe or array
- min_volatility()[source]#
Minimize volatility.
Corresponding AMPL code:
set A ordered; param S{A, A}; param lb default 0; param ub default 1; var w{A} >= lb <= ub; minimize portfolio_variance: sum {i in A, j in A} w[i] * S[i, j] * w[j]; s.t. portfolio_weights: sum {i in A} w[i] = 1;
ampl.solve()
AMPL version of
pypfopt.EfficientFrontier.min_volatility()with the same interface:- Returns:
asset weights for the volatility-minimising portfolio
- Return type:
OrderedDict
- efficient_risk(target_volatility, market_neutral=False)[source]#
Maximize return for a target risk. The resulting portfolio will have a volatility less than the target (but not guaranteed to be equal).
Corresponding AMPL code:
param target_volatility; param market_neutral default 0; set A ordered; param S{A, A}; param mu{A} default 0; param lb default 0; param ub default 1; var w{A} >= lb <= ub; maximize portfolio_return: sum {i in A} mu[i] * w[i]; s.t. portfolio_variance: sum {i in A, j in A} w[i] * S[i, j] * w[j] <= target_volatility^2; s.t. portfolio_weights: sum {i in A} w[i] = if market_neutral then 0 else 1;
ampl.param["target_volatility"] = target_volatility ampl.param["market_neutral"] = market_neutral ampl.solve()
AMPL version of
pypfopt.EfficientFrontier.efficient_risk()with the same interface:- Parameters:
target_volatility (float) – the desired maximum volatility of the resulting portfolio.
market_neutral – whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound.
market_neutral – bool, optional
- Raises:
ValueError – if
target_volatilityis not a positive floatValueError – if no portfolio can be found with volatility equal to
target_volatilityValueError – if
risk_free_rateis non-numeric
- Returns:
asset weights for the efficient risk portfolio
- Return type:
OrderedDict
- efficient_return(target_return, market_neutral=False)[source]#
Calculate the ‘Markowitz portfolio’, minimising volatility for a given target return.
Corresponding AMPL code:
param target_return; param market_neutral default 0; set A ordered; param S{A, A}; param mu{A} default 0; param lb default 0; param ub default 1; var w{A} >= lb <= ub; minimize portfolio_variance: sum {i in A, j in A} w[i] * S[i, j] * w[j]; s.t. portfolio__return: sum {i in A} mu[i] * w[i] >= target_return; s.t. portfolio_weights: sum {i in A} w[i] = if market_neutral then 0 else 1;
ampl.param["target_return"] = target_return ampl.param["market_neutral"] = market_neutral ampl.solve()
AMPL version of
pypfopt.EfficientFrontier.efficient_return()with the same interface:- Parameters:
target_return (float) – the desired return of the resulting portfolio.
market_neutral (bool, optional) – whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound.
- Raises:
ValueError – if
target_returnis not a positive floatValueError – if no portfolio can be found with return equal to
target_return
- Returns:
asset weights for the Markowitz portfolio
- Return type:
OrderedDict
- max_sharpe(risk_free_rate=0.02)[source]#
Maximize the Sharpe Ratio. The result is also referred to as the tangency portfolio, as it is the portfolio for which the capital market line is tangent to the efficient frontier.
This is a convex optimization problem after making a certain variable substitution. See Cornuejols and Tutuncu (2006) for more.
Corresponding AMPL code:
param risk_free_rate default 0.02; set A ordered; param S{A, A}; param mu{A} default 0; var k >= 0; var z{i in A} >= 0; # scaled weights var w{i in A} = z[i] / k; minimize portfolio_sharpe: sum {i in A, j in A} z[i] * S[i, j] * z[j]; s.t. muz: sum {i in A} (mu[i] - risk_free_rate) * z[i] = 1; s.t. portfolio_weights: sum {i in A} z[i] = k;
ampl.param["risk_free_rate"] = risk_free_rate ampl.solve()
AMPL version of
pypfopt.EfficientFrontier.max_sharpe()with the same interface:- Parameters:
risk_free_rate (float, optional) – risk-free rate of borrowing/lending, defaults to 0.02. The period of the risk-free rate should correspond to the frequency of expected returns.
- Raises:
ValueError – if
risk_free_rateis non-numeric- Returns:
asset weights for the Sharpe-maximising portfolio
- Return type:
OrderedDict
- max_quadratic_utility(risk_aversion=1, market_neutral=False)[source]#
Maximize the given quadratic utility, i.e:
\[\max_w w^T \mu - \frac \delta 2 w^T \Sigma w\]Corresponding AMPL code:
param risk_aversion default 1; param market_neutral default 0; set A ordered; param S{A, A}; param mu{A} default 0; param lb default 0; param ub default 1; var w{A} >= lb <= ub; maximize quadratic_utility: sum {i in A} mu[i] * w[i] - 0.5 * risk_aversion * sum {i in A, j in A} w[i] * S[i, j] * w[j]; s.t. portfolio_weights: sum {i in A} w[i] = if market_neutral then 0 else 1;
ampl.param["risk_aversion"] = risk_aversion ampl.param["market_neutral"] = market_neutral ampl.solve()
AMPL version of
pypfopt.EfficientFrontier.max_quadratic_utility()with the same interface:- Parameters:
risk_aversion (positive float) – risk aversion parameter (must be greater than 0), defaults to 1
market_neutral – whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound.
market_neutral – bool, optional
- Returns:
asset weights for the maximum-utility portfolio
- Return type:
OrderedDict
- add_sector_constraints(sector_mapper, sector_lower, sector_upper)[source]#
Adds constraints on the sum of weights of different groups of assets. Most commonly, these will be sector constraints e.g portfolio’s exposure to tech must be less than x%:
sector_mapper = { "GOOG": "tech", "FB": "tech",, "XOM": "Oil/Gas", "RRC": "Oil/Gas", "MA": "Financials", "JPM": "Financials", } sector_lower = {"tech": 0.1} # at least 10% to tech sector_upper = { "tech": 0.4, # less than 40% tech "Oil/Gas": 0.1 # less than 10% oil and gas }
Corresponding AMPL code:
param lb default 0; param ub default 1; var w{A} >= lb <= ub; set SECTORS default {}; set SECTOR_MEMBERS{SECTORS}; param sector_lower{SECTORS} default -Infinity; param sector_upper{SECTORS} default Infinity; s.t. sector_constraints_lower{s in SECTORS: sector_lower[s] != -Infinity}: sum {i in SECTOR_MEMBERS[s]} w[i] >= sector_lower[s]; s.t. sector_constraints_upper{s in SECTORS: sector_upper[s] != Infinity}: sum {i in SECTOR_MEMBERS[s]} w[i] <= sector_upper[s];
sectors = set(sector_mapper.values()) ampl.set["SECTORS"] = sectors for sector in sectors: ampl.set["SECTOR_MEMBERS"][sector] = [ ticker for ticker, s in sector_mapper.items() if s == sector ] ampl.param["sector_lower"] = sector_lower ampl.param["sector_upper"] = sector_upper
AMPL version of
pypfopt.EfficientFrontier.add_sector_constraints()with the same interface:- Parameters:
sector_mapper ({str: str} dict) – dict that maps tickers to sectors
sector_lower ({str: float} dict) – lower bounds for each sector
sector_upper ({str:float} dict) – upper bounds for each sector
- portfolio_performance(verbose=False, risk_free_rate=0.02)[source]#
After optimising, calculate (and optionally print) the performance of the optimal portfolio. Currently calculates expected return, volatility, and the Sharpe ratio.
- Parameters:
verbose (bool, optional) – whether performance should be printed, defaults to False
risk_free_rate (float, optional) – risk-free rate of borrowing/lending, defaults to 0.02. The period of the risk-free rate should correspond to the frequency of expected returns.
- Raises:
ValueError – if weights have not been calcualted yet
- Returns:
expected return, volatility, Sharpe ratio.
- Return type:
(float, float, float)
- __module__ = 'amplpyfinance.efficient_frontier.efficient_frontier'#