Esempio n. 1
0
def max_quad_utility_weights(rets_bl, covar_bl, config):
    print('Begin max quadratic utility optimization')
    returns, sigmas, weights, deltas = [], [], [], []
    for delta in np.arange(1, 10, 1):
        ef = EfficientFrontier(rets_bl, covar_bl, weight_bounds= \
                (config['min_position_size'] ,config['max_position_size']))
        ef.max_quadratic_utility(delta)
        ret, sigma, __ = ef.portfolio_performance()
        weights_vec = ef.clean_weights()
        returns.append(ret)
        sigmas.append(sigma)
        deltas.append(delta)
        weights.append(weights_vec)
    fig, ax = plt.subplots()
    ax.plot(sigmas, returns)
    for i, delta in enumerate(deltas):
        ax.annotate(str(delta), (sigmas[i], returns[i]))
    plt.xlabel('Volatility (%) ')
    plt.ylabel('Returns (%)')
    plt.title('Efficient Frontier for Max Quadratic Utility Optimization')
    plt.show()
    opt_delta = float(
        input('Enter the desired point on the efficient frontier: '))
    ef = EfficientFrontier(rets_bl, covar_bl, weight_bounds= \
            (config['min_position_size'] ,config['max_position_size']))
    ef.max_quadratic_utility(opt_delta)
    opt_weights = ef.clean_weights()
    opt_weights = pd.DataFrame.from_dict(opt_weights, orient='index')
    opt_weights.columns = ['Max Quad Util']
    return opt_weights, ef
def test_min_volatility_vs_max_sharpe():
    # Test based on issue #75
    expected_returns_daily = pd.Series(
        [0.043622, 0.120588, 0.072331, 0.056586],
        index=["AGG", "SPY", "GLD", "HYG"])
    covariance_matrix = pd.DataFrame(
        [
            [0.000859, -0.000941, 0.001494, -0.000062],
            [-0.000941, 0.022400, -0.002184, 0.005747],
            [0.001494, -0.002184, 0.011518, -0.000129],
            [-0.000062, 0.005747, -0.000129, 0.002287],
        ],
        index=["AGG", "SPY", "GLD", "HYG"],
        columns=["AGG", "SPY", "GLD", "HYG"],
    )

    ef = EfficientFrontier(expected_returns_daily, covariance_matrix)
    ef.min_volatility()
    vol_min_vol = ef.portfolio_performance(risk_free_rate=0.00)[1]

    ef = EfficientFrontier(expected_returns_daily, covariance_matrix)
    ef.max_sharpe(risk_free_rate=0.00)
    vol_max_sharpe = ef.portfolio_performance(risk_free_rate=0.00)[1]

    assert vol_min_vol < vol_max_sharpe
def test_weight_bounds_minus_one_to_one():
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(-1, 1))
    assert ef.max_sharpe()
    ef2 = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                            weight_bounds=(-1, 1))
    assert ef2.min_volatility()
def test_efficient_frontier_init_errors():
    df = get_data()
    mean_returns = df.pct_change().dropna(how="all").mean()
    with pytest.raises(TypeError):
        EfficientFrontier("test", "string")

    with pytest.raises(TypeError):
        EfficientFrontier(mean_returns, mean_returns)
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_none_bounds():
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(None, 0.3))
    ef.min_volatility()
    w1 = ef.weights

    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(-1, 0.3))
    ef.min_volatility()
    w2 = ef.weights
    np.testing.assert_array_almost_equal(w1, w2)
def test_bound_failure():
    # Ensure optimization fails when lower bound is too high or upper bound is too low
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(0.06, 0.13))
    with pytest.raises(exceptions.OptimizationError):
        ef.min_volatility()

    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(0, 0.04))
    with pytest.raises(exceptions.OptimizationError):
        ef.min_volatility()
