Post List

2019년 3월 24일 일요일

퀀트 매니저는 어떻게 포트폴리오를 구성할까? (인덱스펀드 구성 예시)



아마 여러분이 퀀트 포트폴리오 매니저 업무를 담당한다면, 십중팔구 (인핸스드) 인덱스 펀드 매니지, 즉 패시브펀드 업무를 담당하실 겁니다. 인핸스드 인덱스 펀드의 운용은 그 중심이 되는 기초지수의 추종, 그리고 알파를 내기 위한 여러 방법으로 차익거래, 대차, 배팅 등의 방법을 이용합니다. 


먼저, 인덱스 펀드의 가장 중심이 되는 기초지수의 추종에 대해 살펴보도록 하겠습니다. 가장 많이 사용되는 기초지수는 각 국가의 주가지수이며, 우리나라의 경우 KOSPI 200지수를 대상으로 합니다.

기초지수는 일반적으로 시가총액가중평균 방법을 사용합니다. 따라서 지수를 정확히 복제하기 위해서는 지수 내에 있는 각 종목들의 시가총액을비중을 알 필요가 있습니다. 시가총액은 주가 * 상장주식수로 이루어집니다. 그러나 각 기업의 상장된 모든 주식이 (자사주 등의 이유로) 거래가 되는 것은 아니며, 상장된 주식 중 유동적으로 거래되는 비율을 유동비라고 합니다. 따라서 지수를 만들때 시가총액은 주가 * 상장주식수 * 유동비로 계산됩니다.


종목
종가
주식수
유동비
1
삼성전자
45,850
5,969,782,550
0.7879
2
SK하이닉스
75,900
728,002,365
0.7389
3
셀트리온
200,500
125,461,438
0.6670
4
POSCO
258,000
87,186,835
0.8117
5
신한지주
43,300
474,199,587
0.8978
6
LG화학
382,000
70,592,343
0.6611
7
현대차
124,500
213,668,187
0.6577
8
NAVER
128,000
164,813,395
0.7877
9
KB금융
43,000
418,111,537
0.8452
10
현대모비스
213,500
97,343,863
0.6712


위 테이블은 국내 상위 10 종목의 종가, 주식수 및 유동비입니다. 위의 주식들로 구성된 가상의 지수를 추종하기 위해 어떻게 포트폴리오를 구성해야 하는지 살펴보도록 합시다.


const = tibble(
  '종목' = c('삼성전자','SK하이닉스','셀트리온','POSCO','신한지주',
           'LG화학','현대차','NAVER','KB금융','현대모비스'),
  '종가' = c(45850,75900,200500,258000,43300,
           382000,124500,128000,43000,213500),
  '주식수' = c(5969782550,728002365,125461438,87186835,474199587,
            70592343,213668187,164813395,418111537,97343863),
  '유동비' = c(0.7879,0.7389,0.667,0.8117,0.8978,
            0.6611,0.6577,0.7877,0.8452,0.6712)
  )

const = const %>% 
  mutate(cap = 종가 * 주식수 * 유동비,
         cap.ratio = cap / sum(cap))
const
# A tibble: 10 x 6
   종목         종가     주식수 유동비     cap cap.ratio
 1 삼성전자    45850 5969782550  0.788 2.16e14    0.551 
 2 SK하이닉스  75900  728002365  0.739 4.08e13    0.104 
 3 셀트리온   200500  125461438  0.667 1.68e13    0.0429
 4 POSCO      258000   87186835  0.812 1.83e13    0.0467
 5 신한지주    43300  474199587  0.898 1.84e13    0.0471
 6 LG화학     382000   70592343  0.661 1.78e13    0.0456
 7 현대차     124500  213668187  0.658 1.75e13    0.0447
 8 NAVER      128000  164813395  0.788 1.66e13    0.0425
 9 KB금융      43000  418111537  0.845 1.52e13    0.0389
10 현대모비스 213500   97343863  0.671 1.39e13    0.0357


