---
title: "Key Concepts"
description: "The Execution model receives an array of risk-adjusted targets from the risk model and places trades in the market to satisfy the targets."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/execution/key-concepts
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/execution/key-concepts.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/34%20Algorithm%20Framework/06%20Execution/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 > Execution > 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/execute.png) The Execution model receives an array of risk-adjusted `PortfolioTarget` objects from the [Risk Management model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/risk-management/key-concepts.md) and places trades in the market to satisfy the targets. The Execution model only receives updates to the portfolio target share counts. It doesn't necessarily receive all of the targets at once.

## Set Models

To set an Execution model, in the `initialize` method, call the `set_execution` method.

```python
# Satisify portfolio targets by immediately sending market orders.
self.set_execution(ImmediateExecutionModel())
```

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

## Model Structure

Execution models should extend the `ExecutionModel` class. Extensions of the `ExecutionModel` must implement the `execute` method, which receives an array of `PortfolioTarget` objects at every [time step](https://www.quantconnect.com/docs/v2/writing-algorithms/key-concepts/time-modeling/timeslices.md) and is responsible for reaching the target portfolio as efficiently as possible. The [Portfolio Construction model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts.md) creates the `PortfolioTarget` objects, the [Risk Management model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/risk-management/key-concepts.md) may adjust them, and then the Execution model places the orders to fulfill them.

```python
# Execution Model scaffolding structure example
class MyExecutionModel(ExecutionModel):

    # Fill the supplied portfolio targets efficiently
    def execute(self, algorithm: QCAlgorithm, targets: List[PortfolioTarget]) -> None:
        pass

    # Optional: Securities changes event for handling new securities.
    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        # Security additions and removals are pushed here.
        # This can be used for setting up algorithm state.
        # changes.added_securities
        # changes.removed_securities
        pass

    # Optional: Order event for handling new order events.
    def on_order_event(self, algorithm: QCAlgorithm, order_event: OrderEvent) -> None:
        # Order events are pushed here.
        # This can be used for tracking order status or handling invalid and cancelled orders.
        pass
```

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

The following table describes the properties of the `PortfolioTarget` class that you may access in the Execution model:

| Property | Data Type | Description |
| --- | --- | --- |
| `symbol` | `Symbol` | Asset to trade |
| `quantity` | `float` | Number of units to hold |

To view a full example of an `ExecutionModel` subclass, see the [ImmediateExecutionModel](https://github.com/QuantConnect/Lean/blob/master/Algorithm/Execution/ImmediateExecutionModel.py) in the LEAN GitHub repository.

## 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 Execution 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 Execution model. If you need to save data for individual securities, add custom members to the respective `Security` object.

```python
class MyExecutionModel(ExecutionModel):
    _securities = []

    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        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)
```

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

## Universe Timing Considerations

If the Execution 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 some common practices for implementing the execution model.

### Example 1: Iceberg Execution

The following algorithm simulates an account with large capital that equally holds the most liquid stocks. To hide the footprint and avoid a large market impact that might erode the profit margin, we can set up an iceberg execution system, submitting only 10% of the volume of the bid/ask side order book to the order direction.

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

        # Add a universe of the most liquid stocks since their trend is more capital-supported.
        self.add_universe_selection(QC500UniverseSelectionModel())
        # Emit insights all for selected stocks.
        self.add_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(7)))
        # Equal weighting on each insight to dissipate capital risk evenly.
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())

        # Iceberg ordering to hide traces and avoid market impact.
        # Since quote data will be used, ensure the asset class and resolution are compatible.
        self.set_execution(IcebergExecutionModel(0.1))

