본문 바로가기

DA

5/31 수_미국 기준금리 vs. 코스피

728x90

1. 미국 기준금리와 코스피의 상관관계
새싹에서 진행했던 개인 프로젝트에서 코스피와 다른 지수들 간의 상관관계를 시각화로 알아보는 작업을 했었다.

마지막 프로젝트에서 주가 예측으로 자동 매매 시 가중치를 주기 위해,
미국 기준금리와 원자재 선물 등의 지표를 넣자는 의견이 나왔었으나 사이즈가 너무 커져서 배제했던 부분!

 

이번에 체인지업에서 경신스하는 중 "이번 주 코스피…'美 부채한도 협상' 타결여부에 달렸다"라는 기사에서
"5월 FOMC 의사록에서 추가 금리 인상 관련 내용을 확인할 필요가 있다" 내용을 본 김에 웹크롤링 복기할 겸 데이터를 가져와봤다.

 

미국 기준금리는 야후 파이낸스에서 가져올 수가 없고,
CSV로 파일을 받을 수 있는 것은 2008년 자료부터 있어서 데이터가 작았다.

 

미국 연방공개시장위원회(FOMC, Federal Open Market Committee) 사이트에서는 'Federal Funds Target Rate, Upper Limit'가 어디에 있는지 찾을 수 없었고,
Investing.com는 Max 탭 선택 후 '더 보여주기'를 클릭하면 1982년 09월 28일부터 데이터를 가져올 수 있었다.

https://kr.investing.com/economic-calendar/interest-rate-decision-168

FFTRUL

 

셀레니움까지 쓰기는 귀찮았는데 역시 Investing.com은 비싸게 군다.

from selenium import webdriver as wd
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, ElementNotInteractableException, TimeoutException, InvalidSessionIdException

ig_e = (NoSuchElementException, StaleElementReferenceException,)

url = 'https://kr.investing.com/economic-calendar/interest-rate-decision-168'

chrome_options = wd.ChromeOptions()
chrome_options.add_argument("--incognito") # 시크릿 모드
chrome_options.add_argument("--no-sandbox") # Bypass OS security model ***** 최소 옵션
chrome_options.add_argument("--lang=ko_KR") # 한국어 설정
chrome_options.add_argument("--start-maximized") # open Browser in maximized mode
chrome_options.add_argument("--disable-infobars") # disabling infobars
chrome_options.add_argument("--disable-extensions") # disabling extensions
chrome_options.add_argument("--disable-dev-shm-usage") # overcome limited resource problems. 메모리가 부족해서 에러가 발생하는 것을 막아줌 ***** 최소 옵션
chrome_options.add_argument("--disable-setuid-sandbox") # 크롬 드라이버에 setuid를 하지 않음으로써 크롬의 충돌을 막아줌
chrome_options.add_argument("--remote-debugging-port=9222") # 실행된 크롬창을 사용하도록 지정 (원격 디버깅 설정)
chrome_options.add_argument("user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36") # 사람인 척 하기

driver = wd.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options) # Selenium 4 버전 대
driver.get(url)
driver.implicitly_wait(3) # 처음 접속 시 대기(페이지 로딩 끝나면 진행)

try:
        for i in range(66): # FFTRUL 1982년 09월 28일~
                elem = WebDriverWait(driver, 5, ignored_exceptions=ig_e)\
                        .until(EC.element_to_be_clickable((By.XPATH, '/html/body/div[5]/section/div[12]/div[1]'))) # '더 보여주기' 클릭
                elem.click()
                driver.implicitly_wait(2)

except ElementNotInteractableException:
        print(f"{i}번째 클릭에서 끝")

except TimeoutException:
        print(f"{i}번째 클릭에서 끝")

except InvalidSessionIdException:
        print(f"{i}번째 클릭에서 끝")

finally:
        driver.close()

