| sim_portfoptim | R Documentation |
Simulate a portfolio optimization strategy using online (recursive) updating of the covariance matrix.
sim_portfoptim(rets, dimax, lambdaf, lambdacov, lambdaw)
rets |
A time series or matrix of asset returns. |
dimax |
An integer equal to the number of eigen
values used for calculating the reduced inverse of the covariance
matrix (the default is |
lambdaf |
A decay factor which multiplies the past asset returns. |
lambdacov |
A decay factor which multiplies the past covariance. |
lambdaw |
A decay factor which multiplies the past portfolio weights. |
The function sim_portfoptim() simulates a portfolio optimization
strategy. The strategy calculates the maximum Sharpe portfolio weights
in-sample at every point in time, and applies them in the
out-of-sample time interval. It updates the trailing covariance
matrix recursively, instead of using past batches of data. The function
sim_portfoptim() uses three different decay factors for averaging past
values, to reduce the variance of its forecasts.
The function sim_portfoptim() first scales the returns by their
trailing volatilities:
r^s_t = \frac{r_t}{\sigma_{t-1}}
Returns scaled by their volatility are more stationary so they're easier to model.
Then at every point in time, the function sim_portfoptim() calls
the function HighFreq::push_covar() to update the trailing
covariance matrix of the returns:
\bar{r}_t = \lambda_c \bar{r}_{t-1} + (1 - \lambda_c) r^s_t
\hat{r}_t = r^s_t - \bar{r}_t
{cov}_t = \lambda_c {cov}_{t-1} + (1 - \lambda^2_c) \hat{r}^T_t \hat{r}_t
Where \lambda_c is the decay factor which multiplies the past mean
and covariance.
It then calls the function HighFreq::calc_inv() to calculate the
reduced inverse of the covariance matrix using its eigen
decomposition:
\strong{C}^{-1} = \strong{O}_{dimax} \, \Sigma^{-1}_{dimax} \, \strong{O}^T_{dimax}
See the function HighFreq::calc_inv() for details.
It then calculates the in-sample weights of the maximum Sharpe portfolio, by multiplying the inverse covariance matrix times the trailing means of the asset returns:
\bar{r}_t = \lambda \bar{r}_{t-1} + (1 - \lambda) r^s_t
\strong{w}_t = \strong{C}^{-1} \bar{r}_t
Note that the decay factor \lambda is different from the decay
factor \lambda_c used for updating the trailing covariance
matrix.
It then scales the weights so their sum of squares is equal to one:
\strong{w}_t = \frac{\strong{w}_t}{\sqrt{\sum{\strong{w}^2_t}}}
It then calculates the trailing mean of the weights:
\bar{\strong{w}}_t = \lambda_w \bar{\strong{w}}_{t-1} + (1 - \lambda_w) \strong{w}_t
Note that the decay factor \lambda_w is different from the decay
factor \lambda used for updating the trailing means.
It finally calculates the out-of-sample portfolio returns by multiplying the trailing mean weights times the scaled asset returns:
r^p_t = \bar{\strong{w}}_{t-1} r^s_t
Applying weights to scaled returns means trading stock amounts with unit
dollar volatility. So if the weight is equal to 2 then we should
purchase an amount of stock with dollar volatility equal to 2
dollars. Trading stock amounts with unit dollar volatility improves
portfolio diversification.
The function sim_portfoptim() uses three different decay factors
for averaging past values, to reduce the variance of its forecasts. The
value of the decay factor \lambda must be in the range between
0 and 1.
If \lambda is close to 1 then the decay is weak and past
values have a greater weight, so the trailing values have a greater
dependence on past data. This is equivalent to a long look-back
interval.
If \lambda is much less than 1 then the decay is strong and
past values have a smaller weight, so the trailing values have a weaker
dependence on past data. This is equivalent to a short look-back
interval.
The function sim_portfoptim() returns multiple columns of data,
with the same number of rows as the input argument rets. The first
column contains the strategy returns and the remaining columns contain the
portfolio weights.
A matrix of strategy returns and the portfolio weights, with
the same number of rows as the argument rets.
## Not run:
# Load ETF returns
retp <- rutils::etfenv$returns[, c("VTI", "TLT", "DBC", "USO", "XLF", "XLK")]
retp <- na.omit(retp)
datev <- zoo::index(retp) # dates
# Simulate a portfolio optimization strategy
dimax <- 6
lambdaf <- 0.978 # Decay factor
lambdacov <- 0.995
lambdaw <- 0.9
pnls <- HighFreq::sim_portfoptim(retp, dimax, lambdaf, lambdacov, lambdaw)
colnames(pnls) <- c("pnls", "VTI", "TLT", "DBC", "USO", "XLF", "XLK")
pnls <- xts::xts(pnls, order.by=datev)
# Plot dygraph of strategy
wealthv <- cbind(retp$VTI, pnls$pnls*sd(retp$VTI)/sd(pnls$pnls))
colnames(wealthv) <- c("VTI", "Strategy")
endd <- rutils::calc_endpoints(wealthv, interval="weeks")
dygraphs::dygraph(cumsum(wealthv)[endd], main="Portfolio Optimization Strategy Returns") %>%
dyOptions(colors=c("blue", "red"), strokeWidth=2) %>%
dyLegend(width=300)
# Plot dygraph of weights
symbolv <- "VTI"
stockweights <- cbind(cumsum(get(symbolv, retp)), get(symbolv, pnls))
colnames(stockweights)[2] <- "Weight"
colnamev <- colnames(stockweights)
endd <- rutils::calc_endpoints(pnls, interval="weeks")
dygraphs::dygraph(stockweights[endd], main="Returns and Weight") %>%
dyAxis("y", label=colnamev[1], independentTicks=TRUE) %>%
dyAxis("y2", label=colnamev[2], independentTicks=TRUE) %>%
dySeries(axis="y", label=colnamev[1], strokeWidth=2, col="blue") %>%
dySeries(axis="y2", label=colnamev[2], strokeWidth=2, col="red")
## End(Not run) # end dontrun
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.