def optimizePortEfficient(port, weights, start, plot = False, short = False, printBasicStats=True, how = 'Sharpe'):
    #Getting Data
    df = bpf.getData(port, start)
    #Plotting the portfolio
    if plot: 
        bpf.plotPort(df, port)
        
    if printBasicStats:
        bpf.basicStats(df, weights, start)
    #Optimization for Sharpe using Efficient Frontier
    if short: 
        bounds = (-1,1)
    else:
        bounds = (0,1)
    mu = df.pct_change().mean() * 252
    S = risk_models.sample_cov(df)
    #Method and constraints for optimization
    if how == 'Sharpe':
        # Maximized on Sharpe Ratio
        ef = EfficientFrontier(mu, S, weight_bounds=bounds) #Here the weight bounds are being used to allow short positions as well
        weights = ef.max_sharpe()
        cleaned_weights = dict(ef.clean_weights())
        print("Weights of an optimal portfolio maximised on Sharpe Ratio:")
        print(cleaned_weights)
        ef.portfolio_performance(verbose = True)
        bpf.getDiscreteAllocations(df, weights)
        plotting.plot_weights(weights)
        return weights 
    elif how == "Vol":
        # Minimized on Volatility
        efi = EfficientFrontier(mu, S, weight_bounds=bounds)
        w = dict(efi.min_volatility())
        print("Weights of an optimal portfolio minimized on Volatilty (Risk):")
        print(w)
        efi.portfolio_performance(verbose = True)
        bpf.getDiscreteAllocations(df, w)
        plotting.plot_weights(w)
        return w
    elif how == "targetRisk":
        #Optimized for a given target risk
        efi = EfficientFrontier(mu, S, weight_bounds=bounds)
        efi.efficient_risk(0.25)
        w = dict(efi.clean_weights())
        if w ==None:
            print("No portfolio possible at the given risk level")
        else:
            print("Weights of an optimal portfolio for given risk:")
            print(w)
            efi.portfolio_performance(verbose = True)
            bpf.getDiscreteAllocations(df, w)
            plotting.plot_weights(w)
        return w
def test_custom_bounds_different_values():
    bounds = [(0.01, 0.13), (0.02, 0.11)] * 10
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=bounds)
    ef.min_volatility()
    assert (0.01 <= ef.weights[::2]).all() and (ef.weights[::2] <= 0.13).all()
    assert (0.02 <= ef.weights[1::2]).all() and (ef.weights[1::2] <=
                                                 0.11).all()
    np.testing.assert_almost_equal(ef.weights.sum(), 1)

    bounds = ((0.01, 0.13), (0.02, 0.11)) * 10
    assert EfficientFrontier(*setup_efficient_frontier(data_only=True),
                             weight_bounds=bounds)
def test_bounds_errors():
    assert EfficientFrontier(*setup_efficient_frontier(data_only=True),
                             weight_bounds=(0, 1))

    with pytest.raises(TypeError):
        EfficientFrontier(*setup_efficient_frontier(data_only=True),
                          weight_bounds=(0.06, 1, 3))

    with pytest.raises(TypeError):
        # Not enough bounds
        bounds = [(0.01, 0.13), (0.02, 0.11)] * 5
        EfficientFrontier(*setup_efficient_frontier(data_only=True),
                          weight_bounds=bounds)
def test_max_sharpe_long_weight_bounds():
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(0.03, 0.13))
    ef.max_sharpe()
    np.testing.assert_almost_equal(ef.weights.sum(), 1)
    assert ef.weights.min() >= 0.03
    assert ef.weights.max() <= 0.13

    bounds = [(0.01, 0.13), (0.02, 0.11)] * 10
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=bounds)
    ef.max_sharpe()
    assert (0.01 <= ef.weights[::2]).all() and (ef.weights[::2] <= 0.13).all()
    assert (0.02 <= ef.weights[1::2]).all() and (ef.weights[1::2] <=
                                                 0.11).all()
Esempio n. 12
0
def calculEF(dataframe):
    mu = expected_returns.mean_historical_return(dataframe)
    S = risk_models.sample_cov(dataframe)
    ef = EfficientFrontier(mu, S)
    weights = ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)
    ef.portfolio_performance(verbose=True)
    return pd.DataFrame([weights])
