简体   繁体   中英

How to pick only efficient frontier points in a plot of portfolio performance?

The name of this question does not do it justice. This is best explained by numerical example. Let's say I have the following portfolio data, called data .

> data
    Stdev AvgReturn
1   1.92      0.35
2   1.53      0.34
3   1.39      0.31
4   1.74      0.31
5   1.16      0.30
6   1.27      0.29
7   1.78      0.28
8   1.59      0.27
9   1.05      0.27
10  1.17      0.26
11  1.62      0.25
12  1.33      0.25
13  0.96      0.24
14  1.47      0.24
15  1.09      0.24
16  1.20      0.24
17  1.49      0.23
18  1.01      0.23
19  0.88      0.22
20  1.21      0.22
21  1.37      0.22
22  1.09      0.22
23  0.95      0.21
24  0.81      0.21

I have already sorted the data data.frame by AvgReturn to make this (what I believe to be easier). My goal is to essentially eliminate all the points that do not make sense to choose, ie, I would not want a portfolio where I choose a lower AvgReturn but receive a higher Stdev (assuming stdev is an appropriate measure of risk, but I am assuming that for now).

Essentially, does any know of an efficient (in the code sense) way to choose the "rational" portfolio choices. I have manually created a third column to this data frame to show you which portfolio choices should be kept. I would want to remove portfolio 4 because I would never choose it since I can choose portfolio 3 and receive the same return and a lower stdev. Similarly, I would never choose 8 because I can choose 5 with a higher return and a lower stdev.

> res
    Stdev AvgReturn  Keep
1   1.92      0.35  TRUE
2   1.53      0.34  TRUE
3   1.39      0.31  TRUE
4   1.74      0.31 FALSE
5   1.16      0.30  TRUE
6   1.27      0.29 FALSE
7   1.78      0.28 FALSE
8   1.59      0.27 FALSE
9   1.05      0.27  TRUE
10  1.17      0.26 FALSE
11  1.62      0.25 FALSE
12  1.33      0.25 FALSE
13  0.96      0.24  TRUE
14  1.47      0.24 FALSE
15  1.09      0.24 FALSE
16  1.20      0.24 FALSE
17  1.49      0.23 FALSE
18  1.01      0.23 FALSE
19  0.88      0.22  TRUE
20  1.21      0.22 FALSE
21  1.37      0.22 FALSE
22  1.09      0.22 FALSE
23  0.95      0.21 FALSE
24  0.81      0.21  TRUE

The only way I can think of solving this issue is by looping through and checking each condition. This, however, will be relatively inefficient in R my preferred language for this solution. I am having difficulty thinking of a vectorized solution. Any help is appreciated!

EDIT Here I believe is a solution:

domstrat <- function(data){
  keep <- c(-1,sign(diff(cummin(data[[1]]))))
  data <- data[which(keep!=0),]

  return(data)
}

   Stdev AvgReturn
1   1.92      0.35
2   1.53      0.34
3   1.39      0.31
5   1.16      0.30
9   1.05      0.27
13  0.96      0.24
19  0.88      0.22
24  0.81      0.21

This uses the function cummax to identify a series of qualifying points by then testing against the original data:

> data <- data[order(data$Stdev),]
> data[ which(data$AvgReturn == cummax(data$AvgReturn)) , ]
   Stdev AvgReturn
24  0.81      0.21
19  0.88      0.22
13  0.96      0.24
9   1.05      0.27
5   1.16      0.30
3   1.39      0.31
2   1.53      0.34
1   1.92      0.35


> plot(data)
> points( data[ which(data$AvgReturn == cummax(data$AvgReturn)) , ] , col="green")

It's not actually the convex hull but what might be called the "monotonically increasing hull".

在此处输入图片说明

You can define a custom R function which contains some logic to decide whether or not to keep a certain portfolio depending on the standard deviation and the average return:

>portfolioKeep <- function(x){
+ # x[1] contains the Stdev for the input row
+ # x[2] contains the AvgReturn for the input row
+ # make your decision based on these inputs here...
+ # and remember to return either "TRUE" or "FALSE"
+ }

Next we can use an apply function on your input data frame to come up with the Keep column you want:

# your 'input' data frame
input.mat <- data.matrix(input)
# apply custom function to rows
keep <- apply(input.mat, 1, portfolioKeep)
# bind keep vector to input data frame
input <- cbind(input, keep)

The above code first converts the input data frame into a numeric matrix so that we can use the apply function on it. The apply function will run portfolioKeep on each row, returning either "TRUE" or "FALSE." Finally, we roll the Keep column up into the original data frame for convenience.

And now you can do your reporting easily with the data frame input with which you started.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM