PGR21.com
- 자유 주제로 사용할 수 있는 게시판입니다.
- 토론 게시판의 용도를 겸합니다.
Date 2020/11/01 14:16:59
Name 모찌피치모찌피치
Subject [일반] 효율적 투자기회선을 그려보자!
0. 시작하기 전에
저는 딱히 전공자도 아니고 석사 출신도 아니며 그저 경제학 학부졸업생 나부랭이로 이하 본문에는 오류가 포함되어있을 확률이 높으며, 해당 오류에 대한 지적은 감사합니다.


아래 내용은 모두 R을 통하여 작성되었습니다.


1. 효율적 투자기회선이란?
 - 정해진 위험수준 하에서 가장 높은 수익률을 달성하는 시장 포트폴리오를 '효율적 포트폴리오'라고 하며, 그 효율적 포트폴리오들을 위험-수익 좌표공간에 나타낸 것이 효율적 투자기회선입니다. 그리고 무위험수익률로부터 효율적 투자기회선 상의 단 한 점만 지나가도록 선을 그을 수가 있는데, 해당 선을 자본시장선이라고 하며 개인은 자신의 무차별곡선과 자본시장선 상의 접점을 보유함으로써 효용을 극대화할 수 있습니다.

What's The Difference Between 45% Return And 28%? The Efficient Frontier |  Seeking Alpha
<그림 1> 투자기회선은 대충 저렇게 생겼다.


2. 사전 정보수집
효율적 투자기회선을 그리려면 우선 포트폴리오를 구해야합니다. 코스피 시장을 기준으로 상위 5개 사(전체 상장사를 처리하려면 시간이 오래걸리니까)를 뽑아 포트폴리오를 구성해봅시다.

library(quantmod)
library(plotly)
library(PerformanceAnalytics)
library(timetk)
library(tidyverse)

위 다섯 개의 라이브러리를 우선 로딩해줍니다. 네이버 파이낸스로 가서 확인해보니 한국의 코스피 시가총액 기준 상위 5개 사는 "삼성전자, SK하이닉스, NAVER, 삼성바이오로직스, LG화학"이군요. 해당 5개 사의 ticker를 통해 주가 데이터를 받아봅시다. 한국거래소에서 직접 받아도 되지만 코드가 20배는 길어지므로 야후 파이낸스 자료를 사용하겠습니다.

ticker = c('005930.KS', '000660.KS', '035420.KS', '207940.KS', '051910.KS')
price_data = getSymbols(ticker, from = '2017-01-01', to = '2020-10-31')
prices = do.call(cbind,
                 lapply(ticker, function(x) Cl(get(x))))
rets = Return.calculate(prices, method = 'log') %>%
  na.omit()

각 사의 ticker를 통해 주가데이터를 받아오고, prices에는 종가(Cl)만 남긴 후 rets에 각 날짜 종가 기준 로그수익률을 저장합니다. 이제 자료가 준비되었으니 다음 단계인 포트폴리오 구성으로 가봅시다.


3. 효율적 투자기회선 그리기
num_port = 500
all_wts = matrix(nrow = num_port, ncol = length(ticker))
port_returns = vector('numeric', length = num_port)
port_risk = vector('numeric', length = num_port)
port_sr = vector('numeric', length = num_port)

500번의 무작위 시행으로 우선 시도해보겠습니다. all_wts에는 5개 사의 비중을 담을 것이며, port_returns와 port_risk는 각 비중 별 수익과 위험, port_sr에는 샤프 비율을 담을 겁니다.

샤프 비율이란? 수익률/표준편차로 구하며, 위험 대비 수익률이 얼마나 좋았는가를 측정하는 지표입니다.

for (i in seq_along(port_returns)) {
  wts = runif(n = length(ticker))
  wts = wts/sum(wts)
  
  all_wts[i,] = wts
  
  port = Return.portfolio(R = rets, weights = wts, verbose = TRUE)
  
  a = StdDev.annualized(port$returns)[1]
  b = SharpeRatio.annualized(port$returns, Rf = 0.0155/360)[1]
  c = a*b
  
  port_returns[i] = c
  port_risk[i] = a
  port_sr[i] = b
}

