R диаграма за анализ на портфолио. Функция EfficientFrontier

Опитвам се да използвам функцията chart.EfficientFrontier в пакета portfolioanalytics в R, за да начертая ефективен граничен обект, който създадох, но продължава да се проваля. По принцип се опитвам да намеря граница, която ще минимизира анализираното стандартно отклонение. В крайна сметка, след като накарам това да работи, бих искал да максимизирам и годишната възвръщаемост.

Първо създадох функция за годишно стандартно отклонение, използвайки този код

pasd <- function(R, weights){
  as.numeric(StdDev(R=R, weights=weights)*sqrt(12)) # hardcoded for monthly data
  # as.numeric(StdDev(R=R, weights=weights)*sqrt(4)) # hardcoded for quarterly data
}

Импортирах csv файл с месечна възвръщаемост и моят обект на портфолио изглежда така:

> prt
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = colnames(returns))

Number of assets: 3 
Asset Names
[1] "Global REITs"      "Au REITs"          "Au Util and Infra"

Constraints
Enabled constraint types
        - leverage 
        - long_only 

Objectives:
Enabled objective names
        - mean 
        - pasd 

Сега успешно създавам ефективен граничен обект, използвайки този ред:

prt.ef <- create.EfficientFrontier(R = returns, portfolio = prt, type = "DEoptim", match.col = "pasd")

Но когато се опитам да го начертая, получавам следните съобщения за грешка.

> chart.EfficientFrontier(prt.ef, match.col="pasd")
Error in StdDev(R = R, weights = weights) : 
  argument "weights" is missing, with no default
In addition: There were 26 warnings (use warnings() to see them)
Error in StdDev(R = R, weights = weights) : 
  argument "weights" is missing, with no default
Error in StdDev(R = R, weights = weights) : 
  argument "weights" is missing, with no default
Error in xlim[2] * 1.15 : non-numeric argument to binary operator

Някой знае ли защо е така? Когато използвам summary(prt.ef), мога да видя теглата, но защо функцията chart.EfficientFrontier не работи?


person user3381431    schedule 03.06.2015    source източник
comment
chart.EfficientFrontier очаква да може да извика pasd без тегла за изчисляване на сигма за всеки актив и с тегла за изчисляване на сигмата на портфейла. За да премахнете съобщението за грешка, накарайте pasd да работи като StdDev, като използвате pasd <- function(R, weights=NULL) as.numeric(StdDev(R=R, weights=weights)*sqrt(12)). Освен това, за да направите последователен анализ, трябва да приведете възвръщаемостта на годишна база, но тогава оптимизацията ще изчисли едни и същи тегла за месечни и годишни портфейли.   -  person WaltS    schedule 03.06.2015
comment
Промених функцията pasd както предложихте, но графиката.EfficientFrontier все още ми дава грешки. chart.EfficientFrontier(prt.ef, match.col="pasd") Error in StdDev(R = R, weights = weights) : object 'NuLL' not found Error in StdDev(R = R, weights = weights) : object 'NuLL' not found Error in StdDev(R = R, weights = weights) : object 'NuLL' not found Error in xlim[2] * 1.15 : non-numeric argument to binary operator   -  person user3381431    schedule 04.06.2015


Отговори (4)


Както предложи @WaltS, трябва да сте последователни при внедряването на функции за годишна средна и рискова възвръщаемост.

Но всъщност, за да получите годишна статистика, имате две възможности, не използвате нито една:

1) Направете оптимизацията с месечни данни, с оригиналните функции за възвръщаемост на риска в спецификацията. За чертане можете да направите анализиране

Port.Anua.Returns=prt.ef$frontier[,1]*12 
Port.Anua.StDev=prt.ef$frontier[,2]*12^.5

Теглата ще бъдат еднакви за месечни или годишни портфейли.

prt.ef$frontier[,-(1:3)]

2) Трансформирайте месечната си възвръщаемост в годишна възвръщаемост, умножена по 12. След това направете оптимизацията с обичайната процедура, всички рискове и възвръщаемост ще бъдат вече годишни в prt.ef$frontier.

Свързано с назъбената линия в EF. Използвайки спецификацията на вашето портфолио, успях да пресъздам същото поведение. За следния график използвах edhec данни, вашата спецификация с оригинални mean и StdDev в целите:

data(edhec)
returns <- edhec[,1:3]

въведете описание на изображението тук

