liveBook · Manning

Do-not-increase-deviation constraint

As explained in Build a Robo-Advisor with Python (From Scratch)

A do-not-increase-deviation constraint prevents an optimized position from being farther from its target than the current position.

A do-not-increase-deviation constraint prevents optimized positions from moving farther from their target weights than the current portfolio. It blocks purchases in currently overweight assets and sales in currently underweight assets. section 13.3.2

AGG below target

Suppose a target portfolio holds $4,000 of AGG while the current portfolio holds $3,800. The constraint prevents selling AGG, imposing a lower bound of $3,800 on its optimized position. section 13.3.2

The implementation aggregates current holdings by ticker, calculates target dollar positions from target weights and total investment value, then adds asset-level buy or sell restrictions based on each deviation. section 13.3.2

Listing 13.6 DoNotIncreaseDeviationConstraint
class DoNotIncreaseDeviationConstraint(Constraint):

    def __init__(self,
                 target_weights: pd.Series):
        """ Constraint that prohibits buying in currently overweight
        assets and selling in currently underweight assets """
        self.target_weights = target_weights

    def generate_constraint(self, date, holdings, variables, port_info):

        all_assets = variables['buys'].keys()
        current_port = holdings[['ticker', 'value']]. \   #1
            groupby(['ticker']). \
            sum()['value']. \
            reindex(list(all_assets)). \
            fillna(0.0)

        target_port = self.target_weights * port_info['investment_value']
        cons = []
        for asset in all_assets:
            if current_port[asset] >= target_port[asset]:
                cons.append(variables['buys'][asset] == 0)

            if asset not in variables['sells']:
                continue

            if current_port[asset] <= target_port[asset]:
                for sell in variables['sells'][asset].values():
                    cons.append(sell == 0)

        return cons        

Inspect the Python constraint class and its portfolio-level restriction logic. section 13.3.2

How Build a Robo-Advisor with Python (From Scratch) covers it

How deep it goes
Implements current-position aggregation, target-dollar calculation, and asset-level restrictions, but omits broader optimizer behavior.
Examples
Uses a $10,000 portfolio with a $4,000 AGG target and a current AGG position of $3,800.
What it assumes
Assumes target weights, current holdings, asset values, and an investment value are available.
What you can do after
Configure a rebalancing optimizer to avoid selling underweight assets or buying overweight assets.
How it connects
Connects portfolio rebalancing with target weights, portfolio positions, MaxDeviationConstraint, and DoNotTradePastTargetConstraint.

This explainer was generated by Manning's knowledge graph from the text of Build a Robo-Advisor with Python (From Scratch). The section links point to the parts of the book it draws on.