class IcebergExecutionModel(ExecutionModel):
    def __init__(self, order_percent_volume: float = 0.1) -> None:
        self.targets_collection = PortfolioTargetCollection()
        # The maximum order size is taken from the order book in percentage.
        self.maximum_order_quantity_percent_volume = order_percent_volume

    def execute(self, algorithm: QCAlgorithm, targets: List[PortfolioTarget]) -> None:
        # update the complete set of portfolio targets with the new targets
        self.targets_collection.add_range(targets)

        if not self.targets_collection.is_empty:
            for target in self.targets_collection.order_by_margin_impact(algorithm):
                symbol = target.symbol

                # Calculate the remaining quantity to be ordered
                unordered_quantity = OrderSizing.get_unordered_quantity(algorithm, target)
                # adjust order size to respect maximum order size based on a percentage of current volume
                order_size = self.get_order_size_for_percent_volume(algorithm.securities[symbol], self.maximum_order_quantity_percent_volume, unordered_quantity)

                if order_size != 0:
                    algorithm.market_order(symbol, order_size)

            self.targets_collection.clear_fulfilled(algorithm)

    def get_order_size_for_percent_volume(self, security: Security, maximum_percent_current_volume: float, desired_order_size: float) -> float:
        # Take N% from the order book according to the order direction.
        max_order_size = maximum_percent_current_volume * (security.bid_size if desired_order_size > 0 else security.ask_size)
        order_size = min(max_order_size, abs(desired_order_size))
        return np.sign(desired_order_size) * OrderSizing.adjust_by_lot_size(security, order_size)
```

### Example 2: More Favorable Than Signal Time

Some algorithms are signaling based on daily close data. However, their orders often emit when the next market opens, making the filling price slip. To avoid this slippage negatively impacting your expectations, we can construct a custom execution model to place orders only when the price is more favorable than the last close price. Although we did not set a time limit in this example, you may consider doing so.

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

        # Add a universe of the most liquid stocks since their trend is more capital-supported.
        self.add_universe_selection(QC500UniverseSelectionModel())
        # Emit insights all for selected stocks.
        self.add_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(7)))
        # Equal weighting on each insight to dissipate capital risk evenly.
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())

        # Only place an order if the price is more favorable than the price at the insight signal.
        self.set_execution(FavorableExecutionModel())

class FavorableExecutionModel(ExecutionModel):
    def __init__(self) -> None:
        self.targets_collection = PortfolioTargetCollection()
        self.lag_price_by_symbol = {}

    def execute(self, algorithm: QCAlgorithm, targets: List[PortfolioTarget]) -> None:
        self.targets_collection.add_range(targets)
        if not self.targets_collection.is_empty:
            for target in self.targets_collection.order_by_margin_impact(algorithm):
                symbol = target.symbol
                security = algorithm.securities[symbol]
                # Calculate the remaining quantity to be ordered
                quantity = OrderSizing.get_unordered_quantity(algorithm, target, security, True)

                if quantity != 0:
                    above_minimum_portfolio = BuyingPowerModelExtensions.above_minimum_order_margin_portfolio_percentage(
                        security.buying_power_model,
                        security,
                        quantity,
                        algorithm.portfolio,
                        algorithm.settings.minimum_order_margin_portfolio_percentage)
                    if above_minimum_portfolio:
                        # Cache the price at signal emission to compare if the current price is more favorable.
                        if not symbol in self.lag_price_by_symbol:
                            history = algorithm.history[TradeBar](symbol, 1, Resolution.DAILY)
                            self.lag_price_by_symbol[symbol] = list(history)[-1].close

                        # Only order if the price is more favorable than the price at signal emission.
                        if (security.price <= self.lag_price_by_symbol[symbol] and quantity > 0)\
                        or (security.price >= self.lag_price_by_symbol[symbol] and quantity < 0):
                            algorithm.market_order(security, quantity)
                    elif not PortfolioTarget.minimum_order_margin_percentage_warning_sent:
                        # will trigger the warning if it has not already been sent
                        PortfolioTarget.minimum_order_margin_percentage_warning_sent = False

            self.targets_collection.clear_fulfilled(algorithm)
```