각 5개 사에 대한 비중을 무작위로 생성하여 wts에 담고, 각 비중으로부터 수익률을 구하여 port에 담고, 그 후 연간표준편차, 연간수익률, 연간 샤프비율을 구하는 일련의 과정입니다. SharpeRatio.annualized 함수의 Rf에는 대충 국채수익률이 10월 말 기준 1.55%길래 넣어보았습니다. 0으로 진행하셔도 무방합니다.

즉 무작위로 생성된 비중의 포트폴리오가 각각 얼마만큼의 위험과 표준편차를 보유하고 있는지 계산하는 작업입니다. 이 작업을 500번 수행하여 결과를 한 번 보도록 하죠.

all_wts = tk_tbl(all_wts)
colnames(all_wts) = colnames(rets)

pf_val = tibble(ret = port_returns, risk = port_risk, sr = port_sr)
pf_val = tk_tbl(cbind(all_wts, pf_val))

all_wts를 작업하기 쉽도록 matrix에서 tibble로 변환하고, pf_val에 모든 정보를 담습니다. pf_val에 담기는 정보는 all_wts의 비중, 그리고 해당 비중으로부터 산출된 위험과 수익이 담겨있습니다. 잠시 어떤 정보가 담겨있나 확인해봅시다.

head(pf_val)
# A tibble: 6 x 8
  X005930.KS.Close X000660.KS.Close X035420.KS.Close X207940.KS.Close X051910.KS.Close   ret  risk    sr
             <dbl>            <dbl>            <dbl>            <dbl>            <dbl> <dbl> <dbl> <dbl>
1          0.312             0.0519           0.216            0.167            0.253  0.138 0.242 0.569
2          0.286             0.272            0.212            0.185            0.0454 0.128 0.248 0.515
3          0.0340            0.152            0.148            0.266            0.400  0.171 0.280 0.609
4          0.0216            0.256            0.554            0.0833           0.0844 0.109 0.248 0.440
5          0.00254           0.242            0.280            0.286            0.190  0.164 0.273 0.599
6          0.0730            0.366            0.0101           0.214            0.337  0.152 0.273 0.557

1행에 적힌 1번 포트폴리오의 경우 삼성전자 31.2%, SK하이닉스 5.19%, NAVER 21.6%, 삼성바이오로직스 16.7%, LG화학 13.8%을 보유한 포트폴리오이고, 연간수익률은 13.8%, 연간표준편차는 24.2%, 샤프비율은 56.9%네요.

min_var = pf_val[which.min(pf_val$risk),]
max_sr = pf_val[which.max(pf_val$sr),]

min_var에는 최소분산포트폴리오를, max_sr에는 최대샤프비율포트폴리오를 담았습니다. 최소분산포트폴리오의 경우 시장포트폴리오 중 가장 적은 위험을 보유한 포트폴리오이며, 최대샤프비율포트폴리오의 경우 Tangency Portfolio로 Y-절편에서 해당 점까지 이어서 효율적투자기회선을 그릴 포트폴리오입니다.

p = ggplot(aes(x = risk, y = ret, color = sr), data = pf_val) +
  geom_point() + theme_classic() +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(labels = scales::percent) +
  labs(x = 'risk', y = 'return') +
  geom_point(aes(x = risk, y = ret, color = sr), data = min_var, color = 'red') +
  geom_point(aes(x = risk, y = ret, color = sr), data = max_sr, color = 'red') +
  annotate('text', x = 0.33, y = 0.22, label = "Tangency Portfolio") +
  annotate('text', x = 0.24, y = 0.11, label = "Minimum variance portfolio")
  
ggplotly(p)

