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
- The objects:
priceVector
,grossVal
,curPortfol
that are defined in several examples, such as in “Passive, no benchmark (minimum variance)” xaLWvar06
variance matrix from “Returns to variance matrix” 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
: weightrisk.fraction
: fraction of variance (by default)positions
: monetary valuelower.trade
,upper.trade
: units (shares, contracts, …)threshold
: unitsforced.trade
: units
See also
Navigate
- Back to “Optimize Trades”
- Back to the top level of “Portfolio Probe Cookbook”