Asset limits

Task

Restrict amounts for individual assets in various ways.

Preparation

This presumes that you can do a basic portfolio optimization.  For example, that you have mastered “Passive, no benchmark (minimum variance)”.

  • Portfolio Probe

You need to have the Portfolio Probe package loaded into your R session:

require(PortfolioProbe)

If you don’t have Portfolio Probe, see “Demo or Buy”.

Doing the example

 Doing it

We will do optimizations that:

  • limit maximum weight of assets
  • limit the contribution to portfolio variance of assets
  • limit minimum and maximum amount in a long-only portfolio
  • limit amount traded by asset

Limit maximum weight of assets

We perform an optimization that restricts the maximum weight to 10%:

opMaxWt <- trade.optimizer(priceVector, 
   variance=xaLWvar06, existing=curPortfol, 
   gross=grossVal, long.only=TRUE, 
   utility="minimum variance", max.weight=.1)

The summary of the weights is:

> summary(valuation(opMaxWt)$weight)
     Min.   1st Qu.    Median      Mean   3rd Qu. 
0.0006019 0.0111200 0.0213500 0.0270300 0.0328800 
     Max. 
0.1000000

The maximum weight is very close to 10%.  From the mean weight you could correctly infer that the number of names in the optimal portfolio is 37.

Limit the contribution to portfolio variance of assets

The constraint in the previous example — limiting maximum weight — is quite commonly done, but is not likely to be what people really want.  Closer to what is wanted is to constrain the amount of risk that each asset brings to the portfolio.

Do that with:

opRiskFrac <- trade.optimizer(priceVector, 
   variance=xaLWvar06, existing=curPortfol, 
   gross=grossVal, long.only=TRUE, 
   utility="minimum variance", risk.fraction=.1)

There is only one change between this and the previous example: “max.weight” is changed to “risk.fraction“.

The summary of the weights for this portfolio is:

> summary(valuation(opRiskFrac)$weight)
     Min.   1st Qu.    Median      Mean   3rd Qu. 
0.0006879 0.0110400 0.0211400 0.0270300 0.0321500 
     Max. 
0.1033000

Since we are restricting risk rather than weight, the maximum weight can be bigger than 10%.  In this case it is only slightly more, but it could be substantially more if an asset had a particularly low contribution to the variance.

Limit minimum and maximum amount in a long-only portfolio

There is no “min.weight” argument to match the max.weight argument.  One way of getting a minimum weight constraint is to use the positions argument.

To get a minimum weight of 5% and maximum weight of 20% for all assets, we can set up the positions argument as:

posArg <- cbind(rep(.05, 10), .2) * grossVal

This argument expects its inputs in terms of money.  The first few rows look like:

> head(posArg, 3)
         [,1]   [,2]
[1,] 151671.5 606686
[2,] 151671.5 606686
[3,] 151671.5 606686

Now we can do the optimization.  Note that we are restricting the optimization to only use the first 10 assets (the ones that are in the existing portfolio):

opMinMaxWt <- trade.optimizer(head(priceVector, 10), 
   variance=xaLWvar06, existing=curPortfol, 
   gross=grossVal, long.only=TRUE, 
   utility="minimum variance", positions=posArg)

The summary of weights is:

> summary(valuation(opMinMaxWt)$weight)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.05000 0.05001 0.05661 0.10000 0.17170 0.20000

Essentially the full range of weights is achieved.

Limit amount traded by asset

We can limit the number of units (that is, shares, contracts, …) that are traded by asset with the arguments lower.trade and upper.trade.  We’ll do an example using only lower.trade.

First we create a vector for the maximum number of shares we are willing to sell for each of the assets in the existing portfolio:

lowArg <- seq(-1500, by=100, length=10)
names(lowArg) <- names(curPortfol)

This looks like:

XA101 XA103 XA105 XA107 XA108 XA111 XA113 XA115 
-1500 -1400 -1300 -1200 -1100 -1000  -900  -800 
XA120 XA126 
 -700  -600

Now we use that vector in the optimization:

opLowTrade <- trade.optimizer(priceVector, 
   variance=xaLWvar06, existing=curPortfol, 
   gross=grossVal, long.only=TRUE, 
   utility="minimum variance", lower.trade=lowArg)

The trade for the optimization looks like:

> opLowTrade$trade
XA101 XA103 XA105 XA107 XA108 XA111 XA113 XA115 
-1000 -1400 -1300 -1200 -1100 -1000  -900  -800 
XA120 XA126 XA280 XA470 XA569 XA675 XA684 XA778 
 -700  -600  8411   690  3290  2696   112   135

We see the limits biting for all of the assets in the existing portfolio except for the first.  It doesn’t bite for the first because it can’t — the long-only constraint is stronger for that asset.

Explanation

Maximum weight

A blanket weight constraint as in the first example is almost surely better as a risk fraction constraint.  However, there are maximum weight constraints that do make sense.  For example, you may want to hold no more than x days of average volume of each asset.  That can be stated as a maximum weight, but will be a different weight for each asset.

To give such a constraint, give a named vector to max.weight.  For example:

max.weight=c(XA101=.057, XA103=.015, XA120=.712)

This constrains the three assets named but does not constrain any others.

Risk fraction

The generalization of the max.weight argument explained just above also works for risk.fraction.  It is possible to put a lower bound on the risk fractions as well (in the same spirit as the third example).

Minimum and maximum amount

The matrix that we gave to the positions argument had all its rows the same.  In order to give different amounts for different assets, the matrix needs row names that are the identifiers of the assets.

Lower and upper trade

The values in lower.trade need to be negative or zero, and the values in upper.trade need to be positive or zero.

Otherwise, they would be forcing a trade.  You can force trades, but you need to use either the forced.trade argument or the positions argument.

Further details

The example with minimum weights was restricted to have only 10 assets in the universe because that form of constraint says that all assets should obey it.  If instead you want a minimum weight given that the asset is in the portfolio at all, then that is a threshold constraint.  Threshold constraints may be imposed either with the threshold argument or with the positions argument (using more columns).

For long-short portfolios, max.weight constrains the absolute value of the weight.  In this case weight is defined as the position value divided by the gross value of the portfolio.

Troubleshooting

  •  Be mindful that these arguments are mostly in different units from each other:
  • max.weight: weight
  • risk.fraction: fraction of variance (by default)
  • positions: monetary value
  • lower.trade, upper.trade: units (shares, contracts, …)
  • threshold: units
  • forced.trade: units

See also

Navigate