각 종목의 시가총액을 구한 후, 이를 전체 합으로 나누어 시총비중을 구하였습니다. 이제 특정 펀드의 투자금액이 100억일 경우 각 종목 당 투자금액을 어떻게 할지 살펴보겠습니다.


invest = 10000000000
const = const %>%
  mutate(inv.number = floor(invest * cap.ratio / 종가),
         inv.money = inv.number * 종가,
         inv.ratio = inv.money / sum(inv.money))
const
# A tibble: 10 x 9
   종목         종가     주식수 유동비     cap cap.ratio inv.number  inv.money inv.ratio
 1 삼성전자    45850 5969782550  0.788 2.16e14    0.551      120282 5514929700    0.552 
 2 SK하이닉스  75900  728002365  0.739 4.08e13    0.104       13755 1044004500    0.104 
 3 셀트리온   200500  125461438  0.667 1.68e13    0.0429       2139  428869500    0.0429
 4 POSCO      258000   87186835  0.812 1.83e13    0.0467       1809  466722000    0.0467
 5 신한지주    43300  474199587  0.898 1.84e13    0.0471      10887  471407100    0.0471
 6 LG화학     382000   70592343  0.661 1.78e13    0.0456       1193  455726000    0.0456
 7 현대차     124500  213668187  0.658 1.75e13    0.0447       3593  447328500    0.0447
 8 NAVER      128000  164813395  0.788 1.66e13    0.0425       3319  424832000    0.0425
 9 KB금융      43000  418111537  0.845 1.52e13    0.0389       9037  388591000    0.0389
10 현대모비스 213500   97343863  0.671 1.39e13    0.0357       1670  356545000    0.0357

invest - (const$inv.money %>% sum)
[1] 1044700


먼저 각 종목당 투자되는 주식수는 총투자금액 * 시총비중 / 종가를 통해 계산되며, 소수점 이하로를 투자를 할 수 없으므로 내림을 해주도록 합니다. 펀드의 투자금액 100억원과 각 종목 별 투자금액을 합한 금액의 차릴 보면 대략 100만원 정도(0.01% 가량)가 남습니다. 이처럼 실제 포트폴리오의 구성에서는 모델 포트폴리오와는 다르게 약간의 현금이 남기 마련입니다.

또한 위 처럼 99.9% 금액을 주식으로 투자하는 경우도 잘 없습니다. 펀드는 매일매일 설정과 해지가 일어납니다. 만일 여러분이 위와 같이 포트폴리오를 구성했는데 내일 5억원의 해지가 발생한다면 어떡할까요? 5억원치에 해당하는 주식을 매도해야 하며, 이때 발생하는 세금은 여러분 펀드에 손해가 되게 됩니다. 따라서 대부분 펀드는 이러한 현금이 필요할 경우를 대비하여 지수의 일부를 선물로 추종하게 됩니다. 

100억원 중 90억원의 금액을 현물(실제 주식)로 추종하고, 나머지 10억원의 금액은 선물을 매수하여 추종하면 간단합니다. KOSPI 200 선물의 한 계약당 명목금액은 지수 * 250,000 이므로 KOSPI 200 지수가 300pt임을 가정하면 한 계약당 명목금액이 7천 5백만원 정도 됩니다. 따라서 나머지 10억원에 해당하는 선물 13개를 매수(7천5백*13 = 9억 7천 5백)하면, 총 100억원의 명목금액이 맞춰지게 됩니다. 선물의 경우 매수를 하더라도 현금이 들어가지 않기 때문에 펀드에는 아직 10억원 가량의 현금이 남게되며, 이를 통해 해지 금액을 지불하고, 해당 금액만큼의 선물을 매도하면 됩니다. (선물의 경우 매매시 세금이 거의 없다고 봐도 무방합니다.)

위와 같이 펀드를 구성할 경우 기초지수의 수익률을 거의 복제가 가능합니다. 그러나 우리는 언제나 지수+알파를 원합니다. 이를 위해 위에서 살펴본 것처럼 차익거래, 대차, 배팅 등의 방법이 이용됩니다.


