---
title: "Key Concepts"
description: "The Portfolio Construction model receives Insight objects from the Alpha model and creates PortfolioTarget objects for the Risk Management model."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/34%20Algorithm%20Framework/04%20Portfolio%20Construction/01%20Key%20Concepts
modified: 2026-10-29
index: https://www.quantconnect.com/llms.txt
---

# Key Concepts

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Algorithm Framework > Portfolio Construction > Key Concepts

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

![](https://cdn.quantconnect.com/web/i/docs/algorithm-framework/portfolio-construction.png)The Portfolio Construction model receives `Insight` objects from the Alpha model and creates `PortfolioTarget` objects for the Risk Management model. A `PortfolioTarget` provides the number of units of an asset to hold.

## Set Models

To set a Portfolio Construction model, in the `initialize` method, call the `set_portfolio_construction` method.

```python
# Create PortfolioTarget objects to form an equal weighted portfolio based on the Insight objects.
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
```

To view all the pre-built Portfolio Construction models, see [Supported Models](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/supported-models.md).

## Model Structure

Portfolio Construction models should extend the `PortfolioConstructionModel` class or one of the [supported models](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/supported-models.md). Extensions of the `PortfolioConstructionModel` class should implement the `create_targets` method, which receives an array of `Insight` objects from the Alpha model at every [time step](https://www.quantconnect.com/docs/v2/writing-algorithms/key-concepts/time-modeling/timeslices.md) and returns an array of `PortfolioTarget` objects. The Portfolio Construction model seeks to answer the question, "how many units should I buy based on the insight predictions I've been presented?".

If you don't override the `create_targets` method, the base class implementation calls the model's `is_rebalance_due`, `determine_target_percent`, and `get_target_insights` helper methods. The `get_target_insights` method, in turn, calls the model's `should_create_target_for_insight` method. You can override any of these helper methods. If you don't override the `create_targets` method from the `PortfolioConstructionModel` class, your class must at least override the `determine_target_percent` method.

```python
 # Portfolio construction scaffolding class; basic method arguments.
class MyPortfolioConstructionModel(PortfolioConstructionModel):
    # Create list of PortfolioTarget objects from Insights.
    def create_targets(self, algorithm: QCAlgorithm, insights: List[Insight]) -> List[PortfolioTarget]:
        return super().create_targets(algorithm, insights)

    # Determine if the portfolio should rebalance based on the provided rebalancing function.
    def is_rebalance_due(self, insights: List[Insight], algorithmUtc: datetime) -> bool:
        return super().is_rebalance_due(insights, algorithmUtc)

    # Determine the target percent for each insight.
    def determine_target_percent(self, activeInsights: List[Insight]) -> dict[Insight, float]:
        return {}

    # Get the target insights to calculate a portfolio target percent.
    # They will be piped to the determine_target_percent method.
    def get_target_insights(self) -> List[Insight]:
        return super().get_target_insights()

    # Determine if the portfolio construction model should create a target for this insight.
    def should_create_target_for_insight(self, insight: Insight) -> bool:
        return super().should_create_target_for_insight(insight)

    # Track universe changes.
    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        super().on_securities_changed(algorithm, changes)
```

The Portfolio Construction model should [remove expired insights](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager.md#08-Remove-Insights) from the [Insight Manager](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager.md). The `create_targets` definition of the base `PortfolioConstructionModel` class already removes them during each rebalance. Therefore, if you override the `create_targets` method and don't call the `create_targets` definition of the base class, your new method definition should remove expired insights from the Insight Manager.

The model should also remove all a security's insights from the Insight Manager when the security is removed from the [universe](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/key-concepts.md). The `on_securities_changed` definition of the base `PortfolioConstructionModel` class already does this. Therefore, if you override the `on_securities_changed` method and don't call the `on_securities_changed` definition of the base class, your new method definition should remove the security's insights from the Insight Manager.

The `algorithm` argument that the methods receive is an instance of the base `QCAlgorithm` class, not your subclass of it.

You may use the `PortfolioBias` enumeration in the definition of Portfolio Construction model methods. The `PortfolioBias` enumeration has the following members:

*Members of `QuantConnect.Algorithm.Framework.Portfolio.PortfolioBias` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Algorithm.Framework.Portfolio.PortfolioBias&language=python*

To view a full example of a `PortfolioConstructionModel` subclass, see the [EqualWeightingPortfolioConstructionModel](https://github.com/QuantConnect/Lean/blob/master/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py) in the LEAN GitHub repository.

## Multi-Alpha Algorithms

If you add [multiple Alpha models](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/alpha/key-concepts.md#03-Multi-Alpha-Algorithms), each Alpha model receives the current slice in the order that you add the Alphas. The combined stream of Insight objects is passed to the Portfolio Construction model.

Each Portfolio Construction model has a unique method to combine Insight objects. The base `PortfolioConstructionModel` that most PCM's inherit from doesn't combine information from Insight objects with the same `Symbol` - but just gets the most recent active insight. To combine the active insights differently, override the `get_target_insights`, and return all active insights. The `determine_target_percent` method implements the combination criteria and determines the target for each `Symbol`.

```python
# Implement MultipleAlphaPortfolioConstructionModel to handle and utilize insights from multiple Alpha models.
# The get_target_insights method retrieves current active insights, and determine_target_percent allocates portfolio weights accordingly for integrating and balancing multiple Alpha signals within the portfolio.
class MultipleAlphaPortfolioConstructionModel(PortfolioConstructionModel):
    def get_target_insights(self) -> List[Insight]:
        return self.algorithm.insights.get_active_insights(self.algorithm.utc_time)

    def determine_target_percent(self, activeInsights: List[Insight]) -> dict[Insight, float]:
        return {}
```

## Portfolio Targets

The Portfolio Construction model returns `PortfolioTarget` objects, which are passed to the Risk Management model.

To create a `PortfolioTarget` object based on a quantity, pass the `Symbol` and quantity to the `PortfolioTarget` constructor.

```python
# Create a new portfolio target for 1200 IBM shares.
target = PortfolioTarget("IBM", 1200)
```

To create a `PortfolioTarget` object based on a portfolio weight, call the `percent` method. This method is only available for margin accounts.

```python
# Calculate target equivalent to 10% of portfolio value
target = PortfolioTarget.percent(algorithm, "IBM", 0.1)
```

To include more information in the `PortfolioTarget` object, pass a `tag` argument to the constructor or the `percent` method.

The `create_targets` method of your Portfolio Construction model must return an array of `PortfolioTarget` objects.

```python
return [ PortfolioTarget("IBM", 1200) ]
```

## Portfolio Target Collection

The `PortfolioTargetCollection` class is a helper class to manage `PortfolioTarget` objects. The class manages an internal dictionary that has the security `Symbol` as the key and a `PortfolioTarget` as the value.

### Add Portfolio Targets

To add a `PortfolioTarget` to the `PortfolioTargetCollection`, call the `add` method.

```python
self.targets_collection.add(portfolio_target)
```

To add a list of `PortfolioTarget` objects, call the `add_range` method.

```python
self.targets_collection.add_range(portfolio_targets)
```

### Check Membership

To check if a `PortfolioTarget` exists in the `PortfolioTargetCollection`, call the `contains` method.

```python
target_in_collection = self.targets_collection.contains(portfolio_target)
```

To check if a Symbol exists in the `PortfolioTargetCollection`, call the `contains_key` method.

```python
symbol_in_collection = self.targets_collection.contains_key(symbol)
```

To get all the Symbol objects, use the `keys` property.

```python
symbols = self.targets_collection.keys
```

### Access Portfolio Targets

To access the `PortfolioTarget` objects for a Symbol, index the `PortfolioTargetCollection` with the Symbol.

```python
portfolio_target = self.targets_collection[symbol]
```

To iterate through the `PortfolioTargetCollection`, call the `get_enumerator` method.

```python
enumerator = self.targets_collection.get_enumerator()
```

To get all the `PortfolioTarget` objects, use the `values` property

```python
portfolio_targets = self.targets_collection.values
```

### Order Portfolio Targets by Margin Impact

To get an enumerable where position reducing orders are executed first and the remaining orders are executed in decreasing order value, call the `order_by_margin_impact` method.

```python
for target in self.targets_collection.order_by_margin_impact(algorithm):
    # Place order
```

This method won't return targets for securities that have no data yet. This method also won't return targets for which the sum of the current holdings and open orders quantity equals the target quantity.

### Remove Portfolio Targets

To remove a `PortfolioTarget` from the `PortfolioTargetCollection`, call the `remove` method.

```python
remove_successful = self.targets_collection.remove(symbol)
```

To remove all the `PortfolioTarget` objects, call the `clear` method.

```python
self.targets_collection.clear()
```

To remove all the `PortfolioTarget` objects that have been fulfilled, call the `clear_fulfilled` method.

```python
self.targets_collection.clear_fulfilled(algorithm)
```

## Rebalance Frequency

If you use a Portfolio Construction model that is a subclass of the `PortfolioConstructionModel` class, you can set the rebalancing frequency of the model with a function. The rebalancing function receives the Coordinated Universal Time (UTC) of the algorithm and should return the next rebalance UTC time or `None`. If the function returns `None`, the model doesn't rebalance unless the [rebalance settings](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts.md#08-Rebalance-Settings) trigger a rebalance. For a full example of a custom rebalance function, see the [PortfolioRebalanceOnCustomFuncRegressionAlgorithm](https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/PortfolioRebalanceOnCustomFuncRegressionAlgorithm.py).

If you use a Portfolio Construction model with the following characteristics, you can also set the rebalancing frequency of the model with a `timedelta`, `Resolution`, or [DateRules](https://www.quantconnect.com/docs/v2/writing-algorithms/scheduled-events.md#04-Date-Rules):

- The model is a subclass of the `EqualWeightingPortfolioConstructionModel` class.
- The model constructor calls the `EqualWeightingPortfolioConstructionModel` constructor.
- The model doesn't override the `create_targets` method.

To check which of the pre-built Portfolio Construction models support this functionality, see [Supported Models](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/supported-models.md).

## Rebalance Settings

By default, portfolio construction models create `PortfolioTarget` objects to rebalance the portfolio when any of the following events occur:

- The model's rebalance function signals it's time to rebalance
- The Alpha model emits new insights
- The universe changes

To disable rebalances when the Alpha model emits insights or when insights expire, set `rebalance_portfolio_on_insight_changes` to false.

```python
# Disable automatic portfolio rebalancing upon insight change, allowing for manual control over when portfolio adjustments are made based on insights.
self.settings.rebalance_portfolio_on_insight_changes = False
```

To disable rebalances when security changes occur, set `rebalance_portfolio_on_security_changes` to false.

```python
# Disable automatic portfolio rebalancing upon security change, allowing for manual control over when portfolio adjustments are made based on security additions or removals.
self.settings.rebalance_portfolio_on_security_changes = False
```

## Portfolio Optimizer Structure

Some portfolio construction models contain an optimizer that accepts the historical returns of each security and returns a list of optimized portfolio weights. Portfolio optimizer models must implement the `IPortfolioOptimizer` interface, which has an `optimize` method.

```python
# Implement an equal-weighted portfolio optimizer to assign equal weights to all securities, providing basic diversification to reduce risk compared to a concentrated portfolio.
class MyPortfolioOptimizer:

    def optimize(self, historicalReturns: pd.DataFrame, expectedReturns: pd.Series = None, covariance: pd.DataFrame = None) -> pd.Series:
        # Create weights
        #  For example, equal-weighting:
        num_assets = historical_returns.shape[1]
        weights = [1/num_assets] * num_assets

        return weights
```

The following table describes the arguments the `optimize` method accepts:

| Argument | Data Type | Description | Default Value |
| --- | --- | --- | --- |
| `historical_returns` | `DataFrame` | Matrix of historical returns where each column represents a security and each row returns for the given date/time (size: K x N) |  |
| `expected_returns` | `Series` | Array of double with the portfolio annualized expected returns (size: K x 1) | `None` |
| `covariance` | `DataFrame` | Multi-dimensional array of double with the portfolio covariance of annualized returns (size: K x K) | `None` |

The method should return a K x 1 array of double objects that represent the portfolio weights.

To view all the pre-built portfolio optimization algorithms, see [Supported Optimizers](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/supported-optimizers.md).

To view a full example of an `IPortfolioOptimizer` implementation, see the [MaximumSharpeRatioPortfolioOptimizer](https://github.com/QuantConnect/Lean/blob/master/Algorithm.Framework/Portfolio/MaximumSharpeRatioPortfolioOptimizer.py) in the LEAN GitHub repository.

If you define a custom optimizer and want to use it as the `optimizer` argument for one of the [pre-built Portfolio Construction models](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/supported-models.md), import the Python version of the Portfolio Construction model into your project file. For example, to pair your optimizer with the [Black Litterman Optimization Model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/supported-models.md#09-Black-Litterman-Optimization-Model), add the following line:

```python
from Portfolio.BlackLittermanOptimizationPortfolioConstructionModel import BlackLittermanOptimizationPortfolioConstructionModel
```

## Track Security Changes

The [Universe Selection model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/key-concepts.md) may select a dynamic universe of assets, so you should not assume a fixed set of assets in the Portfolio Construction model. When the Universe Selection model adds and removes assets from the universe, it triggers an `on_securities_changed` event. In the `on_securities_changed` event handler, you can initialize the security-specific state or load any history required for your Portfolio Construction model. If you need to save data for individual securities, add custom members to the respective `Security` object.

```python
class MyPortfolioConstructionModel(PortfolioConstructionModel):
    _securities = []

    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        super().on_securities_changed(algorithm, changes)
        for security in changes.added_securities:
            # Store and manage Symbol-specific data
            security.indicator = algorithm.sma(security.symbol, 20)
            algorithm.warm_up_indicator(security.symbol, security.indicator)

            self._securities.append(security)

        for security in changes.removed_securities:
            if security in self.securities:
                algorithm.deregister_indicator(security.indicator)
                self._securities.remove(security)
```

## Universe Timing Considerations

If the Portfolio Construction model manages some indicators or [consolidators](https://www.quantconnect.com/docs/v2/writing-algorithms/consolidating-data/getting-started.md) for securities in the universe and the universe selection runs during the indicator sampling period or the consolidator aggregation period, the indicators and consolidators might be missing some data. For example, take the following scenario:

- The security resolution is minute
- You have a consolidator that aggregates the security data into daily bars to update the indicator
- The universe selection runs at noon

In this scenario, you create and [warm-up the indicator](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/manual-indicators.md#06-Warm-Up-Indicators) at noon. Since it runs at noon, the history request that gathers daily data to warm up the indicator won't contain any data from the current day and the consolidator that updates the indicator also won't aggregate any data from before noon. This process doesn't cause issues if the indicator only uses the close price to calculate the indicator value (like the [simple moving average](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/simple-moving-average.md) indicator) because the first consolidated bar that updates the indicator will have the correct close price. However, if the indicator uses more than just the close price to calculate its value (like the [True Range](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/true-range.md) indicator), the open, high, and low values of the first consolidated bar may be incorrect, causing the initial indicator values to be incorrect.

## Examples

The following examples demonstrate common practices for implementing the framework portfolio construction model.

### Example 1: All-Weather Portfolio

The following algorithm uses `RiskParityPortfolioConstructionModel` to construct an all-weather portfolio that dissipates 1-year daily variance equally among ETFs that represent different asset classes, including stock market (SPY), bond (TLT), gold (GLD), oil (USO), and agricultural(DBA).

```python
class FrameworkPortfolioConstructionModelAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)

        # Add a universe of selected lists of ETFs that represent various asset classes.
        etfs = [
            Symbol.create("SPY", SecurityType.EQUITY, Market.USA),  # stock market
            Symbol.create("TLT", SecurityType.EQUITY, Market.USA),  # bond
            Symbol.create("GLD", SecurityType.EQUITY, Market.USA),  # gold
            Symbol.create("USO", SecurityType.EQUITY, Market.USA),  # oil
            Symbol.create("DBA", SecurityType.EQUITY, Market.USA)   # agricultural
        ]
        self.add_universe_selection(ManualUniverseSelectionModel(etfs))
        # Emit insights for all selected ETFs.
        self.add_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(1)))# Using risk parity PCM to construct an all-weather portfolio to dissipate risk.
        self.set_portfolio_construction(RiskParityPortfolioConstructionModel())
```

### Example 2: Protective Position

The following algorithm implements an equal-weighting portfolio on the given insights. To hedge catastrophic downside risk, each position would order a protective call/put 10% OTM.

```python
class FrameworkPortfolioConstructionModelAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(1000000)

        # Set to raw data normalization for a strike and the underlying price fair comparison.
        self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW
        # Add a universe of the market representative SPY.
        spy = [Symbol.create("SPY", SecurityType.EQUITY, Market.USA)]
        self.add_universe_selection(ManualUniverseSelectionModel(spy))
        # Emit insights for all selected ETFs. Each insight lasts for 7 days for the option hedge expiry.
        self.add_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(7)))
        # Using a custom PCM to control the order size and hedge option position ordering with a 10% OTM threshold.
        self.set_portfolio_construction(ProtectivePositionPortfolioConstructionModel(0.1))

class ProtectivePositionPortfolioConstructionModel(PortfolioConstructionModel):
    def __init__(self, threshold: float) -> None:
        self.threshold = threshold

    def create_targets(self, algorithm: QCAlgorithm, insights: List[Insight]) -> List[PortfolioTarget]:
        targets = []
        if not insights:
            return targets

        # Equally invest in each position group to dissipate the capital risk.
        fund_per_insight = algorithm.portfolio.total_portfolio_value / len(insights)

        for insight in insights:
            underlying = insight.symbol
            # Hedge position option right: long position should purchase OTM put, while short should purchase OTM call.
            right = OptionRight.PUT if insight.direction == InsightDirection.UP else OptionRight.CALL
            threshold = 1 - self.threshold if insight.direction == InsightDirection.UP else 1 + self.threshold

            # Select a protective option position that expires in 7 days.
            hedge = sorted([x for x in algorithm.option_chain(underlying) if x.expiry < algorithm.time + timedelta(7) and x.right == right],
                           key=lambda x: (x.expiry, -abs(x.strike - x.underlying_last_price * threshold)),
                           reverse=True)[0]
            # Request the protective position data for trading.
            hedge_symbol = algorithm.add_option_contract(hedge).symbol

            # Each insight will be ordered by the position group of the underlying and the hedging option positions.
            contract_multiplier = algorithm.securities[underlying].symbol_properties.contract_multiplier
            quantity = fund_per_insight // (contract_multiplier * (hedge.bid_price + hedge.underlying_last_price))
            targets.append(PortfolioTarget(underlying, int(quantity * contract_multiplier)))
            targets.append(PortfolioTarget(hedge_symbol, int(quantity)))

        return targets
```
