R

[R 연습] KOPSI 200을 추종하는 KODEX 200 ETF를 매매하는 전략을 선형 회귀 분석으로 도출하라.

오렌지사과키위 2023. 11. 30. 14:09

문제

KOSPI 200을 추종하는 ETF로 KODEX 200이 있다. 매일 종가를 기준으로 이를 보유할지 또는 보유하지 않을지를 결정하고 싶다. 선형 회귀 분석으로 이러한 전략을 도출하고 지속 보유 전략 대비 그 성능을 평가하라.

 

주의

Yahoo Finance 또는 Google Finance의 (Google Sheet에서 불러온 경우도 포함) 국내 시세 데이터는 완벽하지 않습니다. 결측값이 있거나 잘못된 값이 있는 경우가 꽤 있습니다. 실사용 목적으로는 보다 완전한 데이터를 이용하는 것이 바람직합니다.

여기에서 제시하는 결과는 통계적 유의성을 전혀 고려하지 않았습니다. 또한 데이터 적용 기간에 따라 결과에 상당한 차이가 있을 수 있습니다.

여기에서 제시하는 결과는 각종 수수료, 세금, 슬리피지 등이 고려되지 않았습니다.

 

코드

library(PerformanceAnalytics)  ## for chart.CumReturns()
library(dplyr)  ## for mutate()
library(modelr)  ## for lm()
library(quantmod)  ## for getSymbols()

# Ticker names.
ticker <- "069500.KS"  ## KODEX 200

# Get daily stock price from Yahoo Finance.
stock <- getSymbols(ticker, src = "yahoo", auto.assign = FALSE) %>%
  na.omit()

# Calculate day/night/whole gains.
day_gains <- Cl(stock) / Op(stock) - 1 %>%
  setNames("day_gain")
night_gains <- Op(stock) / timeSeries::lag(Cl(stock)) - 1 %>%
  setNames("night_gains")
whole_gains <- Cl(stock) / timeSeries::lag(Cl(stock)) - 1 %>%
  setNames("whole_gains")

# Preparing features and target.
features_and_target <- cbind(
  day_gains,
  timeSeries::lag(day_gains),
  timeSeries::lag(day_gains, k = 2),
  night_gains,
  timeSeries::lag(night_gains),
  timeSeries::lag(night_gains, k = 2),
  
  timeSeries::lag(whole_gains, k = -1)
  ) %>%
  setNames(c("day_0", "day_1", "day_2",
             "night_0", "night_1", "night_2",
             "target")) %>%
  na.omit()

# Derive a linear regression model.
model <- lm(target ~ day_0 + day_1 + day_2 + night_0 + night_1 + night_2,
            features_and_target)

# Add predictions by using the model.
features_and_target.df <- fortify.zoo(features_and_target) %>%
  add_predictions(model)

# Add gains by using predictions. (if pred > 0 then hold the stock)
features_and_target.df <- features_and_target.df %>%
  mutate(actual = ifelse(pred > 0, target, 0))

# Draw a cumulative gain chart.
# Draw a cumulative gain chart.
chart.CumReturns(
  features_and_target.df[c("Index", "day_0", "night_0", "target", "actual")],
  legend.loc = "topleft")

 

결과 및 해석

당일 종가에 KODEX 200을 보유할지 여부를 결정하기 위하여 6가지 변수를 사용했습니다. day_0, day_1, day_2는 당일(당일 시가에 매수해서 당일 종가에 매도), 전일, 그리고 전전일 주간 수익률이며, night_0, night_1, night_2는 당일(전일 종가에 매수해서 당일 시가에 매도), 전일, 그리고 전전일 야간 수익률입니다. target은 당일 종가에 매수해서 익일 종가에 매도하는 경우의 수익률입니다.
 
최종적으로 생성된 그래프는 아래와 같습니다. 지난 예제에서 살펴본 바와 같이 KODEX 200은 대체적으로 주간(까만 선)에는 하락하고, 야간(빨간 선)에는 상승합니다. 이 두 가지가 조합된 target(초록선)은 그 중간 정도에 위치하며 전반적으로 상승합니다. 파란 선은 도출된 선형 회귀 분석 모델을 이용하여 상승이 기대되면 종가에 매수해서 다음날 종가까지 보유한 결과입니다. 단순히 계속 보유한 초록선에 비해 수익률이 조금 더 높습니다. 
 

 
그래프나 아래 표에서와 같이 2007년 2월 초순부터 2009년 4월 중순까지의 데이터가 유실되어 있음을 확인할 수 있습니다. Yahoo Finance나 Google Finance 데이터를 이용할 때에는 이러한 점에 대해 충분히 유의해야 합니다.
 

head(stock[,1], 10)
----------

           069500.KS.Open
2007-01-29          18160
2007-01-30          18100
2007-01-31          18200
2007-02-01          18100
2007-02-02          18305
2007-02-05          18670
2007-02-06          18785
2007-02-07          18830
2009-04-17          17560
2009-04-20          17460

 
생성된 선형 회귀 모델을 살펴보면 아래와 같습니다. 요약을 보면 각 입력 변수(feature, 통계에서는 독립 변수)에 대해 통계적 유의성이 표시되어 있습니다. 별 3개(***)가 붙은 입력 변수가 2개 있습니다. day_0와 night_0입니다. day_0에 대해서는 음의 계수가 붙어있고, night_0에 대해서는 양의 계수가 붙어 있습니다. 간단하게 말하면 어제 야간에는 상승하였고, 오늘 주간에는 하락했다면 매수하는 게 유리하다는 의미로 해석할 수 있습니다.
 

# Summary of the generated model.
summary(model)
----------

Call:
lm(formula = target ~ day_0 + day_1 + day_2 + night_0 + night_1 + 
    night_2, data = features_and_target)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.078309 -0.005379  0.000400  0.005663  0.093998 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.000118   0.000185   0.638 0.523579    
day_0       -0.078496   0.021527  -3.646 0.000270 ***
day_1        0.018825   0.021586   0.872 0.383223    
day_2       -0.031740   0.021392  -1.484 0.137958    
night_0      0.090831   0.026931   3.373 0.000752 ***
night_1      0.041004   0.027143   1.511 0.130958    
night_2      0.058735   0.027098   2.167 0.030264 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01098 on 3583 degrees of freedom
Multiple R-squared:  0.00932,	Adjusted R-squared:  0.007661 
F-statistic: 5.618 on 6 and 3583 DF,  p-value: 8.153e-06

 
 
최종 수익률 표는 아래와 같습니다. 주간 하락 경향과 야간 상승 경향을 뚜렷하게 확인할 수 있습니다. 지속 보유 전략을 사용했다면 해당 기간 동안 83% 수익률인데 비해, 선형 회귀 분석으로 도출한 모델을 이용했다면 110% 수익률을 거둘 수 있었습니다. (주의: 이 글의 목적은 단순히 모델을 생성하는 방법을 연습하기 위한 것입니다. 해당 모델을 생성/평가한 방법은 실제 사용할 수 있을 수준의 엄밀성이 없습니다.) 

colSums(
  features_and_target.df[
    c("day_0", "night_0", "target", "pred", "actual")] * 100.0)
----------

    day_0   night_0    target      pred    actual 
-87.11527 171.68579  83.15393  83.15393 110.55964
도움이 되었다면, 이 글을 친구와 공유하는 건 어떻까요?

facebook twitter kakaoTalk naver band