먼저 차익거래에 대해 살펴봅시다. 파생상품에 대해 공부해보셨으면 알다시피 선물의 가격은 현물 가격(주가 지수)와 무위험 이자율, 배당, 만기에 의해 가격이 결정되게 됩니다. 





그러나 가끔 이러한 이론가격과 실제 선물의 가격이 벌어지게 되는 일이 생기며, 이를 이용하여 현물 바스켓과 선물간의 차익거래, 현물 바스켓과 ETF간의 차익거래, ETF와 선물간의 차익거래 등을 통해 아비트리지 수익을 얻을 수 있는 경우가 존재합니다. 또한 합병 차익거래, 배당 차익거래 등 상당히 낮은 위험으로 수익률을 얻을 수 있는 기회가 종종 나타나기도 합니다. 대부분의 퀀트 매니저들은 이러한 기회를 놓치지 않고 수익을 얻습니다.

대차는 말그대로 주식을 빌려주는 행위입니다. 인덱스 펀드를 구성하는 종목들은 지수의 구성종목이 바뀌지 않는 이상 거의 바뀔일이 없으므로, 매도를 할 일도 잘 없습니다. 주식을 빌려주어 조금의 수익이라도 더 보태는 것이 좋습니다.

마지막으로는 배팅에 대한 내용입니다. 매니저의 스타일에 따라 배팅을 하는 방법은 제각각이지만, 일반적으로 많이 사용되는 팩터 모형을 이용하여 특정 종목에 비중을 더욱 싣습니다. 이부분에 대해서 자세히 알아보도록 하겠습니다.

먼저 KOSPI 200 지수를 구성하는 종목들의 시총비에 대해 살펴보겠습니다. 실제 업무에서는 기초지수 제공자(KOSPI 200의 경우 한국거래소)가 각 종목들의 비중을 판매하지만, 여러분이 손쉽게 그 비중을 구할 수 있는 방법도 있습니다. 바로 해당 지수를 추종하는 ETF의 PDF를 통해서 말이죠. 




ETF의 경우 보유종목과 비중을 실시간으로 공표해야 하기에 이를 통해 간단히 기초지수의 종목별 비중을 추론할 수 있습니다. 먼저 KODEX 200 ETF의 PDF를 크롤링하여 그 비중을 추출해 보도록 하겠습니다.


library(httr)
library(rvest)
library(readxl)
library(dplyr)
library(ggplot2)
library(tidyr)

date = '20190322'

pdf_200 = POST(url = 'http://www.kodex.com/excel_pdf.do',
               query = list(
                 fId = '2ETF01',
                 gijunYMD = date
               ),
               write_disk(tf <- tempfile(fileext = '.xls')))
pdf_data = read_excel(tf, skip = 2, col_names = TRUE)
pdf_data
# A tibble: 202 x 9
    번호 종목명     ISIN         종목코드         수량 `비중(%)` `평가금액(원)` `현재가(원)` `등락(원)`
 1     1 원화예금   KRD010010001 KRD010010001 28653419    0            28653419            0          0
 2     2 삼성전자   KR7005930003 005930           8220    0.268       376887000        46550        700
 3     3 SK하이닉스 KR7000660001 000660            977    0.0527       74154300        76100        200
 4     4 셀트리온   KR7068270008 068270            172    0.0245       34486000       199500      -1000
 5     5 POSCO      KR7005490008 005490            120    0.0220       30960000       259000       1000
 6     6 신한지주   KR7055550008 055550            710    0.0219       30743000        43250        -50
 7     7 LG화학     KR7051910008 051910             80    0.0217       30560000       379500      -2500
 8     8 현대차     KR7005380001 005380            245    0.0217       30502500       123500      -1000
 9     9 NAVER      KR7035420009 035420            221    0.0201       28288000       127000      -1000
10    10 KB금융     KR7105560007 105560            619    0.0189       26617000        42500       -500
# ... with 192 more rows


