def test_custom_objective_min_var(): ef = setup_efficient_frontier() ef.min_volatility() built_in = ef.weights # With custom objective ef = setup_efficient_frontier() ef.custom_objective(objective_functions.volatility, ef.cov_matrix, 0) custom = ef.weights np.testing.assert_allclose(built_in, custom, atol=1e-7)
def test_max_sharpe_L2_reg_reduces_sharpe(): # L2 reg should reduce the number of small weights at the cost of Sharpe ef_no_reg = setup_efficient_frontier() ef_no_reg.max_sharpe() sharpe_no_reg = ef_no_reg.portfolio_performance()[2] ef = setup_efficient_frontier() ef.gamma = 1 ef.max_sharpe() sharpe = ef.portfolio_performance()[2] assert sharpe < sharpe_no_reg
def test_clean_weights_short(): ef = setup_efficient_frontier() ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1) ) ef.max_sharpe() # In practice we would never use such a high cutoff number_tiny_weights = sum(np.abs(ef.weights) < 0.05) cleaned = ef.clean_weights(cutoff=0.05) cleaned_weights = cleaned.values() clean_number_tiny_weights = sum(abs(i) < 0.05 for i in cleaned_weights) assert clean_number_tiny_weights == number_tiny_weights
def test_custom_objective_sharpe_L2(): ef = setup_efficient_frontier() ef.gamma = 2 ef.max_sharpe() built_in = ef.weights # With custom objective ef = setup_efficient_frontier() ef.custom_objective(objective_functions.negative_sharpe, ef.expected_returns, ef.cov_matrix, 2) custom = ef.weights np.testing.assert_allclose(built_in, custom, atol=1e-7)
def test_bounds_errors(): with pytest.raises(ValueError): EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(0.06, 1) ) assert EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(0, 1) ) with pytest.raises(ValueError): EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(0.06, 1, 3) )
def test_efficient_return_many_values(): ef = setup_efficient_frontier() for target_return in np.arange(0.19, 0.30, 0.01): ef.efficient_return(target_return) np.testing.assert_almost_equal(ef.weights.sum(), 1) mean_return = ef.portfolio_performance()[0] assert abs(target_return - mean_return) < 0.05
def test_efficient_risk_many_values(): ef = setup_efficient_frontier() for target_risk in np.arange(0.16, 0.21, 0.01): ef.efficient_risk(target_risk) np.testing.assert_almost_equal(ef.weights.sum(), 1) volatility = ef.portfolio_performance()[1] assert abs(target_risk - volatility) < 0.05
def test_custom_lower_bound(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(0.02, 1) ) ef.max_sharpe() assert ef.weights.min() >= 0.02 np.testing.assert_almost_equal(ef.weights.sum(), 1)
def test_custom_upper_bound(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(0, 0.10) ) ef.max_sharpe() ef.portfolio_performance() assert ef.weights.max() <= 0.1 np.testing.assert_almost_equal(ef.weights.sum(), 1)
def test_clean_weights_error(): ef = setup_efficient_frontier() ef.max_sharpe() with pytest.raises(ValueError): ef.clean_weights(rounding=1.3) with pytest.raises(ValueError): ef.clean_weights(rounding=0) assert ef.clean_weights(rounding=3)
def test_custom_objective_equal_weights(): ef = setup_efficient_frontier() def new_objective(weights): return (weights ** 2).sum() ef.custom_objective(new_objective) np.testing.assert_allclose(ef.weights, np.array([1 / 20] * 20))
def test_max_sharpe_input_errors(): with pytest.raises(ValueError): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), gamma="2" ) with warnings.catch_warnings(record=True) as w: ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), gamma=-1) assert len(w) == 1 assert issubclass(w[0].category, UserWarning) assert ( str(w[0].message) == "in most cases, gamma should be positive" ) with pytest.raises(ValueError): ef.max_sharpe(risk_free_rate="0.2")
def test_max_quadratic_utility_market_neutral(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1) ) ef.max_quadratic_utility(market_neutral=True) np.testing.assert_almost_equal(ef.weights.sum(), 0) np.testing.assert_allclose( ef.portfolio_performance(), (1.248936321062371, 1.0219175004907117, 1.2025787996313317), )
def test_custom_convex_abs_exposure(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) ef.add_constraint(lambda x: cp.norm(x, 1) <= 2) ef.convex_objective( objective_functions.portfolio_variance, cov_matrix=ef.cov_matrix, weights_sum_to_one=False, )
def test_efficient_frontier_error(): ef = setup_efficient_frontier() with pytest.raises(ValueError): EfficientFrontier(ef.expected_returns[:-1], ef.cov_matrix) with pytest.raises(TypeError): EfficientFrontier(0.02, ef.cov_matrix) with pytest.raises(ValueError): EfficientFrontier(ef.expected_returns, None) with pytest.raises(TypeError): EfficientFrontier(ef.expected_returns, 0.01)
def test_efficient_return_L2_reg(): ef = setup_efficient_frontier() ef.add_objective(objective_functions.L2_reg, gamma=1) w = ef.efficient_return(0.25) 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_allclose(ef.portfolio_performance(), (0.25, 0.20033592447690426, 1.1480716731187948))
def test_efficient_return_market_neutral_warning(): # This fails ef = setup_efficient_frontier() with warnings.catch_warnings(record=True) as w: ef.efficient_return(0.25, market_neutral=True) assert len(w) == 1 assert issubclass(w[0].category, RuntimeWarning) assert (str( w[0].message ) == "Market neutrality requires shorting - bounds have been amended")
def test_efficient_return_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.efficient_return(0.25) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.25, 0.1682647442258144, 1.3668935881968987) ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_max_sharpe_L2_reg_with_shorts(): ef_no_reg = setup_efficient_frontier() ef_no_reg.max_sharpe() initial_number = sum(ef_no_reg.weights > 0.01) ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) ef.add_objective(objective_functions.L2_reg) w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.2995338981166366, 0.2234696161770517, 1.2508810052063901), ) new_number = sum(ef.weights > 0.01) assert new_number >= initial_number
def test_max_sharpe_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.4937195216716211, 0.29516576454651955, 1.6049270564945908), ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.max_sharpe() long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_min_volatility_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.min_volatility() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.1516319319875544, 0.1555915367269669, 0.8460095886741129), ) # Shorting should reduce volatility volatility = ef.portfolio_performance()[1] ef_long_only = setup_efficient_frontier() ef_long_only.min_volatility() long_only_volatility = ef_long_only.portfolio_performance()[1] assert volatility < long_only_volatility
def test_efficient_return_market_neutral_warning(): # This fails ef = setup_efficient_frontier() with pytest.warns(RuntimeWarning) as w: ef.efficient_return(0.25, market_neutral=True) assert len(w) == 1 assert ( str(w[0].message) == "Market neutrality requires shorting - bounds have been amended" )
def test_clean_weights_error(): ef = setup_efficient_frontier() with pytest.raises(AttributeError): ef.clean_weights() ef.min_volatility() with pytest.raises(ValueError): ef.clean_weights(rounding=1.3) with pytest.raises(ValueError): ef.clean_weights(rounding=0) assert ef.clean_weights(rounding=3)
def test_min_volatility_short(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) w = ef.min_volatility() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.17225673749865328, 0.15559209747801794, 0.9752992044136976), ) # Shorting should reduce volatility volatility = ef.portfolio_performance()[1] ef_long_only = setup_efficient_frontier() ef_long_only.min_volatility() long_only_volatility = ef_long_only.portfolio_performance()[1] assert volatility < long_only_volatility
def test_efficient_return_market_neutral(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1) ) w = ef.efficient_return(0.25, market_neutral=True) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 0) assert (ef.weights < 1).all() and (ef.weights > -1).all() np.testing.assert_almost_equal( ef.portfolio_performance(), (0.25, 0.20567621957479246, 1.1182624830289896) ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert long_only_sharpe > sharpe
def test_efficient_return_market_neutral(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) w = ef.efficient_return(0.25, market_neutral=True) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 0) assert (ef.weights < 1).all() and (ef.weights > -1).all() np.testing.assert_almost_equal( ef.portfolio_performance(), (0.24999999999755498, 0.20567338787141307, 1.1087493060316183), ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert long_only_sharpe > sharpe
def test_max_quadratic_utility_with_shorts(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) ef.max_quadratic_utility() np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (1.3318330413711252, 1.0198436183533854, 1.2863080356272452), )
def test_max_sharpe_short(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.40723757138191374, 0.24823079451957306, 1.5524922427959371), ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.max_sharpe() long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_efficient_risk_market_neutral(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) w = ef.efficient_risk(0.21, market_neutral=True) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) np.testing.assert_almost_equal(ef.weights.sum(), 0) assert (ef.weights < 1).all() and (ef.weights > -1).all() np.testing.assert_allclose( ef.portfolio_performance(), (0.28640632960825885, 0.21, 1.2686015698590967), atol=1e-6, ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_risk(0.21) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert long_only_sharpe > sharpe
def test_max_sharpe_L2_reg_with_shorts(): ef_no_reg = setup_efficient_frontier() ef_no_reg.max_sharpe() initial_number = sum(ef_no_reg.weights > 0.01) ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) ef.gamma = 1 w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.32360478341793864, 0.20241509658051923, 1.499911758296975), ) new_number = sum(ef.weights > 0.01) assert new_number >= initial_number
def test_max_quadratic_utility_with_shorts(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) ef.max_quadratic_utility() np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (1.4170505733098597, 1.0438577623242156, 1.3383533884915872), )
def test_min_volatility_short(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) w = ef.min_volatility() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.1719799152621441, 0.1555954785460613, 0.9767630568850568), ) # Shorting should reduce volatility volatility = ef.portfolio_performance()[1] ef_long_only = setup_efficient_frontier() ef_long_only.min_volatility() long_only_volatility = ef_long_only.portfolio_performance()[1] assert volatility < long_only_volatility
def test_efficient_return_L2_reg(): ef = setup_efficient_frontier() ef.add_objective(objective_functions.L2_reg, gamma=1) w = ef.efficient_return(0.25) 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_allclose(ef.portfolio_performance(), (0.25, 0.20961660883459776, 1.0972412981906703))
def test_efficient_risk(): ef = setup_efficient_frontier() w = ef.efficient_risk(0.19) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose(ef.portfolio_performance(), (0.2857747021087114, 0.19, 1.3988133092245933), atol=1e-6)
def test_efficient_return(): ef = setup_efficient_frontier() w = ef.efficient_return(0.25) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose(ef.portfolio_performance(), (0.25, 0.1738877891235972, 1.3226920714748545), atol=1e-6)
def test_max_sharpe_error(): ef = setup_efficient_frontier() with pytest.raises(ValueError): ef.max_sharpe(risk_free_rate="0.02") # An unsupported constraint type, which is incidentally meaningless. v = cp.Variable((2, 2), PSD=True) ef.add_constraint(lambda _: v >> np.zeros((2, 2))) with pytest.raises(TypeError): ef.max_sharpe()
def test_ef_plot_utility(): plt.figure() ef = setup_efficient_frontier() delta_range = np.arange(0.001, 50, 1) ax = plotting.plot_efficient_frontier( ef, ef_param="utility", ef_param_range=delta_range, showfig=False ) assert len(ax.findobj()) == 125 plt.clf() plt.close()
def test_efficient_return(): ef = setup_efficient_frontier() w = ef.efficient_return(0.25) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.25, 0.17388778912324757, 1.3204920206007777), atol=1e-6 )
def test_efficient_return_market_neutral_warning(): ef = setup_efficient_frontier() with warnings.catch_warnings(record=True) as w: ef.efficient_return(0.25, market_neutral=True) assert len(w) == 1 assert issubclass(w[0].category, RuntimeWarning) assert ( str(w[0].message) == "Market neutrality requires shorting - bounds have been amended" )
def test_efficient_risk(): ef = setup_efficient_frontier() w = ef.efficient_risk(0.19) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.2857747021121558, 0.19, 1.396492876), atol=1e-6 )
def test_efficient_return_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.efficient_return(0.25) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.25, 0.168264744226909, 1.3640929002973508) ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_clean_weights(): ef = setup_efficient_frontier() ef.max_sharpe() number_tiny_weights = sum(ef.weights < 1e-4) cleaned = ef.clean_weights(cutoff=1e-4, rounding=5) cleaned_weights = cleaned.values() clean_number_tiny_weights = sum(i < 1e-4 for i in cleaned_weights) assert clean_number_tiny_weights == number_tiny_weights # Check rounding cleaned_weights_str_length = [len(str(i)) for i in cleaned_weights] assert all([length == 7 or length == 3 for length in cleaned_weights_str_length])
def test_efficient_risk_market_neutral(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1) ) w = ef.efficient_risk(0.19, market_neutral=True) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 0) assert (ef.weights < 1).all() and (ef.weights > -1).all() np.testing.assert_almost_equal( ef.portfolio_performance(), (0.2309497469661495, 0.19000021138101422, 1.1021245569881066) ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert long_only_sharpe > sharpe
def test_max_sharpe_L2_reg_with_shorts(): ef_no_reg = setup_efficient_frontier() ef_no_reg.max_sharpe() initial_number = sum(ef_no_reg.weights > 0.01) ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) ef.gamma = 1 w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.3236047844566581, 0.20241509723550233, 1.4969817524033966), ) new_number = sum(ef.weights > 0.01) assert new_number >= initial_number
def test_min_volatility_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.min_volatility() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.1719799158957379, 0.15559547854162945, 0.9734986722620801), ) # Shorting should reduce volatility volatility = ef.portfolio_performance()[1] ef_long_only = setup_efficient_frontier() ef_long_only.min_volatility() long_only_volatility = ef_long_only.portfolio_performance()[1] assert volatility < long_only_volatility
def test_constrained_ef_plot_utility(): ef = setup_efficient_frontier() ef.add_constraint(lambda w: w[0] >= 0.2) ef.add_constraint(lambda w: w[2] == 0.15) ef.add_constraint(lambda w: w[3] + w[4] <= 0.10) delta_range = np.linspace(0.001, 20, 100) ax = plotting.plot_efficient_frontier(ef, ef_param_range=delta_range, showfig=False) assert len(ax.findobj()) == 125
def test_max_sharpe_risk_free_rate(): ef = setup_efficient_frontier() ef.max_sharpe() _, _, initial_sharpe = ef.portfolio_performance() ef.max_sharpe(risk_free_rate=0.10) _, _, new_sharpe = ef.portfolio_performance(risk_free_rate=0.10) assert new_sharpe <= initial_sharpe ef.max_sharpe(risk_free_rate=0) _, _, new_sharpe = ef.portfolio_performance(risk_free_rate=0) assert new_sharpe >= initial_sharpe
def test_efficient_return_L2_reg(): ef = setup_efficient_frontier() ef.gamma = 1 w = ef.efficient_return(0.25) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) assert all([i >= 0 for i in w.values()]) np.testing.assert_allclose(ef.portfolio_performance(), (0.25, 0.20032972845476912, 1.1481071819692497))
def test_efficient_risk_short(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) w = ef.efficient_risk(0.19) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.30468522897430295, 0.19, 1.4983424153337392), atol=1e-6, ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_max_sharpe_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.40723757138191374, 0.24823079451957306, 1.5524922427959371), ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.max_sharpe() long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_efficient_risk_sector_constraints_manual(): sector_mapper = { "GOOG": "tech", "AAPL": "tech", "FB": "tech", "AMZN": "tech", "BABA": "tech", "GE": "utility", "AMD": "tech", "WMT": "retail", "BAC": "fig", "GM": "auto", "T": "auto", "UAA": "airline", "SHLD": "retail", "XOM": "energy", "RRC": "energy", "BBY": "retail", "MA": "fig", "PFE": "pharma", "JPM": "fig", "SBUX": "retail", } sector_upper = { "tech": 0.2, "utility": 0.1, "retail": 0.2, "fig": 0.4, "airline": 0.05, "energy": 0.2, } sector_lower = {"utility": 0.01, "fig": 0.02, "airline": 0.01} ef = setup_efficient_frontier() for sector in sector_upper: is_sector = [sector_mapper[t] == sector for t in ef.tickers] ef.add_constraint( lambda w: cp.sum(w[is_sector]) <= sector_upper[sector]) for sector in sector_lower: is_sector = [sector_mapper[t] == sector for t in ef.tickers] ef.add_constraint( lambda w: cp.sum(w[is_sector]) >= sector_lower[sector]) weights = ef.efficient_risk(0.19) for sector in list(set().union(sector_upper, sector_lower)): sector_sum = 0 for t, v in weights.items(): if sector_mapper[t] == sector: sector_sum += v assert sector_sum <= sector_upper.get(sector, 1) + 1e-5 assert sector_sum >= sector_lower.get(sector, 0) - 1e-5
def test_efficient_return_market_neutral(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1) ) w = ef.efficient_return(0.25, market_neutral=True) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 0) assert (ef.weights < 1).all() and (ef.weights > -1).all() np.testing.assert_almost_equal( ef.portfolio_performance(), (0.25, 0.20567621957041887, 1.1087335497769277) ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert long_only_sharpe > sharpe
def test_min_volatility(): ef = setup_efficient_frontier() w = ef.min_volatility() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.17915572327783236, 0.1591542642140098, 0.9971057459792518), )
def test_max_sharpe_long_only(): ef = setup_efficient_frontier() w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.3303554237026972, 0.21671629636481254, 1.4288438866031374), )
def test_efficient_risk_short(): ef = EfficientFrontier( *setup_efficient_frontier(data_only=True), weight_bounds=(None, None) ) w = ef.efficient_risk(0.19) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.30468522897560224, 0.19, 1.4947624032507056), atol=1e6, ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_return(0.25) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_efficient_return_L2_reg(): ef = setup_efficient_frontier() ef.gamma = 1 w = ef.efficient_return(0.25) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.25, 0.20032972838376054, 1.1470454626523598) )
def test_max_sharpe_L2_reg_many_values(): ef = setup_efficient_frontier() ef.max_sharpe() # Count the number of weights more 1% initial_number = sum(ef.weights > 0.01) for a in np.arange(0.5, 5, 0.5): ef.gamma = a ef.max_sharpe() np.testing.assert_almost_equal(ef.weights.sum(), 1) new_number = sum(ef.weights > 0.01) # Higher gamma should reduce the number of small weights assert new_number >= initial_number initial_number = new_number
def test_max_sharpe_L2_reg(): ef = setup_efficient_frontier() ef.gamma = 1 w = ef.max_sharpe() assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.3062919882686126, 0.20291367026287507, 1.4087639167552641), )
def test_efficient_return_semicovariance(): df = get_data() ef = setup_efficient_frontier() ef.cov_matrix = risk_models.semicovariance(df, benchmark=0) w = ef.efficient_return(0.12) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) assert set(w.keys()) == set(ef.expected_returns.index) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose( ef.portfolio_performance(), (0.12000000000871075, 0.06948386214063361, 1.4319423610177537) )