최근 유행하는 Asset Allocation 시리즈
(Adaptive, Dynamic, Protective,....) 를 살펴 보면
결국 모멘텀이 꺾이는 자산을 비우고
모멘텀이 살아 있는 종목으로 바톤 터치 할 것,
여의치 않을 땐 채권이나 유동자금으로 비중을 늘려
Drawdown을 최대한 막을 것. 정도로 요약됩니다.
먼저, 국내 주식과 채권 만으로
모멘텀 기반의 Asset Allocation 이 Working 하는지
심플한 테스트를 합니다.
위 그림은 KOSPI의 12개월 롤링 수익률 입니다.
12개월 수익률이 "< 0" 이 될 경우,
주식 비중을 0% 로 만들고, 채권에 전액 투자 합니다.
그렇지 않을 경우 주식 비중을 100% 로 유지합니다.
채권 수익률은 국고채10년 지수를 이용하였으며,
2008년 2월 까진 해당 값이 없어
월 채권 수익률을 simple하게 0.18%로 두었습니다.
역사적으로 국내 주식 시장이 크게 하락한 시점은
IMF, IT 버블, 카드 대란, 서브프라임, 유로존 위기 정도 입니다.
2011년 유로존 위기를 제외하고는
주식 포지션을 줄임으로써
수익률 방어가 상당히 잘 되었습니다.
(특히 2008년의 수익률 방어가 상당히 눈에 띕니다)
Drawdown 그래프를 보면,
그 효과를 더욱 확연히 볼 수 있습니다.
##################
이번에는 유니버스를 확장하여, MKF 섹터기준의 적용입니다.
먼저, 아래 5개 전략을 기준으로 백테스트를 시행합니다.
1) 전 섹터 동일가중
2) 전 섹터 분산가중
3) 상위 5개 섹터 동일가중
4) 상위 5개 섹터 분산가중
5) 상위 5개 섹터 최소분산
1)
|
2)
|
3)
|
4)
|
5)
|
|
Cum Ret
|
2.2858
|
2.2929
|
3.8505
|
3.6069
|
3.9082
|
Ann Ret
|
0.0855
|
0.0857
|
0.1151
|
0.1111
|
0.1160
|
Ann Std
|
0.1968
|
0.1847
|
0.2475
|
0.2551
|
0.2419
|
Ann Sharpe
|
0.4344
|
0.4638
|
0.4648
|
0.4355
|
0.4794
|
1)번과 2)번 처럼 전 섹터에 투자하는 것은
KOSPI 대비 유의미한 알파가 나오지 않지만
3), 4), 5) 처럼 모멘텀을 이용한 전략은
초과수익이 나오는 것이 확인됩니다.
##################
이번에는 기존에 연구한 다양한 모멘텀 전략에 따른
수익률을 비교해 봅니다.
1) 단순 12개월 동일가중
2) 최근 1개월 제외한 단순 12개월 동일가중
3) 12개월 수익률 / 변동성 (1개월 제외, 위험조정) 동일가중
4) 위험 조정 분산가중
5) 위험조정 최소분산
1)
|
2)
|
3)
|
4)
|
5)
|
|
Cum Ret
|
3.8505
|
4.0667
|
6.2406
|
6.0437
|
6.4367
|
Ann Ret
|
0.1151
|
0.1184
|
0.1463
|
0.1441
|
0.1484
|
Ann Std
|
0.2475
|
0.2492
|
0.2186
|
0.2272
|
0.2107
|
Ann Sharpe
|
0.4648
|
0.4752
|
0.6694
|
0.6342
|
0.7044
|
기존 연구와 마찬가지로 단순히 모멘텀을 사용하기 보다는
최근 1개월을 제외하거나, 변동성을 조절해 주는 것이
뛰어난 성과로 연결됩니다.
특히 변동성 조절 & 최소분산까지 가미하면
수익률, 변동성, Drawdown 모든 측면에서
향상된 성과를 보입니다.
##################
위의 방법론은 국내 자산만을 대상으로 하였고,
유의미한 수익률이 나옴을 보였습니다.
다음엔 글로벌 자산에 까지
영역을 확장한 결과를 보이겠습니다.
##################
###### (1) STOCK & BOND CASE ######
library(PerformanceAnalytics)
library(quantmod)
library(FRAPO)
price = read.csv("raw.csv", row.names = 1, header = TRUE)
ro = nrow(price)
co = ncol(price)
ret = matrix(0,ro, co)
for(i in 1:2) {
ret[, i] = Delt(price[, i]) }
ret_kospi = as.matrix(ret[,1])
ret_bond = as.matrix(ret[,2])
ret_kospi[is.na(ret_kospi)] = 0
ret_bond[is.na(ret_bond)] = 0.0018
ret_kospi_12 = rbind(matrix(0,11,1),rollapply((ret_kospi+1), 12, prod) - 1)
ret = matrix(0,ro,1)
rownames(ret_kospi_12) = rownames(price)
rownames(ret) = rownames(price)
rownames(ret_kospi) = rownames(price)
rownames(ret_bond) = rownames(price)
for (i in which(rownames(ret_kospi_12) == "1995-11-30") : (ro-1) ) {
if ( ret_kospi_12[i] < 0 ) {
w = 0 }
else {
w = 1
}
w_b = 1 - w
ret[i+1] = w * ret_kospi[i+1,] + w_b * ret_bond[i+1,]
}
par(mfrow = c(3,1))
chart.CumReturns(cbind(ret_kospi,ret))
legend('topleft', c("KOSPI", "AAA"), col = 1:2, lty=1, bty='n')
chart.RollingPerformance(ret_kospi, 12)
barplot(t(cbind(apply.yearly(ret, Return.cumulative), apply.yearly(ret_kospi, Return.cumulative) )), col=1:2, beside = TRUE)
legend('topleft', c("KOSPI", "AAA"), col = 1:2, lty=1, bty='n')
###### (2) SECTOR CASE ######
price_d = read.csv("sector.csv", row.names=1)
ret_d = matrix(0,dim(price_d)[1], dim(price_d)[2])
for (i in 1 : dim(price_d)[2] ) {
ret_d[,i] = Delt(price_d[, i])
}
rownames(ret_d) = rownames(price_d)
date_m = as.matrix(read.csv("date_m.csv", header = FALSE))
price_m = matrix(0,dim(date_m)[1],dim(price_d)[2])
rownames(price_m) = date_m
for (i in 1 : dim(price_m)[1]) {
for (j in 1 : dim(price_m)[2]) {
price_m[i,j] = price_d[which(rownames(ret_d) == rownames(price_m)[i]) , j ]
}
}
ret_m = matrix(0,dim(price_m)[1], dim(price_m)[2])
for (i in 1 : dim(price_m)[2] ) {
ret_m[,i] = Delt(price_m[, i])
}
rownames(ret_m) = rownames(price_m)
ret_kospi = as.matrix(ret_m[,dim(ret_m)[2]])
ret_kospi_12 = rbind(matrix(0,11,1),rollapply((ret_kospi+1), 12, prod) - 1)
ret_m = ret_m[, 1 : (dim(ret_m)[2]-1)]
ret_d = ret_d[, 1 : (dim(ret_d)[2]-1)]
ro = dim(ret_m)[1]
co = dim(ret_m)[2]
num = 5
ret_mom = matrix(0,ro,co)
for (i in which(rownames(ret_m) == "2002-01-31") : ro ) {
for (j in 1: co) {
ret_mom[i,j] = price_m[i,j] / price_m[i-12,j] - 1
}
}
ret_mom_1m = matrix(0,ro,co)
for (i in which(rownames(ret_m) == "2002-01-31") : ro ) {
for (j in 1: co) {
ret_mom_1m[i,j] = price_m[i-1,j] / price_m[i-12,j] - 1
}
}
rownames(ret_mom) = rownames(price_m)
rownames(ret_mom_1m) = rownames(price_m)
std_roll = rbind( matrix(0,12,co), rollapply(ret_m,11,sd)[-1,][-nrow(rollapply(ret_m,11,sd)[-1,]),] )
#----------- AAA Compare -----------#
ret_port = matrix(0,dim(ret_m),5)
rownames(ret_port) = rownames(price_m)
colnames(ret_port) = c("EW","VW","MOM & EW","MOM& VW","MOM & MV" )
ret_mom_stra = matrix(0,dim(ret_m),5)
rownames(ret_mom_stra) = rownames(price_m)
colnames(ret_mom_stra) = c("12M","12M SKIP 1M","Risk Adjusted","Risk Adjusted & VW ", "Risk Adjusted & MV")
ret_mom_mix = matrix(0,dim(ret_m),5)
rownames(ret_mom_mix) = rownames(price_m)
for ( i in which(rownames(ret_m) == "2002-01-31") : ( dim(price_m)[1] - 1 ) ) {
#----------- 1. EW -----------#
ret_port[i+1,1] = mean(ret_m[i+1,])
#----------- 2. Volatility Weighted -----------#
sd = matrix(0, 1, dim(ret_m)[2] )
for (w in 1 : dim(ret_m)[2]) {
sd[1, w] = sd( ret_d[ ( which(rownames(ret_d) == rownames(ret_m)[i]) - 60 ) : which(rownames(ret_d) == rownames(ret_m)[i]), w])
}
sd = as.matrix( (1/sd) / sum((1/sd)) )
ret_port[i+1 , 2] = ret_m[i+1,] %*% t(sd)
#----------- 3. TOP 5 MOMENTUM, EW -----------#
ret_port[i+1, 3] = mean(ret_m[ i+1, which(rank(-ret_mom[i,]) <= num)])
#----------- 4. TOP 5 MOMENTUM, VW -----------#
k = which(rank(-ret_mom[i,]) <= num)
sd_vw = sd[, k]
sd_vw = (1 / sd_vw) / sum( 1/ sd_vw)
ret_port[i+1 , 4] = ret_m[i+1,k] %*% sd_vw
#----------- 5. TOP 10 MOMENTUM, MV -----------#
MV는 생략합니다
#----------- Momentum Compare -----------#
ret_mom_stra[i+1, 1] = mean(ret_m[ i+1, which(rank(-ret_mom[i,]) <= num)])
ret_mom_stra[i+1, 2] = mean(ret_m[ i+1, which(rank(-ret_mom_1m[i,]) <= num)])
ret_mom_stra[i+1, 3] = mean(ret_m[ i+1, which(rank(- ret_mom_1m[i,] / std_roll[i, ]) <= num)])
k2 = which(rank(- ret_mom_1m[i,] / std_roll[i, ]) <= num)
sd_mom = sd[,k2 ]
sd_mom = (1/sd_mom) / sum(1/sd_mom)
ret_mom_stra[i+1, 4] = ret_m[i+1,k2] %*% sd_mom
MV는 생략합니다
}
ret_port = ret_port[ which(rownames(ret_port) == "2002-01-31") : nrow(ret_port) , ]
ret_mom_stra = ret_mom_stra[ which(rownames(ret_mom_stra) == "2003-01-31") : nrow(ret_mom_stra) , ]
ret_kospi = as.matrix(ret_kospi[ which(rownames(ret_kospi) == "2003-01-31") : nrow(ret_kospi) , ])
table.AnnualizedReturns(ret_port)
table.AnnualizedReturns(ret_mom_stra)
Return.cumulative(ret_port)
Return.cumulative(ret_mom_stra)
#----------- CUMULATIVE RETURN -----------#
plot(0,type='n',axes=FALSE,ann=FALSE)
par(mfrow = c(2,1))
chart.CumReturns(cbind(ret_port, ret_kospi), main = "Adaptive Asset Allocation")
legend('topleft',c("EW","VW","MOM & EW","MOM& VW","MOM & MV","KOSPI" ),col=1:6, lty=1, cex = 0.5, bty='n')
chart.CumReturns(cbind(ret_mom_stra, ret_kospi), main = "Comparing Momentum")
legend('topleft',c("12M","12M SKIP 1M","Risk Adjusted","Risk Adjusted & VW ", "Risk Adjusted & MV", "KOSPI"),col=1:6, lty=1, cex = 0.5, bty='n')
#----------- YEARLY BARPLOT -----------#
plot(0,type='n',axes=FALSE,ann=FALSE)
par(mfrow = c(2,1))
barplot(t(cbind( apply.yearly(ret_port, Return.cumulative), apply.yearly(ret_kospi, Return.cumulative) )), col=1:6, beside = TRUE)
legend('topright',c("EW","VW","MOM & EW","MOM & VW","MOM & MV","KOSPI" ), horiz = TRUE, col=1:6, lty=1, cex = 0.4, bty='n')
barplot(t(cbind( apply.yearly(ret_mom_stra, Return.cumulative), apply.yearly(ret_kospi, Return.cumulative) )), col=1:6, beside = TRUE)
legend('topright',c("12M","12M SKIP 1M","Risk Adjusted","Risk Adjusted & VW ", "Risk Adjusted & MV", "KOSPI"), horiz = TRUE, col=1:6, lty=1, cex = 0.4, bty='n')
#----------- DRAWDOWN -----------#
plot(0,type='n',axes=FALSE,ann=FALSE)
par(mfrow = c(2,1))
chart.Drawdown(ret_port, main = "")
legend('bottomleft',c("EW","VW","MOM & EW","MOM& VW","MOM & MV"), horiz= TRUE, col=1:5, lty=1, cex = 0.6, bty='n')
chart.Drawdown(ret_mom_stra, main = "")
legend('bottomleft',c("12M","12M SKIP 1M","Risk Adjusted","Risk Adjusted & VW ", "Risk Adjusted & MV"), horiz = TRUE, col=1:5, lty=1, cex = 0.6, bty='n')
12개월 수익률 / 변동성 (1개월 제외, 위험조정)을 사용했다는 것은 결국 샤프지수가 높은 자산에 투자한다는 것이군요! 감사합니다
답글삭제