Portfolio Construction
Key Concepts
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 at every time step 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 Dictionary<Insight, double> DetermineTargetPercent(List<Insight> activeInsights) { return new Dictionary<Insight, double>(); } // 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 Portfolio Construction model should remove expired insights from the Insight Manager. The CreateTargets
definition of the base PortfolioConstructionModel
class already removes them during each rebalance. Therefore, if you override the CreateTargets
method and don't call the CreateTargets
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. The OnSecuritiesChanged
definition of the base PortfolioConstructionModel
class already does this. Therefore, if you override the OnSecuritiesChanged
method and don't call the OnSecuritiesChanged
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:
To view a full example of a PortfolioConstructionModel
subclass, see the EqualWeightingPortfolioConstructionModelEqualWeightingPortfolioConstructionModel in the LEAN GitHub repository.
Multi-Alpha Algorithms
If you add multiple Alpha models, 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 GetTargetInsights
, and return all active insights. The DetermineTargetPercent
implements the combination criteria, and determine the target for each Symbol
.
public class MultipleAlphaPortfolioConstructionModel : PortfolioConstructionModel { protected override List<Insight> GetTargetInsights() { return InsightCollection.GetActiveInsights(Algorithm.UtcTime).ToList(); } protected override Dictionary<Insight, double> DetermineTargetPercent(List<Insight> activeInsights) { return new Dictionary<Insight, double>(); } }
class MultipleAlphaPortfolioConstructionModel(PortfolioConstructionModel): def GetTargetInsights(self) -> List[Insight]: return list(self.InsightCollection.GetActiveInsights(self.Algorithm.UtcTime)) def DetermineTargetPercent(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.
// 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 None
null
. If the function returns None
null
, 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 timedelta
TimeSpan
, 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:
Argument | Data Type | Description | Default Value |
---|---|---|---|
historicalReturns | double[,] DataFrame | Matrix of annualized historical returns where each column represents a security and each row returns for the given date/time (size: K x N) | |
expectedReturns | double[] Series | Array of double with the portfolio annualized expected returns (size: K x 1) | null None |
covariance | double[,] DataFrame | Multi-dimensional array of double with the portfolio covariance of annualized returns (size: K x K) | null 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.
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