[R-SIG-Finance] Sharpe's algorithm for portfolio improvement
Enrico Schumann
enricoschumann at yahoo.de
Wed Aug 3 22:58:53 CEST 2011
[see below]
> -----Ursprüngliche Nachricht-----
> Von: r-sig-finance-bounces at r-project.org
> [mailto:r-sig-finance-bounces at r-project.org] Im Auftrag von
> John P. Burkett
> Gesendet: Mittwoch, 3. August 2011 18:36
> An: R-SIG-Finance at r-project.org
> Betreff: Re: [R-SIG-Finance] Sharpe's algorithm for portfolio
> improvement
>
> Enrico Schumann wrote:
>
> > But you can also directly use the constraint in the creation of the
> > new solutions, which is what Sharpe suggested (and what works quite
> > well, and not just for smooth functions/constraints): if
> you only add
> > zero-sum changes to a feasible portfolio, it will remain
> feasible with
> > respect to the budget constraint. If you want
> min/max-holding sizes,
> > choose asset weights such that the min/max-constraints remain
> > unviolated. This is straightforward in methods like Simulated
> > Annealing/Threshold Accepting, but somewhat more difficult in 'DE'.
> > (Which does not mean that 'DE' is not as good as the other methods,
> > just that the efficient approaches possibly differ,
> depending on the
> > method.)
>
> Thanks, Enrico. I'm interested in using the adding-up
> constraint in the creation of new solutions, via methods such
> as simulated annealing or threshold accepting. I imagine
> that the DMOF package is useful for implementing that
> approach. Are there also other packages that I should consider?
>
There are many packages in R that handle optimisation, but I am not really
familiar with most if them. The task view for optimisation was already
suggested to you; you may also want to browse R-Forge/RForge.
I cannot comment on the model that you gave your other e-mail -- even
though, this objective function will essentially pick portfolios of just
very few assets with very high returns; I hope you have faith that your
return scenarios are representative of what will happen in the future.
Here is a complete example with TAopt from the NMOF package; it is mostly
copy--pasted from a package vignette. Since I don't have your data, I create
random data and put all data into one list which I later pass to the
function TAopt.
# --- #
require("NMOF")
# the neighbourhood: see the package vignette on portfolio optimisation
resample <- function(x, ...) x[sample.int(length(x), ...)] # see ?sample
neighbourU <- function(sol, data){
wn <- sol$w
toSell <- wn > data$winf
toBuy <- wn < data$wsup
i <- resample(which(toSell), size = 1L)
j <- resample(which(toBuy), size = 1L)
eps <- runif(1) * data$eps
eps <- min(wn[i] - data$winf, data$wsup - wn[j], eps)
wn[i] <- wn[i] - eps
wn[j] <- wn[j] + eps
Rw <- sol$Rw + data$R[,c(i,j)] %*% c(-eps,eps)
list(w = wn, Rw = Rw)
}
# random data
na <- 31L # number of assets
nr <- 20L # number of return obs
randomData <- rnorm(nr*na)*0.01 # random data: a plain matrix ;)
dim(randomData) <- c(nr, na)
# collect all in 'data': eps is stepsize for TA, winf/wsup are min/max
holding sizes
data <- list(R = randomData, na = na, nr = nr, eps = 0.5/100, winf = 0,
wsup = 1)
# --- #
The neighbourhood may look a bit complicated, but it uses the following
fact: in a given iteration, your objective function can be split into two
pieces: first, you compute portfolio returns Rw <- Data %*% w; then you
compute your actual objective for these returns, say, fun(Rw). The
multiplication will typically take much of the computing time (in particular
if the actual objective is quite cheap); see the following test. But the
neighbourhood function changes only two weights: increase one, decrease one.
So you can update the multiplication. In your case, this may not make much
difference >> but try with 100 or more return observations
Thus, the representation of a solution is a list with two components: the
weight vector w, and the return associated with this portfolio Rw (ie, Rw <-
Data %*% w). In the objective function, you can compute anything from sol$w
and sol$Rw.
# ---
# create a feasible random solution
w0 <- runif(data$na); w0 <- w0/sum(w0)
x0 <- list(w = w0, Rw = randomData %*% w0)
system.time(for (i in 1:100000) Rw <- randomData %*% w0)
system.time(for (i in 1:100000) ignore <- sum(Rw/(0.5 + Rw)))
# test the neighbourhood
x1 <- neighbourU(x0, data)
all.equal(data$R %*% x1$w, x1$Rw)
barplot(x0$w - x1$w) # difference between x0 and x1
# the objective function
Neu31 <- function(sol, data){
u <- sol$Rw/(0.5 + sol$Rw)
objfun <- -sum(u)/data$nr
objfun
}
# ---
Your objective: I have droppped the x1, x2, ... since they do not appear to
be needed. Also, you use an object 'nr', but never pass it. R will find it
if it is in the global environment, but I like it better to pass all objects
directly or make the dependence explicit by using environments. It remains
to run the algorithm.
# ---
# settings: see ?TAopt
algo <- list(x0 = x0, neighbour = neighbourU, nS = 2000L, nT = 10L,
nD = 1000L, q = 0.20, printBar = FALSE, printDetail = FALSE)
system.time(res <- TAopt(Neu31, algo = algo,data = data))
res$OFvalue # the obj fun value of the solution
res$xbest$w # the best weights
sum(res$xbest$w) # is budget satisfied?
# TA is a stochastic method: run restarts
allRes <- restartOpt(fun=TAopt, OF = Neu31, n = 20L, algo = algo, data =
data)
allF <- sapply(allRes, `[[`, "OFvalue") # realised objective functions
allw <- sapply(allRes,`[[`, c("xbest","w")) # weights
hope that helps,
Enrico
> Best regards,
> John
>
> --
> John P. Burkett
> Department of Economics
> University of Rhode Island
> Kingston, RI 02881-0808
> USA
>
> phone (401) 874-9195
>
> _______________________________________________
> R-SIG-Finance at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-sig-finance
> -- Subscriber-posting only. If you want to post, subscribe first.
> -- Also note that this is not the r-help list where general R
> questions should go.
More information about the R-SIG-Finance
mailing list