Това поведение трябва да бъде повлияно от спецификацията или алгоритъма за оптимизация, който използвате. Направих същата оптимизация с solve.QP от пакет quadprog. Това е резултатът. въведете описание на изображението тук

Актуализация

Кодът е тук:

require(quadprog) 
#min_x(-d^T x + 1/2 b^T D x) r.t A.x>=b
MV_QP<-function(nx, tarRet, Sig=NULL,long_only=FALSE){
  if (is.null(Sig)) Sig=cov(nx)
  dvec=rep(0,ncol(Sig))
  meq=2
  Amat=rbind(rep(1,ncol(Sig)),
             apply(nx,2,mean) )
  bvec=c(1,tarRet )
  if (long_only) {
    meq=1
    Amat=Amat[-1,]
    Amat=rbind(Amat,
               diag(1,ncol(Sig)),
               rep(1,ncol(Sig)),
               rep(-1,ncol(Sig)))
    bvec=bvec[-1]
    bvec=c(bvec,
               rep(0,ncol(Sig)),.98,-1.02)
  }
  sol  <- solve.QP(Dmat=Sig, dvec, t(Amat), bvec, meq=meq)$solution 
}

steps=50
x=returns
 µ.b <- apply(X = x, 2, FUN = mean) 
long_only=TRUE
range.bl <- seq(from = min(µ.b), to = max(µ.b)*ifelse(long_only,1,1.6), length.out = steps) 
risk.bl <- t(sapply(range.bl, function(targetReturn) { 
  w <- MV_QP(x, targetReturn,long_only=long_only) 
  c(sd(x %*% w),w)  }))

weigthsl=round(risk.bl[,-1],4)
colnames(weigthsl)=colnames(x)
weigthsl
risk.bl=risk.bl[,1]
rets.bl= weigthsl%*%µ.b
fan=12
plot(x = risk.bl*fan^.5, y = rets.bl*fan,col=2,pch=21,
     xlab = "Annualized Risk ", 
     ylab = "Annualized Return", main = "long only EF with solve.QP")
person Robert    schedule 04.06.2015
comment
Благодаря, ще опитам това, но това, което не разбирам, е годишната месечна възвръщаемост, като ги умножа по 12. Вече импортирам поредица от месечни възвръщаемости и бих имала годишна възвръщаемост там чрез верижно свързване на всяка месечна възвръщаемост (1+ r1)*(1+r2)*(1+r3).....(1+r12) - 1 - person user3381431; 05.06.2015
comment
Също така можете ли да публикувате кода за оптимизация с помощта на solve.QP? - person user3381431; 05.06.2015
comment
Разбрах, че искате еквивалентната годишна възвращаемост и годишното стандартно отклонение от месечните данни за EF, за това просто включвате скалата=12. В този случай се очаква да инвестирате всеки месец. Друга история е да трансформирате проблема, за да правите инвестиции всяка година, за това ще ви е необходима дълга поредица от годишни възвръщаемости, в този случай да, можете да комбинирате месечната възвръщаемост. - person Robert; 05.06.2015

Като добавим към коментарите на Робърт, изчисляването на оптимизацията с месечна възвращаемост е квадратичен програмен проблем с линейни ограничения. Когато mean е целта return и StdDev или var е целта risk, optimize.portfolio и create.EfficientFrontier избират метода ROI като решаващ метод, който използва solve.QP, ефективен инструмент за решаване на този вид проблеми. Когато целта risk се промени на pasd, тези функции не разпознават това като QP проблем, така че използвайте DEoptim общ инструмент за решаване на нелинейни проблеми, който може би е по-подходящ за решаване на неизпъкнали проблеми, отколкото на изпъкнали QP. Вижте Диференциална еволюция с DEoptim . Това изглежда е причината за назъбената ефективна граница.

За да накарате create.EfficientFrontier да използва solve.QP, което е много по-ефективно и точно за този тип проблем, можете да направите персонализирана моментна функция за изчисляване на средната стойност и дисперсията и след това да я посочите с аргумента momentFUN. Въпреки това, create.EfficientFrontier поне отчасти използва средства, изчислени директно от резултатите, вместо да използва mu от momentFUN. За да се справите с това, умножете възвръщаемостта и разделете дисперсията на 12, както е показано в примера по-долу.

library(PortfolioAnalytics)
  data(edhec)
  returns <- edhec[,1:3]
#  define moment function
  annualized.moments <- function(R, scale=12, portfolio=NULL){
    out <- list()
    out$mu <-    matrix(colMeans(R), ncol=1)
    out$sigma <- cov(R)/scale
    return(out)
  }
