Example #1
0
def test_efficient_semivariance_vs_heuristic():
    benchmark = 0
    es = setup_efficient_semivariance()
    es.efficient_return(0.20)
    mu_es, semi_deviation, _ = es.portfolio_performance()
    np.testing.assert_almost_equal(mu_es, 0.2)

    mean_return, historic_returns = setup_efficient_semivariance(
        data_only=True)

    pairwise_semivariance = risk_models.semicovariance(historic_returns,
                                                       returns_data=True,
                                                       benchmark=0,
                                                       frequency=1)
    ef = EfficientFrontier(mean_return, pairwise_semivariance)
    ef.efficient_return(0.20)
    mu_ef, _, _ = ef.portfolio_performance()
    # mu_ef *= 252

    portfolio_returns = historic_returns @ ef.weights
    drops = np.fmin(portfolio_returns - benchmark, 0)
    T = historic_returns.shape[0]
    semivariance = np.sum(np.square(drops)) / T * 252
    semi_deviation_ef = np.sqrt(semivariance)

    assert semi_deviation < semi_deviation_ef
    assert mu_es / semi_deviation > mu_ef / semi_deviation_ef
Example #2
0
def test_min_semivariance_extra_constraints():
    es = setup_efficient_semivariance()
    w = es.min_semivariance()
    assert w["GOOG"] < 0.02 and w["AAPL"] > 0.02

    es = setup_efficient_semivariance()
    es.add_constraint(lambda x: x[0] >= 0.03)
    es.add_constraint(lambda x: x[1] <= 0.03)
    w = es.min_semivariance()
    assert w["GOOG"] >= 0.025 and w["AAPL"] <= 0.035
def test_parametrization():
    es = setup_efficient_semivariance()
    es.efficient_risk(0.19)
    es.efficient_risk(0.19)

    es = setup_efficient_semivariance()
    es.efficient_return(0.25)
    es.efficient_return(0.25)

    es = setup_efficient_semivariance()
    es.max_quadratic_utility(1)
    es.max_quadratic_utility(1)
Example #4
0
def test_max_quadratic_utility_range():
    # increasing risk_aversion should lower both vol and return
    es = setup_efficient_semivariance()
    es.solver = "ECOS"
    es.max_quadratic_utility(risk_aversion=0.01)
    prev_ret, prev_semivar, _ = es.portfolio_performance()
    for delta in [0.1, 0.5, 1, 3, 5, 10]:
        es = setup_efficient_semivariance()
        es.max_quadratic_utility(risk_aversion=delta)
        print(es.portfolio_performance())
        ret, semivar, _ = es.portfolio_performance()
        assert ret < prev_ret and semivar < prev_semivar
        prev_ret = ret
        prev_semivar = semivar
Example #5
0
def test_min_semivariance_tx_costs():
    # Baseline
    es = setup_efficient_semivariance()
    es.min_semivariance()
    w1 = es.weights

    # Pretend we were initally equal weight
    es = setup_efficient_semivariance()
    prev_w = np.array([1 / es.n_assets] * es.n_assets)
    es.add_objective(objective_functions.transaction_cost, w_prev=prev_w)
    es.min_semivariance()
    w2 = es.weights

    # TX cost should  pull closer to prev portfolio
    assert np.abs(prev_w - w2).sum() < np.abs(prev_w - w1).sum()
Example #6
0
def test_efficient_semivariance_vs_heuristic_weekly():
    benchmark = 0
    _, historic_returns = setup_efficient_semivariance(data_only=True)
    weekly_returns = historic_returns.resample("W").sum()
    mean_weekly_returns = weekly_returns.mean(axis=0)

    es = EfficientSemivariance(mean_weekly_returns,
                               weekly_returns,
                               frequency=52)
    es.efficient_return(0.20 / 52)
    mu_es, semi_deviation, _ = es.portfolio_performance()

    pairwise_semivariance = risk_models.semicovariance(weekly_returns,
                                                       returns_data=True,
                                                       benchmark=0,
                                                       frequency=1)
    ef = EfficientFrontier(mean_weekly_returns, pairwise_semivariance)
    ef.efficient_return(0.20 / 52)
    mu_ef, _, _ = ef.portfolio_performance()
    portfolio_returns = historic_returns @ ef.weights
    drops = np.fmin(portfolio_returns - benchmark, 0)
    T = weekly_returns.shape[0]
    semivariance = np.sum(np.square(drops)) / T * 52
    semi_deviation_ef = np.sqrt(semivariance)

    assert semi_deviation < semi_deviation_ef
    assert mu_es / semi_deviation > mu_ef / semi_deviation_ef
