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 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 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_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 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 optimise(portfolio_data): returns = pd.Series(get_returns(portfolio_data)) covariance_matrix = pd.DataFrame(get_cov_matrix(portfolio_data)) ef = EfficientFrontier(returns, covariance_matrix) ef.min_volatility() # max_sharpe or min_volatility cleaned_weights = ef.clean_weights() fix_keys(cleaned_weights, portfolio_data) print(cleaned_weights) ef.save_weights_to_file("weights.csv") # saves to file ef.portfolio_performance(verbose=True)
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 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) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose(ef.portfolio_performance(), (0.25, 0.17149595234895817, 1.3411395245760582)) 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_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 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 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_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_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) 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.1833060046337015, 1.2547324920403273)) 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_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) np.testing.assert_almost_equal(ef.weights.sum(), 1) np.testing.assert_allclose(ef.portfolio_performance(), (0.25, 0.16826225873038014, 1.3669137793315087)) 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 build_portfolio_weights(stock_data, weights_filename): # Calculate expected returns and sample covariance if isinstance(stock_data, pd.Series): stock_data = stock_data.to_frame() mu = expected_returns.mean_historical_return(stock_data) S = risk_models.sample_cov(stock_data) # Optimize for maximal Sharpe ratio ef = EfficientFrontier(mu, S) raw_weights = ef.max_sharpe() cleaned_weights = ef.clean_weights() ef.save_weights_to_file(weights_filename) # saves to file ef.portfolio_performance(verbose=True) return cleaned_weights
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) 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.20567263154580923, 1.1182819914898223)) 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 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 optimise_portfolio(stock_price, Portfolio): returns = stock_price.pct_change().dropna() # Calculate expected returns and sample covariance mu = expected_returns.mean_historical_return(stock_price) S = risk_models.sample_cov(stock_price) # Optimise for maximal Sharpe 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 print(cleaned_weights) ef.portfolio_performance(verbose=True) Portfolio.ef = ef Portfolio.mu = mu Portfolio.S = S Portfolio.weights = cleaned_weights
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_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_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_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.4072439477276246, 0.24823487545231313, 1.5599900981762558), ) 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.1721356467349655, 0.1555915367269669, 0.9777887019776287), ) # 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_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_efficient_frontier_expected_returns_list(): """Cover the edge case that the expected_returns param is a list.""" ef = setup_efficient_frontier() ef.min_volatility() ef_r = EfficientFrontier(expected_returns=ef.expected_returns.tolist(), cov_matrix=ef.cov_matrix) ef_r.min_volatility() np.testing.assert_equal(ef.portfolio_performance(), ef_r.portfolio_performance())
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_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.2552600197428133, 0.21, 1.1202858085349783), 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_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_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_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.20999999995100788, 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