ggplot 툴을 사용해 이제 포트폴리오를 그래프로 나타내봅시다. 위에 설명드린 pf_val 안의 risk와 ret을 좌표로 나타낸 모습은 다음과 같습니다.

XfA2YE5.png
<그림 2> 500개 무작위 포트폴리오

Minimum 부분이 살짝 잘렸네요. 어쨌건 500개의 무작위 포트폴리오를 좌표평면에 나타낸 모습은 위와 같습니다! 이제 가장자리에 있는 녀석들만 모아서 선으로 이으면 효율적 투자기회선을 완성할 수 있을 것 같네요.

foo = pf_val
foo$quantile = ntile(foo$risk, 100)
d = aggregate(ret ~ quantile, data = foo, max)
pf_line2 = pf_val[which((pf_val$ret %in% d$ret)),]

pf_val을 foo에 담고(그냥 pf_val에 진행하셔도 무방합니다만, 어쩌다보니 저렇게 되었네요.) pf_val의 risk를 100개의 블록으로 나누어 각 블록에서 가장 높은 수익률을 기록한 포트폴리오만 남깁시다. 해당 자료를 pf_line2에 담고 그리면 다음과 같은 그림이 나옵니다.

p2 = ggplot(aes(x = risk, y = ret, color = sr), data = pf_line2) +
  geom_point() + theme_classic() +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(labels = scales::percent) +
  labs(x = 'risk', y = 'return')

ggplotly(p2)

UuhnJVj.png
<그림 3> 효율적 투자기회선 밑작업

이제 해당 점들을 이어서 효율적 투자기회선을 그려봅시다.

p2 = ggplot(aes(x = risk, y = ret, color = sr), data = pf_line2) +
  geom_point() + theme_classic() +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(labels = scales::percent) +
  labs(x = 'risk', y = 'return') +
  geom_smooth(method = 'lm', formula = y ~ log(x), alpha = 0) +
  geom_smooth(method = 'loess', col = 'red', level = 0, alpha = 0)

ggplotly(p2)

아래 geom_smooth 항목이 두 개가 추가되었는데, 하나는 해당 데이터를 로그스케일 선형회귀를 사용해서 그려본 값이고, 하나는 loess 방식을 사용해서 그려본 값입니다.

QVTiiRq.png
<그림 4> 효율적 투자기회선

파란색이 로그 리그레션, 빨간색이 loess 리그레션인데 빨간색 선이 보기에는 더 예쁘지만 수식으로 나타낼 수 없다는 단점을 가지고 있습니다. 본 효율적 투자기회선을 수식으로 표현하고 싶다면 로그 리그레션을 사용하시면 되겠네요.

뭔가 느낌상으로는 envelope curve를 사용해야할 것 같지만, 해당 내용을 자세히 모르므로 일단 이 정도에서 넘어가겠습니다.



4. 그래서 뭐?
자, 이제 효율적 투자기회선을 그려보았습니다. 그렇다면이 투자기회선으로 무엇을 할 수 있을까요? 아까 위에서 이 투자기회선으로부터 투자자의 최적자산배분을 도출할 수 있다고 말씀드렸습니다. 이제부터 최적자산배분을 한 번 계산해보겠습니다.

우선 필요한 정보는 다음과 같습니다.

무위험자산수익률 : .0155 (위에서 언급했습니다.)
Tangency Portfolio의 {위험, 수익률} = {.3341, .2146}

위 자료를 토대로 계산하면 자본시장선의 경우 Y-절편 .0155, 기울기 .5959를 가진 직선이 됩니다.

y = .5959 * x + .0155    ------ 식 1

일반적인 무차별곡선의 형태인 위험회피성향의 투자자를 가정하여 무차별곡선을 다음과 같이 표현하겠습니다.

y = U + 2 * x^2 (2는 임의의 위험회피계수)     ------ 식 2