Example #7
0
def test_efficient_risk_low_risk():
    es = setup_efficient_semivariance()
    es.min_semivariance()
    min_value = es.portfolio_performance()[1]

    # Should fail below
    with pytest.raises(SolverError):
        es = setup_efficient_semivariance()
        es.efficient_risk(min_value - 0.01)

    es = setup_efficient_semivariance()
    es.efficient_risk(min_value + 0.01)
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.22187394011764086, min_value + 0.01, 2.1248023794940822),
        rtol=1e-4,
        atol=1e-4,
    )
Example #8
0
def test_efficient_return_short():
    es = EfficientSemivariance(*setup_efficient_semivariance(data_only=True),
                               weight_bounds=(None, None))
    w = es.efficient_return(0.25)
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.25, 0.09073654273906914, 2.534811096726317),
        rtol=1e-4,
        atol=1e-4,
    )
    sortino = es.portfolio_performance()[2]

    ef_long_only = setup_efficient_semivariance()
    ef_long_only.efficient_return(0.25)
    long_only_sortino = ef_long_only.portfolio_performance()[2]

    assert sortino > long_only_sortino
Example #9
0
def test_min_semivariance_different_solver():
    es = setup_efficient_semivariance(solver="ECOS")
    w = es.min_semivariance()
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= 0 for i in w.values()])
    test_performance = (0.10258693150198975, 0.08495982152369484,
                        0.9720704448391136)
    np.testing.assert_allclose(es.portfolio_performance(),
                               test_performance,
                               rtol=1e-2,
                               atol=1e-2)

    es = setup_efficient_semivariance(solver="OSQP")
    w = es.min_semivariance()
    np.testing.assert_allclose(es.portfolio_performance(),
                               test_performance,
                               rtol=1e-2,
                               atol=1e-2)
Example #10
0
def test_max_quadratic_utility_with_shorts():
    es = EfficientSemivariance(*setup_efficient_semivariance(data_only=True),
                               weight_bounds=(-1, 1))
    es.max_quadratic_utility()
    np.testing.assert_almost_equal(es.weights.sum(), 1)

    np.testing.assert_allclose(
        es.portfolio_performance(),
        (3.2806086360944846, 1.0219729896939227, 3.190503730505662),
        rtol=1e-4,
        atol=1e-4,
    )
Example #11
0
def test_max_quadratic_utility_market_neutral():
    es = EfficientSemivariance(*setup_efficient_semivariance(data_only=True),
                               weight_bounds=(-1, 1),
                               solver="ECOS")
    es.max_quadratic_utility(market_neutral=True)
    np.testing.assert_almost_equal(es.weights.sum(), 0)
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (3.106826930927578, 0.9789014670659041, 3.153358161960706),
        rtol=1e-4,
        atol=1e-4,
    )
Example #12
0
def test_max_quadratic_utility():
    es = setup_efficient_semivariance()
    w = es.max_quadratic_utility(risk_aversion=2)
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)

    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.45386915148502716, 0.1730946893389541, 2.506542246570167),
        rtol=1e-4,
        atol=1e-4,
    )
def test_efficient_risk_L2_reg():
    es = setup_efficient_semivariance()
    es.add_objective(objective_functions.L2_reg, gamma=1)
    weights = es.efficient_risk(0.19)

    assert isinstance(weights, dict)
    assert set(weights.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    np.testing.assert_array_less(np.zeros(len(weights)), es.weights + 1e-4)
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.31339234055845905, 0.14360466712901232, 2.0430557475884794),
        rtol=1e-4,
        atol=1e-4,
    )

    ef2 = setup_efficient_semivariance()
    ef2.efficient_risk(0.19)

    # L2_reg should pull close to equal weight
    equal_weight = np.full((es.n_assets, ), 1 / es.n_assets)
    assert (np.abs(equal_weight - es.weights).sum() <
            np.abs(equal_weight - ef2.weights).sum())