dates = driver.find_elements(By.CSS_SELECTOR, '#eventHistoryTable168 > tbody tr td:nth-child(1)') # 발표일 엘리먼트
dates = [date.text for date in dates]
rates = driver.find_elements(By.CSS_SELECTOR, '#eventHistoryTable168 > tbody tr td:nth-child(3) > span:nth-child(1)') # 실제 기준금리 엘리먼트
rates = [rate.text for rate in rates]

df = pd.DataFrame(columns=['dates', 'rates'])

df['dates'] = dates
df['rates'] = rates

df['rates'] = df['rates'].apply(lambda x: x.replace('%', ''))
df['rates'] = pd.to_numeric(df['rates'], errors='coerce') # 결측치가 있어 제거해주기 전에 미리 dtype 변환

df.dropna(subset=['rates'], axis=0, how='any', inplace=True)
df = df.reset_index(drop=True)

df.info()
df

코스피 발표 시점은 1983년 1월 4일부터이지만 야후 파이낸스에서는 1996년 12월 11일부터 데이터가 나온다.

코스피와 기준금리의 데이터 사이즈가 다르므로, 이를 맞추기 위해 코스피의 날짜 기준으로 DF들을 병합했다.

 

미국 기준금리 발표는 1년에 8회다. 발표 전까지 이전의 금리가 유지되므로 결측치는 먼저 ffill, 나머지는 bfill로 채워주었다.

# 날짜 교집합만 남기기 위해 kospi DF 기준으로 병합
KOSPIFFTRUL = pd.merge(kospi, FFTRUL, how = 'left', left_on = 'Date', right_on = 'Date')

코스피와 비교할 미국 기준금리 DF는 이 둘을 병합한 DF에서 필요한 컬럼들만 가져와 대체했다.

 

1996년 12월 11일 ~ 2023년 5월 30일, 26년치의 데이터는 6,524개.
(영업일 250일 * 26년)

import pandas as pd
from pandas_datareader import data as pdr # 데이터 로딩 및 시각화를 위한 라이브러리
import yfinance as yf # 금융 데이터 가져오는 Yahoo Finance
yf.pdr_override() # 둘이 합체!
from scipy import stats # 회귀 분석을 위한 수학, 과학, 엔지니어링용 핵심 패키지 모음(넘파이 기반)
import matplotlib.pylab as plt # 그래프 출력
from datetime import datetime, timedelta
import os

kospi = pdr.get_data_yahoo('^KS11', '1983-01-04') # KOSPI
yesterday = datetime.now().date() - timedelta(days=1)
workspace = os.getcwd() # 현재 작업 경로 확인

# Tickers on Yahoo Finance
symbols = {
    '^DJI': 'DOW Jones Industrial Average',
    'DX-Y.NYB': 'US Dollar Index',
    'KRW=X': 'US Dollar',
    'FFTRUL': 'Federal Funds Target Rate, Upper Limit',
}

