Skip to content
New: Star-import, signal unraveling, Python 3.12 support, and more

#
base module

Base functions and classes for portfolio optimization.

## prepare_returns function

    prepare_returns(
        returns,
        nan_to_zero=True,
        dropna_rows=True,
        dropna_cols=True,
        dropna_any=True
    )

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:

    >>> from vectorbtpro import *

    >>> data = vbt.YFData.pull(["MSFT", "AMZN", "KO", "MA"])

100%

    >>> 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_convex` to 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(
        asset_classes,
        columns,
        col_indices=None
    )

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_assets_constraints(
        constraints
    )

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_assets_views(
        views
    )

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(
        constraints
    )

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(
        views
    )

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(
        constraints
    )

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_pypfopt_cov_matrix(
        cov_matrix='ledoit_wolf',
        **kwargs
    )

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_wolf` with 'constant_variance' as shrinkage factor
- 'ledoit_wolf_single_factor': `pypfopt.risk_models.CovarianceShrinkage.ledoit_wolf` with 'single_factor' as shrinkage factor
- 'ledoit_wolf_constant_correlation': `pypfopt.risk_models.CovarianceShrinkage.ledoit_wolf` with 'constant_correlation' as shrinkage factor
- 'oracle_approximating': `pypfopt.risk_models.CovarianceShrinkage.ledoit_wolf` with 'oracle_approximating' as shrinkage factor

Any function is resolved using resolve_pypfopt_func_call().

## resolve_pypfopt_expected_returns function

    resolve_pypfopt_expected_returns(
        expected_returns='mean_historical_return',
        **kwargs
    )

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_pypfopt_func_call(
        pypfopt_func,
        **kwargs
    )

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_pypfopt_optimizer(
        optimizer='efficient_frontier',
        **kwargs
    )

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

    resolve_riskfolio_func_kwargs(
        riskfolio_func,
        unused_arg_names=None,
        func_kwargs=None,
        **kwargs
    )

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_zero` is True or `dropna_any` is 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_zero` is False.

`factors` :`array_like`
  A dataframe that contains the factors.
`port` :`Portfolio` or `HCPortfolio`
  Already initialized portfolio.
`port_cls` :`str` or `type`

  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
`opt_method` :`str` or `callable`

  Optimization method.

  Supports the following values:

  - None or 'optimization': Uses `port.optimization` (where `port` is 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
`stats_methods` :`str` or `sequence` of `str`

  Sequence of stats methods to call before optimization.

  If None, tries to automatically populate the sequence using `opt_method` and `model`. For example, calls `port.assets_stats` if `model="Classic"` is used. Also, if `func_kwargs` is 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 `Portfolio` is used, will use factors constraints if `factors_stats` is used, otherwise assets constraints. If the class `HCPortfolio` is 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_stats` is 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` :`list` of `str`
  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 `w` from the pre-optimization step.
`func_kwargs` :`dict`

  Further keyword arguments by function.

  Can be used to override any arguments from `kwargs` matched 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()

100%

    >>> 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_pfopt_func_kwargs(
        pypfopt_func,
        kwargs=None
    )

Select keyword arguments belonging to `pypfopt_func`.

## PortfolioOptimizer class

    PortfolioOptimizer(
        wrapper,
        alloc_records,
        allocations,
        **kwargs
    )

Class that exposes methods for generating allocations.

Superclasses

- Analyzable
- AttrResolverMixin
- Cacheable
- Chainable
- Comparable
- Configured
- ExtPandasIndexer
- HasSettings
- IndexingBase
- PandasIndexer
- 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()
- 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.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()

### 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

    PortfolioOptimizer.column_stack(
        *objs,
        wrapper_kwargs=None,
        **kwargs
    )

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,
        execute_kwargs=None,
        random_subset=None,
        index_stack_kwargs=None,
        wrapper_kwargs=None,
        show_progress=None,
        pbar_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

    PortfolioOptimizer.from_allocations(
        wrapper,
        allocations,
        **kwargs
    )

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

    PortfolioOptimizer.from_initial(
        wrapper,
        allocations,
        **kwargs
    )

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,
        jitted_loop=False,
        jitted=None,
        chunked=None,
        template_context=None,
        execute_kwargs=None,
        random_subset=None,
        index_stack_kwargs=None,
        wrapper_kwargs=None,
        show_progress=None,
        pbar_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_pypfopt(
        wrapper=None,
        **kwargs
    )

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

    PortfolioOptimizer.from_random(
        wrapper,
        direction='longonly',
        n=None,
        seed=None,
        **kwargs
    )

Generate random allocations.

Uses PortfolioOptimizer.from_allocate_func().

Uses random_allocate_func_nb() and a Numba-compiled loop.

### from_riskfolio class method

    PortfolioOptimizer.from_riskfolio(
        returns,
        wrapper=None,
        **kwargs
    )

PortfolioOptimizer.from_optimize_func() applied on Riskfolio-Lib.

### from_uniform class method

    PortfolioOptimizer.from_uniform(
        wrapper,
        **kwargs
    )

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

    PortfolioOptimizer.get_allocations(
        squeeze_groups=True
    )

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=dict(
            title='Start',
            calc_func=<function PortfolioOptimizer.<lambda> at 0x7ff34bf8b5b0>,
            agg_func=None,
            tags='wrapper'
        ),
        end=dict(
            title='End',
            calc_func=<function PortfolioOptimizer.<lambda> at 0x7ff34bf8b640>,
            agg_func=None,
            tags='wrapper'
        ),
        period=dict(
            title='Period',
            calc_func=<function PortfolioOptimizer.<lambda> at 0x7ff34bf8b6d0>,
            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 0x7ff34bf8b760>,
            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_shape` for rebalancing dates.
`add_trace_kwargs` :`dict`
  Keyword arguments passed to `add_trace`.
`fig` :`Figure` or `FigureWidget`
  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

    PortfolioOptimizer.resample(
        *args,
        **kwargs
    )

Perform resampling on PortfolioOptimizer.

### row_stack class method

    PortfolioOptimizer.row_stack(
        *objs,
        wrapper_kwargs=None,
        **kwargs
    )

Stack multiple PortfolioOptimizer instances along rows.

Uses ArrayWrapper.row_stack() to stack the wrappers.

### simulate method

    PortfolioOptimizer.simulate(
        close,
        **kwargs
    )

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

    pfopt_func_dict(
        *args,
        **kwargs
    )

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

#### Cookie consent

We use cookies to recognize your repeated visits and preferences, as well as to measure the effectiveness of our documentation and whether users find what they're searching for. With your consent, you're helping us to make our documentation better.

- Google Analytics
- GitHub

Manage settings