Example #14
0
def test_min_semivariance():
    es = setup_efficient_semivariance()
    w = es.min_semivariance()
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= -1e-5 for i in w.values()])

    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.1024524845740464, 0.08497381732237187, 0.970328121911246),
        rtol=1e-4,
        atol=1e-4,
    )
Example #15
0
def test_efficient_return_L2_reg():
    es = setup_efficient_semivariance()
    es.add_objective(objective_functions.L2_reg, gamma=1)
    w = es.efficient_return(0.25)
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= 0 for i in w.values()])
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.25, 0.12068369208695134, 1.9058084487031388),
        rtol=1e-4,
        atol=1e-4,
    )
Example #16
0
def test_min_semivariance_L2_reg():
    es = setup_efficient_semivariance()
    es.add_objective(objective_functions.L2_reg, gamma=1)
    weights = es.min_semivariance()
    assert isinstance(weights, dict)
    assert set(weights.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= 0 for i in weights.values()])

    ef2 = setup_efficient_semivariance()
    ef2.min_semivariance()

    # L2_reg should pull close to equal weight
    equal_weight = np.full((es.n_assets, ), 1 / es.n_assets)
    assert (np.abs(equal_weight - es.weights).sum() <
            np.abs(equal_weight - ef2.weights).sum())

    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.11684586840374926, 0.11286393006990993, 0.8580763432902009),
        rtol=1e-4,
        atol=1e-4,
    )
Example #17
0
def test_efficient_return():
    es = setup_efficient_semivariance()
    w = es.efficient_return(0.25)
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= -1e-5 for i in w.values()])

    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.25, 0.10035962239578998, 2.291760094911752),
        rtol=1e-4,
        atol=1e-4,
    )
Example #18
0
def test_efficient_risk_market_neutral():
    es = EfficientSemivariance(*setup_efficient_semivariance(data_only=True),
                               weight_bounds=(-1, 1))
    w = es.efficient_risk(0.21, market_neutral=True)
    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 0)
    assert (es.weights < 1).all() and (es.weights > -1).all()
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.9257112257221027, 0.21, 4.312873624163129),
        rtol=1e-4,
        atol=1e-4,
    )
Example #19
0
def test_max_quadratic_utility_L2_reg():
    es = setup_efficient_semivariance()
    es.add_objective(objective_functions.L2_reg, gamma=5)
    weights = es.max_quadratic_utility()

    assert isinstance(weights, dict)
    assert set(weights.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= 0 for i in weights.values()])
    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.11717839698599568, 0.11286470279298752, 0.8610167269410781),
        rtol=1e-4,
        atol=1e-4,
    )

    ef2 = setup_efficient_semivariance()
    ef2.max_quadratic_utility()

    # L2_reg should pull close to equal weight
    equal_weight = np.full((es.n_assets, ), 1 / es.n_assets)
    assert (np.abs(equal_weight - es.weights).sum() <
            np.abs(equal_weight - ef2.weights).sum())
Example #20
0
def test_efficient_risk():
    es = setup_efficient_semivariance()
    w = es.efficient_risk(0.2)

    assert isinstance(w, dict)
    assert set(w.keys()) == set(es.tickers)
    np.testing.assert_almost_equal(es.weights.sum(), 1)
    assert all([i >= -1e-5 for i in w.values()])

    np.testing.assert_allclose(
        es.portfolio_performance(),
        (0.4567521774612227, 0.2, 2.183678863194344),
        rtol=1e-4,
        atol=1e-4,
    )
Example #21
0
def test_min_semivariance_sector_constraints():
    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}

    es = setup_efficient_semivariance()
    es.add_sector_constraints(sector_mapper, sector_lower, sector_upper)
    weights = es.min_semivariance()

    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