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()
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])
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)
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
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
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
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
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))
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, )
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)
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)
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)
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), )