무차별곡선이 자본시장선과 접하는 점은 미분을 통해 구할 수 있죠? 식 1과 식 2를 미분하여 x와 y값을 구하면 {x, y} = {.148975, .1063747}이며, 해당 수치를 식 2에 대입하면 U = .061876을 도출할 수 있습니다.

이제 무차별곡선 그래프를 그려보겠습니다.

indiff_curve = function(risk, U0, A) {
  ret = U0 + A*risk^2
  return (ret)
}

risk_grid = 0:40/100
util_level = 0.061876
risk_aversion = 2

return = indiff_curve(risk_grid, U0=util_level, A=risk_aversion)
idc = data.frame(sd = risk_grid, r = return)

0부터 40%까지의 x값에 대해 y값을 도출한 뒤, 두 값을 하나의 프레임 안에 묶는 작업입니다. indiff_curve 함수를 정의한 후 risk_grid를 0~40%까지 함수 안에 넣어서 나온 y값을 idc 안에 묶었습니다.

이제 마지막으로 그래프를 그려보겠습니다.

p3 = ggplot(aes(x = risk, y = ret), data = pf_val) +
  geom_point(color = 'lightblue') + theme_classic() +
  scale_y_continuous(labels = scales::percent, limits = c(0, .4)) +
  scale_x_continuous(labels = scales::percent, limits = c(0, .4)) +
  labs(x = 'risk', y = 'return') +
  geom_point(aes(x = risk, y = ret, color = sr), data = min_var, color = 'red') +
  geom_point(aes(x = risk, y = ret, color = sr), data = max_sr, color = 'red') +
  annotate('text', x = 0.33, y = 0.22, label = "Tangency Portfolio") +
  annotate('text', x = 0.22, y = 0.12, label = "Minimum variance portfolio") +
  geom_line(aes(x = sd, y = r), data = idc) +
  geom_abline(intercept = 0.0155, slope = .5959, col = 'red') +
  geom_point(aes(x = .148975, y = .1063747), color = 'red') +
  annotate('text', x = .14, y = .10, label = 'Choice')

ggplotly(p3)

자본시장선은 절편에서부터 시작하니까 scale_y와 scale_x를 0에서 40%까지로 설정했습니다. geom_abline을 통해 자본시장선을 그리고 geom_line을 통해 바로 위에서 계산한 무차별곡선을 그리며, geom_point로 그 위에서 계산한 접점을 나타냈습니다. 완성된 그래프는 다음과 같습니다.

mORUbV6.png
<그림 5> 자본시장선 및 투자자의 최적자산배분

Tangency Portfolio의 구성비율은 다음과 같습니다.

> max_sr
# A tibble: 1 x 8
  X005930.KS.Close X000660.KS.Close X035420.KS.Close X207940.KS.Close X051910.KS.Close   ret  risk    sr
             <dbl>            <dbl>            <dbl>            <dbl>            <dbl> <dbl> <dbl> <dbl>
1           0.0279            0.106           0.0584            0.491            0.317 0.215 0.334 0.642

삼성전자 2.79%, SK하이닉스 10.6%, NAVER 5.84%, 삼성바이오로직스 49.1%, LG화학 31.7%를 들고 있군요! 삼바로와 LG 비중이 높은 건 제가 기간을 특수하게 잡아서 그런 것이 아닐까 싶네요.

두 점 사이의 거리를 계산하는 공식에 의하면 대충 Y절편에서 Choice까지의 거리와 Tangency까지의 거리는 0.47:1 정도가 되는군요!



5. 결론
즉 2017년 1월부터 2020년 10월까지의 최적 자산배분은

국채를 47%, 삼성전자를 1.5%, SK하이닉스를 5.6%, NAVER를 3.1%, 삼성바이오로직스를 26.0%, LG화학을 16.8%을 보유하는 것이 되는군요!

물론 딱히 현실적인 이야기는 아닙니다.



6. 마치며
교육을 받으면서 어떠한 과정이건 수많은 이론을 마주치게 되는데, 과연 그런 이론을 직접 검증해볼 수 있을까? 하는 의문이 들어 구글링을 통하 짜깁기해가며 누덕누덕 기워서 적은 내용입니다.

