fixes #82 – pass None as expected returns

This commit is contained in:
robertmartin8
2020-04-10 11:05:22 +08:00
parent adeb1e9454
commit 85f83ac6f8
3 changed files with 41 additions and 23 deletions

View File

@@ -374,6 +374,7 @@ def portfolio_performance(
else:
tickers = list(range(len(expected_returns)))
new_weights = np.zeros(len(tickers))
for i, k in enumerate(tickers):
if k in weights:
new_weights[i] = weights[k]
@@ -385,25 +386,25 @@ def portfolio_performance(
raise ValueError("Weights is None")
sigma = np.sqrt(objective_functions.portfolio_variance(new_weights, cov_matrix))
mu = objective_functions.portfolio_return(
new_weights, expected_returns, negative=False
)
# new_weights.dot(expected_returns)
# sharpe = -objective_functions.negative_sharpe(
# new_weights, expected_returns, cov_matrix, risk_free_rate=risk_free_rate
# )
if expected_returns is not None:
mu = objective_functions.portfolio_return(
new_weights, expected_returns, negative=False
)
sharpe = objective_functions.sharpe_ratio(
new_weights,
expected_returns,
cov_matrix,
risk_free_rate=risk_free_rate,
negative=False,
)
if verbose:
print("Expected annual return: {:.1f}%".format(100 * mu))
print("Annual volatility: {:.1f}%".format(100 * sigma))
print("Sharpe Ratio: {:.2f}".format(sharpe))
return mu, sigma, sharpe
sharpe = objective_functions.sharpe_ratio(
new_weights,
expected_returns,
cov_matrix,
risk_free_rate=risk_free_rate,
negative=False,
)
if verbose:
print("Expected annual return: {:.1f}%".format(100 * mu))
print("Annual volatility: {:.1f}%".format(100 * sigma))
print("Sharpe Ratio: {:.2f}".format(sharpe))
return mu, sigma, sharpe
else:
if verbose:
print("Annual volatility: {:.1f}%".format(100 * sigma))
return None, sigma, None

View File

@@ -84,15 +84,19 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
else: # use integer labels
tickers = list(range(len(expected_returns)))
if cov_matrix.shape != (len(expected_returns), len(expected_returns)):
raise ValueError("Covariance matrix does not match expected returns")
if expected_returns is not None:
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)
@staticmethod
def _validate_expected_returns(expected_returns):
if expected_returns is None:
raise ValueError("expected_returns must be provided")
warnings.warn(
"No expected returns provided. You may only use ef.min_volatility()"
)
return None
elif isinstance(expected_returns, pd.Series):
return expected_returns.values
elif isinstance(expected_returns, list):

View File

@@ -65,6 +65,19 @@ def test_min_volatility():
)
def test_min_volatility_no_rets():
# Should work with no rets, see issue #82
df = get_data()
S = risk_models.sample_cov(df)
ef = EfficientFrontier(None, S)
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()])
np.testing.assert_almost_equal(ef.portfolio_performance()[1], 0.15915084514118694)
def test_min_volatility_tx_costs():
# Baseline
ef = setup_efficient_frontier()