# define portfolio
  prt <- portfolio.spec(assets=colnames(returns))
  prt <- add.constraint(portfolio=prt, type="long_only")
  #  leverage defaults to weight_sum = 1 so is equivalent to full_investment constraint
  prt <- add.constraint(portfolio=prt, type="leverage")
  prt <- add.objective(portfolio=prt, type="risk", name="StdDev")
# calculate and plot efficient frontier
  prt_ef <- create.EfficientFrontier(R=12*returns, portfolio=prt, type="mean-StdDev", 
                                      match.col = "StdDev", momentFUN="annualized.moments", scale=12)
  xlim <- range(prt_ef$frontier[,2])*c(1, 1.5)
  ylim <- range(prt_ef$frontier[,1])*c(.80, 1.05)
  chart.EfficientFrontier(prt_ef, match.col="StdDev", chart.assets = FALSE, 
                          labels.assets = FALSE, xlim=xlim, ylim=ylim )
  points(with(annualized.moments(12*returns, scale=12), cbind(sqrt(diag(sigma)), mu)), pch=19 ) 
  text(with(annualized.moments(12*returns, scale=12), cbind(sqrt(diag(sigma)), mu)), 
       labels=colnames(returns), cex=.8, pos=4) 
  chart.EF.Weights(prt_ef, match.col="StdDev")

Средните стойности и стандартните отклонения на активите също трябва да бъдат коригирани и така са нанесени извън chart.EfficientFrontier и показани на диаграмата по-долу.

въведете описание на изображението тук

В крайна сметка би било по-просто, както предлага Робърт, да се изчислят теглата за ефективната граница, като се използват месечните възвръщаемости и след това да се изчислят възвръщаемостта на портфейла и стандартните отклонения, като се използват годишните средни стойности на активите и стандартните отклонения и месечните тегла, които са същото и в двата случая. Въпреки това, може би този пример е полезен, за да покаже използването на потребителски моментни и целеви функции.

person WaltS    schedule 05.06.2015
comment
Благодаря за изчерпателния отговор. Току-що започнах да използвам R и ми отнема години да правя неща в него. Засега прибягнах да направя това в excel, но ще е добре да погледна назад към това и да се опитам да разбера кода. - person user3381431; 08.06.2015

Не открива причината за грешката, но задаването на ограниченията частично работи!

prt.ef$frontier  #see the EF
xylims=apply(prt.ef$frontier[,c(2,1)],2,range)*c(.98,1.01)
chart.EfficientFrontier(prt.ef, match.col="pasd", 
      main="Portfolio Optimization", 
      xlim=xylims[,1], ylim=xylims[,2])
#or
plot(prt.ef$frontier[,c(2,1)],col=2)
person Robert    schedule 03.06.2015
comment
Можете ли да обясните какво правят ограниченията? По някаква причина границата, която виждам, е назъбена линия, а не гладка крива. - person user3381431; 04.06.2015

добре, така че опитах функцията pasd, предложена от WaltS, и графиката. EfficientFrontier изглежда работеше, но ми даде назъбена линия, а не гладка линия.

Сега създадох функция за годишно връщане, използвайки този код:

pamean <- function(R, weights=NULL){Return.annualized(apply(as.xts(t(t(R) * weights)),1,sum))}

и добави това като цел към моето портфолио prt.

> prt
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = colnames(returns))

Number of assets: 3 
Asset Names
[1] "Global REITs"      "Au REITs"          "Au Util and Infra"

Constraints
Enabled constraint types
        - long_only 
        - leverage 

Objectives:
Enabled objective names
        - pamean 
        - pasd 

След това отново създавам ефективната граница, използвайки този ред:

> prt.ef <- create.EfficientFrontier(R=returns, portfolio=prt, type="DEoptim", match.col="pasd")

но когато използвам функцията за обобщение, виждам, че е генерирана само 1 гранична точка. Какво означава съобщението за грешка и защо е генерирана само 1 точка?

> summary(prt.ef)
**************************************************
PortfolioAnalytics Efficient Frontier 
**************************************************

Call:
create.EfficientFrontier(R = returns, portfolio = prt, type = "DEoptim", 
    match.col = "pasd")

Efficient Frontier Points: 1 

Error in `colnames<-`(`*tmp*`, value = character(0)) : 
  attempt to set 'colnames' on an object with less than two dimensions
person user3381431    schedule 04.06.2015