저는 컴공 교육을 들은 적도 없고, 경영학이나 통계학 전공 출신도 아니라 본문 중 틀린 내용이 존재할 확률이 높으니 해당 내용 지적해주시면 감사히 배우겠습니다.

사실 글은 길게 적었지만 딱히 현실에 도움 될 내용도 없고, 놀라운 통찰이 담겨있는 것도 아니라 단순 소일거리로 봐주시면 될 것 같습니다.

담원 우승 축하합니다.

통합규정 1.3 이용안내 인용

"Pgr은 '명문화된 삭제규정'이 반드시 필요하지 않은 분을 환영합니다.
법 없이도 사는 사람, 남에게 상처를 주지 않으면서 같이 이야기 나눌 수 있는 분이면 좋겠습니다."
StayAway
20/11/01 14:32
수정 아이콘
이게 퀀트투자라는 것과 비슷한 놈인가요? 올해초 쯤인가 뭐 좀 해보려다가 엑셀을 못해서 그만둬버렸습니다 ㅜㅜ
모든 포트폴리오 관련 책들의 결론을 보면 채권비율이 생각보다 많이 높은게 인상적이더군요.
개인들은 절대 그렇게 안할거 같다는 생각도 들고..
모찌피치모찌피치
20/11/01 20:37
수정 아이콘
퀀트투자의 일종이긴 하겠습니다만, 굉장히 원시적이라 현실적인 함의는 없다고 봐야할 듯 합니다.
Scavenging Hyena
20/11/01 14:34
수정 아이콘
한국시장은 역사가 짧기도 하고 시장도 100% 효율적으로 돌아가지 않아서 좀 그렇고...
브릿지워터의 올웨더포트폴리오가 글내용과 비슷한 개념일거 같네요.
20/11/01 14:35
수정 아이콘
배운 내용인데 저렇게 적용이 되는군요.더 노력해야지...
아마추어샌님
20/11/01 15:33
수정 아이콘
잘 읽고 갑니다 추천!
도우너 어서오고
20/11/01 16:01
수정 아이콘
R로 프로그래밍하는게 도움 많이되네요 감사합니다
20/11/01 16:01
수정 아이콘
음...완벽하게 이해했다.
개그니
20/11/01 16:31
수정 아이콘
좋은 내용 감사드립니다. 혹시 관련 참고문헌 또는 교과서 좀 부탁드려도 될까요? 최근 업무 때문에 R공부를 좀 했는데, 이런 용도로 쓸 수 있다니 신세계네요.
모찌피치모찌피치
20/11/01 20:41
수정 아이콘
본문 자체는 구글링을 통해 얻어낸 지식이 대부분이고, R 관련해서는 Henry's Quantopia 블로그, Fast campus 무료강의, 책은 Practical Time Seties Analysis with R을 보긴 했으나 본문과는 거의 무관할 것 같습니다...
개그니
20/11/01 22:18
수정 아이콘
감사합니다. 공부좀 해봐야 겠네요...
20/11/01 17:32
수정 아이콘
자산배분 관련해서 투자기회선은 몇 번 봤는데
단계적으로 프로그래밍 한 건 처음 보네요
봉그리
20/11/01 18:19
수정 아이콘
논문 쓰는 R 통계 공부보다 이걸 먼저 배웠어야 했는데.. 감사합니다.
맘대로살리
20/11/01 19:32
수정 아이콘
요새 매일 업무하느라 R을 돌리고 있었는데, 이걸보니 좀더 열심히 R을 배워야 겠다는 생각이 듭니다
덕분에 좋은자료 보고 갑니다!

