base module¶
Base functions and classes for portfolio optimization.
prepare_returns function¶
Prepare returns.
pypfopt_optimize function¶
pypfopt_optimize(
target=None,
target_is_convex=None,
weights_sum_to_one=None,
target_constraints=None,
target_solver=None,
target_initial_guess=None,
objectives=None,
constraints=None,
sector_mapper=None,
sector_lower=None,
sector_upper=None,
discrete_allocation=None,
allocation_method=None,
silence_warnings=None,
ignore_opt_errors=None,
ignore_errors=None,
**kwargs
)
Get allocation using PyPortfolioOpt.
First, it resolves the optimizer using resolve_pypfopt_optimizer(). Depending upon which arguments it takes, it may further resolve expected returns, covariance matrix, etc. Then, it adds objectives and constraints to the optimizer instance, calls the target metric, extracts the weights, and finally, converts the weights to an integer allocation (if requested).
To specify the optimizer, use optimizer (see resolve_pypfopt_optimizer()). To specify the expected returns, use expected_returns (see resolve_pypfopt_expected_returns()). To specify the covariance matrix, use cov_matrix (see resolve_pypfopt_cov_matrix()). All other keyword arguments in **kwargs are used by resolve_pypfopt_func_call().
Each objective can be a function, an attribute of pypfopt.objective_functions, or an iterable of such.
Each constraint can be a function or an interable of such.
The target can be an attribute of the optimizer, or a stand-alone function. If target_is_convex is True, the function is added as a convex function. Otherwise, the function is added as a non-convex function. The keyword arguments weights_sum_to_one and those starting with target are passed pypfopt.base_optimizer.BaseConvexOptimizer.convex_objective and pypfopt.base_optimizer.BaseConvexOptimizer.nonconvex_objective respectively.
Set ignore_opt_errors to True to ignore any target optimization errors. Set ignore_errors to True to ignore any errors, even those caused by the user.
If discrete_allocation is True, resolves pypfopt.discrete_allocation.DiscreteAllocation and calls allocation_method as an attribute of the allocation object.
Any function is resolved using resolve_pypfopt_func_call().
For defaults, see pypfopt under pfopt.
Usage
- Using mean historical returns, Ledoit-Wolf covariance matrix with constant variance, and efficient frontier:
>>> vbt.pypfopt_optimize(prices=data.get("Close"))
{'MSFT': 0.13324, 'AMZN': 0.10016, 'KO': 0.03229, 'MA': 0.73431}
- EMA historical returns and sample covariance:
>>> vbt.pypfopt_optimize(
... prices=data.get("Close"),
... expected_returns="ema_historical_return",
... cov_matrix="sample_cov"
... )
{'MSFT': 0.08984, 'AMZN': 0.0, 'KO': 0.91016, 'MA': 0.0}
- EMA historical returns, efficient Conditional Value at Risk, and other parameters automatically passed to their respective functions. Optimized towards lowest CVaR:
>>> vbt.pypfopt_optimize(
... prices=data.get("Close"),
... expected_returns="ema_historical_return",
... optimizer="efficient_cvar",
... beta=0.9,
... weight_bounds=(-1, 1),
... target="min_cvar"
... )
{'MSFT': 0.14779, 'AMZN': 0.07224, 'KO': 0.77552, 'MA': 0.00445}
- Adding custom objectives:
>>> vbt.pypfopt_optimize(
... prices=data.get("Close"),
... objectives=["L2_reg"],
... gamma=0.1,
... target="min_volatility"
... )
{'MSFT': 0.22228, 'AMZN': 0.15685, 'KO': 0.28712, 'MA': 0.33375}
- Adding custom constraints:
>>> vbt.pypfopt_optimize(
... prices=data.get("Close"),
... constraints=[lambda w: w[data.symbols.index("MSFT")] <= 0.1]
... )
{'MSFT': 0.1, 'AMZN': 0.10676, 'KO': 0.04341, 'MA': 0.74982}
- Optimizing towards a custom convex objective (to add a non-convex objective, set
target_is_convexto False):
>>> 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
>>> pypfopt_optimize(
... prices=data.get("Close"),
... target=logarithmic_barrier_objective
... )
{'MSFT': 0.24595, 'AMZN': 0.23047, 'KO': 0.25862, 'MA': 0.26496}
resolve_asset_classes function¶
Resolve asset classes for Riskfolio-Lib.
Supports the following formats:
- None: Takes columns where the bottom-most level is assumed to be assets
- Index: Each level in the index must be a different asset class set
- Nested dict: Each sub-dict must be a different asset class set
- Sequence of strings or ints: Matches them against level names in the columns. If the columns have a single level, or some level names were not found, uses the sequence directly as one class asset set named 'Class'.
- Sequence of dicts: Each dict becomes a row in the new DataFrame
- DataFrame where the first column is the asset list and the next columns are the different asset’s classes sets (this is the target format accepted by Riskfolio-Lib). See an example here.
Note
If asset_classes is neither None nor a DataFrame, the bottom-most level in columns gets renamed to 'Assets' and becomes the first column of the new DataFrame.
resolve_assets_constraints function¶
Resolve asset constraints for Riskfolio-Lib.
Apart from the target format, also accepts a sequence of dicts such that each dict becomes a row in a new DataFrame. Dicts don't have to specify all column names, the function will autofill any missing elements/columns.
resolve_assets_views function¶
Resolve asset views for Riskfolio-Lib.
Apart from the target format, also accepts a sequence of dicts such that each dict becomes a row in a new DataFrame. Dicts don't have to specify all column names, the function will autofill any missing elements/columns.
resolve_factors_constraints function¶
Resolve factors constraints for Riskfolio-Lib.
Apart from the target format, also accepts a sequence of dicts such that each dict becomes a row in a new DataFrame. Dicts don't have to specify all column names, the function will autofill any missing elements/columns.
resolve_factors_views function¶
Resolve factors views for Riskfolio-Lib.
Apart from the target format, also accepts a sequence of dicts such that each dict becomes a row in a new DataFrame. Dicts don't have to specify all column names, the function will autofill any missing elements/columns.
resolve_hrp_constraints function¶
Resolve HRP constraints for Riskfolio-Lib.
Apart from the target format, also accepts a sequence of dicts such that each dict becomes a row in a new DataFrame. Dicts don't have to specify all column names, the function will autofill any missing elements/columns.
resolve_pypfopt_cov_matrix function¶
Resolve the covariance matrix.
cov_matrix can be an array, an attribute of pypfopt.risk_models, a function, or one of the following options:
- 'sample_cov':
pypfopt.risk_models.sample_cov - 'semicovariance' or 'semivariance':
pypfopt.risk_models.semicovariance - 'exp_cov':
pypfopt.risk_models.exp_cov - 'ledoit_wolf' or 'ledoit_wolf_constant_variance':
pypfopt.risk_models.CovarianceShrinkage.ledoit_wolfwith 'constant_variance' as shrinkage factor - 'ledoit_wolf_single_factor':
pypfopt.risk_models.CovarianceShrinkage.ledoit_wolfwith 'single_factor' as shrinkage factor - 'ledoit_wolf_constant_correlation':
pypfopt.risk_models.CovarianceShrinkage.ledoit_wolfwith 'constant_correlation' as shrinkage factor - 'oracle_approximating':
pypfopt.risk_models.CovarianceShrinkage.ledoit_wolfwith 'oracle_approximating' as shrinkage factor
Any function is resolved using resolve_pypfopt_func_call().
resolve_pypfopt_expected_returns function¶
Resolve the expected returns.
expected_returns can be an array, an attribute of pypfopt.expected_returns, a function, or one of the following options:
- 'mean_historical_return':
pypfopt.expected_returns.mean_historical_return - 'ema_historical_return':
pypfopt.expected_returns.ema_historical_return - 'capm_return':
pypfopt.expected_returns.capm_return - 'bl_returns':
pypfopt.black_litterman.BlackLittermanModel.bl_returns
Any function is resolved using resolve_pypfopt_func_call().
resolve_pypfopt_func_call function¶
Resolve arguments using resolve_pypfopt_func_kwargs() and call the function with that arguments.
resolve_pypfopt_func_kwargs function¶
resolve_pypfopt_func_kwargs(
pypfopt_func,
cache=None,
var_kwarg_names=None,
used_arg_names=None,
**kwargs
)
Resolve keyword arguments passed to any optimization function with the layout of PyPortfolioOpt.
Parses the signature of pypfopt_func, and for each accepted argument, looks for an argument with the same name in kwargs. If not found, tries to resolve that argument using other arguments or by calling other optimization functions.
Argument frequency gets resolved with (global) freq and year_freq using ReturnsAccessor.get_ann_factor().
Any argument in kwargs can be wrapped using pfopt_func_dict to define the argument per function rather than globally.
Note
When providing custom functions, make sure that the arguments they accept are visible in the signature (that is, no variable arguments) and have the same naming as in PyPortfolioOpt.
Functions market_implied_prior_returns and BlackLittermanModel.bl_weights take risk_aversion, which is different from arguments with the same name in other functions. To set it, pass delta.
resolve_pypfopt_optimizer function¶
Resolve the optimizer.
optimizer can be an instance of pypfopt.base_optimizer.BaseOptimizer, an attribute of pypfopt, a subclass of pypfopt.base_optimizer.BaseOptimizer, or one of the following options:
- 'efficient_frontier':
pypfopt.efficient_frontier.EfficientFrontier - 'efficient_cdar':
pypfopt.efficient_frontier.EfficientCDaR - 'efficient_cvar':
pypfopt.efficient_frontier.EfficientCVaR - 'efficient_semivariance':
pypfopt.efficient_frontier.EfficientSemivariance - 'black_litterman' or 'bl':
pypfopt.black_litterman.BlackLittermanModel - 'hierarchical_portfolio', 'hrpopt', or 'hrp':
pypfopt.hierarchical_portfolio.HRPOpt - 'cla':
pypfopt.cla.CLA
Any function is resolved using resolve_pypfopt_func_call().
resolve_riskfolio_func_kwargs function¶
Select keyword arguments belonging to riskfolio_func.
riskfolio_optimize function¶
riskfolio_optimize(
returns,
nan_to_zero=None,
dropna_rows=None,
dropna_cols=None,
dropna_any=None,
factors=None,
port=None,
port_cls=None,
opt_method=None,
stats_methods=None,
model=None,
asset_classes=None,
constraints_method=None,
constraints=None,
views_method=None,
views=None,
solvers=None,
sol_params=None,
freq=None,
year_freq=None,
pre_opt=None,
pre_opt_kwargs=None,
pre_opt_as_w=None,
func_kwargs=None,
silence_warnings=None,
return_port=None,
ignore_errors=None,
**kwargs
)
Get allocation using Riskfolio-Lib.
Args
returns:array_like- A dataframe that contains the returns of the assets.
nan_to_zero:bool- Whether to convert NaN values to zero.
dropna_rows:bool-
Whether to drop rows with all NaN/zero values.
Gets applied only if
nan_to_zerois True ordropna_anyis False. dropna_cols:bool- Whether to drop columns with all NaN/zero values.
dropna_any:bool-
Whether to drop any NaN values.
Gets applied only if
nan_to_zerois False. factors:array_like- A dataframe that contains the factors.
port:PortfolioorHCPortfolio- Already initialized portfolio.
port_cls:strortype-
Portfolio class.
Supports the following values:
- None: Uses
Portfolio - 'hc' or 'hcportfolio' (case-insensitive): Uses
HCPortfolio - Other string: Uses attribute of
riskfolio - Class: Uses a custom class
- None: Uses
opt_method:strorcallable-
Optimization method.
Supports the following values:
- None or 'optimization': Uses
port.optimization(whereportis a portfolio instance) - 'wc' or 'wc_optimization': Uses
port.wc_optimization - 'rp' or 'rp_optimization': Uses
port.rp_optimization - 'rrp' or 'rrp_optimization': Uses
port.rrp_optimization - 'owa' or 'owa_optimization': Uses
port.owa_optimization - String: Uses attribute of
port - Callable: Uses a custom optimization function
- None or 'optimization': Uses
stats_methods:strorsequenceofstr-
Sequence of stats methods to call before optimization.
If None, tries to automatically populate the sequence using
opt_methodandmodel. For example, callsport.assets_statsifmodel="Classic"is used. Also, iffunc_kwargsis not empty, adds all functions whose name ends with '_stats'. model:str- The model used to optimize the portfolio.
asset_classes:any-
Asset classes matrix.
See resolve_asset_classes() for possible formats.
constraints_method:str-
Constraints method.
Supports the following values:
- 'assets' or 'assets_constraints': assets constraints
- 'factors' or 'factors_constraints': factors constraints
- 'hrp' or 'hrp_constraints': HRP constraints
If None and the class
Portfoliois used, will use factors constraints iffactors_statsis used, otherwise assets constraints. If the classHCPortfoliois used, will use HRP constraints. constraints:any-
Constraints matrix.
See resolve_assets_constraints() for possible formats of assets constraints, resolve_factors_constraints() for possible formats of factors constraints, and resolve_hrp_constraints() for possible formats of HRP constraints.
views_method:str-
Views method.
Supports the following values:
- 'assets' or 'assets_views': assets views
- 'factors' or 'factors_views': factors views
If None, will use factors views if
blfactors_statsis used, otherwise assets views. views:any-
Views matrix.
See resolve_assets_views() for possible formats of assets views and resolve_factors_views() for possible formats of factors views.
solvers:listofstr- Solvers.
sol_params:dict- Solver parameters.
freq:frequency_like-
Frequency to be used to compute the annualization factor.
Make sure to provide it when using views.
year_freq:frequency_like-
Year frequency to be used to compute the annualization factor.
Make sure to provide it when using views.
pre_opt:bool- Whether to pre-optimize the portfolio with
pre_opt_kwargs. pre_opt_kwargs:dict- Call riskfolio_optimize() with these keyword arguments and use the returned portfolio for further optimization.
pre_opt_as_w:bool- Whether to use the weights as
wfrom the pre-optimization step. func_kwargs:dict-
Further keyword arguments by function.
Can be used to override any arguments from
kwargsmatched with the function, or to add more arguments. Will be wrapped with pfopt_func_dict and passed to select_pfopt_func_kwargs() when calling each Riskfolio-Lib's function. silence_warnings:bool- Whether to silence all warnings.
return_port:bool- Whether to also return the portfolio.
ignore_errors:bool- Whether to ignore any errors, even those caused by the user.
**kwargs- Keyword arguments that will be passed to any Riskfolio-Lib's function that needs them (i.e., lists any of them in its signature).
For defaults, see riskfolio under pfopt.
Usage
- Classic Mean Risk Optimization:
>>> from vectorbtpro import *
>>> data = vbt.YFData.pull(["MSFT", "AMZN", "KO", "MA"])
>>> returns = data.close.vbt.to_returns()
>>> vbt.riskfolio_optimize(
... returns,
... method_mu='hist', method_cov='hist', d=0.94, # assets_stats
... model='Classic', rm='MV', obj='Sharpe', hist=True, rf=0, l=0 # optimization
... )
{'MSFT': 0.26297126323056036,
'AMZN': 0.13984467450137006,
'KO': 0.35870315943426767,
'MA': 0.238480902833802}
- The same by splitting arguments:
>>> vbt.riskfolio_optimize(
... returns,
... func_kwargs=dict(
... assets_stats=dict(method_mu='hist', method_cov='hist', d=0.94),
... optimization=dict(model='Classic', rm='MV', obj='Sharpe', hist=True, rf=0, l=0)
... )
... )
{'MSFT': 0.26297126323056036,
'AMZN': 0.13984467450137006,
'KO': 0.35870315943426767,
'MA': 0.238480902833802}
- Asset constraints:
>>> vbt.riskfolio_optimize(
... returns,
... constraints=[
... {
... "Type": "Assets",
... "Position": "MSFT",
... "Sign": "<=",
... "Weight": 0.01
... }
... ]
... )
{'MSFT': 0.009999990814976588,
'AMZN': 0.19788481506569947,
'KO': 0.4553600308839969,
'MA': 0.336755163235327}
- Asset class constraints:
>>> vbt.riskfolio_optimize(
... returns,
... asset_classes=["C1", "C1", "C2", "C2"],
... constraints=[
... {
... "Type": "Classes",
... "Set": "Class",
... "Position": "C1",
... "Sign": "<=",
... "Weight": 0.1
... }
... ]
... )
{'MSFT': 0.03501297245802569,
'AMZN': 0.06498702655063979,
'KO': 0.4756624658301967,
'MA': 0.4243375351611379}
- Hierarchical Risk Parity (HRP) Portfolio Optimization:
>>> vbt.riskfolio_optimize(
... returns,
... port_cls="HCPortfolio",
... model='HRP',
... codependence='pearson',
... rm='MV',
... rf=0,
... linkage='single',
... max_k=10,
... leaf_order=True
... )
{'MSFT': 0.19091632057853536,
'AMZN': 0.11069893826556164,
'KO': 0.28589872132122485,
'MA': 0.41248601983467814}
select_pfopt_func_kwargs function¶
Select keyword arguments belonging to pypfopt_func.
PortfolioOptimizer class¶
Class that exposes methods for generating allocations.
Superclasses
- Analyzable
- AttrResolverMixin
- Cacheable
- Chainable
- Comparable
- Configured
- ExtPandasIndexer
- HasSettings
- IndexApplier
- IndexingBase
- Itemable
- PandasIndexer
- Paramable
- Pickleable
- PlotsBuilderMixin
- Prettified
- StatsBuilderMixin
- Wrapping
Inherited members
- Analyzable.cls_dir
- Analyzable.column_only_select
- Analyzable.config
- Analyzable.group_select
- Analyzable.iloc
- Analyzable.indexing_kwargs
- Analyzable.loc
- Analyzable.range_only_select
- Analyzable.rec_state
- Analyzable.self_aliases
- Analyzable.wrapper
- Analyzable.xloc
- AttrResolverMixin.deep_getattr()
- AttrResolverMixin.post_resolve_attr()
- AttrResolverMixin.pre_resolve_attr()
- AttrResolverMixin.resolve_attr()
- AttrResolverMixin.resolve_shortcut_attr()
- Cacheable.get_ca_setup()
- Chainable.pipe()
- Configured.copy()
- Configured.equals()
- Configured.get_writeable_attrs()
- Configured.prettify()
- Configured.replace()
- Configured.resolve_merge_kwargs()
- Configured.update_config()
- HasSettings.get_path_setting()
- HasSettings.get_path_settings()
- HasSettings.get_setting()
- HasSettings.get_settings()
- HasSettings.has_path_setting()
- HasSettings.has_path_settings()
- HasSettings.has_setting()
- HasSettings.has_settings()
- HasSettings.reset_settings()
- HasSettings.resolve_setting()
- HasSettings.resolve_settings_paths()
- HasSettings.set_settings()
- IndexApplier.add_levels()
- IndexApplier.drop_duplicate_levels()
- IndexApplier.drop_levels()
- IndexApplier.drop_redundant_levels()
- IndexApplier.rename_levels()
- IndexApplier.select_levels()
- IndexingBase.indexing_setter_func()
- PandasIndexer.xs()
- Pickleable.decode_config()
- Pickleable.decode_config_node()
- Pickleable.dumps()
- Pickleable.encode_config()
- Pickleable.encode_config_node()
- Pickleable.file_exists()
- Pickleable.getsize()
- Pickleable.load()
- Pickleable.loads()
- Pickleable.modify_state()
- Pickleable.resolve_file_path()
- Pickleable.save()
- PlotsBuilderMixin.build_subplots_doc()
- PlotsBuilderMixin.override_subplots_doc()
- PlotsBuilderMixin.plots()
- StatsBuilderMixin.build_metrics_doc()
- StatsBuilderMixin.override_metrics_doc()
- StatsBuilderMixin.stats()
- Wrapping.apply_to_index()
- Wrapping.as_param()
- Wrapping.items()
- Wrapping.regroup()
- Wrapping.resolve_column_stack_kwargs()
- Wrapping.resolve_row_stack_kwargs()
- Wrapping.resolve_self()
- Wrapping.resolve_stack_kwargs()
- Wrapping.select_col()
- Wrapping.select_col_from_obj()
- Wrapping.split()
- Wrapping.split_apply()
alloc_records property¶
Allocation ranges of type AllocRanges or points of type AllocPoints.
allocations property¶
Calls PortfolioOptimizer.get_allocations() with default arguments.
column_stack class method¶
Stack multiple PortfolioOptimizer instances along columns.
Uses ArrayWrapper.column_stack() to stack the wrappers.
fill_allocations method¶
PortfolioOptimizer.fill_allocations(
dropna=None,
fill_value=nan,
wrap_kwargs=None,
squeeze_groups=True
)
Fill an empty DataFrame with allocations.
Set dropna to 'all' to remove all NaN rows, or to 'head' to remove any rows coming before the first allocation.
filled_allocations property¶
Calls PortfolioOptimizer.fill_allocations() with default arguments.
from_allocate_func class method¶
PortfolioOptimizer.from_allocate_func(
wrapper,
allocate_func,
*args,
every=None,
normalize_every=False,
at_time=None,
start=None,
end=None,
exact_start=False,
on=None,
add_delta=None,
kind=None,
indexer_method='bfill',
indexer_tolerance=None,
skip_not_found=True,
index_points=None,
rescale_to=None,
parameterizer_cls=None,
param_search_kwargs=None,
name_tuple_to_str=None,
group_configs=None,
pre_group_func=None,
jitted_loop=False,
jitted=None,
chunked=None,
template_context=None,
group_execute_kwargs=None,
execute_kwargs=None,
random_subset=None,
clean_index_kwargs=None,
wrapper_kwargs=None,
**kwargs
)
Generate allocations from an allocation function.
Generates date points and allocates at those points.
Similar to PortfolioOptimizer.from_optimize_func(), but generates points using ArrayWrapper.get_index_points() and makes each point available as index_point in the context.
If jitted_loop is True, see allocate_meta_nb().
Also, in contrast to PortfolioOptimizer.from_optimize_func(), creates records of type AllocPoints.
Usage
- Allocate uniformly every day:
>>> from vectorbtpro import *
>>> data = vbt.YFData.pull(
... ["MSFT", "AMZN", "AAPL"],
... start="2010-01-01",
... end="2020-01-01"
... )
>>> close = data.get("Close")
>>> def uniform_allocate_func(n_cols):
... return np.full(n_cols, 1 / n_cols)
>>> pfo = vbt.PortfolioOptimizer.from_allocate_func(
... close.vbt.wrapper,
... uniform_allocate_func,
... close.shape[1]
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
Date
2010-01-04 00:00:00-05:00 0.333333 0.333333 0.333333
2010-01-05 00:00:00-05:00 0.333333 0.333333 0.333333
2010-01-06 00:00:00-05:00 0.333333 0.333333 0.333333
2010-01-07 00:00:00-05:00 0.333333 0.333333 0.333333
2010-01-08 00:00:00-05:00 0.333333 0.333333 0.333333
... ... ... ...
2019-12-24 00:00:00-05:00 0.333333 0.333333 0.333333
2019-12-26 00:00:00-05:00 0.333333 0.333333 0.333333
2019-12-27 00:00:00-05:00 0.333333 0.333333 0.333333
2019-12-30 00:00:00-05:00 0.333333 0.333333 0.333333
2019-12-31 00:00:00-05:00 0.333333 0.333333 0.333333
[2516 rows x 3 columns]
- Allocate randomly every first date of the year:
>>> def random_allocate_func(n_cols):
... weights = np.random.uniform(size=n_cols)
... return weights / weights.sum()
>>> pfo = vbt.PortfolioOptimizer.from_allocate_func(
... close.vbt.wrapper,
... random_allocate_func,
... close.shape[1],
... every="AS-JAN"
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
Date
2011-01-03 00:00:00+00:00 0.160335 0.122434 0.717231
2012-01-03 00:00:00+00:00 0.071386 0.469564 0.459051
2013-01-02 00:00:00+00:00 0.125853 0.168480 0.705668
2014-01-02 00:00:00+00:00 0.391565 0.169205 0.439231
2015-01-02 00:00:00+00:00 0.115075 0.602844 0.282081
2016-01-04 00:00:00+00:00 0.244070 0.046547 0.709383
2017-01-03 00:00:00+00:00 0.316065 0.335000 0.348935
2018-01-02 00:00:00+00:00 0.422142 0.252154 0.325704
2019-01-02 00:00:00+00:00 0.368748 0.195147 0.436106
- Specify index points manually:
>>> pfo = vbt.PortfolioOptimizer.from_allocate_func(
... close.vbt.wrapper,
... random_allocate_func,
... close.shape[1],
... index_points=[0, 30, 60]
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
Date
2010-01-04 00:00:00+00:00 0.257878 0.308287 0.433835
2010-02-17 00:00:00+00:00 0.090927 0.471980 0.437094
2010-03-31 00:00:00+00:00 0.395855 0.148516 0.455629
- Specify allocations manually:
>>> def manual_allocate_func(weights):
... return weights
>>> pfo = vbt.PortfolioOptimizer.from_allocate_func(
... close.vbt.wrapper,
... manual_allocate_func,
... vbt.RepEval("weights[i]", context=dict(weights=[
... [1, 0, 0],
... [0, 1, 0],
... [0, 0, 1]
... ])),
... index_points=[0, 30, 60]
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
Date
2010-01-04 00:00:00+00:00 1 0 0
2010-02-17 00:00:00+00:00 0 1 0
2010-03-31 00:00:00+00:00 0 0 1
- Use Numba-compiled loop:
>>> @njit
... def random_allocate_func_nb(i, idx, n_cols):
... weights = np.random.uniform(0, 1, n_cols)
... return weights / weights.sum()
>>> pfo = vbt.PortfolioOptimizer.from_allocate_func(
... close.vbt.wrapper,
... random_allocate_func_nb,
... close.shape[1],
... index_points=[0, 30, 60],
... jitted_loop=True
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
Date
2010-01-04 00:00:00+00:00 0.231925 0.351085 0.416990
2010-02-17 00:00:00+00:00 0.163050 0.070292 0.766658
2010-03-31 00:00:00+00:00 0.497465 0.500215 0.002319
Hint
There is no big reason of using the Numba-compiled loop, apart from when having to rebalance many thousands of times. Usually, using a regular Python loop and a Numba-compiled allocation function should suffice.
from_allocations class method¶
Pick allocations from a (flexible) array.
Uses PortfolioOptimizer.from_allocate_func().
If allocations is a DataFrame, uses its index as labels. If it's a Series or dict, uses it as a single allocation without index, which by default gets assigned to each index. If it's neither one of the above nor a NumPy array, tries to convert it into a NumPy array.
If allocations is a NumPy array, uses pick_idx_allocate_func_nb() and a Numba-compiled loop. Otherwise, uses a regular Python function to pick each allocation (which can be a dict, Series, etc.). Selection of elements is done in a flexible manner, meaning a single element will be applied to all rows, while one-dimensional arrays will be also applied to all rows but also broadcast across columns (as opposed to rows).
from_filled_allocations class method¶
PortfolioOptimizer.from_filled_allocations(
allocations,
valid_only=True,
nonzero_only=True,
unique_only=True,
wrapper=None,
**kwargs
)
Pick allocations from an already filled array.
Uses PortfolioOptimizer.from_allocate_func().
Uses pick_point_allocate_func_nb() and a Numba-compiled loop.
Extracts allocation points using get_alloc_points_nb().
from_initial class method¶
Allocate once at the first index.
Uses PortfolioOptimizer.from_allocations() with on=0.
from_optimize_func class method¶
PortfolioOptimizer.from_optimize_func(
wrapper,
optimize_func,
*args,
every=None,
normalize_every=False,
split_every=True,
start_time=None,
end_time=None,
lookback_period=None,
start=None,
end=None,
exact_start=False,
fixed_start=False,
closed_start=True,
closed_end=False,
add_start_delta=None,
add_end_delta=None,
kind=None,
skip_not_found=True,
index_ranges=None,
index_loc=None,
rescale_to=None,
alloc_wait=1,
parameterizer_cls=None,
param_search_kwargs=None,
name_tuple_to_str=None,
group_configs=None,
pre_group_func=None,
splitter_cls=None,
eval_id=None,
jitted_loop=False,
jitted=None,
chunked=None,
template_context=None,
group_execute_kwargs=None,
execute_kwargs=None,
random_subset=None,
clean_index_kwargs=None,
wrapper_kwargs=None,
**kwargs
)
Generate allocations from an optimization function.
Generates date ranges, performs optimization on the subset of data that belongs to each date range, and allocates at the end of each range.
This is a parameterized method that allows testing multiple combinations on most arguments. First, it checks whether any of the arguments is wrapped with Param and combines their values. It then combines them over group_configs, if provided. Before execution, it additionally processes the group config using pre_group_func.
It then resolves the date ranges, either using the ready-to-use index_ranges or by passing all the arguments ranging from every to jitted to ArrayWrapper.get_index_ranges(). The optimization function optimize_func is then called on each date range by first substituting any templates found in *args and **kwargs. To forward any reserved arguments such as jitted to the optimization function, specify their names in forward_args and forward_kwargs.
Note
Make sure to use vectorbt's own templates to select the current date range (available as index_slice in the context mapping) from each array.
If jitted_loop is True, see optimize_meta_nb(). Otherwise, must take template-substituted *args and **kwargs, and return an array or dictionary with asset allocations (also empty).
Note
When jitted_loop is True and in case of multiple groups, use templates to substitute by the current group index (available as group_idx in the context mapping).
All allocations of all groups are stacked into one big 2-dim array where columns are assets and rows are allocations. Furthermore, date ranges are used to fill a record array of type AllocRanges that acts as an indexer for allocations. For example, the field col stores the group index corresponding to each allocation. Since this record array does not hold any information on assets themselves, it has its own wrapper that holds groups instead of columns, while the wrapper of the PortfolioOptimizer instance contains regular columns grouped by groups.
Usage
- Allocate once:
>>> from vectorbtpro import *
>>> data = vbt.YFData.pull(
... ["MSFT", "AMZN", "AAPL"],
... start="2010-01-01",
... end="2020-01-01"
... )
>>> close = data.get("Close")
>>> def optimize_func(df):
... sharpe = df.mean() / df.std()
... return sharpe / sharpe.sum()
>>> df_arg = vbt.RepEval("close.iloc[index_slice]", context=dict(close=close))
>>> pfo = vbt.PortfolioOptimizer.from_optimize_func(
... close.vbt.wrapper,
... optimize_func,
... df_arg,
... end="2015-01-01"
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
alloc_group Date
group 2015-01-02 00:00:00+00:00 0.402459 0.309351 0.288191
- Allocate every first date of the year:
>>> pfo = vbt.PortfolioOptimizer.from_optimize_func(
... close.vbt.wrapper,
... optimize_func,
... df_arg,
... every="AS-JAN"
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
alloc_group Date
group 2011-01-03 00:00:00+00:00 0.480693 0.257317 0.261990
2012-01-03 00:00:00+00:00 0.489893 0.215381 0.294727
2013-01-02 00:00:00+00:00 0.540165 0.228755 0.231080
2014-01-02 00:00:00+00:00 0.339649 0.273996 0.386354
2015-01-02 00:00:00+00:00 0.350406 0.418638 0.230956
2016-01-04 00:00:00+00:00 0.332212 0.141090 0.526698
2017-01-03 00:00:00+00:00 0.390852 0.225379 0.383769
2018-01-02 00:00:00+00:00 0.337711 0.317683 0.344606
2019-01-02 00:00:00+00:00 0.411852 0.282680 0.305468
- Specify index ranges manually:
>>> pfo = vbt.PortfolioOptimizer.from_optimize_func(
... close.vbt.wrapper,
... optimize_func,
... df_arg,
... index_ranges=[
... (0, 30),
... (30, 60),
... (60, 90)
... ]
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
alloc_group Date
group 2010-02-16 00:00:00+00:00 0.340641 0.285897 0.373462
2010-03-30 00:00:00+00:00 0.596392 0.206317 0.197291
2010-05-12 00:00:00+00:00 0.437481 0.283160 0.279358
- Test multiple combinations of one argument:
>>> pfo = vbt.PortfolioOptimizer.from_optimize_func(
... close.vbt.wrapper,
... optimize_func,
... df_arg,
... every="AS-JAN",
... start="2015-01-01",
... lookback_period=vbt.Param(["3MS", "6MS"])
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
lookback_period Date
3MS 2016-01-04 00:00:00+00:00 0.282725 0.234970 0.482305
2017-01-03 00:00:00+00:00 0.318100 0.269355 0.412545
2018-01-02 00:00:00+00:00 0.387499 0.236432 0.376068
2019-01-02 00:00:00+00:00 0.575464 0.254808 0.169728
6MS 2016-01-04 00:00:00+00:00 0.265035 0.198619 0.536346
2017-01-03 00:00:00+00:00 0.314144 0.409020 0.276836
2018-01-02 00:00:00+00:00 0.322741 0.282639 0.394621
2019-01-02 00:00:00+00:00 0.565691 0.234760 0.199549
- Test multiple cross-argument combinations:
>>> pfo = vbt.PortfolioOptimizer.from_optimize_func(
... close.vbt.wrapper,
... optimize_func,
... df_arg,
... every="AS-JAN",
... group_configs=[
... dict(start="2015-01-01"),
... dict(start="2019-06-01", every="MS"),
... dict(end="2014-01-01")
... ]
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
group_config Date
0 2016-01-04 00:00:00+00:00 0.332212 0.141090 0.526698
2017-01-03 00:00:00+00:00 0.390852 0.225379 0.383769
2018-01-02 00:00:00+00:00 0.337711 0.317683 0.344606
2019-01-02 00:00:00+00:00 0.411852 0.282680 0.305468
1 2019-07-01 00:00:00+00:00 0.351461 0.327334 0.321205
2019-08-01 00:00:00+00:00 0.418411 0.249799 0.331790
2019-09-03 00:00:00+00:00 0.400439 0.374044 0.225517
2019-10-01 00:00:00+00:00 0.509387 0.250497 0.240117
2019-11-01 00:00:00+00:00 0.349983 0.469181 0.180835
2019-12-02 00:00:00+00:00 0.260437 0.380563 0.359000
2 2012-01-03 00:00:00+00:00 0.489892 0.215381 0.294727
2013-01-02 00:00:00+00:00 0.540165 0.228755 0.231080
2014-01-02 00:00:00+00:00 0.339649 0.273997 0.386354
- Use Numba-compiled loop:
>>> @njit
... def optimize_func_nb(i, from_idx, to_idx, close):
... mean = vbt.nb.nanmean_nb(close[from_idx:to_idx])
... std = vbt.nb.nanstd_nb(close[from_idx:to_idx])
... sharpe = mean / std
... return sharpe / np.sum(sharpe)
>>> pfo = vbt.PortfolioOptimizer.from_optimize_func(
... close.vbt.wrapper,
... optimize_func_nb,
... np.asarray(close),
... index_ranges=[
... (0, 30),
... (30, 60),
... (60, 90)
... ],
... jitted_loop=True
... )
>>> pfo.allocations
symbol MSFT AMZN AAPL
Date
2010-02-17 00:00:00+00:00 0.336384 0.289598 0.374017
2010-03-31 00:00:00+00:00 0.599417 0.207158 0.193425
2010-05-13 00:00:00+00:00 0.434084 0.281246 0.284670
Hint
There is no big reason of using the Numba-compiled loop, apart from when having to rebalance many thousands of times. Usually, using a regular Python loop and a Numba-compiled optimization function suffice.
from_pypfopt class method¶
PortfolioOptimizer.from_optimize_func() applied on pypfopt_optimize().
If a wrapper is not provided, parses the wrapper from prices or returns, if provided.
from_random class method¶
Generate random allocations.
Uses PortfolioOptimizer.from_allocate_func().
Uses random_allocate_func_nb() and a Numba-compiled loop.
from_riskfolio class method¶
PortfolioOptimizer.from_optimize_func() applied on Riskfolio-Lib.
from_uniform class method¶
Generate uniform allocations.
Uses PortfolioOptimizer.from_allocate_func().
from_universal_algo class method¶
PortfolioOptimizer.from_universal_algo(
algo,
S=None,
n_jobs=1,
log_progress=False,
valid_only=True,
nonzero_only=True,
unique_only=True,
wrapper=None,
**kwargs
)
Generate allocations using Universal Portfolios.
S can be any price, while algo must be either an attribute of the package, subclass of universal.algo.Algo, instance of universal.algo.Algo, or instance of universal.result.AlgoResult.
Extracts allocation points using get_alloc_points_nb().
get_allocations method¶
Get a DataFrame with allocation groups concatenated along the index axis.
indexing_func method¶
PortfolioOptimizer.indexing_func(
*args,
wrapper_meta=None,
alloc_wrapper_meta=None,
alloc_records_meta=None,
**kwargs
)
Perform indexing on PortfolioOptimizer.
mean_allocation property¶
Get the mean allocation per column.
metrics class variable¶
Metrics supported by PortfolioOptimizer.
HybridConfig(
start_index=dict(
title='Start Index',
calc_func=<function PortfolioOptimizer.<lambda> at 0x1647842c0>,
agg_func=None,
tags='wrapper'
),
end_index=dict(
title='End Index',
calc_func=<function PortfolioOptimizer.<lambda> at 0x164784360>,
agg_func=None,
tags='wrapper'
),
total_duration=dict(
title='Total Duration',
calc_func=<function PortfolioOptimizer.<lambda> at 0x164784400>,
apply_to_timedelta=True,
agg_func=None,
tags='wrapper'
),
total_records=dict(
title='Total Records',
calc_func='alloc_records.count',
tags='alloc_records'
),
coverage=dict(
title='Coverage',
calc_func='alloc_records.get_coverage',
overlapping=False,
check_alloc_ranges=True,
tags=[
'alloc_ranges',
'coverage'
]
),
overlap_coverage=dict(
title='Overlap Coverage',
calc_func='alloc_records.get_coverage',
overlapping=True,
check_alloc_ranges=True,
tags=[
'alloc_ranges',
'coverage'
]
),
mean_allocation=dict(
title='Mean Allocation',
calc_func='mean_allocation',
post_calc_func=<function PortfolioOptimizer.<lambda> at 0x1647844a0>,
tags='allocations'
)
)
Returns PortfolioOptimizer._metrics, which gets (hybrid-) copied upon creation of each instance. Thus, changing this config won't affect the class.
To change metrics, you can either change the config in-place, override this property, or overwrite the instance variable PortfolioOptimizer._metrics.
plot method¶
PortfolioOptimizer.plot(
column=None,
dropna='head',
line_shape='hv',
plot_rb_dates=None,
trace_kwargs=None,
add_shape_kwargs=None,
add_trace_kwargs=None,
fig=None,
**layout_kwargs
)
Plot allocations.
Args
column:str- Name of the allocation group to plot.
dropna:int- See PortfolioOptimizer.fill_allocations().
line_shape:str- Line shape.
plot_rb_dates:bool-
Whether to plot rebalancing dates.
Defaults to True if there are no more than 20 rebalancing dates.
trace_kwargs:dict- Keyword arguments passed to
plotly.graph_objects.Scatter. add_shape_kwargs:dict- Keyword arguments passed to
fig.add_shapefor rebalancing dates. add_trace_kwargs:dict- Keyword arguments passed to
add_trace. fig:FigureorFigureWidget- Figure to add traces to.
**layout_kwargs- Keyword arguments for layout.
Usage
- Continuing with the examples under PortfolioOptimizer.from_optimize_func():
>>> from vectorbtpro import *
>>> pfo = vbt.PortfolioOptimizer.from_random(
... vbt.ArrayWrapper(
... index=pd.date_range("2020-01-01", "2021-01-01"),
... columns=["MSFT", "AMZN", "AAPL"],
... ndim=2
... ),
... every="MS",
... seed=40
... )
>>> pfo.plot().show()
plots_defaults property¶
Defaults for PlotsBuilderMixin.plots().
Merges PlotsBuilderMixin.plots_defaults and plots from pfopt.
resample method¶
Perform resampling on PortfolioOptimizer.
row_stack class method¶
Stack multiple PortfolioOptimizer instances along rows.
Uses ArrayWrapper.row_stack() to stack the wrappers.
run_allocation_group class method¶
PortfolioOptimizer.run_allocation_group(
wrapper,
group_configs,
group_index,
group_idx,
pre_group_func=None
)
Run an allocation group.
run_optimization_group class method¶
PortfolioOptimizer.run_optimization_group(
wrapper,
group_configs,
group_index,
group_idx,
pre_group_func=None,
silence_warnings=False
)
Run an optimization group.
simulate method¶
Run Portfolio.from_optimizer() on this instance.
stats_defaults property¶
Defaults for StatsBuilderMixin.stats().
Merges StatsBuilderMixin.stats_defaults and stats from pfopt.
subplots class variable¶
Subplots supported by PortfolioOptimizer.
HybridConfig(
alloc_ranges=dict(
title='Allocation Ranges',
plot_func='alloc_records.plot',
check_alloc_ranges=True,
tags='alloc_ranges'
),
plot=dict(
title='Allocations',
plot_func='plot',
tags='allocations'
)
)
Returns PortfolioOptimizer._subplots, which gets (hybrid-) copied upon creation of each instance. Thus, changing this config won't affect the class.
To change subplots, you can either change the config in-place, override this property, or overwrite the instance variable PortfolioOptimizer._subplots.
pfopt_func_dict class¶
Dict that contains optimization functions as keys.
Keys can be functions themselves, their names, or _def for the default value.
Superclasses
- Comparable
- Pickleable
- Prettified
builtins.dict- pdict
Inherited members
- Pickleable.decode_config()
- Pickleable.decode_config_node()
- Pickleable.dumps()
- Pickleable.encode_config()
- Pickleable.encode_config_node()
- Pickleable.file_exists()
- Pickleable.getsize()
- Pickleable.load()
- Pickleable.loads()
- Pickleable.modify_state()
- Pickleable.resolve_file_path()
- Pickleable.save()
- pdict.equals()
- pdict.load_update()
- pdict.prettify()
- pdict.rec_state