Диаграмма аналитики портфеля R.EfficientFrontier function
Я пытаюсь использовать функцию 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
Кто-нибудь знает, почему это так? Когда я использую сводку (prt.ef), я могу видеть вес, но почему происходит сбой функции chart.EfficientFrontier?
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")
В добавление к комментариям Роберта, расчет оптимизации с ежемесячной доходностью является задачей квадратичного программирования с линейными ограничениями. когда 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
и показано на графике ниже.
В конце дня было бы проще, как предлагает Роберт, вычислить весовые коэффициенты для эффективной границы с использованием месячной доходности, а затем вычислить доходность портфеля и стандартные отклонения с использованием среднегодовых активов и стандартных отклонений и месячных весов, которые являются одинаковые в обоих случаях. Однако, возможно, этот пример полезен для демонстрации использования пользовательских моментных и целевых функций.
Не находит причину ошибки, но установка пределов частично работает!
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)
Хорошо, так что я попробовал pasd
функция, которую предложил WaltS, и график. Эффективная граница, казалось, работала, но она дала мне неровную линию, а не плавную линию.
Теперь я создал функцию возврата в годовом исчислении, используя этот код:
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 граница. Что означает ошибка msg и почему был получен только 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