def Regression(opponent:str='^DJI', filepath:str=workspace): # Regression 함수 선언
    """미국 기준금리/다우존스 지수/달러 인덱스/달러와 KOSPI 지수 사이의 상관관계를 확인하기 위한 회귀 그래프를 그려주는 함수\n
    opponent='종목 티커', filepath='그래프를 저장할 경로'를 매개변수로 받음"""

    if opponent == 'FFTRUL':
        versus = FFTRUL
        df = pd.DataFrame({'X':versus['Value'], 'Y':kospi['Close']}) # 가로축 독립변수 x: 미국 기준금리, 세로축 종속변수 y: KOSPI 지수
    else: 
        versus = pdr.get_data_yahoo(opponent, '1996-12-11')
        df = pd.DataFrame({'X':versus['Close'], 'Y':kospi['Close']}) # 가로축 독립변수 x: 다우존스 지수/달러 인덱스/달러, 세로축 종속변수 y: KOSPI 지수

    df = df.fillna(method='bfill') # 뒤의 값으로 결측치 대체
    df = df.fillna(method='ffill') # 마지막 행의 결측치를 앞의 값으로 대체

    regr = stats.linregress(df.X, df.Y) # 선형 회귀 모델 생성
    regr_line = f'Y = {regr.slope:.2f}  X + {regr.intercept:.2f}' # y의 기대치를 나타내는 회귀식. slope: 기울기, intercept: 절편

    plt.figure(figsize=(7, 7)) # 그래프 사이즈
    plt.plot(df.X, df.Y, '.') # X/Y의 좌표를 .으로 찍음 (Scatter Plot, 산점도)
    plt.plot(df.X, regr.slope * df.X + regr.intercept, 'r') # 회귀선을 붉은 선으로 그림
    
    plt.ylabel('KOSPI') # Y축은 KOSPI

    if opponent == '^DJI': # 다우존스 지수와 KOSPI 지수를 비교하고 싶다면
        plt.xlabel(symbols[opponent]) # X축은 DOW
        plt.legend(['DOW x KOSPI', regr_line]) # 범례
        # R 결정계수(상관계수의 제곱): 추정된 회귀선이 변수 사이의 관계를 얼마나 잘 설명하는지. 0 ~ 1
        plt.title(f'DOW x KOSPI (R = {regr.rvalue:.2f})\n1996-12-11 ~ {yesterday}') # 그래프 제목
        plt.savefig(f'{filepath}/DowKospi_Regr.png', format="png", dpi=150) # 그래프 저장. dpi는 해상도

    elif opponent == 'DX-Y.NYB': # 달러 인덱스와 KOSPI 지수를 비교하고 싶다면
        plt.xlabel(symbols[opponent]) # X축은 USDX
        plt.legend(['USDX x KOSPI', regr_line])
        plt.title(f'USDX x KOSPI (R = {regr.rvalue:.2f})\n1996-12-11 ~ {yesterday}')
        plt.savefig(f'{filepath}/USDXKospi_Regr.png', format="png", dpi=150)

    elif opponent == 'KRW=X': # 달러와 KOSPI 지수를 비교하고 싶다면
        plt.xlabel(symbols[opponent]) # X축은 USD
        plt.legend(['USD x KOSPI', regr_line])
        plt.title(f'USD x KOSPI (R = {regr.rvalue:.2f})\n1996-12-11 ~ {yesterday}')
        plt.savefig(f'{filepath}/USDKospi_Regr.png', format="png", dpi=150)

    elif opponent == 'FFTRUL': # FFTRUL와 KOSPI 지수를 비교하고 싶다면
        plt.xlabel(symbols[opponent]) # X축은 FFTRUL
        plt.legend(['FFTRUL x KOSPI', regr_line])
        plt.title(f'FFTRUL x KOSPI (R = {regr.rvalue:.2f})\n1996-12-11 ~ {yesterday}')
        plt.savefig(f'{filepath}/FFTRULKospi_Regr.png', format="png", dpi=150)

    plt.show() # 그래프 그리기
    print(f'{symbols[opponent]}와 KOSPI의 회귀 그래프를 경로: {filepath}에 저장하였습니다.')

 

결과는요..!

가로축 독립변수 x: 미국 기준금리, 세로축 종속변수 y: KOSPI 지수

R 결정계수 -0.55 ㄷㄷㄷ. 예상했던 것보다 변수 사이의 관계를 잘 설명하지는 못 한다.

전에 분석했던 다우지수와 코스피의 경우 R 결정계수가 0.83로 연관성이 드러나며, 회귀선이 분명하게 우상향 하고 있다.

 

달러 환율과 달러 인덱스, 미국 기준금리는 코스피 범위(변동폭) 정도만 참고할 수 있을 거 같다. :(

728x90

'DA' 카테고리의 다른 글

4/17 월  (0) 2023.04.17
퀀트 기반 종목 선정을 위한 주식 데이터 분석 1('퀀트 투자 무작정 따라하기' by 강환국)  (0) 2023.04.14
3/1 수  (0) 2023.03.02
2/26 일  (0) 2023.02.26
2/22 수  (0) 2023.02.22