[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