본문 바로가기
내가 좋아하는 정보들/뒤쳐지지 말자, IT

[DataFrame/Matplotlib] 미국 금리에 따른 국내 상장 채권 ETF의 가격 추이 알아보기, matplotlib 알아보기

by 앩옭 2022. 8. 31.

목차

    Intro

    쨔잔~ 오늘의 실습 결과물

     

    저는 Python과 Pandas, 그리고 Matplotlib를 조금씩 야매로 배웠습니다.

    한편 채권을 조금씩 사모으려고 네이버 증권에서

    이리저리 여러 종목 페이지를 오가며 가격 흐름을 캡쳐하던 중,

     

    이런 노가다를 하고 있었음

    Pykrx 모듈에서 티커로 종가를 한번에 불러모아

    Matplotlib으로 그리면 원하는 자료가 나오겠다는 생각이 들었습니다. 

     

     

     

    오늘 게시글에서는 이 과정을 정리해 보았습니다. 

     

    저는 Jupyter Notebook을 이용하였는데, 다른 툴을 이용하셔도 상관 없어 보입니다.

     

    초보자분들껜 도움 되길 바라고, 

    더 좋은 방법이 있으면 친절히 알려주시면 감사하겠습니다.

     

     

    모듈 설치와 준비물 챙기기 

    먼저 주식 정보 스크래핑 모듈인 pykrx와 대표적인 파이썬 시각화 모듈인 matplotlib를 설치합니다.

    (판다스는 이미 설치되어 있습니다)

    터미널에서는 pip 명령어를, 쥬피터 노트북과 같은 환경에서는 !pip 명령어로 설치할 수 있습니다.

    설치한 김에 미리 사용할 수 있도록 모두 임포트합니다. 

    !pip install pykrx
    !pip install matplotlib
    
    
    from pykrx import stock
    import pandas as pd
    import matplotlib.pyplot as plt

     그 다음엔 네이버 금융 탭에서 관심있는 종목의 티커를 긁어옵니다. 

    여담으로, 네이버는 이상하게도 22년 8월 31일 현재

    '네이버 금융'과 '네이버 증권'페이지를 동시에 갖고 있는데,

    둘 다 대충 증권 얘기를 하지만

    '네이버 금융' 페이지가 더 상세한 정보를 얻을 수 있습니다.

    네이버 증권은 왜 있는거지? 싶습니다.

     

    네이버 금융 : https://finance.naver.com/

     

    네이버 금융

    국내 해외 증시 지수, 시장지표, 뉴스, 증권사 리서치 등 제공

    finance.naver.com

    네이버 증권 : https://m.stock.naver.com/

     

    네이버 증권

    관심종목의 실시간 주가를 가장 빠르게 확인하는 곳

    m.stock.naver.com

     

     

    티커로 원하는 기간의 주가 정보 불러오기   

    여러 종목의 주가 정보를 조회할 예정이기 때문에

    원하는 작업을 수행하는 함수를 정의하려고 합니다. 

    def PriceTrend(name, ticker):
        df = stock.get_market_ohlcv("20190301", "20220830", f"{ticker}")[['종가']]
        # 조회된 정보 중 '종가' 컬럼만을 선택

    저는 코로나가 시작되기 약 1년 전인

    2019년 3월부터 2022년 8월 31일 현재까지의 '종가'를 가격 흐름으로 보려고 합니다.

     

    pykrx의 stock.get_market_ohlcv(시작일자, 종료일자, 티커) 메서드는

    아래와 같이 관심있는 종목의 기간에 따른 시가, 고가, 저가, 종가, 거래량 정보를

    데이터프레임 형태로 반환합니다. 함수는 이 반환값을 df라는 변수로 저장합니다. 

    이 중 종가 컬럼만을 인덱싱합니다. [['종가']]로 인덱싱 한 이유는 인덱싱 결과가

    시리즈가 아니라 데이터프레임 형태로 유지되기 위함입니다. 

    이렇게 김밥 한 줄이 생겼습니다! 

    나중에는 여러 종목의 종가 정보를 나란히 붙일 건데, 

    컬럼 이름이 똑같이 '종가'이면 곤란합니다.

    따라서 컬럼 이름을 함수에 입력한 이름으로 변경합니다. 

    def PriceTrend(name, ticker):
        df = stock.get_market_ohlcv("20190301", "20220830", f"{ticker}")[['종가']]
        # 조회된 정보 중 '종가' 컬럼만을 선택 
        
        df.rename(columns={'종가' : name }, inplace = True)
        # 함수를 이용해 호출된 서로 다른 종목의 컬럼명이 모두 '종가'이므로, 상품 이름으로 변경 
        
        return df

     

     

    Concat 함수로 여러 주가 정보 합친 데이터프레임 생성하기    

    우선 예쁘게 만들어 둔 함수에 

    관심있는 종목 이름(나만 알아보는 이름)과 티커를 입력해 

    종가 데이터프레임을 생성합니다. 

     

    # TIGER KOSPI
    kospi = PriceTrend("kospi", 277630)
    
    # KOSEF 국고채3년
    KR_bonds_3 = PriceTrend("KR_bonds_3", 114470)
    
    # KOSEF 국고채10년
    KR_bonds_10 = PriceTrend("KR_bonds_10", 148070)
    
    # KBSTAR KIS국고채30년Enhanced
    KR_bonds_30 = PriceTrend("KR_bonds_30", 385560)
    
    # TIGER 미국채10년선물
    US_bonds_10 = PriceTrend("US_bonds_10", 305080)
    
    # KODEX 미국채울트라30년선물(H)
    US_bonds_30 = PriceTrend("US_bonds_30", 304660)

     

    그 다음, 세 개 이상의 데이터프레임을 합치기 위해

    합칠 데이터프레임을 리스트에 저장합니다.

    새로 생성될 데이터프레임 = pd.concat( 합칠 데이터프레임 리스트, axis=1 )으로 입력하여

    데이터프레임 '세로로' 붙이기를 실행합니다. 

     

    df_list = [kospi, KR_bonds_3, KR_bonds_10, KR_bonds_30, US_bonds_10, US_bonds_30]
    df = pd.concat(df_list, axis=1)

     

    관심있는 종목들의 종가가 Concat으로 잘 연결된 모습입니다.

     

     

    fillna() 메서드로 결측치를 근처 값으로 채우기

    그런데 Concat으로 연결된 ETF들의 가격 정보를 모니,

    NaN으로 표시된 값이 보입니다.

     

    KR_bonds_30이 나타내는 KBSTAR KIS국고채30년Enhanced은

    2021년 05월 26일에야 상장해서, 상장 이전은 결측치로 나타납니다.

     

    다른 종목들은 현재도 상장되어 있고,

    첫 값도 멀쩡하므로 결측치는 KR_bonds_30 컬럼에만 있다고 간주합니다.

     

    결측치는 다양한 방법으로 채울 수 있는데, 

    저는 결국 모든 종목을 각자 최초 측정일의 가격에 대비한 변화 흐름으로 파악하고자 합니다.

    즉 특정일의 가격/최초일의 가격으로 나타낼 것입니다. 

     

    그래서 우선은 결측치를 가장 가까이에 있는 나중 값으로 채웁니다. 

     

    df = df.fillna(method='bfill')
    # 가장 가까이에 있는 '이전' 값으로 채우고 싶다면 method='ffill'

     

    한편, 결측치를 가장 가까이에 있는 '이전' 값으로 채우고 싶다면

    method를 'ffill'로 입력합니다.

     

    이렇게 결측치를 처리했습니다. 

    한편, 저는 종목의 가격 자체보다 

    금리와 (단,중,장기) 채권 ETF 별 가격 추이가 알고 싶습니다.

     

    따라서 2019-03-01 의 가격으로 모든 값을 나누어 상대적인 움직임을 관찰합니다.  

    df1 = df/df.iloc[0]
    df1

     

    위와 같이 계산했을 때

    연산이 브로드캐스팅 되어 

    아래와 같은 결과를 얻습니다. 

     

     

     

    기준 금리 정보를 데이터프레임화하기 

    # 한국 기준금리 https://www.bok.or.kr/portal/singl/baseRate/list.do?dataSeCd=01&menuNo=200643
    # 미국 기준금리 https://kr.investing.com/economic-calendar/interest-rate-decision-168

     

    그 다음 위 자료를 참조로 기준금리를 입력합니다.

    누가 기간별로 엑셀에 정리해줬으면 좋았을 텐데...

    저는 바보라서 아래처럼 했습니다. 

    가만히 보다가 얘 왜 이렇게 하지? 싶으면 누가 알려주세요.. 

    df2= df1.copy()

    먼저 주가 흐름이 담긴 데이터프레임을 카피합니다.

    주가 정보가 아니라 인덱스를 사용하기 위해서입니다.

    그 데이터프레임에는 원하는 기간이 DatetimeIndex 자료형으로 인덱스에 담겨 있어요. 

    df2['US_baseInterest'] = 2.5
    df2['US_baseInterest'].loc["2019-7-31":"2019-9-17"] = 2.25
    df2['US_baseInterest'].loc["2019-9-18":"2019-10-29"] = 2.0
    df2['US_baseInterest'].loc["2019-10-30":"2020-3-2"] = 1.75
    df2['US_baseInterest'].loc["2020-3-3":"2022-3-14"] = 1.25
    df2['US_baseInterest'].loc["2020-3-15":"2022-3-15"] = 0.25
    df2['US_baseInterest'].loc["2022-3-16":"2022-5-3"] = 0.5
    df2['US_baseInterest'].loc["2022-5-4":"2022-6-14"] = 1.0
    df2['US_baseInterest'].loc["2022-6-15":"2022-7-26"] = 1.75
    
    df3 = df2[['KR_baseInterest', 'US_baseInterest']]
    # 기준 금리 정보가 담긴 컬럼들만 지정하여 따로 저장하기

    df2에서 'US_baseInterest' 컬럼을 새로 생성하고, 모든 값을 2.5로 지정합니다.

    그다음 미국 기준 금리에 따라 

    df2['US_baseInterest'].loc["시작기간":"종료기간"] = 해당 기간의 기준금리 

    로 일일히 입력해 줍니다.

     

    ㅎㅎ 

     

    df3 = df2[['KR_baseInterest', 'US_baseInterest']]

    그 다음, df3라는 새로운 데이터프레임을 설정하고 

    여기에 주가 흐름이 아닌 미국 기준금리만 담기도록 지정합니다. 

     

    처음에는 한국 기준금리도 반영하려 했는데, 크게 의미 없을 것 같아 빼서 명령어가 이렇습니다. 

     

     

    방법 (1) Matplotlib에서 두 그래프를 하나의 캔버스 안에 그리기  

     

    기준금리와 가격 흐름을 matplotlib로 각각 막대그래프/선그래프로 동시에 그려 보겠습니다.

     

    matplotlib는 쓰긴 쓰는데 왜 되는지 잘 모르겠는 라이브러립니다.

    fig는 뭐고 ax는 대체 뭐야?

    fig = plt.figure(figsize =(14, 8)) #캔버스 생성 
    ax = fig.add_subplot() #프레임 생성

    인터넷에 열심히 찾아보니, fig 지정은 그림을 그릴 커다란 캔버스를 생성하는 것과 같고

    ax 지정은 그 캔버스 안에 프레임을 생성하는 것이라고 합니다.

     

    캔버스 안에 여러 프레임 인가봐요. 

    설명하자면 이런 구조

     


    먼저 코드 전체는 이렇습니다.

    lines = ax.plot(df1)
    ax.legend(handles=lines, labels=list(df1.columns))
    plt.grid(True, axis="y")
    plt.title("Changes in bond price by U.S Base interest")
    
    bars = ax.bar(df3.index, df3['US_baseInterest'], color='beige',width=5.5)
    
    plt.show()

    lines = ax.plot(df1) 

    ax.plot은 선그래프를 그리는 명령어입니다. 

    lines라는 변수로 이름도 달아 주었습니다.

     

    df1에는 주가 흐름이 담겨 있었습니다.

    고로 df1으로 선 그래프를 그리라고 명령한 것입니다.

     

    여기까지만 하고 plt.show()로 그래프를 내놓으라 하면

    이런 흐름이 만들어집니다. 

    역동적이고 좋긴 한데, 뭐가 뭔지 모르겠습니다.

    그러므로 범례를 설정해야만 합니다. 

     

    범례를 구체적으로 설정하기

     보통 범례는 plt.legend() 한 줄 넣고 마는데

    한 프레임 안에 여러 그래프를 넣을 땐

    범주를 구체적으로 설정해야 하는 것으로 보입니다. 

     

    아까 ax라는 변수에 프레임을 바인딩하고, 

    그 프레임 안에 그림을 그렸습니다.

     

    따라서 plt이 아닌 ax라는 프레임에 직접 범주 정보를 할당해 보겠습니다. 

    ax.legend(handles=lines, labels=list(df1.columns)) 

    handles는 맥락상 범주를 달고 싶은 그래프를 말하는 것 같습니다.

    아까 그린 'lines'를 물려줍니다.

     

    labels 는 iterable한 구조를 받을 수 있다고 합니다.

    그래서 df1.columns를 리스트로 만들어 물려 주니 잘 작동합니다. 

     

    plt.grid(True, axis="y")
    plt.title("Changes in bond price by U.S Base interest")

    로 각각 그리드를 형성하고, 제목을 답니다. 

    이렇게까지 하면 아래와 같이 그려집니다. 

     

    크 보기 좋습니다! 

    그런데 아직 기준금리 정보를 입력하지 않았어요.

     

    ax라는 프레임 안에 새로운 막대 그래프를 그려달라고 명령해 봅시다.

    bars = ax.bar(df3.index, df3['US_baseInterest'], color='beige',width=5.5)

    하나 더 그려달라

     

    ax.bar는 ax라는 프레임에 막대 그래프를 그려줍니다.

    ax.plot으로 명령하는 선형 그래프와 달리, 

    막대그래프는 x값, y값을 인자로 받아요.

     

    따라서 x값엔 기준금리의 정보를 담은 df3의 index 정보를 물려주고,

    y값에는 df3의 미국 기준 금리 정보를 컬럼으로 지정하여 물려줍니다.

    color 와 width 는 취향것 선택합니다. 

     

    저는 베이지로 했는데, 다른 색상 고유 명사가 보고 싶다면 아래로 가보세요. 

    Matplotlib CSS 색상 이름 보러 가기 

    그러면 이렇게 기준금리와 채권 ETF 상품들의 가격 추이를

    한번에 볼 수 있도록 그래프가 완성됩니다. 

     

     

     

    방법 (2) Matplotlib에서 두 그래프를 하나의 캔버스 안에 그리기  

    금방은 내용을 파악하기 쉽도록 기준금리와 가격 추이를

    한번에 표현했습니다.

     

    이번에는 한 캔버스(fig) 안에 여러 프레임(ax)을 형성하여

    여러 칸의 그래프를 그려 보겠습니다. 

     

    전체 코드는 이렇습니다. 

     

    fig = plt.figure(figsize =(14, 10)) #캔버스 생성 
    ax = fig.add_subplot(2,1,1)
    ax2 = fig.add_subplot(2,1,2)#프레임 생성 
    
    lines = ax.plot(df1)
    ax.legend(handles=lines, labels=list(df1.columns))
    plt.grid(True, axis="y")
    plt.title("Changes in bond price by U.S Base interest")
    
    bars = ax2.bar(df3.index, df3['US_baseInterest'], color='beige',width=5.5)
    
    plt.show()

    아까 코드와의 차이점은,

    아까는 fig.add_subplot 명령을 한 번 수행했던 반면

    이번에는 두 번 수행했으며, 각각 결과를 ax, ax2 두 개의 변수에 반환했습니다. 

     

    또한 fig.add_subplot 메서드의 인자로 어떤 좌표를 입력한 것을 알 수 있는데요, 

    이는 fig.add_subplot(행, 열, 순서) 인 것으로 파악됩니다. 

     

    예를 들어 제가 이렇게 입력했을 때,

    ax = fig2.add_subplot(2,1,1)
    ax2 = fig2.add_subplot(2,1,2)

    생성되는 구조는 위 그림의 오른쪽과 비슷할 겁니다.

     

    이제 그래프를 그립니다.

    lines = ax.plot(df1)
    ax.legend(handles=lines, labels=list(df1.columns))
    plt.grid(True, axis="y")
    plt.title("Changes in bond price by U.S Base interest")
    
    bars = ax2.bar(df3.index, df3['US_baseInterest'], color='beige',width=5.5)

    선 그래프에 대해서는 위와 동일하나,

    막대 그래프를 그릴 때 

    ax가 아닌 ax2에서 메서드를 실행합니다.

     

    이 실행을 도식화하면 아래와 같고요, 

     

    그래프는 아래처럼 오와 열을 맞춰 작성됩니다 ㅎㅎ

     

    열심히 만들어 놨으니 노트북을 그대로 덮지 말고 저는

    생각이라는 것을 해봐야 하겠죠. 

     

    살펴보니

    첫쨰, 미국, 국고채 30년물은 최근 금리 인상기에

    코스피와 비슷한 등락을 보이네요. 

     

    한국의 단기-중기 채권은 금리 인상과 맞물려 금리가 증가하나, 

    손실폭은 드라마틱하지 않습니다. 

     

    미국채10년물은 한국 10년물과 달리

    가격이 기준일로부터 붕 떠 있는데 

    이유를 잘 모르겠습니다. 

     

    코리아 디스카운트인 것일까요? 

    이 이유가 블로그의 숙제가 되어 다음 글의 주제가 될 것 같습니다. 

     


     

    파이썬/판다스와 관련해서는 이렇게 길게 글을 써보는 게 처음이라

    보는 사람이 과연 원하는 정보를 얻어갈 수 있을지 걱정이 됩니다.

    하지만 공부한 거 정리하고 쉐어 할 겸 꾸준히 써 보려고요! 

     

    긴 글 봐 주셔서 감사합니다. 

    반응형

    댓글