Esempio n. 13
0
def min_var(my_portfolio, perf=True) -> list:
    ohlc = yf.download(
        my_portfolio.portfolio,
        start=my_portfolio.start_date,
        end=my_portfolio.end_date,
        progress=False,
    )
    prices = ohlc["Adj Close"].dropna(how="all")
    prices = prices.filter(my_portfolio.portfolio)

    mu = expected_returns.capm_return(prices)
    S = risk_models.CovarianceShrinkage(prices).ledoit_wolf()

    ef = EfficientFrontier(mu, S)
    ef.add_objective(objective_functions.L2_reg,
                     gamma=my_portfolio.diversification)
    if my_portfolio.min_weights is not None:
        ef.add_constraint(lambda x: x >= my_portfolio.min_weights)
    if my_portfolio.max_weights is not None:
        ef.add_constraint(lambda x: x <= my_portfolio.max_weights)
    ef.min_volatility()
    weights = ef.clean_weights()

    wts = weights.items()

    result = []
    for val in wts:
        a, b = map(list, zip(*[val]))
        result.append(b)

    if perf is True:
        ef.portfolio_performance(verbose=True)

    return flatten(result)
Esempio n. 14
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
Esempio n. 15
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
Esempio n. 16
0
def allocate(df):
    tickers = [df.columns[k] for k in range(df.shape[1])]

    ohlc = yf.download(tickers, start="2010-01-01", end="2020-01-01")
    prices = ohlc["Adj Close"]

    market_prices = yf.download("^BVSP", start="2010-01-01",
                                end="2020-01-01")["Adj Close"]

    mcaps = {}
    for t in tickers:
        stock = yf.Ticker(t)
        mcaps[t] = stock.info["marketCap"]

    S = risk_models.CovarianceShrinkage(prices).ledoit_wolf()
    delta = black_litterman.market_implied_risk_aversion(market_prices)

    market_prior = black_litterman.market_implied_prior_returns(
        mcaps, delta, S)

    bl = BlackLittermanModel(S,
                             pi="market",
                             market_caps=mcaps,
                             risk_aversion=delta,
                             absolute_views=df.to_dict('records')[0])

    ret_bl = bl.bl_returns()
    S_bl = bl.bl_cov()

    ef = EfficientFrontier(ret_bl, S_bl)
    ef.add_objective(objective_functions.L2_reg)
    ef.max_sharpe()
    weights = ef.clean_weights()

    return weights
Esempio n. 17
0
    def optimizePortfolio(self, option='sharpe'):
        '''
            Optimize for maximal Sharpe ratio / minimum volatility (Default to Sharpe)
            TODO: Allow for more options
            Return EfficientFrontier object
        '''

        mu = self.calculateMu()
        S = self.calculateSampleCovarianceMatrix()
        ef = EfficientFrontier(mu, S)

        if option == 'sharpe':
            weights = ef.max_sharpe(
            )  #Maximize the Sharpe ratio, and get the raw weights

        elif option == 'volatility':
            weights = ef.min_volatility()

        else:
            raise SystemExit("Not found optimize option (Sharpe/Volatility)")

        self.cleaned_weights = ef.clean_weights()
        print(
            self.cleaned_weights
        )  #Note the weights may have some rounding error, meaning they may not add up exactly to 1 but should be close
        ef.portfolio_performance(verbose=True)

        return ef
def test_efficient_return_short():
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(None, None))
    target_return = 0.25
    weights_sum = 1.0  # Not market neutral, weights must sum to 1.
    w = ef.efficient_return(target_return)
    assert isinstance(w, dict)
    assert set(w.keys()) == set(ef.tickers)
    np.testing.assert_almost_equal(ef.weights.sum(), weights_sum)
    w_expected = simple_ef_weights(ef.expected_returns, ef.cov_matrix,
                                   target_return, weights_sum)
    np.testing.assert_almost_equal(ef.weights, w_expected)
    vol_expected = np.sqrt(
        objective_functions.portfolio_variance(w_expected, ef.cov_matrix))
    sharpe_expected = objective_functions.sharpe_ratio(w_expected,
                                                       ef.expected_returns,
                                                       ef.cov_matrix,
                                                       negative=False)
    np.testing.assert_allclose(ef.portfolio_performance(),
                               (target_return, vol_expected, sharpe_expected))
    sharpe = ef.portfolio_performance()[2]

    ef_long_only = setup_efficient_frontier()
    ef_long_only.efficient_return(target_return)
    long_only_sharpe = ef_long_only.portfolio_performance()[2]

    assert sharpe > long_only_sharpe
