mirror of
https://github.com/robertmartin8/PyPortfolioOpt.git
synced 2022-11-27 18:02:41 +03:00
Cleaned up Pat's PR
This commit is contained in:
@@ -141,14 +141,16 @@ class BaseConvexOptimizer(BaseOptimizer):
|
||||
- ``save_weights_to_file()`` saves the weights to csv, json, or txt.
|
||||
"""
|
||||
|
||||
def __init__(self, n_assets, tickers=None, weight_bounds=(0, 1), solver=None, verbose=False):
|
||||
def __init__(
|
||||
self, n_assets, tickers=None, weight_bounds=(0, 1), solver=None, verbose=False
|
||||
):
|
||||
"""
|
||||
: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, optional (see cvxpy.Problem#_solve for default. spoiler: it's ECOS)
|
||||
:param solver: name of solver. list available solvers with: ``cvxpy.installed_solvers()``
|
||||
:type solver: str, optional. Defaults to "ECOS"
|
||||
:param verbose: whether performance and debugging info should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
"""
|
||||
|
||||
@@ -451,18 +451,18 @@ class BlackLittermanModel(base_optimizer.BaseOptimizer):
|
||||
"""
|
||||
return self.bl_weights(risk_aversion)
|
||||
|
||||
def portfolio_performance(self, risk_free_rate=0.02, verbose=False):
|
||||
def portfolio_performance(self, verbose=False, risk_free_rate=0.02):
|
||||
"""
|
||||
After optimising, calculate (and optionally print) the performance of the optimal
|
||||
portfolio. Currently calculates expected return, volatility, and the Sharpe ratio.
|
||||
This method uses the BL posterior returns and covariance matrix.
|
||||
|
||||
:param verbose: whether performance should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
:param risk_free_rate: 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.
|
||||
:type risk_free_rate: float, optional
|
||||
:param verbose: whether performance should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
:raises ValueError: if weights have not been calcualted yet
|
||||
:return: expected return, volatility, Sharpe ratio.
|
||||
:rtype: (float, float, float)
|
||||
|
||||
@@ -445,15 +445,15 @@ class CLA(base_optimizer.BaseOptimizer):
|
||||
# Overrides parent method since set_weights does nothing.
|
||||
raise NotImplementedError("set_weights does nothing for CLA")
|
||||
|
||||
def portfolio_performance(self, risk_free_rate=0.02, verbose=False):
|
||||
def portfolio_performance(self, verbose=False, risk_free_rate=0.02):
|
||||
"""
|
||||
After optimising, calculate (and optionally print) the performance of the optimal
|
||||
portfolio. Currently calculates expected return, volatility, and the Sharpe ratio.
|
||||
|
||||
:param risk_free_rate: risk-free rate of borrowing/lending, defaults to 0.02
|
||||
:type risk_free_rate: float, optional
|
||||
:param verbose: whether performance should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
:param risk_free_rate: risk-free rate of borrowing/lending, defaults to 0.02
|
||||
:type risk_free_rate: float, optional
|
||||
:raises ValueError: if weights have not been calculated yet
|
||||
:return: expected return, volatility, Sharpe ratio.
|
||||
:rtype: (float, float, float)
|
||||
|
||||
@@ -53,7 +53,15 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
|
||||
- ``save_weights_to_file()`` saves the weights to csv, json, or txt.
|
||||
"""
|
||||
|
||||
def __init__(self, expected_returns, cov_matrix, weight_bounds=(0, 1), gamma=0, solver=None, verbose=False):
|
||||
def __init__(
|
||||
self,
|
||||
expected_returns,
|
||||
cov_matrix,
|
||||
weight_bounds=(0, 1),
|
||||
gamma=0,
|
||||
solver=None,
|
||||
verbose=False,
|
||||
):
|
||||
"""
|
||||
:param expected_returns: expected returns for each asset. Can be None if
|
||||
optimising for volatility only (but not recommended).
|
||||
@@ -93,7 +101,9 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
|
||||
if cov_matrix.shape != (len(expected_returns), len(expected_returns)):
|
||||
raise ValueError("Covariance matrix does not match expected returns")
|
||||
|
||||
super().__init__(len(tickers), tickers, weight_bounds, solver=solver, verbose=verbose)
|
||||
super().__init__(
|
||||
len(tickers), tickers, weight_bounds, solver=solver, verbose=verbose
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_expected_returns(expected_returns):
|
||||
@@ -346,17 +356,17 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
|
||||
|
||||
return self._solve_cvxpy_opt_problem()
|
||||
|
||||
def portfolio_performance(self, risk_free_rate=0.02, verbose=False):
|
||||
def portfolio_performance(self, verbose=False, risk_free_rate=0.02):
|
||||
"""
|
||||
After optimising, calculate (and optionally print) the performance of the optimal
|
||||
portfolio. Currently calculates expected return, volatility, and the Sharpe ratio.
|
||||
|
||||
:param verbose: whether performance should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
:param risk_free_rate: 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.
|
||||
:type risk_free_rate: float, optional
|
||||
:param verbose: whether performance should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
:raises ValueError: if weights have not been calcualted yet
|
||||
:return: expected return, volatility, Sharpe ratio.
|
||||
:rtype: (float, float, float)
|
||||
|
||||
@@ -172,12 +172,14 @@ class HRPOpt(base_optimizer.BaseOptimizer):
|
||||
self.set_weights(weights)
|
||||
return weights
|
||||
|
||||
def portfolio_performance(self, risk_free_rate=0.02, frequency=252, verbose=False):
|
||||
def portfolio_performance(self, verbose=False, risk_free_rate=0.02, frequency=252):
|
||||
"""
|
||||
After optimising, calculate (and optionally print) the performance of the optimal
|
||||
portfolio. Currently calculates expected return, volatility, and the Sharpe ratio
|
||||
assuming returns are daily
|
||||
|
||||
:param verbose: whether performance should be printed, defaults to False
|
||||
:type verbose: bool, optional
|
||||
:param risk_free_rate: 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.
|
||||
@@ -185,8 +187,6 @@ class HRPOpt(base_optimizer.BaseOptimizer):
|
||||
:param frequency: number of time periods in a year, defaults to 252 (the number
|
||||
of trading days in a year)
|
||||
:type frequency: int, optional
|
||||
: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, volatility, Sharpe ratio.
|
||||
:rtype: (float, float, float)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import json
|
||||
import os
|
||||
import cvxpy
|
||||
import numpy as np
|
||||
import pytest
|
||||
from random import random
|
||||
from mock import patch, Mock
|
||||
from pypfopt import EfficientFrontier
|
||||
from pypfopt import exceptions
|
||||
from tests.utilities_for_tests import get_data, setup_efficient_frontier
|
||||
@@ -46,10 +43,6 @@ def test_weight_bounds_minus_one_to_one():
|
||||
assert ef.max_sharpe()
|
||||
assert ef.min_volatility()
|
||||
|
||||
# TODO: fix
|
||||
# assert ef.efficient_return(0.05)
|
||||
# assert ef.efficient_risk(0.20)
|
||||
|
||||
|
||||
def test_none_bounds():
|
||||
ef = EfficientFrontier(
|
||||
@@ -206,54 +199,3 @@ def test_save_weights_to_file():
|
||||
|
||||
os.remove("tests/test.txt")
|
||||
os.remove("tests/test.json")
|
||||
|
||||
def assert_verbose_option(optimize_for_method, *args, solver=None):
|
||||
# using a random number for `verbose` simply to test that what is received
|
||||
# by the method is passed on to Problem#solve
|
||||
verbose=random()
|
||||
|
||||
ef = setup_efficient_frontier(solver=solver, verbose=verbose)
|
||||
|
||||
with patch("cvxpy.Problem.solve") as mock:
|
||||
with pytest.raises(exceptions.OptimizationError):
|
||||
# we're not testing the behavior of ef.min_volatility, just that it
|
||||
# passes the verbose kwarg on to Problem#solve.
|
||||
# mocking Problem#solve causes EfficientFrontier#min_volatility to
|
||||
# raise an error, but it is safe to ignore it
|
||||
optimize_for_method(ef, *args)
|
||||
|
||||
# mock.assert_called_with(verbose=verbose) doesn't work here because
|
||||
# sometimes the mock is called with more kwargs. all we want to know is
|
||||
# whether the value of verbose is passed on to Problem#solve
|
||||
_name, _args, kwargs = mock.mock_calls[0]
|
||||
assert kwargs['verbose'] == verbose
|
||||
|
||||
def test_min_volatility_verbose_option():
|
||||
assert_verbose_option(EfficientFrontier.min_volatility)
|
||||
|
||||
def test_min_volatility_verbose_option_with_solver():
|
||||
assert_verbose_option(EfficientFrontier.min_volatility, solver=cvxpy.settings.ECOS)
|
||||
|
||||
def test_max_sharpe_verbose_option():
|
||||
assert_verbose_option(EfficientFrontier.max_sharpe)
|
||||
|
||||
def test_max_sharpe_verbose_option_with_solver():
|
||||
assert_verbose_option(EfficientFrontier.min_volatility, solver=cvxpy.settings.ECOS)
|
||||
|
||||
def test_max_quadratic_utility_verbose_option():
|
||||
assert_verbose_option(EfficientFrontier.max_quadratic_utility)
|
||||
|
||||
def test_max_quadratic_utility_verbose_option_with_solver():
|
||||
assert_verbose_option(EfficientFrontier.min_volatility, solver=cvxpy.settings.ECOS)
|
||||
|
||||
def test_efficient_risk_verbose_option():
|
||||
assert_verbose_option(EfficientFrontier.efficient_risk, 0.1)
|
||||
|
||||
def test_efficient_risk_verbose_option_with_solver():
|
||||
assert_verbose_option(EfficientFrontier.min_volatility, solver=cvxpy.settings.ECOS)
|
||||
|
||||
def test_efficient_return_verbose_option():
|
||||
assert_verbose_option(EfficientFrontier.efficient_return, 0.01)
|
||||
|
||||
def test_efficient_return_verbose_option_with_solver():
|
||||
assert_verbose_option(EfficientFrontier.min_volatility, solver=cvxpy.settings.ECOS)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import warnings
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import warnings
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
Reference in New Issue
Block a user