Task
Optimize the trade given various utilities and some simple constraints for the case where the net value is close to zero.
For the general case, the only difference is that the net value is not constrained to be near zero.
Preparation
- vector of asset prices
- vector of expected returns
- variance matrix for the assets
- current portfolio (if it exists)
- Portfolio Probe
You need the prices at which assets trade, and a variance matrix of the asset returns. You also need an expected return for each asset in the universe.
The holdings of the current portfolio need to be in a vector with names that are the asset identifiers.
You also 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
xaLWvar06
variance matrix from “Returns to variance matrix” examplexaMACD
matrix from “Compute a technical indicator” example to use for expected returnspprobeData
package
You need to have the package loaded into your R session:
require(pprobeData)
Doing it
We’ll do a few optimizations:
- Maximize the information ratio
- Maximize a mean-variance utility
- Maximize expected return given a maximum volatility
Preliminaries
The inputs we need in order to get our optimal portfolio are:
- vector of prices at which the assets may be traded
- variance matrix of the asset returns
- vector of expected returns
- desired value of the new portfolio
- appropriate constraints
- current portfolio (optional)
prices
We start by naming the vector of prices that we want to use:
priceVector <- xassetPrices[251,]
These are the prices at the close of the last trading day of 2006. The first few values are:
> head(priceVector) XA101 XA103 XA105 XA107 XA108 XA111 33.56 72.25 74.39 192.06 5.91 15.98
The requirement for the prices is that it be a vector of positive numbers with names (that are the asset identifiers).
expected returns
We can get the MACD signal value for the same time point as the prices to use as the expected returns:
> expRet <- xaMACD[251,] / 100 > head(expRet) XA101 XA103 XA105 0.009178291 0.009114743 0.011844481 XA107 XA108 XA111 0.008825741 0.014268342 -0.003350204
current portfolio
We create an object to serve as the current portfolio:
dnCur <- (1:10) * 1000 names(dnCur) <- head(names(priceVector), 10) dnCur[c(4,5,7,9)] <- -dnCur[c(4,5,7,9)]
What is expected is a numeric vector of the number of units of each asset in the portfolio. The names of the vector are the identifiers of the assets that are used in the price vector and the variance matrix.
> dnCur XA101 XA103 XA105 XA107 XA108 XA111 XA113 XA115 1000 2000 3000 -4000 -5000 6000 -7000 8000 XA120 XA126 -9000 10000
portfolio value
We can see the valuation of the current portfolio with:
> valuation(dnCur, priceVector)$total gross net long short 3013430 -45170 1484130 1529300
We can see more clearly by formatting the numbers:
> format(valuation(dnCur, priceVector)$total, + nsmall=2, big.mark=",") gross net long "3,013,430.00" " -45,170.00" "1,484,130.00" short "1,529,300.00"
Suppose that the net asset value and the desired gearing means that we want the gross value to be about $2,900,000.
> dnGross <- 2.9e6 > dnGross [1] 2900000
Suppose also that we want the net value to be within $20,000 of zero:
> dnNet <- c(-20000, 20000) > dnNet [1] -20000 20000
Optimization: information ratio
We’re now ready to do an optimization. The only constraint that we impose besides the gross and net values is that no more than 10 assets may be in the portfolio.
dnMaxInfo <- trade.optimizer(priceVector, variance=xaLWvar06, expected.return=expRet, existing=dnCur, gross=dnGross, net=dnNet, port.size=10)
We are not specifying what utility to use, so it will use the default which is to maximize the information ratio.
Optimization: mean-variance utility
If we are using a mean-variance utility, we need to decide what risk aversion to use.
dnMeanVar <- trade.optimizer(priceVector, variance=xaLWvar06, expected.return=expRet, existing=dnCur, gross=dnGross, net=dnNet, port.size=10, utility="mean-variance", risk.aversion=10)
We’ve added two arguments to the previous optimization. We use the utility
argument to state the form of utility to use, and we use the risk.aversion
argument to state the risk aversion that we want to use.
Note that the form of the utility is: expected return minus risk aversion times variance. Some have a one-half in the last term.
Optimization: maximum return with volatility constraint
An often convenient form of optimization is to constrain the volatility to some maximum value and then maximize the expected return. Here we constrain volatility to 5%.
dnMaxRetVC <- trade.optimizer(priceVector, variance=xaLWvar06, expected.return=expRet, existing=dnCur, gross=dnGross, net=dnNet, port.size=10, utility="maximum return", var.constraint=.05^2/252)
We need to translate our 5% volatility into the scale of the variance, which is daily.
Print results
The resulting (first) object is printed like:
> dnMaxInfo $new.portfolio XA238 XA385 XA481 XA576 XA678 XA699 XA715 1676 -2972 6793 -6881 11318 -5698 4786 XA814 XA893 XA952 -6367 12097 -16065 $trade XA101 XA103 XA105 XA107 XA108 XA111 XA113 -1000 -2000 -3000 4000 5000 -6000 7000 XA115 XA120 XA126 XA238 XA385 XA481 XA576 -8000 9000 -10000 1676 -2972 6793 -6881 XA678 XA699 XA715 XA814 XA893 XA952 11318 -5698 4786 -6367 12097 -16065 $results objective negutil cost penalty -6.673668 -6.673668 0.000000 0.000000 $converged [1] TRUE $objective.utility [1] "information ratio" $alpha.values A0 0.02634241 $var.values V0 1.558052e-05 $utility.values [1] -6.673668 $existing XA101 XA103 XA105 XA107 XA108 XA111 XA113 XA115 1000 2000 3000 -4000 -5000 6000 -7000 8000 XA120 XA126 -9000 10000 $violated NULL $timestamp [1] "Thu Sep 27 11:22:23 2012" [2] "Thu Sep 27 11:22:43 2012" $call trade.optimizer(prices = priceVector, variance = xaLWvar06, expected.return = expRet, existing = dnCur, gross = dnGross, net = dnNet, port.size = 10)
The first two components are the new (optimal) portfolio and the trade to achieve that. There are some additional components to the object that are not shown.
Explanation
Optimization strategy
The optimization with the volatility constraint is the easiest to do in practice. This is because we don’t need the variance and the expected returns to be on the same scale. We merely need to decide what (expected) volatility we are willing to tolerate.
The mean-variance formulation assumes either that the variance and expected returns are matched in scale, or that the risk aversion takes the mismatch into account. Maximizing the information ratio assumes that the scales are matched.
Technical details
portfolio value
It is mandatory that the value of the resulting portfolio be specified. For long-short portfolios the most likely specification is to use both gross.value
and net.value
. These are both ranges.
We gave only one number for the gross value in the examples. When only one number is given, then that is taken to be the maximum and a slightly smaller number is used as the minimum. It is advised to always give a range for net.value
(though net.value=0
will do something semi-reasonable).
other output components
One component of the output to pay special attention to is ‘violated
‘ — this states which constraints, if any, are violated. You want this to be NULL
.
It is probably not important whether ‘converged
‘ is TRUE
or FALSE
. The optimization is likely to be good enough with or without convergence.
Further Details
You can see more about the optimization with the summary of the object:
> summary(dnMaxInfo) $results objective negutil cost penalty -6.673668 -6.673668 0.000000 0.000000 $objective.utility [1] "information ratio" $alpha.values A0 0.02634241 $var.values V0 1.558052e-05 $number.of.assets existing trade 10 20 new open 10 10 close universe.total 10 350 tradable select.universe 350 350 positions.notrade 0 $opening.positions [1] "XA238" "XA385" "XA481" "XA576" "XA678" [6] "XA699" "XA715" "XA814" "XA893" "XA952" $closing.positions [1] "XA101" "XA103" "XA105" "XA107" "XA108" [6] "XA111" "XA113" "XA115" "XA120" "XA126" $value.limits lower upper gross 2899710 2900000 net -20000 20000 long 1439855 1460000 short 1439855 1460000 $valuation.new.portfolio gross net long short 2899973.18 19999.76 1459986.47 1439986.71 $valuation.trade gross net long short 5913403.18 65169.76 2989286.47 2924116.71 $valuation.trade.fraction.of.gross gross net long short 2.03912340 0.02247254 1.03079797 1.00832543
This has some pieces that are also in the print
method, but new information as well. We see that all of the current portfolio was sold off — a trade to make the broker happy.
Troubleshooting
- The variance matrix needs to contain all of the assets that are in the price vector. It can have additional assets — these will be ignored. The order of the assets in the variance does not matter.
- All of the prices need to be in the same currency. You have to check that — the code has no way of knowing.
- It will still work if the object given as the prices is a one-column or one-row matrix. But it will complain about other matrices. The same is true for expected returns.
See also
Navigate
- Back to “Optimize Trades”
- Back to the top level of “Portfolio Probe Cookbook”