def test_custom_tracking_error():
    df = get_data()
    historical_rets = expected_returns.returns_from_prices(df).dropna()
    benchmark_rets = historical_rets["AAPL"]
    historical_rets = historical_rets.drop("AAPL", axis=1)
    S = risk_models.sample_cov(historical_rets, returns_data=True)

    opt = BaseConvexOptimizer(
        n_assets=len(historical_rets.columns),
        tickers=list(historical_rets.columns),
        weight_bounds=(0, 1),
    )

    opt.convex_objective(
        objective_functions.ex_post_tracking_error,
        historic_returns=historical_rets,
        benchmark_returns=benchmark_rets,
    )
    w = opt.clean_weights()

    ef = EfficientFrontier(None, S)
    ef.convex_objective(
        objective_functions.ex_post_tracking_error,
        historic_returns=historical_rets,
        benchmark_returns=benchmark_rets,
    )
    w2 = ef.clean_weights()

    assert w == w2
Esempio n. 20
0
    def portfolio_opt(self):
        portfolio = self.portfolio.split(",")
        df = self.db.load_data(TableName.DAY,
                               time_from=self.time_from,
                               symbols=portfolio)
        # df = FinI.add_date_col(df)
        df = df[["close", "sym"]]
        df2 = df.copy()
        df2 = df2.drop(columns=["close", "sym"])
        for sym in portfolio:
            df2[sym] = df[df["sym"] == sym]["close"]

        df2 = df2.drop_duplicates()

        # df = df.transpose()
        # df.index = pd.to_datetime(df['date']).astype(int) / 10**9
        st.write(df2)
        # Calculate expected returns and sample covariance
        mu = expected_returns.mean_historical_return(df2)
        S = risk_models.sample_cov(df2)

        # Optimize for maximal Sharp ratio
        ef = EfficientFrontier(mu, S)
        raw_weights = ef.max_sharpe()
        cleaned_weights = ef.clean_weights()
        # ef.save_weights_to_file("weights.csv")  # saves to file
        st.write(cleaned_weights)
        mu, sigma, sharpe = ef.portfolio_performance(verbose=True)
        st.write("Expected annual return: {:.1f}%".format(100 * mu))
        st.write("Annual volatility: {:.1f}%".format(100 * sigma))
        st.write("Sharpe Ratio: {:.2f}".format(sharpe))
Esempio n. 21
0
def test_custom_nonconvex_objective_market_neutral_efficient_risk():
    # Recreate the market-neutral efficient_risk optimiser using this API
    target_risk = 0.19
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(-1, 1))

    weight_constr = {"type": "eq", "fun": lambda w: np.sum(w)}
    risk_constr = {
        "type": "eq",
        "fun":
        lambda w: target_risk**2 - np.dot(w.T, np.dot(ef.cov_matrix, w)),
    }
    constraints = [weight_constr, risk_constr]

    ef.nonconvex_objective(
        lambda w, mu: -w.T.dot(mu),
        objective_args=(ef.expected_returns),
        weights_sum_to_one=False,
        constraints=constraints,
    )
    np.testing.assert_allclose(
        ef.portfolio_performance(),
        (0.2591296227818582, target_risk, 1.258574109251818),
        atol=1e-6,
    )
