Portfolio Construction

Key Concepts

Introduction

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 SetPortfolioConstruction method.

SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())

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

Model Structure

Portfolio Construction models should extend the PortfolioConstructionModel class. Extensions of the PortfolioConstructionModel class should implement the CreateTargets method, which receives an array of Insight objects from the Alpha model 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?".

// Portfolio construction scaffolding class; basic method args.
class MyPortfolioConstructionModel : PortfolioConstructionModel
{
    // REQUIRED: Will determine the target percent for each insight
    protected override void Dictionary<Insight, double> DetermineTargetPercent(List<Insight> activeInsights)
    {
        return new List<Insight>();
    }

    // Gets the target insights to calculate a portfolio target percent for, they will be piped to DetermineTargetPercent()
    protected override List<Insight> GetTargetInsights()
    {
        return new List<Insight>();
    }

    // Create list of PortfolioTarget objects from Insights
    public override List<PortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights)
    {
        return new List<PortfolioTarget>();
    }

    // Determine if the portfolio construction model should create a target for this insight
    protected override bool ShouldCreateTargetForInsight(Insight insight) => true;

    // Determines if the portfolio should be rebalanced base on the provided rebalancing func
    protected virtual bool IsRebalanceDue(Insight[] insights, DateTime algorithmUtc) => true;

    // OPTIONAL: Security change details
    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
    }
}
 # Portfolio construction scaffolding class; basic method args.
class MyPortfolioConstructionModel(PortfolioConstructionModel):
    # REQUIRED: Will determine the target percent for each insight
    def DetermineTargetPercent(self, activeInsights: List[Insight]) -> Dict[Insight, float]:
        return []

    # Gets the target insights to calculate a portfolio target percent for, they will be piped to DetermineTargetPercent()
    def GetTargetInsights(self) -> List[Insight]:
        return []
    
    # Create list of PortfolioTarget objects from Insights
    def CreateTargets(self, algorithm: QCAlgorithm, insights: List[Insight]) -> List[PortfolioTarget]:
        return []

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

    # Determines if the portfolio should be rebalanced base on the provided rebalancing func
    def IsRebalanceDue(self, insights: List[Insight], algorithmUtc: datetime) -> bool:
        return True

    # OPTIONAL: Security change details
    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.

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

To view a full example of a PortfolioConstructionModel subclass, see the EqualWeightingPortfolioConstructionModelEqualWeightingPortfolioConstructionModel in the LEAN GitHub repository.

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.

// Create a new portfolio target for 1200 IBM shares.
var target = new PortfolioTarget("IBM", 1200);
# 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.

// Calculate target equivalent to 10% of portfolio value
var target = PortfolioTarget.Percent(algorithm, "IBM", 0.1);
# Calculate target equivalent to 10% of portfolio value
target = PortfolioTarget.Percent(algorithm, "IBM", 0.1)

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

return new PortfolioTarget[] {  new PortfolioTarget("IBM", 1200)  };
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.

_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)

Copy Portfolio Targets

To copy a subset of the PortfolioTarget objects in the PortfolioTargetCollection to an array, call the CopyTo method. The arrayIndex argument is the zero-based index in the array at which copying begins.

_targetsCollection.CopyTo(array, arrayIndex);
self.targets_collection.CopyTo(array, arrayIndex)

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 Nonenull. If the function returns Nonenull, the model doesn't rebalance unless the rebalance settings trigger a rebalance. For a full example of a custom rebalance function, see the PortfolioRebalanceOnCustomFuncRegressionAlgorithmPortfolioRebalanceOnCustomFuncRegressionAlgorithm.

If you use a Portfolio Construction model with the following characteristics, you can also set the rebalancing frequency of the model with a timedeltaTimeSpan, Resolution, or DateRules:

  • The model is a subclass of the EqualWeightingPortfolioConstructionModel class.
  • The model constructor calls the EqualWeightingPortfolioConstructionModel constructor.
  • The model doesn't override the CreateTargets method.

To check which of the pre-built Portfolio Construction models support this functionality, see Supported Models.

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 RebalancePortfolioOnInsightChanges to false.

// In Initialize
Settings.RebalancePortfolioOnInsightChanges = false;
# In Initialize
self.Settings.RebalancePortfolioOnInsightChanges = False

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

// In Initialize
Settings.RebalancePortfolioOnSecurityChanges = false;
# In Initialize
self.Settings.RebalancePortfolioOnSecurityChanges = 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.

public class MyPortfolioOptimizer : IPortfolioOptimizer
{
    public double[] Optimize(double[,] historicalReturns, double[] expectedReturns = null, double[,] covariance = null)
    {
        // Create weights
        return weights;
    }
}
class MyPortfolioOptimizer:

    def Optimize(self, historicalReturns: pd.DataFrame, expectedReturns: pd.Series = None, covariance: pd.DataFrame = None) -> pd.Series:
        # Create weights
        return weights

The following table describes the arguments the Optimize method accepts:

ArgumentData TypeDescriptionDefault Value
historicalReturnsdouble[,]DataFrameMatrix of annualized historical returns where each column represents a security and each row returns for the given date/time (size: K x N)
expectedReturnsdouble[]SeriesArray of double with the portfolio annualized expected returns (size: K x 1)nullNone
covariancedouble[,]DataFrameMulti-dimensional array of double with the portfolio covariance of annualized returns (size: K x K)nullNone

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.

To view a full example of an IPortfolioOptimizer implementation, see the MaximumSharpeRatioPortfolioOptimizerMaximumSharpeRatioPortfolioOptimizer 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 Portfolio Construction 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 Portfolio Construction model.

class MyPortfolioConstructionModel : PortfolioConstructionModel
{
    private Dictionary<symbol, symboldata> _symbolDataBySymbol = new Dictionary<symbol, symboldata>();

    public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes)
    {
        base.OnSecuritiesChanged(algorithm, changes)
        foreach (var security in changes.AddedSecurities)
        {               
            _symbolDataBySymbol[security.Symbol] = new SymbolData(security.Symbol);
        }

        foreach (var security in changes.RemovedSecurities)
        {
            if (_symbolDataBySymbol.ContainsKey(security.Symbol))
            {
                _symbolDataBySymbol.Remove(security.Symbol);
            }
        }
    }

    public class SymbolData 
    {
        private Symbol _symbol;

        public SymbolData(Symbol symbol)
        {
            _symbol = symbol;
            // Store and manage Symbol-specific data
        }
    }
}
class MyPortfolioConstructionModel(PortfolioConstructionModel):
    symbol_data_by_symbol = {}

    def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        super().OnSecuritiesChanged(algorithm, changes)
        for security in changes.AddedSecurities:
            self.symbol_data_by_symbol[security.Symbol] = SymbolData(security.Symbol)

        for security in changes.RemovedSecurities:
            if security.Symbol in self.symbol_data_by_symbol:
                self.symbol_data_by_symbol.pop(security.Symbol, None)

class SymbolData:
    def __init__(self, symbol):
        self.symbol = symbol
        # Store and manage Symbol-specific data

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: