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 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 optimMarkowitz(datatrain, datatest, pmin, pmax, optimmodel, returnmodel, riskmodel, Gam, rf): try: if returnmodel == 'historical': mu = expected_returns.mean_historical_return(datatrain) elif returnmodel == 'emahistorical': mu = expected_returns.ema_historical_return(datatrain) if riskmodel == 'historicalcov': S = risk_models.sample_cov(datatrain) elif riskmodel == 'exphistoricalcov': S = risk_models.exp_cov(datatrain) ef = EfficientFrontier(mu, S, weight_bounds=(pmin, pmax)) #gamma>0 permet de forcer l'optimiseur à utiliser plus de titres ef.add_objective(objective_functions.L2_reg, gamma=Gam) if optimmodel == 'min_volatility': ef.min_volatility() elif optimmodel == 'max_sharpe': ef.max_sharpe(risk_free_rate=rf) cleaned_weights = ef.clean_weights() #round and clean ... ef.save_weights_to_file( '/Users/Maxime/AMUNDI/PortMgmnt/ModulePyPortfolioOpt/OptimiseurProjet/weights.csv' ) # save to file perf = ef.portfolio_performance(verbose=True, risk_free_rate=rf) weightsfinal = pd.read_csv( '/Users/Maxime/AMUNDI/PortMgmnt/ModulePyPortfolioOpt/OptimiseurProjet/weights.csv', header=None) #For the following chart poids = weightsfinal.to_numpy() poids = poids[:, 1] RankedDataFrame = pd.DataFrame(index=datatest.index) for i, rows in weightsfinal.iterrows(): RankedDataFrame[rows[0]] = datatest[rows[0]] weightsfinal.rename(columns={ 0: ' Asset Class', 1: 'Poids' }, inplace=True) weightsfinal['Poids'] = round(weightsfinal['Poids'] * 100, 4) except ValueError: print('Le modèle spécifié est incorrect') return poids, RankedDataFrame, cleaned_weights, S, mu, perf
def test_efficient_risk_market_neutral_L2_reg(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) ef.add_objective(objective_functions.L2_reg) 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.10755645826336145, 0.11079556786108302, 0.7902523535340413), atol=1e-6, )
def test_efficient_risk_market_neutral_L2_reg(): ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(-1, 1)) ef.add_objective(objective_functions.L2_reg) 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.12790320789339854, 0.1175336636355454, 0.9180621496492316), atol=1e-6, )
def test_max_sharpe_L2_reg_with_shorts(): ef_no_reg = setup_efficient_frontier() ef_no_reg.max_sharpe() initial_number = sum(ef_no_reg.weights > 0.01) ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) ef.add_objective(objective_functions.L2_reg) 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.3076093180094401, 0.22415982749409985, 1.2830546901496447), ) new_number = sum(ef.weights > 0.01) assert new_number >= initial_number
def test_max_sharpe_L2_reg_with_shorts(): ef_no_reg = setup_efficient_frontier() ef_no_reg.max_sharpe() initial_number = sum(ef_no_reg.weights > 0.01) ef = EfficientFrontier(*setup_efficient_frontier(data_only=True), weight_bounds=(None, None)) ef.add_objective(objective_functions.L2_reg) 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.2995338981166366, 0.2234696161770517, 1.2508810052063901), ) new_number = sum(ef.weights > 0.01) assert new_number >= initial_number
def mean_var(my_portfolio, vol_max=0.15, perf=True) -> list: # changed to take in desired timeline, the problem is that it would use all historical data 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) # sometimes we will pick a date range where company isn't public we can't set price to 0 so it has to go to 1 prices = prices.fillna(1) 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.efficient_risk(vol_max) 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)
:type skew: np.ndarray :param cov_matrix: covariance matrix :type cov_matrix: np.ndarray :param negative: whether quantity should be made negative (so we can minimise) :type negative: boolean :return: (negative) Sharpe ratio :rtype: float """ sk = w @ skew sign = -1 if negative else 1 return sign * sk ef_skew = EfficientFrontier(exp_ret, S, weight_bounds=(0.0, 0.15)) ef_skew.add_sector_constraints(sector_mapper, sector_lower, sector_upper) ef_skew.add_objective(objective_functions.L2_reg, gamma=20) #ef.add_objective(minimize_negative_skew, skew= skw) ef_skew.convex_objective(minimize_negative_skew, skew=skw) #ef.max_sharpe() weights_skew = ef_skew.clean_weights() weights_skew ef_skew.portfolio_performance(verbose=True) # %% weight_dict = { "Miniumum Variance" : weights_minvol, "Maximum Sharpe Ratio" : weights_maxsharpe, "Maximum Quadratic Utility" : weights_maxquad, "Efficient Target Risk" : weights_effrisk, "Efficient Target Return" : weights_effret,
print("Funds remaining: ${:.2f}".format(leftover)) """ Expected annual return: 33.0% Annual volatility: 21.7% Sharpe Ratio: 1.43 Discrete allocation: {'AAPL': 5.0, 'FB': 11.0, 'BABA': 5.0, 'AMZN': 1.0, 'BBY': 7.0, 'MA': 14.0, 'PFE': 50.0, 'SBUX': 5.0} Funds remaining: $8.42 """ # Long-only minimum volatility portfolio, with a weight cap and regularisation # e.g if we want at least 15/20 tickers to have non-neglible weights, and no # asset should have a weight greater than 10% ef = EfficientFrontier(mu, S, weight_bounds=(0, 0.10)) ef.add_objective(objective_functions.L2_reg, gamma=0.1) weights = ef.min_volatility() print(weights) ef.portfolio_performance(verbose=True) """ {'GOOG': 0.0584267903998156, 'AAPL': 0.0369081348579286, 'FB': 0.0997609043032782, 'BABA': 0.1, 'AMZN': 0.0, 'GE': 0.0646457157900559, 'AMD': 0.0, 'WMT': 0.1, 'BAC': 0.0, 'GM': 0.1, 'T': 0.1,
class OptPortfolio(BaseEstimator): ''' Portfolio optimization used both for train and validation ''' def __init__(self, target_function: str = "efficient_risk", target_function_params: dict = {}, budget: int = 10000): self.target_function = target_function self.target_function_params = target_function_params self.budget = budget self.frequency = None # self.mu = expected_returns.mean_historical_return(prices) # self.S = risk_models.CovarianceShrinkage(prices).ledoit_wolf() # self.ef = EfficientFrontier(self.mu, self.S) # self.ef.add_objective(objective_functions.L2_reg) # getattr(self.ef,target_function)(**target_function_params) # self.weights = self.ef.clean_weights() # # self.da = DiscreteAllocation(self.weights, prices.iloc[-1], total_portfolio_value=money) # alloc, leftover = self.da.lp_portfolio() # print(f"Leftover: ${leftover:.2f}") # print(f"Alloc: ${alloc:.2f}") # self.discrete_portfolio = ( # prices[[k for k, v in alloc.items()]] * np.asarray([[v for k, v in alloc.items()]])).dropna().sum( # axis=1) 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 predict(self, prices: pd.DataFrame): ''' Predicts portfolio given found optimal portoflio weights. :param prices: :return: optimal portofolio (in currency of input) ''' self.allocate(prices) # da = DiscreteAllocation(self.coef_, prices.iloc[-1], total_portfolio_value=self.budget) # alloc, leftover = da.lp_portfolio() # print(f"Leftover: ${leftover:.2f}") # print(f"Alloc: ${alloc}") discrete_portfolio = ( prices[[k for k, v in self.alloc.items()]] * np.asarray([[v for k, v in self.alloc.items()]])).dropna() return discrete_portfolio def allocate(self, prices: pd.DataFrame) -> Tuple[dict, float]: ''' Given weights for portfolio and last prices, finds best discrete allocation of assets. :param prices: :return: allocation and leftover of portfolio ''' da = DiscreteAllocation(self.coef_, prices.iloc[-1], total_portfolio_value=self.budget) self.alloc, self.leftover = da.lp_portfolio() print("Period end:", prices.index[0]) print(f"Leftover: ${self.leftover:.2f}") print(f"Alloc: ${self.alloc}") return self.alloc, self.leftover @property def weights(self): return self.coef_ @property def allocations(self): return self.alloc @property def leftover(self): return self.leftover
vtabx_history = vtabx_raw_history[[ 'Adj Close' ]].copy().rename(columns={"Adj Close": "VTABX"}) merged_history = pd.merge(vtsax_history, vtiax_history, left_index=True, right_index=True) merged_history = merged_history.merge(vbtlx_history, left_index=True, right_index=True) merged_history = merged_history.merge(vtabx_history, left_index=True, right_index=True) print(merged_history) # Calculate expected returns and sample covariance mu = expected_returns.mean_historical_return(merged_history) print(mu) S = risk_models.sample_cov(merged_history) print(S) # Optimise for 10% return, maybe reasonable for long term goals ef = EfficientFrontier(mu, S) ef.add_objective(objective_functions.L2_reg, gamma=1) raw_weights = ef.max_sharpe() #raw_weights = ef.efficient_return(target_return=0.12) cleaned_weights = ef.clean_weights() print(cleaned_weights) ef.portfolio_performance(verbose=True)
#different risk models; guessing that you can also use the pandas.cov function S = risk_models.semicovariance(prices) T = risk_models.CovarianceShrinkage(prices).ledoit_wolf() plotting.plot_covariance(S) plotting.plot_covariance(T) #equal weights initial_weights = np.array([1 / len(tickers)] * len(tickers)) print(initial_weights) #transaction cost objective ef = EfficientFrontier(mum, T) # 1% broker commission ef.add_objective(objective_functions.transaction_cost, w_prev=initial_weights, k=0.01) ef.min_volatility() weights = ef.clean_weights() weights # smaller broker comms ef = EfficientFrontier(mu, S) ef.add_objective(objective_functions.transaction_cost, w_prev=initial_weights, k=0.001) ef.min_volatility() weights = ef.clean_weights() weights #limit number of zero weights