# ################################## HOW TO USE #################################### # # # # This is a Jupyter notebook formatted as a script # # Format: https://jupytext.readthedocs.io/en/latest/formats.html#the-percent-format # # # # Save this file and remove the '.txt' extension # # In Jupyter Lab, right click on the Python file -> Open With -> Jupytext Notebook # # Make sure to have Jupytext installed: https://github.com/mwouts/jupytext # # # # ################################################################################## # # %% [markdown] # # Integrations # ## PyPortfolioOpt # %% from pypfopt.expected_returns import mean_historical_return from pypfopt.risk_models import CovarianceShrinkage from pypfopt.efficient_frontier import EfficientFrontier expected_returns = mean_historical_return(data.get("Close")) cov_matrix = CovarianceShrinkage(data.get("Close")).ledoit_wolf() optimizer = EfficientFrontier(expected_returns, cov_matrix) weights = optimizer.max_sharpe() weights # %% [markdown] # ### Parsing # %% from vectorbtpro.portfolio.pfopt.base import resolve_pypfopt_func_kwargs vbt.phelp(mean_historical_return) # %% print(vbt.prettify(resolve_pypfopt_func_kwargs( mean_historical_return, prices=data.get("Close"), freq="1h", year_freq="365d", other_arg=100 ))) # %% print(vbt.prettify(resolve_pypfopt_func_kwargs( EfficientFrontier, prices=data.get("Close") ))) # %% print(vbt.prettify(resolve_pypfopt_func_kwargs( EfficientFrontier, prices=data.get("Close"), expected_returns="ema_historical_return", cov_matrix="sample_cov" ))) # %% [markdown] # ### Auto-optimization # %% vbt.pypfopt_optimize(prices=data.get("Close")) # %% S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(None, S, weight_bounds=(-1, 1)) ef.min_volatility() weights = ef.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns=None, weight_bounds=(-1, 1), target="min_volatility" ) # %% from pypfopt.expected_returns import capm_return mu = capm_return(data.get("Close")) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(mu, S, weight_bounds=(-1, 1)) for symbol, direction in direction_mapper.items(): idx = data.symbols.index(symbol) if direction == "long": ef.add_constraint(lambda w, _idx=idx: w[_idx] >= 0) if direction == "short": ef.add_constraint(lambda w, _idx=idx: w[_idx] <= 0) ef.max_sharpe() weights = ef.clean_weights() weights # %% constraints = [] for symbol, direction in direction_mapper.items(): idx = data.symbols.index(symbol) if direction == "long": constraints.append(lambda w, _idx=idx: w[_idx] >= 0) if direction == "short": constraints.append(lambda w, _idx=idx: w[_idx] <= 0) vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns="capm_return", cov_matrix="ledoit_wolf", target="max_sharpe", weight_bounds=(-1, 1), constraints=constraints, ) # %% sector_mapper = { "ADAUSDT": "DeFi", "BNBUSDT": "DeFi", "BTCUSDT": "Payment", "ETHUSDT": "DeFi", "XRPUSDT": "Payment" } sector_lower = { "DeFi": 0.75 } sector_upper = {} # %% mu = capm_return(data.get("Close")) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(mu, S) ef.add_sector_constraints(sector_mapper, sector_lower, sector_upper) adausdt_index = ef.tickers.index("ADAUSDT") ef.add_constraint(lambda w: w[adausdt_index] == 0.10) ef.max_sharpe() weights = ef.clean_weights() weights # %% adausdt_index = list(sector_mapper.keys()).index("ADAUSDT") vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns="capm_return", sector_mapper=sector_mapper, sector_lower=sector_lower, sector_upper=sector_upper, constraints=[lambda w: w[adausdt_index] == 0.10] ) # %% from pypfopt.objective_functions import L2_reg mu = capm_return(data.get("Close")) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(mu, S) ef.add_sector_constraints(sector_mapper, sector_lower, sector_upper) ef.add_objective(L2_reg, gamma=0.1) ef.efficient_risk(0.15) weights = ef.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns="capm_return", sector_mapper=sector_mapper, sector_lower=sector_lower, sector_upper=sector_upper, objectives=["L2_reg"], gamma=0.1, target="efficient_risk", target_volatility=0.15 ) # %% from pypfopt import EfficientSemivariance from pypfopt.expected_returns import returns_from_prices mu = capm_return(data.get("Close")) returns = returns_from_prices(data.get("Close")) returns = returns.dropna() es = EfficientSemivariance(mu, returns) es.efficient_return(0.01) weights = es.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns="capm_return", optimizer="efficient_semivariance", target="efficient_return", target_return=0.01 ) # %% initial_weights = np.array([1 / len(data.symbols)] * len(data.symbols)) # %% from pypfopt.objective_functions import transaction_cost mu = mean_historical_return(data.get("Close")) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(mu, S) ef.add_objective(transaction_cost, w_prev=initial_weights, k=0.001) ef.add_objective(L2_reg, gamma=0.05) ef.min_volatility() weights = ef.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), objectives=["transaction_cost", "L2_reg"], w_prev=initial_weights, k=0.001, gamma=0.05, target="min_volatility" ) # %% import cvxpy as cp def logarithmic_barrier_objective(w, cov_matrix, k=0.1): log_sum = cp.sum(cp.log(w)) var = cp.quad_form(w, cov_matrix) return var - k * log_sum # %% mu = mean_historical_return(data.get("Close")) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(mu, S, weight_bounds=(0.01, 0.3)) ef.convex_objective(logarithmic_barrier_objective, cov_matrix=S, k=0.001) weights = ef.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), weight_bounds=(0.01, 0.3), k=0.001, target=logarithmic_barrier_objective ) # %% def deviation_risk_parity(w, cov_matrix): cov_matrix = np.asarray(cov_matrix) n = cov_matrix.shape[0] rp = (w * (cov_matrix @ w)) / cp.quad_form(w, cov_matrix) return cp.sum_squares(rp - 1 / n).value # %% mu = mean_historical_return(data.get("Close")) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() ef = EfficientFrontier(mu, S) ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix) weights = ef.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), target=deviation_risk_parity, target_is_convex=False ) # %% sp500_data = vbt.YFData.pull( "^GSPC", start=data.wrapper.index[0], end=data.wrapper.index[-1] ) market_caps = data.get("Close") * data.get("Volume") viewdict = { "ADAUSDT": 0.20, "BNBUSDT": -0.30, "BTCUSDT": 0, "ETHUSDT": -0.2, "XRPUSDT": 0.15 } # %% from pypfopt.black_litterman import ( market_implied_risk_aversion, market_implied_prior_returns, BlackLittermanModel ) S = CovarianceShrinkage(data.get("Close")).ledoit_wolf() delta = market_implied_risk_aversion(sp500_data.get("Close")) prior = market_implied_prior_returns(market_caps.iloc[-1], delta, S) bl = BlackLittermanModel(S, pi=prior, absolute_views=viewdict) rets = bl.bl_returns() ef = EfficientFrontier(rets, S) ef.min_volatility() weights = ef.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns="bl_returns", market_prices=sp500_data.get("Close"), market_caps=market_caps.iloc[-1], absolute_views=viewdict, target="min_volatility" ) # %% from pypfopt import HRPOpt rets = returns_from_prices(data.get("Close")) hrp = HRPOpt(rets) hrp.optimize() weights = hrp.clean_weights() weights # %% vbt.pypfopt_optimize( prices=data.get("Close"), optimizer="hrp", target="optimize" ) # %% [markdown] # ### Argument groups # %% vbt.pypfopt_optimize( prices=data.get("Close"), expected_returns="bl_returns", market_prices=sp500_data.get("Close"), market_caps=market_caps.iloc[-1], absolute_views=viewdict, target="min_volatility", cov_matrix=vbt.pfopt_func_dict({ "EfficientFrontier": "sample_cov", "_def": "ledoit_wolf" }) ) # %% [markdown] # ### Periodically # %% pfo = vbt.PortfolioOptimizer.from_pypfopt( prices=data.get("Close"), every="W" ) pfo.plot().show() # %% pfo = vbt.PortfolioOptimizer.from_pypfopt( prices=data.get("Close"), every="W", target=vbt.Param([ "max_sharpe", "min_volatility", "max_quadratic_utility" ]) ) pfo.plot(column="min_volatility").show() # %% pf = pfo.simulate(data, freq="1h") pf.sharpe_ratio # %% [markdown] # #### Manually # %% def optimize_func(prices, index_slice, **kwargs): period_prices = prices.iloc[index_slice] return vbt.pypfopt_optimize(prices=period_prices, **kwargs) pfo = vbt.PortfolioOptimizer.from_optimize_func( data.symbol_wrapper, optimize_func, prices=data.get("Close"), index_slice=vbt.Rep("index_slice"), every="W" ) # %% [markdown] # ## Riskfolio-Lib # %% import riskfolio as rp returns = data.get("Close").vbt.to_returns() port = rp.Portfolio(returns=returns) port.assets_stats( method_mu="hist", method_cov="hist", d=0.94 ) w = port.optimization( model="Classic", rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) w.T # %% [markdown] # ### Parsing # %% from vectorbtpro.utils.parsing import get_func_arg_names get_func_arg_names(port.assets_stats) # %% from vectorbtpro.portfolio.pfopt.base import resolve_riskfolio_func_kwargs resolve_riskfolio_func_kwargs( port.assets_stats, method_mu="hist", method_cov="hist", model="Classic" ) # %% resolve_riskfolio_func_kwargs( port.assets_stats, method_mu="hist", method_cov="hist", model="Classic", func_kwargs=dict( assets_stats=dict(method_mu="ewma1"), optimization=dict(model="BL") ) ) # %% [markdown] # ### Auto-optimization # %% vbt.riskfolio_optimize(returns) # %% port = rp.Portfolio(returns=returns) port.assets_stats( method_mu="hist", method_cov="hist", d=0.94 ) w = port.optimization( model="Classic", rm="UCI", obj="Sharpe", rf=0, l=0, hist=True ) w.T # %% vbt.riskfolio_optimize( returns, method_mu="hist", method_cov="hist", d=0.94, model="Classic", rm="UCI", obj="Sharpe", rf=0, l=0, hist=True ) # %% port = rp.Portfolio(returns=returns) port.assets_stats( method_mu="hist", method_cov="hist", d=0.94 ) port.wc_stats( box="s", ellip="s", q=0.05, n_sim=3000, window=3, dmu=0.1, dcov=0.1, seed=0 ) w = port.wc_optimization( obj="Sharpe", rf=0, l=0, Umu="box", Ucov="box" ) w.T # %% vbt.riskfolio_optimize( returns, opt_method="wc", method_mu="hist", method_cov="hist", box="s", ellip="s", q=0.05, n_sim=3000, window=3, dmu=0.1, dcov=0.1, seed=0, obj="Sharpe", rf=0, l=0, Umu="box", Ucov="box" ) # %% vbt.riskfolio_optimize( returns, func_kwargs=dict( assets_stats=dict( opt_method="wc", method_mu="hist", method_cov="hist" ), wc_stats=dict( box="s", ellip="s", q=0.05, n_sim=3000, window=3, dmu=0.1, dcov=0.1, seed=0 ), wc_optimization=dict( obj="Sharpe", rf=0, l=0, Umu="box", Ucov="box" ) ) ) # %% port = rp.Portfolio(returns=returns) port.sht = True port.uppersht = 0.3 port.upperlng = 1.3 port.budget = 1.0 port.assets_stats( method_mu="hist", method_cov="hist", d=0.94 ) w = port.optimization( model="Classic", rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) w.T # %% vbt.riskfolio_optimize( returns, sht=True, uppersht=0.3, upperlng=1.3, budget=1.0, method_mu="hist", method_cov="hist", d=0.94, rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) # %% port = rp.Portfolio(returns=returns) port.assets_stats( method_mu="hist", method_cov="hist", d=0.94 ) asset_classes = {"Assets": returns.columns.tolist()} asset_classes = pd.DataFrame(asset_classes) constraints = { "Disabled": [False, False], "Type": ["All Assets", "Assets"], "Set": ["", ""], "Position": ["", "BTCUSDT"], "Sign": [">=", "<="], 'Weight': [0.1, 0.15], "Type Relative": ["", ""], "Relative Set": ["", ""], "Relative": ["", ""], "Factor": ["", ""], } constraints = pd.DataFrame(constraints) A, B = rp.assets_constraints(constraints, asset_classes) port.ainequality = A port.binequality = B w = port.optimization( model="Classic", rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) w.T # %% vbt.riskfolio_optimize( returns, method_mu="hist", method_cov="hist", constraints=[{ "Type": "All Assets", "Sign": ">=", "Weight": 0.1 }, { "Type": "Assets", "Position": "BTCUSDT", "Sign": "<=", "Weight": 0.15 }], d=0.94, rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) # %% tags = [ "Smart contracts", "Smart contracts", "Payments", "Smart contracts", "Payments" ] # %% port = rp.Portfolio(returns=returns) port.assets_stats( method_mu="hist", method_cov="hist", d=0.94 ) asset_classes = { "Assets": returns.columns.tolist(), "Tags": tags } asset_classes = pd.DataFrame(asset_classes) constraints = { "Disabled": [False], "Type": ["Classes"], "Set": ["Tags"], "Position": ["Smart contracts"], "Sign": [">="], 'Weight': [0.8], "Type Relative": [""], "Relative Set": [""], "Relative": [""], "Factor": [""], } constraints = pd.DataFrame(constraints) A, B = rp.assets_constraints(constraints, asset_classes) port.ainequality = A port.binequality = B w = port.optimization( model="Classic", rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) w.T # %% vbt.riskfolio_optimize( returns, method_mu="hist", method_cov="hist", asset_classes={"Tags": tags}, constraints=[{ "Type": "Classes", "Set": "Tags", "Position": "Smart contracts", "Sign": ">=", "Weight": 0.8 }], d=0.94, rm="MV", obj="Sharpe", rf=0, l=0, hist=True ) # %% port = rp.HCPortfolio(returns=returns) w = port.optimization( model="NCO", codependence="pearson", covariance="hist", obj="MinRisk", rm="MV", rf=0, l=2, linkage="ward", max_k=10, leaf_order=True ) w.T # %% vbt.riskfolio_optimize( returns, port_cls="HCPortfolio", model="NCO", codependence="pearson", covariance="hist", obj="MinRisk", rm="MV", rf=0, l=2, linkage="ward", max_k=10, leaf_order=True ) # %% [markdown] # ### Periodically # %% pfo = vbt.PortfolioOptimizer.from_riskfolio( returns=returns, every="W" ) pfo.plot().show() # %% pfo = vbt.PortfolioOptimizer.from_riskfolio( returns=returns, constraints=[{ "Type": "Assets", "Position": "BTCUSDT", "Sign": "<=", "Weight": vbt.Param([0.1, 0.2, 0.3], name="BTCUSDT_maxw") }], every="W", param_search_kwargs=dict(incl_types=list) ) # %% pfo.allocations.groupby("BTCUSDT_maxw").max() # %% [markdown] # ## Universal portfolios # %% from universal import tools, algos with vbt.WarningsFiltered(): algo = algos.CRP() algo_result = algo.run(data.get("Close")) algo_result.weights # %% with vbt.WarningsFiltered(): algo = algos.DynamicCRP( n=30, min_history=7, metric='sharpe', alpha=0.01 ) algo_result = algo.run(data.get("Close").resample("D").last()) down_weights = algo_result.weights down_weights # %% weights = down_weights.vbt.realign( data.wrapper.index, freq="1h", source_rbound=True, target_rbound=True, ffill=False ) weights # %% with vbt.WarningsFiltered(): down_pfo = vbt.PortfolioOptimizer.from_universal_algo( "DynamicCRP", data.get("Close").resample("D").last(), n=vbt.Param([7, 14, 30, 90]), min_history=7, metric='sharpe', alpha=0.01 ) down_pfo.plot(column=90).show() # %% resampler = vbt.Resampler( down_pfo.wrapper.index, data.wrapper.index, target_freq="1h" ) pfo = down_pfo.resample(resampler) # %% pf = pfo.simulate(data, freq="1h") pf.sharpe_ratio # %% [markdown] # ### Custom algorithm # %% from universal.algo import Algo class MeanReversion(Algo): PRICE_TYPE = 'log' def __init__(self, n): self.n = n super().__init__(min_history=n) def init_weights(self, cols): return pd.Series(np.zeros(len(cols)), cols) def step(self, x, last_b, history): ma = history.iloc[-self.n:].mean() delta = x - ma w = np.maximum(-delta, 0.) return w / sum(w) with vbt.WarningsFiltered(): pfo = vbt.PortfolioOptimizer.from_universal_algo( MeanReversion, data.get("Close").resample("D").last(), n=30, every="W" ) pfo.plot().show() # %%