def test_efficient_risk_exp_cov_market_neutral(): df = get_data() ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) ef.cov_matrix = risk_models.exp_cov(df) w = ef.efficient_risk(0.19, 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.3934093962620499, 0.18999999989011893, 1.9653126130421081), atol=1e-6, )
def test_efficient_return_longshort_target(): mu = pd.Series([-0.15, -0.12, -0.1, -0.05, -0.01, 0.02, 0.03, 0.04, 0.05]) cov = pd.DataFrame(np.diag([0.2, 0.2, 0.4, 0.3, 0.1, 0.5, 0.2, 0.3, 0.1])) ef = EfficientFrontier(mu, cov, weight_bounds=(-1, 1)) w = ef.efficient_return(target_return=0.08, market_neutral=True) assert isinstance(w, dict) assert set(w.keys()) == set(ef.tickers) np.testing.assert_almost_equal(ef.weights.sum(), 0) np.testing.assert_allclose( ef.portfolio_performance(), (0.08, 0.16649041068958137, 0.3603811159542937), atol=1e-6, )
def test_efficient_risk_exp_cov_market_neutral(): df = get_data() ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) ef.cov_matrix = risk_models.exp_cov(df) w = ef.efficient_risk(0.19, 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.3908928033782067, 0.18999999995323363, 1.9520673866815672), atol=1e-6, )
def test_min_volatility_sector_constraints(): sector_mapper = { "T": "auto", "UAA": "airline", "SHLD": "retail", "XOM": "energy", "RRC": "energy", "BBY": "retail", "MA": "fig", "PFE": "pharma", "JPM": "fig", "SBUX": "retail", "GOOG": "tech", "AAPL": "tech", "FB": "tech", "AMZN": "tech", "BABA": "tech", "GE": "utility", "AMD": "tech", "WMT": "retail", "BAC": "fig", "GM": "auto", } 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() ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) ef.add_sector_constraints(sector_mapper, sector_lower, sector_upper) weights = ef.min_volatility() 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) 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 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_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 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_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 get_result(l): data = yf.download(tickers=l, period="max", interval="1d", auto_adjust=True) data = data['Close'] mu = expected_returns.mean_historical_return(data) S = CovarianceShrinkage(data).ledoit_wolf() ef = EfficientFrontier(mu, S) raw_weights = ef.max_sharpe() cleaned_weights = ef.clean_weights() data = [] for x in cleaned_weights.keys(): if cleaned_weights[x] != 0: data.append({'name': x, 'y': cleaned_weights[x]}) answer = {'data': data, 'performance': ef.portfolio_performance()} return answer
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_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_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 fit(self, prices: pd.DataFrame): # TODO: make prices instance of portfolio ''' Given prices finds wieghts (coefficients), most optimal portfolio given target function. :param prices: :return: ''' self.frequency = autodetect_frequency(prices) self.mu = expected_returns.mean_historical_return( prices, frequency=self.frequency) self.S = risk_models.CovarianceShrinkage( prices, frequency=self.frequency).ledoit_wolf() # self.S=risk_models.exp_cov(prices) self.ef = EfficientFrontier(self.mu, self.S) self.ef.add_objective(objective_functions.L2_reg) getattr(self.ef, self.target_function)(**self.target_function_params) self.coef_ = self.ef.clean_weights() return self
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.min_volatility() ef.convex_objective( objective_functions.portfolio_variance, cov_matrix=ef.cov_matrix, weights_sum_to_one=False, )
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 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_custom_convex_kelly(): lb = 0.01 ub = 0.3 ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(lb, ub)) def kelly_objective(w, e_returns, cov_matrix, k=3): variance = cp.quad_form(w, cov_matrix) objective = variance * 0.5 * k - w @ e_returns return objective weights = ef.convex_objective(kelly_objective, e_returns=ef.expected_returns, cov_matrix=ef.cov_matrix) for w in weights.values(): assert w >= lb - 1e-8 and w <= ub + 1e-8
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
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 portfolio_opt(input_return_df: pd.DataFrame, input_freq: str, solution: str, weight_bounds = (0,1), risk_aversion=1, market_neutral=False, risk_free_rate=0.0, target_volatility=0.01, target_return=0.11, returns_data=True, compounding=False): """ pyportfolioopt Portfolios Args ---------- input_return_df: pd.DataFrame historical prices only, or historical + forecasts solution: str 'max_sharpe','min_volatility','max_quadratic_utility', 'efficient_risk','efficient_return','custom' input_freq: str daily/monthly Returns ---------- weights_df: 1 x n pd.DataFrame """ if not isinstance(input_return_df, pd.DataFrame): raise ValueError("Not a valid input_price_df. Type should be a pd.DataFrame.") if not isinstance(input_freq, str): raise ValueError("Not a valid input_freq, please enter daily/monthly.") if not isinstance(solution, str): raise ValueError("Not a valid solution.") # annualized mean returns: returns.mean() * frequency mu = calculate_annualized_expected_returns(input_price_df = input_return_df, input_freq = input_freq, returns_data = returns_data, compounding = compounding) # annulized return covariance matrix: returns.cov() * frequency S = calculate_annualized_return_covariance(input_price_df = input_return_df, input_freq = input_freq, returns_data = returns_data, compounding = compounding) # Optimise for maximal Sharpe ratio ef = EfficientFrontier(mu, S, weight_bounds = weight_bounds, gamma = 0) if solution == 'max_sharpe': raw_weights = ef.max_sharpe(risk_free_rate = risk_free_rate) elif solution == 'min_volatility': raw_weights = ef.min_volatility() elif solution == 'max_quadratic_utility': raw_weights = ef.max_quadratic_utility(risk_aversion = risk_aversion, market_neutral = market_neutral) elif solution == 'efficient_risk': raw_weights = ef.efficient_risk(target_volatility = target_volatility, market_neutral = market_neutral) elif solution == 'efficient_return': raw_weights = ef.efficient_return(target_return = target_return, market_neutral = market_neutral) elif solution == 'custom': print('Outside Implement Required.') return None date = input_return_df.index[-1] # the date on which weights are calculated weights_df = pd.DataFrame(raw_weights, columns = raw_weights.keys(), index=[date]) return weights_df
def test_efficient_risk_short(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) w = ef.efficient_risk(0.19) 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.30035471606347336, 0.1900000003049494, 1.4755511348079207), atol=1e-6, ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_risk(0.19) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_sharpe
def test_efficient_risk_short(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) w = ef.efficient_risk(0.19) 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.30468522897430295, 0.19, 1.4983424153337392), atol=1e-6, ) sharpe = ef.portfolio_performance()[2] ef_long_only = setup_efficient_frontier() ef_long_only.efficient_risk(0.19) long_only_sharpe = ef_long_only.portfolio_performance()[2] assert sharpe > long_only_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_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 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_constrained_ef_plot_risk(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) 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) # 100 portfolios with risks between 0.10 and 0.30 risk_range = np.linspace(0.157, 0.40, 100) ax = plotting.plot_efficient_frontier(ef, ef_param="risk", ef_param_range=risk_range, show_assets=True, showfig=False) assert len(ax.findobj()) == 137