Execution

Key Concepts

Introduction

The Execution model receives an array of risk-adjusted PortfolioTarget objects from the Risk Management model 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 SetExecution method.

SetExecution(new ImmediateExecutionModel()); 
self.SetExecution(ImmediateExecutionModel()) 

To view all the pre-built Execution models, see Supported Models.

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 and is responsible for reaching the target portfolio as efficiently as possible. The Portfolio Construction model creates the PortfolioTarget objects, the Risk Management model may adjust them, and then the Execution model places the orders to fulfill them.

 // Basic Execution Model Scaffolding Structure Example
class MyExecutionModel : ExecutionModel {

   // Fill the supplied portfolio targets efficiently.
   public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
   {
      // NOP
   }

   //  Optional: Securities changes event for handling new securities.
   public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
   {
        // Security additions and removals are pushed here.
        // This can be used for setting up algorithm state.
        // changes.AddedSecurities
        // changes.RemovedSecurities
   }
}
# 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 OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        # Security additions and removals are pushed here.
        # This can be used for setting up algorithm state.
        # changes.AddedSecurities
        # changes.RemovedSecurities
        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:

PropertyData TypeDescription
SymbolSymbolAsset to trade
QuantitydecimalfloatNumber of units to hold

To view a full example of an ExecutionModel subclass, see the ImmediateExecutionModelImmediateExecutionModel in the LEAN GitHub repository.

Track Security Changes

The Universe Selection model 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 OnSecuritiesChanged event. In the OnSecuritiesChanged 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 objectcast the Security object to a dynamic object and then save custom members to it.

class MyExecutionModel : ExecutionModel{
    private List<Security> _securities = new List<Security>();

    public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
    {
        foreach (var security in changes.AddedSecurities)
        {               
            // Store and manage Symbol-specific data
            var dynamicSecurity = security as dynamic;
            dynamicSecurity.Sma = SMA(security.Symbol, 20);

            _securities.Add(security);
        }

        foreach (var security in changes.RemovedSecurities)
        {
            if (_securities.Contains(security))
            {
                algorithm.DeregisterIndicator((security as dynamic).Sma);

                _securities.Remove(security);
            }
        }
    }
}
class MyExecutionModel(ExecutionModel):
    securities = []

    def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        for security in changes.AddedSecurities:
            # Store and manage Symbol-specific data
            security.indicator = algorithm.SMA(security.Symbol, 20)
            algorithm.WarmUpIndicator(security.Symbol, security.indicator)

            self.securities.append(security)

        for security in changes.RemovedSecurities:
            if security in self.securities:
                algorithm.DeregisterIndicator(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.

_targetsCollection.Add(portfolioTarget);
self.targets_collection.Add(portfolio_target)

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

_targetsCollection.AddRange(portfolioTargets);
self.targets_collection.AddRange(portfolio_targets)

Check Membership

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

var targetInCollection = _targetsCollection.Contains(portfolioTarget);
target_in_collection = self.targets_collection.Contains(portfolio_target)

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

var symbolInCollection = _targetsCollection.ContainsKey(symbol);
symbol_in_collection = self.targets_collection.ContainsKey(symbol)

To get all the Symbol objects, use the Keys property.

var symbols = _targetsCollection.Keys;
symbols = self.targets_collection.Keys

Access Portfolio Targets

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

var portfolioTarget = _targetsCollection[symbol];
portfolio_target = self.targets_collection[symbol]

To iterate through the PortfolioTargetCollection, call the GetEnumerator method.

var enumerator = _targetsCollection.GetEnumerator();
enumerator = self.targets_collection.GetEnumerator()

To get all the PortfolioTarget objects, use the Values property

var portfolioTargets = _targetsCollection.Values;
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 OrderByMarginImpact method.

foreach (var target in _targetsCollection.OrderByMarginImpact(algorithm))
{
    // Place order
}
for target in self.targets_collection.OrderByMarginImpact(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.

removeSuccessful = _targetsCollection.Remove(symbol);
remove_successful = self.targets_collection.Remove(symbol)

To remove all the PortfolioTarget objects, call the Clear method.

_targetsCollection.Clear();
self.targets_collection.Clear()

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

_targetsCollection.ClearFulfilled(algorithm);
self.targets_collection.ClearFulfilled(algorithm)

Universe Timing Considerations

If the Execution model manages some indicators or consolidators 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 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 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 indicator), the open, high, and low values of the first consolidated bar may be incorrect, causing the initial indicator values to be incorrect.

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: