Post List

2019년 1월 29일 화요일

R에서 ggplot을 이용한 누적수익률 및 연도별 수익률 그래프 그리기



각종 블로그에서 백테스트 한 결과를 보면
다음과 같이 수익률 그래프를 그려놓는 경우가 많습니다.



S&P 500 지수의 수익률을
기본적인 plot() 함수를 사용하여 나타낸 그림입니다.

무엇을 의미하고자 하는지는 알겠지만 아쉬운 점이 있습니다.
가장 큰 문제는 X축에 날짜가 나타나지 않아
어느 시점에서 어떻게 수익률이 움직였는지
확인하기가 힘들다는 사실입니다.

이러한 점을 보완하기위해
누적수익률을 아름답게 그리는 봅을 알아보도록 하겠습니다.

# for data 
library(quantmod)
library(PerformanceAnalytics)
library(magrittr)
library(tidyr)
library(dplyr)
library(lubridate)

# for graph
library(ggplot2)
library(dygraphs)
library(highcharter)


필요한 패키지들을 모두 열어줍니다.
물론 설치가 안된 패키지들은 
install.packages() 를 통해 먼저 설치해 주어야 겠죠.

위의 6가지는 데이터 처리를 위한 패키지 이며,
아래 3가지는 그래프를 위한 패키지 입니다.


symbols = c('SPY', 'TLT')
price = getSymbols(symbols, auto.assign = TRUE) %>% 
  lapply(., function(x) Ad(get(x))) %>%
  do.call(cbind, .) %>%
  `colnames<- span="">(symbols)

price %>% head()
                SPY      TLT
2007-01-03 110.5206 60.15877
2007-01-04 110.7551 60.52355
2007-01-05 109.8717 60.26010
2007-01-08 110.3799 60.36814
2007-01-09 110.2861 60.36814
2007-01-10 110.6535 60.09798


먼저 예제로써 SPY, TLT
두가지 ETF 데이터를 받아옵니다.

그 후 수정주가만을 뽑아 price 변수에 저장하도록 합니다.


R = Return.calculate(price) %>% na.omit()
R.spy = R[, 1]
R.spy.cum = cumprod(1+R.spy) - 1

Return.calculate() 함수를 사용해 수익률을 구한 후,
na.omit() 함수를 통해 1행의 NA 데이터를 지워 줍니다.

먼저 SPY 종목의 수익률만을 뽑아 R.spy 변수에 저장해주며,
cumprod() 함수를 이용해 누적수익률 시계열을 만들어 줍니다.


# using xts
plot(R.spy.cum)



quantmod를 통해 다운로드 받은 데이터는
시계열 형태인 xts 형식을 따릅니다.

따라서 X축에 자동으로 날짜가 생성됩니다.


# using PerformanceAnalytics
chart.TimeSeries(R.spy.cum)


PerformanceAnalytics 패키지의 chart.TimeSeries() 함수를 이용하여도
동일한 결과가 나타납니다.

그러나 이러한 그래프의 가장 큰 단점은
X축에서 연도 데이터만 나타나지 않는다는 점,
그리고 뭔가 이쁘지 않다는 점입니다.

2007-01-04 / 2019-01-28 이 글자도 지워버리고 싶고요


# using ggplot2
ggplot(R.spy.cum, aes(x = Index, y = SPY)) +
  geom_line() 




ggplot2 패키지의 기본함수만 이용하여도
굉장히 깔끔한 그래프를 그릴 수 있습니다.

x축에는 인덱스(날짜), y축에는 데이터를 입력한 후,
geom_line()을 통해 선 그래프를 그려주면
위와 같이 깔끔한 형태로 시계열 그래프가 완성됩니다.


# using dygraph
dygraph(R.spy.cum) %>%
  dyRangeSelector()




dygraphs 패키지를 사용한다면
굉장히 인터랙티브한 그래프를 그릴수도 있습니다.





x축 아래에 있는 셀럭터를 이용하여 원하는 기간을 지정해주면
해당 기간 동안의 수익률만이 나타나기도 하며,
그래프에 마우스를 올려둘 경우
해당 지점의 누적수익률이 우측 상단에 나타납니다.




그래프의 우클릭을 통해 Inspect를 눌러보면
해당 그래프가 그려지는 과정이 보입니다.

본 패키지는 java를 통해 그래프가 그려집니다.



# using highcharter
highchart(type = 'stock') %>%
  hc_add_series(R.spy.cum) %>%
  hc_scrollbar(enabled = FALSE)



highcharter 패키지를 사용할 경우
이보다 더 인터랙티브한 그래프를 그릴 수 있습니다.

좌측 상단의 zoom을 통해 원하는 기간을 고르거나,
우측 상단을 통해 원하는 기간을 직접 입력하거나,
아래의 셀렉터를 통해 원하는 기간을 설정할 수도 있습니다.


한 종목의 경우 위와 같이 쉽게 나타낼 수 있지만,
두 종목 이상의 경우, 그리고 그래프를 아름답게 꾸미려면
어떻게 해야할까요?


이번에는 SPY와 TLT 두개의 데이터가 모두 들어있는
R 변수를 사용하도록 하도록 하겟습니다.


R %>% head()
                     SPY          TLT
2007-01-04  0.0021219569  0.006063555
2007-01-05 -0.0079761534 -0.004352802
2007-01-08  0.0046249927  0.001792894
2007-01-09 -0.0008498196  0.000000000
2007-01-10  0.0033316893 -0.004475225
2007-01-11  0.0043803662 -0.005844156

R.cum = cumprod(1 + R) %>%
    data.frame() %>%
    mutate(Date = rownames(.),
           Date = as.Date(Date)) %>%
    gather(key, value, -Date)

R.cum %>% head()
        Date key     value
1 2007-01-04 SPY 1.0021220
2 2007-01-05 SPY 0.9941289
3 2007-01-08 SPY 0.9987267
4 2007-01-09 SPY 0.9978780
5 2007-01-10 SPY 1.0012026
6 2007-01-11 SPY 1.0055882

먼저 cumprod() 함수를 이용해 누적곱을 구해주며,
이를 데이터프레임 형식으로 바꿉니다.

그 후 mutate() 함수를 이용해 
Date 열을 행이름, 즉 시간 정보를 가져오며,
이를 다시 as.Date() 함수를 이용해 날짜 형태로 바꿉니다.

그룹별 그래프를 그리기 위해 tidyr의 gather() 함수를 사용하여
key와 value 부분으로 나누어주며,
-Date를 통해 시간 부분은 고정시켜 줍니다.

결과를 살펴보면
Date 열에는 시계열 정보,
key 열에는 SPY와 TLT,
value 열에는 각 시계열의 종목 별 누적수익률이 나타나게 됩니다.


ggplot(R.cum, aes(x = Date, group = key, color = key)) +
    geom_line(aes(y = value)) +
    ggtitle('Portfolio Cumulative Return')




x축에는 시계열 정보,
그룹과 컬러는 key, 즉 종목 별로 나누어 주며,
geom_line()을 통해 누적수익률을 그려주도록 합니다.

굉장히 심플하게 두 종목의 누적수익률 그래프가 완성되었습니다.

그러나 좀 더 깔끔하게 정리하고 싶은 몇가지 부분이 있습니다.




1. x축과 y축 라벨 삭제 (Date, Value)
2. 테마 변경 (회색 변경에서 무배경으로)
3. x축에 모든 연도 표시
4. 그래프 양 옆에 비어있는 공간 삭제
5. 제목인 Portfolio Cum....이 가운데로
6. 범례가 우측이 아닌 하단으로
7. 범례의 key 단어 삭제
8. x축 라벨(연도)가 45도 회전되게

등입니다.

이를 ggplot()에 적용하면 다음과 같습니다.


  
  ggplot(R.cum, aes(x = Date, group = key, color = key)) +
    geom_line(aes(y = value)) +
    ggtitle('Portfolio Cumulative Return') +
    xlab(NULL) +
    ylab(NULL) +
    theme_bw() +
    scale_x_date(date_breaks="years", date_labels="%Y",
                 expand = c(0, 0)) +
    theme(plot.title = element_text(hjust = 0.5,
                                    size = 12),
          legend.position = 'bottom', 
          legend.title = element_blank(),
          axis.text.x = element_text(angle = 45, hjust = 1, size = 8),
          panel.grid.minor.x = element_blank()) 




xlabylab은 NULL을 통해 날려줍니다.
theme_bw()는 배경을 흰색으로 바꿔줍니다.

x축이 날짜 형태이므로 scale_x_date를 통해
연도별로 breaks를 주며, %Y를 통해 yyyy 형식으로 나타나게 합니다.
(이는 표준정규식이며 %y는 yy형식입니다.)
expand는 양쪽 빈공간을 의미하며, 0을 통해 날려줍니다.

theme() 함수에는 더욱 세세한 조정을 할 수 있으며,

plot.title을 통해 제목의 위치를 0.5인 가운데로, 사이즈는 12로,
legend.position을 통해 범례 위치는 아래로,
legend.title을 통해 범례 제목을 blank로,
axis.text.x을 통해 x축 라벨의 기울기는 45도, 위치및 사이즈 조정,
panel.grid.minor.x를 통해 보조선을 삭제해 줍니다.


조건을 추가함에 따라 기본 ggplot 보다
좀더 디테일하고 깔끔하게 그래프가 그려졌습니다.

이러한 자세한 조건들은 구글 검색만으로도 쉽게 찾을 수 있습니다.



이번에는 연도별 수익률을 그려보도록 하겠습니다.
사실 누적수익률은 다른 패키지에도 많이 존재하지마,
연도별 수익률이 존재하는 패키지는 그간 찾아보기 힘들었습니다.


R.yr = apply.yearly(R, Return.cumulative) %>%
    data.frame() %>%
    mutate(Date = rownames(.),
           Date = year(Date)) %>% 
    gather(key, value, -Date) 

R.yr = apply.yearly(R, Return.cumulative) %>%
    data.frame() %>%
    mutate(Date = rownames(.),
           Date = year(Date)) %>% 
    gather(key, value, -Date)

R.yr %>% head()
  Date key       value
1 2007 SPY  0.05332173
2 2008 SPY -0.36795009
3 2009 SPY  0.26351799
4 2010 SPY  0.15056109
5 2011 SPY  0.01895012
6 2012 SPY  0.15990361

R.yr %>% tail()
   Date key        value
21 2014 TLT  0.273018783
22 2015 TLT -0.017896827
23 2016 TLT  0.011716029
24 2017 TLT  0.091825977
25 2018 TLT -0.016118937
26 2019 TLT -0.009052736

먼저 apply.yearly() 함수를 이용해 연도별 수익률을 구해줍니다.
그 후는 위와 동일하게 데이터프레임 형태를 바꾸어주며,

Date열은 lubridate 패키지의 year() 함수를 사용하여
yyyy 정보만 남겨두도록 합니다.

결과적으로 Date에는 연도가,
key에는 종목 정보가,
value에는 종목의 해당 년도 수익률이 나타나게 됩니다.


ggplot(R.yr, aes(x = Date, y = value, fill = key)) +
    geom_bar(position = "dodge", stat = "identity") +
    ggtitle('Yearly Return')





geom_bar() 를 통해 막대그래프로 표시하면
손쉽게 연도별 수익률을 나타낼 수 있습니다.

두 종목의 수익률이 옆으로 분리되어야 하므로 
position은 dodge를,

y축은 고유값이어야 하므로 stat은 identity를 지정해줍니다.

그러나 이 그래프도 굉장히 아쉬운 점이 많습니다.





1. x축과 y축 라벨 삭제 (Date, Value)
2. 테마 변경 (회색 변경에서 무배경으로)
3. x축에 모든 연도 표시
4. 그래프 양 옆에 비어있는 공간 삭제
5. 제목인 Yearly Return 이 가운데로
6. 범례가 우측이 아닌 하단으로
7. 범례의 key 단어 삭제
8. x축 라벨(연도)가 45도 회전되게
9. 보조 라인 삭제
10. 각 연도별 수익률이 글자로 표시

등입니다.

이를 ggplot()에 적용하면 다음과 같습니다.


ggplot(R.yr, aes(x = Date, y = value, fill = key)) +
    geom_bar(position = "dodge", stat = "identity") +
    ggtitle('Yearly Return') +
    xlab(NULL) +
    ylab(NULL) +
    theme_bw() +
    scale_x_continuous(breaks = R.yr$Date,
                       expand = c(0.01, 0.01)) +
    theme(plot.title = element_text(hjust = 0.5,
                                    size = 12),
          legend.position = 'bottom', 
          legend.title = element_blank(),
          legend.text = element_text(size=7),
          axis.text.x = element_text(angle = 45, hjust = 1, size = 8),
          panel.grid.minor.x = element_blank()
    ) +
    geom_text(aes(label = paste(round(value * 100, 2), "%"),
                  vjust = ifelse(value >= 0, -0.5, 1.5)),
              position = position_dodge(width = 1),
              size = 3) 




xlab과 ylab은 NULL을 통해 날려주며,
theme_bw()는 배경을 흰색으로 바꿔줍니다.

x축이 연속형이므로 scale_x_continuous를 통해 연도별로 breaks를 주며, 
expand를 통해 양쪽공간을 0.01만 줍니다.
(막대 그래프는 공간을 0으로 줄 경우 매우 답답합니다.)

theme() 함수에서는
plot.title을 통해 제목의 위치를 0.5인 가운데로, 사이즈는 12로,
legend.position을 통해 범례 위치는 아래로,
legend.title을 통해 범례 제목을 blank로,
legend.text를 통해 범례의 글자 크기를,
axis.text.x을 통해 x축 라벨의 기울기는 45도, 위치및 사이즈 조정,
panel.grid.minor.x를 통해 보조선을 삭제해 줍니다.

geom_text()는 그림에 텍스트를 삽입하는 부분으로써,
문구인 label은 연도별 수익률 부분인 value를 %로 바꿔주기 위해
100을 곱해준 후 소수 둘째자리까지 반올림을 하며,
paste() 를 통해 이를 '%' 와 결합하여 줍니다.

즉 0.1331은 13.31 % 로 바뀌게 됩니다.

vjust는 텍스트가 위치하는 높이를 나타내며,
만일 수익률이 0보다 클 경우는 -0.5지점에
0보다 작을 경우는 1.5에 지점에 위치시켜 줍니다.

(0은 각 그래프의 끝 부분을 나타내며
숫자가 음수이면 위로, 양수이면 아래로
내려감을 의미합니다.)

position 역시 글자의 위치를 나타내며,
width = 1, 즉 각 막대 그래프 기준
정가운데에 위치하게 합니다.

이를 통해 각 연도별 수익률 그래프와
해당 값이 그래프가 아름답게 나타나게 되었습니다.




위에서 살펴본 누적수익률 및 연도별 수익률 그래프는
좀더 디테일한 편집을 거쳐
HenryQuant 패키지 내에 업로드할 예정입니다.


댓글 없음:

댓글 쓰기