Esempio n. 22
0
def show_ef(stocks: List[str], other_args: List[str]):
    parser = argparse.ArgumentParser(add_help=False, prog="ef")

    parser.add_argument(
        "-p",
        "--period",
        default="3mo",
        dest="period",
        help="period to get yfinance data from",
        choices=period_choices,
    )
    parser.add_argument(
        "-n", default=300, dest="n_port", help="number of portfolios to simulate"
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return
        if len(stocks) < 2:
            print("Please have at least 2 loaded tickers to calculate weights.\n")
            return

        stock_prices = process_stocks(stocks, ns_parser.period)
        mu = expected_returns.mean_historical_return(stock_prices)
        S = risk_models.sample_cov(stock_prices)
        ef = EfficientFrontier(mu, S)
        fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

        # Generate random portfolios
        n_samples = ns_parser.n_port
        w = np.random.dirichlet(np.ones(len(mu)), n_samples)
        rets = w.dot(mu)
        stds = np.sqrt(np.diag(w @ S @ w.T))
        sharpes = rets / stds
        ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")

        plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True)
        # Find the tangency portfolio
        ef.max_sharpe()
        ret_sharpe, std_sharpe, _ = ef.portfolio_performance()
        ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe")

        ax.set_title("Efficient Frontier")
        ax.legend()
        plt.tight_layout()
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e)
        print("")
def test_bound_input_types():
    bounds = [0.01, 0.13]
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=bounds)
    assert ef
    np.testing.assert_allclose(ef._lower_bounds,
                               np.array([0.01] * ef.n_assets))
    np.testing.assert_allclose(ef._upper_bounds,
                               np.array([0.13] * ef.n_assets))

    lb = np.array([0.01, 0.02] * 10)
    ub = np.array([0.07, 0.2] * 10)
    assert EfficientFrontier(*setup_efficient_frontier(data_only=True),
                             weight_bounds=(lb, ub))
    bounds = ((0.01, 0.13), (0.02, 0.11)) * 10
    assert EfficientFrontier(*setup_efficient_frontier(data_only=True),
                             weight_bounds=bounds)
Esempio n. 24
0
def test_custom_convex_objective_market_neutral_efficient_risk():
    target_risk = 0.19
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(-1, 1))
    ef.efficient_risk(target_risk, market_neutral=True)
    built_in = ef.weights

    # Recreate the market-neutral efficient_risk optimiser using this API
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(-1, 1))
    ef.add_constraint(lambda x: cp.sum(x) == 0)
    ef.add_constraint(
        lambda x: cp.quad_form(x, ef.cov_matrix) <= target_risk**2)
    ef.convex_objective(lambda x: -x @ ef.expected_returns,
                        weights_sum_to_one=False)
    custom = ef.weights
    np.testing.assert_allclose(built_in, custom, atol=1e-7)
Esempio n. 25
0
def max_sharpe_weights(rets_bl, covar_bl, config):
    ef = EfficientFrontier(rets_bl, covar_bl, weight_bounds= \
            (config['min_position_size'] ,config['max_position_size']))
    ef.max_sharpe()
    weights = ef.clean_weights()
    weights = pd.DataFrame.from_dict(weights, orient='index')
    weights.columns = ['Max Sharpe']
    return weights, ef
def test_ef_example():
    df = get_data()
    mu = expected_returns.mean_historical_return(df)
    S = risk_models.sample_cov(df)

    ef = EfficientFrontier(mu, S)
    ef.efficient_return(0.2)
    np.testing.assert_almost_equal(ef.portfolio_performance()[0], 0.2)
Esempio n. 27
0
def test_ef_plot_utility_short():
    ef = EfficientFrontier(*setup_efficient_frontier(data_only=True),
                           weight_bounds=(None, None))
    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()) == 161
def test_ef_example_weekly():
    df = get_data()
    prices_weekly = df.resample("W").first()
    mu = expected_returns.mean_historical_return(prices_weekly, frequency=52)
    S = risk_models.sample_cov(prices_weekly, frequency=52)
    ef = EfficientFrontier(mu, S)
    ef.efficient_return(0.2)
    np.testing.assert_almost_equal(ef.portfolio_performance()[0], 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.13434841843883, 0.9896404148973286, 1.1260134506071473),
    )
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),
    )