이 중 원화예금에 해당하는 부분을 제외한 뒤, 각 평가금액들을 바탕으로 비중을 새롭게 구하며, 종목명, 종목코드, 비중 만을 남기도록 하겠습니다.


# Data cleansing
pdf_data_mod =
  pdf_data %>%
  filter(종목명 != '원화예금') %>%
  mutate(비중 = `평가금액(원)` / sum(`평가금액(원)`)) %>%
  select(종목명, 종목코드, 비중) %>%
  mutate(종목명 = factor(종목명, levels = unique(종목명)))

ggplot(pdf_data_mod, aes(x = 종목명, y = 비중)) +
  geom_point()




삼성전자의 비중이 압도록으로 높습니다. 이제 각 종목들의 팩터를 구해야 합니다. 간단한 예시로 모멘텀에는 12개월 수익률, 밸류에는 PBR, 퀄리티에는 ROE 데이터를 사용하겠습니다. 물론 데이터가이드와 같은 유료 벤더를 통해 해당 데이터를 손쉽게 구할 수 있지만, 크롤링을 통해 온라인에서도 손쉽게 해당 데이터를 구할 수 있습니다.




CompanyGuide의 종목별 정보에서 PBR과 12개월 수익률을 손쉽게 따올 수 있으며, ROE의 경우 상당의 PBR / PER를 통해 계산할 수 있습니다. KOSPI 200에 해당하는 종목들의 종목코드도 이미 보유하고 있기에, for loop를 통하여 해당 팩터 데이터들을 수집해 보도록 하겠습니다.



# Download Factor data from Company Guide
data_factor = list()
for (i in pdf_data_mod$종목코드) {
  # i = '005930'
  url = paste0('http://comp.fnguide.com/SVO2/ASP/SVD_main.asp?pGB=1&gicode=A',i)
  down_page = GET(url) %>% read_html
  
  fct_mom = down_page %>% html_nodes(xpath = '//*[@id="svdMainGrid1"]/table/tbody/tr[3]/td[1]/span[4]') %>%
    html_text() %>% as.numeric()
  if (length(fct_mom) == 0) {
    fct_mom = NA
  }
  
  fct_pbr = down_page %>% html_nodes(xpath = '//*[@id="corp_group2"]/dl[4]/dd') %>%
    html_text() %>% as.numeric()
  fct_per = down_page %>% html_nodes(xpath = '//*[@id="corp_group2"]/dl[1]/dd') %>%
    html_text() %>% as.numeric()
  fct_roe = fct_pbr / fct_per
  
  fct_data = data.frame(fct_mom, fct_pbr, fct_roe)
  data_factor[[i]] = fct_data
  print(i)
  
  Sys.sleep(2)
}

# Bind Data
data_factor_bind = bind_rows(data_factor)
rownames(data_factor_bind) = pdf_data_mod$종목명

summary(data_factor_bind)  
    fct_mom           fct_pbr          fct_roe        
 Min.   :-63.100   Min.   : 0.220   Min.   :0.002087  
 1st Qu.:-23.265   1st Qu.: 0.650   1st Qu.:0.053074  
 Median :-10.320   Median : 0.945   Median :0.078653  
 Mean   : -9.378   Mean   : 1.599   Mean   :0.113277  
 3rd Qu.:  3.248   3rd Qu.: 1.708   3rd Qu.:0.124089  
 Max.   : 49.740   Max.   :14.860   Max.   :1.250000  
 NA's   :3         NA's   :1        NA's   :27  


각 종목들의 팩터 데이터가 구해졌다면, 이를 바탕으로 팩터의 강도를 구해보도록 하겠습니다. 먼저 각 팩터별 랭킹을 구한 후, 이를 다시 Z-Score로 치환하도록 하겠습니다. 또한 모멘텀과 ROE는 높을수록 좋으며, PBR은 낮을수록 좋기때문에, 부호의 통일을 위해 PBR의 Z-Score값에 (-)를 곱하도록 합니다.


