mirror of
https://github.com/robertmartin8/PyPortfolioOpt.git
synced 2022-11-27 18:02:41 +03:00
Hidden API for changing solver
This commit is contained in:
@@ -70,8 +70,6 @@ Basic Usage
|
||||
.. automodule:: pypfopt.efficient_frontier
|
||||
|
||||
.. autoclass:: EfficientFrontier
|
||||
:members:
|
||||
:exclude-members: custom_objective
|
||||
|
||||
.. automethod:: __init__
|
||||
|
||||
@@ -85,6 +83,7 @@ Basic Usage
|
||||
If you want to generate short-only portfolios, there is a quick hack. Multiply
|
||||
your expected returns by -1, then optimise a long-only portfolio.
|
||||
|
||||
.. automethod:: min_volatility
|
||||
|
||||
.. automethod:: max_sharpe
|
||||
|
||||
@@ -110,6 +109,8 @@ Basic Usage
|
||||
:py:meth:`efficient_return`, the optimiser will fail silently and return
|
||||
weird weights. *Caveat emptor* applies!
|
||||
|
||||
.. automethod:: efficient_return
|
||||
|
||||
.. automethod:: portfolio_performance
|
||||
|
||||
.. tip::
|
||||
@@ -123,6 +124,12 @@ Basic Usage
|
||||
weights, expected_returns, cov_matrix, verbose=True, risk_free_rate=0.02
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
PyPortfolioOpt defers to cvxpy's default choice of solver. If you would like to explicitly
|
||||
choose the solver and see verbose output, simply assign ``ef.solver = "ECOS"`` prior to calling
|
||||
the actual optimisation method. You can choose from any of the `supported solvers <https://www.cvxpy.org/tutorial/advanced/index.html#choosing-a-solver>`_.
|
||||
|
||||
Adding objectives and constraints
|
||||
=================================
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ class BaseConvexOptimizer(BaseOptimizer):
|
||||
- ``n_assets`` - int
|
||||
- ``tickers`` - str list
|
||||
- ``weights`` - np.ndarray
|
||||
- ``solver`` - str
|
||||
|
||||
Public methods:
|
||||
|
||||
@@ -144,6 +145,8 @@ class BaseConvexOptimizer(BaseOptimizer):
|
||||
self._upper_bounds = None
|
||||
self._map_bounds_to_constraints(weight_bounds)
|
||||
|
||||
self.solver = None
|
||||
|
||||
def _map_bounds_to_constraints(self, test_bounds):
|
||||
"""
|
||||
Process input bounds into a form acceptable by cvxpy and add to the constraints list.
|
||||
@@ -193,7 +196,11 @@ class BaseConvexOptimizer(BaseOptimizer):
|
||||
"""
|
||||
try:
|
||||
opt = cp.Problem(cp.Minimize(self._objective), self._constraints)
|
||||
opt.solve()
|
||||
|
||||
if self.solver is not None:
|
||||
opt.solve(solver=self.solver, verbose=True)
|
||||
else:
|
||||
opt.solve()
|
||||
except (TypeError, cp.DCPError):
|
||||
raise exceptions.OptimizationError
|
||||
if opt.status != "optimal":
|
||||
|
||||
@@ -28,7 +28,7 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
|
||||
- ``bounds`` - float tuple OR (float tuple) list
|
||||
- ``cov_matrix`` - np.ndarray
|
||||
- ``expected_returns`` - np.ndarray
|
||||
|
||||
- ``solver`` - str
|
||||
|
||||
- Output: ``weights`` - np.ndarray
|
||||
|
||||
@@ -265,7 +265,7 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
|
||||
:return: asset weights for the efficient risk portfolio
|
||||
:rtype: dict
|
||||
"""
|
||||
if not isinstance(target_volatility, float) or target_volatility < 0:
|
||||
if not isinstance(target_volatility, (float, int)) or target_volatility < 0:
|
||||
raise ValueError("target_volatility should be a positive float")
|
||||
|
||||
self._objective = objective_functions.portfolio_return(
|
||||
|
||||
@@ -65,6 +65,28 @@ def test_min_volatility():
|
||||
)
|
||||
|
||||
|
||||
def test_min_volatility_different_solver():
|
||||
ef = setup_efficient_frontier()
|
||||
ef.solver = "ECOS"
|
||||
w = ef.min_volatility()
|
||||
assert isinstance(w, dict)
|
||||
assert set(w.keys()) == set(ef.tickers)
|
||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||
assert all([i >= 0 for i in w.values()])
|
||||
test_performance = (0.179312, 0.159151, 1.001015)
|
||||
np.testing.assert_allclose(ef.portfolio_performance(), test_performance, atol=1e-5)
|
||||
|
||||
ef = setup_efficient_frontier()
|
||||
ef.solver = "OSQP"
|
||||
w = ef.min_volatility()
|
||||
np.testing.assert_allclose(ef.portfolio_performance(), test_performance, atol=1e-5)
|
||||
|
||||
ef = setup_efficient_frontier()
|
||||
ef.solver = "SCS"
|
||||
w = ef.min_volatility()
|
||||
np.testing.assert_allclose(ef.portfolio_performance(), test_performance, atol=1e-3)
|
||||
|
||||
|
||||
def test_min_volatility_no_rets():
|
||||
# Should work with no rets, see issue #82
|
||||
df = get_data()
|
||||
|
||||
Reference in New Issue
Block a user