혹시 R공부할만한 참고도서를 추천해주실수 있을까요?
모찌피치모찌피치
20/11/01 20:42
수정 아이콘
윗 댓글과 비슷하게 인터넷에 널린 무료강의와 구글링만으로 배운 얕은 지식입니다. 특별히 추천해드릴 만한 참고서는 없을 것 같네요...
섹무새
20/11/01 19:48
수정 아이콘
멋있습니다 크
20/11/01 20:50
수정 아이콘
추천
20/11/01 23:05
수정 아이콘
아아 완벽히 이해했어..
20/11/03 11:51
수정 아이콘
오오 멋집니다
목록 삭게로! 맨위로
번호 제목 이름 날짜 조회 추천
공지 [정치] [공지] 정치카테고리 운영 규칙을 변경합니다. [허들 적용 완료] [126] 오호 20/12/30 275251 0
공지 [일반] 자유게시판 글 작성시의 표현 사용에 대해 다시 공지드립니다. [16] empty 19/02/25 341345 10
공지 [일반] [필독] 성인 정보를 포함하는 글에 대한 공지입니다 [51] OrBef 16/05/03 463275 29
공지 [일반] 통합 규정(2019.11.8. 개정) [2] jjohny=쿠마 19/11/08 337551 3
102700 [일반] 한나라가 멸망한 이유: 내우(內憂) [1] 식별252 24/11/20 252 4
102699 [일반] 우크라이나 내 전쟁여론 근황 종전 찬성 52% 반대 38% [77] 뭉땡쓰3643 24/11/20 3643 1
102698 [정치] 트럼프의 집권은 오바마에 대한 실망이 가장 큰 이유였다고 생각되네요. [76] 홍철6567 24/11/20 6567 0
102697 [일반] [스포주의] 이토록 친밀한 배신자 인상적이었던 연출 몇개... [14] Anti-MAGE2619 24/11/20 2619 2
102696 [일반] 현대차 울산공장 연구원 3명 사망… [34] 뜨거운눈물8092 24/11/19 8092 1
102695 [일반] 개인적으로 한국어에는 없어서 아쉬운 표현 [65] 럭키비키잖앙6630 24/11/19 6630 6
102694 [일반] 회삿돈으로 현 경영권을 지켜도 배임이 아닌가? [81] 깃털달린뱀11641 24/11/19 11641 12
102693 [일반] 소리로 찾아가는 한자 51. 급할 극(茍)에서 파생된 한자들 [3] 계층방정2277 24/11/19 2277 1
102692 [일반] MZ세대의 정의를 뒤늦게 알게 되었네요. [16] dhkzkfkskdl8165 24/11/18 8165 2
102691 [일반] 니체의 초인사상과 정신건강 번개맞은씨앗3571 24/11/18 3571 2
102690 [일반] 입이 방정 [1] 김삼관3740 24/11/18 3740 1
102689 [일반] 심상치않게 흘러가는 동덕여대 사태 [305] 아서스18109 24/11/18 18109 44
102687 [일반] 작년에 놓쳤던 크리스마스 케이크 예약했습니다. [12] 가마성5472 24/11/18 5472 0
102686 [일반] 출간 이벤트: 꽃 좋아하시나요? 어머니, 아내, 여친? 전 제가 좋아해요! [99] 망각4416 24/11/17 4416 17
102685 [일반] 스포)저도 써보는 글래디에이터2 - 개연성은 개나 주자 [12] DENALI4949 24/11/17 4949 1
102684 [일반] 실제로 있었던 돈키호테 [3] 식별5707 24/11/17 5707 16
102683 [일반] [팝송] 콜드플레이 새 앨범 "Moon Music" [11] 김치찌개4316 24/11/17 4316 6
102682 [일반] 글래디에이터2 - 이것이 바로 로마다(강 스포일러) [13] 된장까스5399 24/11/17 5399 10
102681 [일반] <글래디에이터 2> - 실망스럽지는 않은데...(약스포) [9] aDayInTheLife3377 24/11/17 3377 5
목록 이전 다음
댓글

+ : 최근 1시간내에 달린 댓글
+ : 최근 2시간내에 달린 댓글
맨 위로