# Calculate Factor Strength
data_factor_scale = apply(data_factor_bind, 2, rank, na.last = 'keep') %>%
  scale() %>% data.frame()
data_factor_scale$fct_pbr = -data_factor_scale$fct_pbr

data_factor_scale %>%
  gather(key, value) %>%
  ggplot(aes(x = value, group = key)) +
  geom_histogram() +
  facet_grid(key ~ .)




각 팩터의 Z-Score가 균등하게 분포되어 있음이 확인됩니다. 이제 이를 바탕으로 멀티팩터의 강도를 계산해야 합니다. AQR의 방법론에 따라 모멘텀 팩터에는 40%의 비중을, 밸류 팩터에는 40%의 비중을, 퀄리티 팩터에는 20%의 비중을 주도록 하겠습니다. 해당 비중은 매니저의 재량에 의해 결정됩니다. 이렇게 결정된 스코어를 내림차순 해준 후 랭킹을 구해주도록 합니다. (스코어가 높을수록 좋은 종목, 스코어가 낮을 수록 나쁜 이므로, 내림차순을 기준으로 랭킹을 구해야 합니다.)

# Bind factor strength
# Momentum 40%, Value 40%, Quality 20%
data_factor_scale = data_factor_scale %>%
  mutate(score = 0.4 * fct_mom + 0.4 * fct_pbr + 0.2 * fct_roe,
         rank = rank(desc(score)),
         종목명 = pdf_data_mod$종목명)


이제 랭킹 상위 10개 종목의 각 팩터값들을 살펴보도록 하겠습니다.


# Top 10
top.10 = data_factor_scale %>% 
  filter(rank <= 10) %>%
  select(종목명) %>% unlist() %>% as.character()

data_factor_bind %>% 
  mutate(종목명 = rownames(.)) %>%
  filter(종목명 %in% top.10) 

   fct_mom fct_pbr    fct_roe       종목명
1     8.86    0.83 0.12749616     SK텔레콤
2     8.15    0.51 0.04214876       기아차
3    29.71    0.68 0.12078153     대림산업
4    43.62    0.92 0.16197183       GS건설
5     9.08    0.58 0.05669599   현대백화점
6     2.57    0.88 0.23466667 대우조선해양
7    38.38    0.56 0.06481481     태광산업
8     1.93    0.65 1.25000000         효성
9    15.80    0.77 0.40314136     대덕전자
10   42.81    0.67 0.09795322     세방전지

전반적으로 12개월 수익률도 높고, PBR은 낮으며, ROE 또한 높은 모습을 보입니다. 이번에는 반대로 하위 20개 종목의 각 팩터값들을 살펴보도록 하겠습니다.

# Bottom 10
bottom.10 = data_factor_scale %>% 
  filter(rank >= 190) %>%
  select(종목명) %>% unlist() %>% as.character()

data_factor_bind %>% 
  mutate(종목명 = rownames(.)) %>%
  filter(종목명 %in% bottom.10) 

   fct_mom fct_pbr     fct_roe     종목명
1   -15.22    1.39          NA 동아에스티
2   -24.17    0.38          NA   동국제강
3   -49.55    0.27          NA 두산중공업
4    -4.46    3.28          NA JW중외제약
5   -37.08    0.59          NA     LG상사
6    14.57    2.38          NA   한세실업
7   -26.89    3.00          NA   일양약품
8   -21.76    1.41          NA  LIG넥스원
9   -22.65    0.66          NA LG하우시스
10      NA    0.41 0.002087258 효성중공업
11    2.99    1.03          NA     쌍용차
12  -63.10    0.22          NA 한진중공업


12개월 수익률이 없거나, PBR이 음수거나, ROE가 없는 경우 하위 종목으로 구성됨이 보입니다. 이러한 종목들의 경우 score값도 NA 즉 결측치로 나타나 있으며, 해당 값들을 최하값으로 강제치환 해주도록 하겠습니다.


