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
수정 아이콘
오오 멋집니다
목록 삭게로! 맨위로
번호 제목 이름 날짜 조회 추천
88611 [일반] 이런저런 이야기. [22] 공기청정기8604 20/11/02 8604 1
88610 [일반] 삼진그룹 영어토익반 - 실화에 브레이크 달기(약스포) [13] aDayInTheLife9429 20/11/02 9429 4
88608 [일반] RTX 3070 싸게 구입할수 있는곳 [15] 9312 20/11/02 9312 1
88606 [일반] 개그우먼 박지선 모친과 자택서 숨진 채 발견 [148] 와칸나이29064 20/11/02 29064 3
88605 [일반] 부동산에 대해서 잘 모르는 분은 아파트만 보세요.. [44] Leeka15148 20/11/02 15148 10
88604 [일반] '커밍 아웃', 사회적 합의, 사전.. [4] 아난7147 20/11/02 7147 1
88603 [일반] 11.04(수) 10 ~ 12:00 서버 점검안내 [23] 당근병아리4910 20/11/02 4910 16
88601 [일반] 다음 ios&watchOS 버전에서, 애플워치 심전도 기능이 지원됩니다. [13] Leeka7574 20/11/02 7574 1
88600 [일반] 제 게으름을 전파하겠습니다 [33] 거짓말쟁이8700 20/11/02 8700 3
88597 [일반] 하드웨어 사이트 유저분이 만들어주신 3070 스펙시트 [22] SAS Tony Parker 10558 20/11/02 10558 0
88596 [일반] 삼성전자와 TSMC의 초미세 파운드리 공정 기술 전쟁 [41] cheme23082 20/11/01 23082 56
88593 [일반] 별점 사회 – 신용이 제일 중요하다. [5] 좋은7542 20/11/01 7542 8
88592 [일반] 노래 몇 곡 [4] 두점5956 20/11/01 5956 2
88591 [일반] 효율적 투자기회선을 그려보자! [18] 모찌피치모찌피치13965 20/11/01 13965 11
88590 [일반] 주식 투자는 자산의 10%만 하라 [69] 비타에듀19019 20/11/01 19019 16
88589 [일반] 2, 4, 6 이야기 [6] jozefow5814 20/11/01 5814 2
88588 [일반] 친일파가 작사 작곡한 애국가 그만 듣고 싶네요 [76] 이스칸다르13703 20/11/01 13703 6
88587 [일반] 2020년 30대들이 생각하는 연애‧결혼, 자녀‧가족, 성건강‧문화, 사회 그리고 행복에 대한 의견 [12] VictoryFood11472 20/11/01 11472 1
88586 [일반] 중국 정부가 위구르 자치구 모스크들을 3분의 1 또는 약 70% 파괴? [92] 아난15654 20/10/31 15654 5
88585 [일반] 초대 제임스 본드 숀 코너리 사망 [57] 이호철11302 20/10/31 11302 4
88584 [일반] 눈물이 멈추지를 않는 하루 [12] M270MLRS10449 20/10/31 10449 5
88581 [일반] [스포] 더 보이즈 시즌 2 소감 [13] 어서오고9814 20/10/31 9814 1
88580 [일반] [일상]제로콜라는 대단하다는 결론 [115] Your Star19752 20/10/31 19752 45
목록 이전 다음
댓글

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