PortfolioOptimizer
PortfolioOptimizer#
-
class sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Simplified wrapper around
Optimizer
to allow quick and easy portfolio optimization with standard constraints.value
args set the relative importance of optimization objectives.Constraint
value
args are only relevant in the case of multiple constraints being applied in which casevalue
adjusts the relative weighting of the constraint.Some requirements accept
dict
orpd.Series
for benchmark weights keyed by asset name. If you wish to use this optimization in aSignalStrategy
allocation then this data must also be time aware. Hence, these arguments can also be passed as apd.DataFrame
with index as datetimes and columns as asset names.Example usage:
import numpy as np import pandas as pd import sigtech.framework as sig np.random.seed(12) n_days = 252 * 10 stock_returns = pd.DataFrame({ 'Stock_A': np.random.normal(loc=+1.00e-3, scale=0.01, size=n_days), # Average performance 'Stock_B': np.random.normal(loc=+1.01e-3, scale=0.02, size=n_days), # Positive return but volatile 'Stock_C': np.random.normal(loc=-1.01e-3, scale=0.01, size=n_days) # Negative performance }) # Construct the optimizer and add a mean-variance requirement. base_opt = sig.PortfolioOptimizer() base_opt.require_mean_variance() base_opt.calculate_weights(stock_returns) ''' Stock_A 3.293694 Stock_B 1.327438 Stock_C -6.395387 dtype: float64 ''' # Copy the previous optimizer to retain the mean-variance setup but add long-only requirement. base_opt.copy().require_long_only().calculate_weights(stock_returns) ''' Stock_A 3.332055e+00 Stock_B 1.325735e+00 Stock_C 1.079088e-24 dtype: float64 ''' # Restrict the weights. The above long-only requirement was applied to a copy so is not applicable here. base_opt.copy().require_weight_limit(min_value=-1, max_value=0.5).calculate_weights(stock_returns) ''' Stock_A 0.5 Stock_B 0.5 Stock_C -1.0 dtype: float64 ''' # Apply an L2 penalty. ``value`` is the L2 lambda parameter. base_opt.copy().require_weights_l2_penalty(value=1e-3).calculate_weights(stock_returns) ''' Stock_A 0.297179 Stock_B 0.368629 Stock_C -0.579929 dtype: float64 ''' # CVaR optimization example base_opt = sig.PortfolioOptimizer() base_opt.prefer_lower_cvar(percentile=10) base_opt.require_fully_invested() base_opt.require_long_only() base_opt.calculate_weights(stock_returns) ''' Stock_A 0.489962 Stock_B 0.130832 Stock_C 0.379206 dtype: float64 ''' # Custom objective example import cvxpy base_opt = sig.PortfolioOptimizer() base_opt.prefer_maximum_return() base_opt.prefer_custom(lambda x, data: data['my_var'] * cvxpy.quad_form(x, data['covariance']), my_var=-2) base_opt.calculate_weights(stock_returns) ''' Stock_A 1.647184 Stock_B 0.663855 Stock_C -3.198348 dtype: float64 '''
-
copy() sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Return a copy of this optimizer.
-
signal_strategy_allocation_kwargs() dict
Return the dict for this object to be used in a
SignalStrategy
constructor for theallocation_kwargs
argument.Example usage:
po = PortfolioOptimizer().require_... sig.SignalStrategy( currency='USD', start_date=dtm.date(2020,1,1), rebalance_frequency='1BD', signal_name=signal.name, allocation_function=sig.signal_library_allocation.optimized_allocations, allocation_kwargs=po.signal_strategy_allocation_kwargs() )
-
calculate_weights(instrument_returns: pandas.core.frame.DataFrame)
Perform portfolio optimization over the history in
instrument_returns
.- Parameters
instrument_returns – History of instrument returns used in optimization
- Returns
pd.Series of optimized weights indexed by columns of
instrument_returns
.
-
require_mean_variance(risk_aversion: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Assign the mean-variance optimization requirements to this problem.
- Parameters
risk_aversion – Relative strength of variance minimization to return maximization (larger value means less return).
-
prefer_mean_variance(risk_aversion: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Assign the mean-variance optimization requirements to this problem.
- Parameters
risk_aversion – Relative strength of variance minimization to return maximization (larger value means less return).
-
require_fully_invested() sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the summed weights to equal 1.
-
require_long_short_neutral() sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the summed weights to equal 0.
-
require_label_limits(min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None, label_identifiers: Optional[list] = None, gross_weight: Optional[bool] = False) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the summed weights in a label group to be between
min_value
andmax_value
.
-
require_net_leverage(min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the summed weights to be between
min_value
andmax_value
.
-
prefer_net_leverage(net_leverage: Union[float, int] = 1, value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Prefer the sum of the weights to be near
net_leverage
.This method adds an objective that pulls the net leverage towards the specified value but allows some deviation if other objectives or constraints will not allow an exact fit. This can be useful in turnover controlled fits where we wish to tend to a net leverage value.
-
require_gross_leverage(min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the sum of the absolute-value weights to be between
min_value
andmax_value
.
-
prefer_gross_leverage(gross_leverage: Union[float, int] = 1, value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Prefer the sum of the absolute-value weights to near
gross_leverage
.This method adds an objective that pulls the gross leverage towards the specified value but allows some deviation if other objectives or constraints will not allow an exact fit. This can be useful in turnover controlled fits where we wish to tend to a gross leverage value.
-
prefer_lower_cvar(value: Union[float, int] = 1, percentile: Optional[Union[float, int]] = 5) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Prefer a lower conditional value-at-risk (CVaR).
This method adds an objective that minimizes the negative 5% CVaR. The penalty is specified with
value
, which should be a positive value.Please see the class docs for an example.
- Parameters
value – Weight given to this objective.
percentile – Percentile for the CVaR (Optional, defaults to 5%).
-
require_cvar(min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None, percentile: Optional[Union[float, int]] = 5) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the 5% conditional value-at-risk (CVaR) to be between
min_value
andmax_value
.Note: The bounds are in terms of the loss, for example to set a maximum CVaR corresponding to loss of 5, set the
max_value
to 5.- Parameters
min_value – Lower bound on term (Optional).
max_value – Upper bound on term (Optional).
percentile – Percentile for the CVaR (Optional, defaults to 5%).
-
prefer_lower_var(value: Union[float, int] = 1, percentile: Optional[Union[float, int]] = 5) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Prefer a lower value-at-risk (VaR).
This method adds an objective that minimizes the negative 5% VaR. The penalty is specified with
value
, which should be a positive value.- Parameters
value – Weight given to this objective.
percentile – Percentile for the VaR (Optional, defaults to 5%).
-
require_var(min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None, percentile: Optional[Union[float, int]] = 5) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require the 5% value-at-risk (VaR) to be between
min_value
andmax_value
.Note: The bounds are in terms of the loss, for example to set a maximum VaR corresponding to loss of 5, set the
max_value
to 5.- Parameters
min_value – Lower bound on term (Optional).
max_value – Upper bound on term (Optional).
percentile – Percentile for the VaR (Optional, defaults to 5%).
-
require_weight_limit(min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None, label_identifiers: Optional[tuple] = None) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require all instrument weights are between
min_value
andmax_value
.
-
require_minimum_variance(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Minimize the variance using the full, empirical covariance matrix.
- Parameters
value – Weight modifier given to this constraint.
-
prefer_minimum_variance(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Minimize the variance using the full, empirical covariance matrix.
- Parameters
value – Weight modifier given to this constraint.
-
require_long_only() sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Require that all instrument weights are >= 0.
-
require_target_volatility(pct_vol_target: Union[float, int] = 10) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Set a target for the annualized portfolio percentage volatility (default=10%).
- Parameters
pct_vol_target – Annualised percentage portfolio volatility (std. dev.) target.
-
require_target_return(pct_return_target: Union[float, int] = 10, value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Set a target for the annualized portfolio percentage return (default=10%).
- Parameters
pct_return_target – Annualised percentage portfolio return target.
value – Weight modifier for this objective.
-
prefer_target_return(pct_return_target: Union[float, int] = 10, value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Set a target for the annualized portfolio percentage return (default=10%).
- Parameters
pct_return_target – Annualised percentage portfolio return target.
value – Weight modifier for this objective.
-
require_maximum_return(value: Union[float, int] = 1, expected_returns: Optional[pandas.core.series.Series] = None) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Maximize the expected portfolio return from historical average returns.
- Parameters
value – Weight modifier given to this constraint.
-
prefer_maximum_return(value: Union[float, int] = 1, expected_returns: Optional[pandas.core.series.Series] = None) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Maximize the expected portfolio return from historical average returns.
- Parameters
value – Weight modifier given to this constraint.
-
require_weights_l1_penalty(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Regularize the portfolio weights with an L1 penalty term.
- Parameters
value – Weight modifier given to this constraint.
-
prefer_weights_l1_penalty(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Regularize the portfolio weights with an L1 penalty term.
- Parameters
value – Weight modifier given to this constraint.
-
require_weights_l2_penalty(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Regularize the portfolio weights with an L2 penalty term.
- Parameters
value – Weight modifier given to this constraint.
-
prefer_weights_l2_penalty(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Regularize the portfolio weights with an L2 penalty term.
- Parameters
value – Weight modifier given to this constraint.
-
require_limited_participation(aum: Union[float, int], initial_weights: Union[pandas.core.series.Series, dict], date: datetime.date, instrument_mapping: Optional[Union[pandas.core.series.Series, dict]] = None, max_participation_pct: Union[float, int] = 10) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Limit the weight change of the portfolio instruments to a percent of median daily volume.
This requirement cannot be used with the
SignalStrategy
allocation setup as we need to know the identity of all assets in the portfolio before we can access the MDVs. This requirement can only be used on a step-by -step basis.- Parameters
aum – Portfolio AUM in USD.
initial_weights – Starting weights of portfolio instruments.
date – Date to extract the average daily volume data.
instrument_mapping – Mapping from
initial_weights
keys to SIGTech tickers with.history('Volume')
attribute.max_participation_pct – Maximum allowed participation percent to limit trades.
-
require_limited_weights_turnover(initial_weights: Optional[Union[pandas.core.series.Series, dict]] = None, max_weight_change: Union[float, int] = 0.05) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Limit the weight change of the portfolio instruments to a set value.
- Parameters
initial_weights – Starting weights of portfolio instruments. If unset (
None
, default) we use the internal method to set the previous weights (initially starting from zero). If set, the user controls the starting weights to allow for use outsideSignalStrategy.allocation_function
.max_weight_change – Maximum allowed portfolio weight change.
-
require_minimum_turnover(initial_weights: Optional[Union[pandas.core.series.Series, dict]] = None, value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Add an objective to the optimization to minimize the portfolio turnover.
- Parameters
initial_weights – Starting weights of portfolio instruments. If unset (
None
, default) we use the internal method to set the previous weights (initially starting from zero). If set, the user controls the starting weights to allow for use outsideSignalStrategy.allocation_function
.value – Weight modifier given to this constraint.
-
prefer_minimum_turnover(initial_weights: Optional[Union[pandas.core.series.Series, dict]] = None, value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Add an objective to the optimization to minimize the portfolio turnover.
- Parameters
initial_weights – Starting weights of portfolio instruments. If unset (
None
, default) we use the internal method to set the previous weights (initially starting from zero). If set, the user controls the starting weights to allow for use outsideSignalStrategy.allocation_function
.value – Weight modifier given to this constraint.
-
require_maximum_diversification(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Add an objective to the optimization to maximise the portfolio diversification by minimizing the sum of squared weights.
- Parameters
value – Weight modifier given to this constraint.
-
prefer_maximum_diversification(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Add an objective to the optimization to maximise the portfolio diversification by minimizing the sum of squared weights.
- Parameters
value – Weight modifier given to this constraint.
-
require_tracking_error_upper_bound(ann_te_pct: Union[float, int], benchmark_weights: Union[pandas.core.frame.DataFrame, pandas.core.series.Series, dict]) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Add a constraint to
problem
to match the tracking error tobenchmark_weights
to <ann_te_pct
- Parameters
ann_te_pct – Annualized tracking-error percentage.
benchmark_weights – Key-value pairs of instrument names to benchmark weights.
-
require_active_share_upper_bound(upper_bound: Union[float, int], benchmark_weights: Union[pandas.core.frame.DataFrame, pandas.core.series.Series, dict]) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Limit \(\frac{1}{2}\sum_{i | w_i - w_{BM} | \leq \texttt{upper_bound}\)
- Parameters
upper_bound – Maximum active share.
benchmark_weights – Key-value pairs of instrument names to benchmark weights. A
pd.DataFrame
may also be passed if the optimization is being run through aSignalStrategy
allocation function.
-
require_equal_risk_contribution(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Target equal risk contribution from each asset.
- Parameters
value – Weight modifier given to this constraint.
-
prefer_equal_risk_contribution(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Target equal risk contribution from each asset.
- Parameters
value – Weight modifier given to this constraint.
-
require_inverse_vol_weighting(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer deprecated
Target weights proportional to inverse volatility weighting (ignoring inter asset correlations).
- Parameters
value – Weight modifier given to this constraint.
-
prefer_inverse_vol_weighting(value: Union[float, int] = 1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Target weights proportional to inverse volatility weighting (ignoring inter asset correlations).
- Parameters
value – Weight modifier given to this constraint.
-
prefer_custom(fn, value: Union[float, int] = 1, label: Optional[str] = None, **kwargs) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Add a custom objective term defined in a python string or function, which can contain numpy or cvxpy terms.
The
fn
takes two inputs as args corresponding to the weights array and a dictionary of additional data.The additional data contains the
covariance
,correlation
,`avg_returns
andbase
, together with the extra kwargs entered.Please see the class docs for an example.
- Parameters
fn – String of python code or callable method taking the weights x and a dictionary of data.
value – Weight modifier given to this objective (optional, defaults to 1).
label – string label for this objective (optional).
-
require_custom(fn, min_value: Optional[Union[float, int]] = None, max_value: Optional[Union[float, int]] = None, label: Optional[str] = None, **kwargs) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Add a custom constraint term defined in a python string or function, which can contain numpy or cvxpy terms.
The
fn
takes two inputs as args corresponding to the weights array and a dictionary of additional data.The additional data contains the
covariance
,correlation
,`avg_returns
andbase
, together with the extra kwargs entered.- Parameters
fn – String of python code or callable method taking the weights x and a dictionary of data.
min_value – Lower bound on term (optional).
max_value – Upper bound on term (optional).
label – string label for this objective (optional).
-
add_constraints(constraints: Union[collections.abc.Callable, list[collections.abc.Callable]]) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Apply user supplied constraints to the optimization problem.
- Parameters
constraints – List of or single callable of
PortfolioOptimizer.require_...
methods.
-
require_signal_tolerance(tolerance=0.1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
If using this
PortfolioOptimizer
in aSignalStrategy
allocation function, this method ensures the asset weights track theSignalStrategy
Signal
to within a tolerance expressed in terms of portfolio weight.- Parameters
tolerance – Maximum allowed deviation from
SignalStrategy
Signal
in units of portfolio weight fraction. I.e. iftolerance=0.1
the maximum deviation from the supplied signal is 10% of the portfolio weight.
-
prefer_signal(value=1) sigtech.framework.analytics.optimization.portfolio_optimizer.PortfolioOptimizer
Add an optimization objective to minimize the difference between the asset weights and the supplied
SignalStrategy
Signal
.- Parameters
value – Weight modifier given to this constraint.
-
get_additional_data() Optional[dict]
Format the additional data into the correct dict of dicts format for use in the SignalStrategy allocation optimization.
-
test_solvers(instrument_returns: pandas.core.frame.DataFrame) pandas.core.frame.DataFrame
Perform portfolio optimization using all available solvers and report back which were successful. This method can be used to choose the best optimizer config. for your problem before doing long loops.
- Parameters
instrument_returns – History of instrument returns used in optimization
- Returns
pd.DataFrame
of the fitting test results.
-
objective_function_values() pandas.core.frame.DataFrame
The objective function values for each objective on each date. This allows analysis of relative objective importance over time to ensure all objectives are considered.
-
solved_weights() pandas.core.frame.DataFrame
The optimized weights from this set of solvers. If run through a SignalStrategy allocation optimization this method allows access to the optimized weights rather than the realised ones used in the strategy which evolve with price changes.
-
calculate_matrices()
Evaluate the standard matrices for use in the optimisation.
-
calculate_optimized_weights(initial_guess: Optional[Union[pandas.core.series.Series, numpy.ndarray]] = None)
Construct and try to solve the optimisation objectives with constraints.
The solvers stored in the list
self.solvers
will be used in order to attempt to solve the problem. If no solver is successful, anOptimizationError
will be raised.
-
calculate_optimized_weights_with_fit(initial_guess: Optional[Union[pandas.core.series.Series, numpy.ndarray]] = None, *fit_args, **fit_kwargs)
Fit an empty
FactorExposures
object to allow us to use theOptimizer
functionality without any factors.Usually a
pd.DataFrame
of instrument returns is expected infit_args
.- Parameters
fit_args – Args to pass to
FactorExposures.fit
.fit_kwargs – Kwargs to pass to
FactorExposures.fit
.
-
covariance_df(covariance_array)
Convert an array to DataFrame.
- Parameters
covariance_array – Input array.
- Returns
pandas DataFrame.