# Change NA to lower number
data_factor_scale %>%
  filter(is.na(score)) %>% head()

data_factor_scale = data_factor_scale %>%
  mutate_at(vars(score), list(~ifelse(is.na(.), min(score, na.rm = TRUE), .)))


이제 팩터 스코어가 준비되었으므로 기초지수 복제 포트폴리오에 팩터투자를 통해 배팅을 해주면 됩니다. 배팅 비중은 매니저의 재량 혹은 펀드의 규정에 따라 다르며, 여기서는 간단하게 5%를 예를 들어보겠습니다. 먼저 가장 간단한 방법은 200 종목 중 상위 100 종목에서 각 5bp씩 투자비중을 차감하고, 멀티팩터 기준 상위 50 종목에 각 10bp 씩 투자비중을 더해주는 방법입니다. 


top.50 = data_factor_scale %>% 
  filter(rank <= 50) %>%
  select(종목명) %>% unlist() %>% as.character()

# 시총 상위 100종목에서 언더 웨이트
method_first = pdf_data_mod %>%
  mutate(투자비중 = 비중,
         시총랭킹 = rank(desc(비중))) %>%
  mutate_at(vars(투자비중), list(~ifelse(시총랭킹 <= 100, 투자비중 - 0.0005, 투자비중)))

sum(method_first$투자비중)

# 상위 50 종목에 오버웨이트
method_first = method_first %>%
  mutate_at(vars(투자비중), list(~ifelse(종목명 %in% top.50, 투자비중 + 0.0010, 투자비중)))

# 둘 간의 비중 비교
method_first %>%
  select(종목명, 비중, 투자비중) %>%
  gather(key, value, -종목명) %>%
  ggplot(aes(x = 종목명)) +
  geom_point(aes(y = value, color = key))



빨간색은 지수 내 비중, 파란색은 투자비중입니다. 삼성전자의 비중이 지나치게 큰 관계로 둘 간의 차이가 잘 확인되지 않습니다. 따라서 삼성전자를 제외한 나머지 종목들의 비중만을 한번 살펴보도록 하겠습니다.


# 삼성전자 제외
p1 = method_first %>%
  slice(-1) %>%
  select(종목명, 비중, 투자비중) %>%
  gather(key, value, -종목명) %>%
  ggplot(aes(x = 종목명)) +
  geom_point(aes(y = value, color = key, shape = key), size = 1)

p1




시총 상위주의 투자비중이 지수비중보다 조금씩 작으며, 멀티팩터 기준 상위 50 종목의 투자비중이 지수 대비 조금씩 많은 것이 확인됩니다. 팩터 모델이 장기적으로 지수 대비 뛰어난 성과를 보인다면, 이런식으로 구성한 포트폴리오도 장기적으로 지수 대비 초과성과를 기록할 수 있을 겁니다.

그러나 위 방법으로 포트폴리오를 구성할 경우 배팅금액이 전체 포트폴리오의 5% 밖에 되지 않으며, 좀더 팩터모델을 활용하여 적극적으로 포트폴리오를 구성하고자 할 수 있습니다. 이럴 경우 전 종목에 팩터의 강도를 활용하여 비중을 조절하는 방법이 있습니다. 이는 예전에 썼는 글을 참조하시면 좋습니다.


# (2) 전체 종목에 팩터 강도 적용
method_second = data_factor_scale %>%
  select(score) %>%
  mutate(cdf = pnorm(score)) %>%
  bind_cols(pdf_data_mod)

method_second = method_second %>%
  mutate(투자비중 = cdf * 비중,
             투자비중 = 투자비중 / sum(투자비중))

먼저 앞에서 계산된 Z-Score를 바탕으로 pnorm() 함수를 이용하여 각 Z-Score 값들의 CDF 값을 구해준 후, 기초지수의 비중과 CDF 값을 곱해주도록 합니다. 즉 Z-Score가 높은 값들은 CDF 값 역시 높아 기초지수의 비중만큼 투자할 것이며, Z-Score가 낮은 값들은 CDF 값이 낮아, 기초지수 대비 매우 작은 비중을 투자할 것입니다. 이를 다시 투자비중의 합으로 나누어 합계가 1이 되도록 표준화를 해줍니다.


method_second %>%
  slice(-1) %>%
  select(종목명, 비중, 투자비중) %>%
  gather(key, value, -종목명) %>%
  ggplot(aes(x = 종목명)) +
  geom_point(aes(y = value, color = key, shape = key), size = 1)




삼성전자를 제외한 종목들의 기초지수 비중과 투자비중의 차이입니다. 첫번째 방법에 비해 훨씬 둘 간의 차이가 큰 것이 확인되며, 이는 전체 포트폴리오가 팩터에 훨씬 노출된 정도가 크다는 것을 의미합니다.

그러나 인덱스 펀드에는 각 종목별 지수내 비중과 투자 비중간에 지나치게 차이가 벌어지는 것을 방지하는 제약이 대부분 있습니다. 예로써 둘 간의 차이가 50bp 이상이 되지 않도록 생각해 봅시다.


method_second = method_second %>%
  mutate(TE = 투자비중 - 비중)

TE.max = 0.005  

method_second %>%
  select(종목명, TE) %>% 
  ggplot(aes(x = 종목명, y = TE)) +
  geom_point() +
  geom_hline(aes(yintercept = TE.max)) + 
  geom_hline(aes(yintercept = -TE.max))



일부 종목의 경우 허용치인 50bp 대비 차이가 더 큰것이 확인됩니다. 이러한 종목의 경우 차이가 50bp 이상 벌어지지 않도록 강제로 변경해주어야 합니다. 즉, 개별종목 투자비중의 상한은 지수내 비중 + 50bp, 하한은 지수내 비중 - 50bp로 바꿔주도록 합니다. 그 후 다시 투자비중의 합이 1이 되도록 표준화를 시행해 줍니다. 해당 작업은 비중의 차이가 50bp 이상 벌어지지 않을 때까지 while() 구문을 사용하여 계산하며, 미세한 비중차이로 인한 무한루프에 빠지는 것을 방지하기 위해 TE.max 값에 매우 작은 값을 더해주도록 합니다.


while (max(abs(method_second$TE)) > (TE.max + 0.0001)) {
  method_second = method_second %>%
    mutate_at(vars(투자비중), list(~ifelse(TE < -TE.max, 비중 - TE.max, 투자비중))) %>%
    mutate_at(vars(투자비중), list(~ifelse(TE > TE.max, 비중 + TE.max, 투자비중))) %>%
    mutate(투자비중 = 투자비중 / sum(투자비중), 
               TE = 투자비중 - 비중)
}

method_second %>%
  select(종목명, TE) %>% 
  ggplot(aes(x = 종목명, y = TE)) +
  geom_point() +
  geom_hline(aes(yintercept = TE.max)) + 
  geom_hline(aes(yintercept = -TE.max))




이제 지수내 비중과 투자비중의 최대 차이가 50bp 이내로 좁혀지게 되었습니다. 마지막으로 첫번째 방법과 두번째 방법의 비중 차이를 한번에 시각화 하도록 하겠습니다. (삼성전자 제외)


p2 = method_second %>%
  slice(-1) %>%
  select(종목명, 비중, 투자비중) %>%
  gather(key, value, -종목명) %>%
  ggplot(aes(x = 종목명)) +
  geom_point(aes(y = value, color = key, shape = key), size = 1)

library(gridExtra)
grid.arrange(p1, p2)




두번째 방법의 포트폴리오가 훨씬 더 기초지수 대비 액티브하게 배팅이 된 것을 확인할 수 있습니다. 팩터가 장기적으로 아웃퍼폼 한다면 두번째 포트폴리오의 아웃퍼폼 정도도 더욱 크겠지만, 트래킹 에러 또한 커질 수 있다는 점은 주의해야겠죠?

댓글 없음:

댓글 쓰기