파이썬 yfinance로 미국 주식 배당금 확인

개요

  1. 코드에 이상이 있다면 깃허브티스토리에 댓글 달아주세요
  2. 지속적으로 프로그램 업데이트 중이며, 맨 아래 업데이트 목차에 업데이트된 내용을 적고 있습니다

저번에 작성한 글에서는 배당귀족주들의 배당금 이력을 조회하는 코드를 작성했다. 그런데 포스팅하는 중에 ‘배당귀족주 말고 모든 종목에 대해 배당금을 조회할 수 있으면 편하지 않을까?’ 라는 생각이 들었고, 이를 코드로 구현했다. 그냥 코드를 복붙하면 누구나 사용할 수 있지만 파이썬이 깔려있지 않은 사람들에게도 공유할 수 있도록 exe 파일도 만들었다. (근데 파일이 너무 무겁다…)

프로그램 설명

예시로 코카콜라를 검색

1. 프로그램에는 다음의 기능들을 담았다.

(1) 나스닥에 상장된 종목의 역대 배당금, 배당률 시각화하여 조회
– 원하는 종목의 티커를 입력 후 검색 버튼 클릭하여 조회 가능
– 배당률은 배당금을 당해의 종가 평균으로 나눔
(2) 시각화된 그래프 이미지 다운로드
– 그래프 저장 버튼 클릭
(3) 종목의 연도별 배당금, 배당률, 종가 평균 데이터 엑셀 파일로 다운로드
– 엑셀 파일 저장 버튼 클릭

2. 화면 설명

(1) 배당금 이력 조회하고 싶은 티커 입력 (대, 소문자 상관없음)
(2) 티커 입력 후 검색 버튼 클릭
(3) ‘엑셀 파일 저장’ 버튼 클릭 시 해당 종목 데이터 엑셀 파일로 저장
(4) ‘그래프 저장’ 버튼 클릭 시 해당 종목 그래프 이미지 파일로 저장

코드

import yfinance as yf
from tkinter import *
from tkinter import filedialog
from tkinter import font
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import pandas as pd
import datetime

df_savefile = pd.DataFrame()  # 엑셀 파일로 저장할 데이터프레임. 처음엔 아무것도 없음


# 배당금, 배당률, 종가 받아오는 함수
def scraping_stock_data():
    global df_savefile

    ### 주식 데이터 스크래핑하고 배당금, 배당률 시각화
    ticker = str(entry_ticker.get()).upper()  # 엔트리에 입력된 특정 종목 티커 받아오기
    stock = yf.Ticker(ticker)  # 주식 데이터 다운로드

    # 주식 정보 데이터프레임 만들기
    df_stockdata = stock.history(interval="1d", period="max")  # 특정 종목 정보 받아오기
    df_stockdata = df_stockdata.reset_index()  # 인덱스 초기화
    df_stockdata["Year"] = (
        df_stockdata["Date"].astype("str").str.split("-").str[0]
    )  # 연도 컬럼 생성

    # 전처리용 데이터프레임 만들기
    df_preprocessing = df_stockdata[
        df_stockdata.Dividends != 0
    ]  # Dividends가 0인 행 모두 삭제
    df_preprocessing = df_preprocessing.reset_index()  # 인덱스 초기화
    df_preprocessing.drop(["index"], axis=1, inplace=True)
    pd.to_datetime(df_preprocessing["Date"])  # 데이터타입 datetime으로 변경

    df_pivot_dividends = pd.pivot_table(
        df_preprocessing, values="Dividends", index="Year", aggfunc=np.sum
    )  # 배당금 피벗 테이블 만들기
    df_pivot_price = pd.pivot_table(
        df_preprocessing, values="Close", index="Year", aggfunc=np.mean
    )  # 종가 피벗 테이블 만들기

    # 종가 테이블, 배당금 테이블 join 하기
    df_merged = pd.merge(
        df_pivot_dividends, df_pivot_price, on="Year", how="inner"
    )  # 인덱스로 join 하기
    df_merged["Dividends yield rate"] = round(
        (df_merged["Dividends"] / df_merged["Close"]) * 100, 1
    )  # 배당률 컬럼 생성
    df_merged = df_merged.reset_index()  # 인덱스 초기화
    df_savefile = df_stockdata  # 저장할 데이터프레임 만들어 주기
    df_savefile["Date"] = (
        df_savefile["Date"].astype("str").str.split().str[0]
    )  # 타임존은 엑셀로 저장이 안 된다.
    make_graph(df_merged, ticker)

    ### 현재 종가 기준 배당률 구하기
    now = datetime.datetime.now()  # 현재 시간
    now_transform = str(now).split()[0]  # 현재 시간을 yyyy-mm-dd 형식으로
    date_lastclose = str(df_stockdata["Date"].iloc[-1]).split()[
        0
    ]  # 가장 최근 종가 날짜
    price_last_close = df_stockdata["Close"].iloc[-1]  # 가장 최근 종가
    date_last_dividends = str(df_preprocessing["Date"].iloc[-1])  # 마지막 배당일

    if (
        str(now).split()[0][:7] == date_last_dividends[:7]
    ):  # 만약 이번달에 배당이 나옴 -> 배당금합 = 11개월 전~이번달 배당합
        start_date = (
            str(now.year - 1) + "-" + str(now.month + 1).zfill(2) + "-01"
        )  # 시작일을 11개월 전 1일로
        df_trailing_dividends = df_preprocessing[
            df_preprocessing["Date"].bewteen(start_date, now_transform)
        ]  # 최근 1년 데이터프레임
        trailing_dividends_yield_rate = (
            df_trailing_dividends["Dividends"].sum / price_last_close
        ) * 100  # 최근 1년 배당률 구하기
    else:
        start_date = (
            str(now.year - 1) + "-" + str(now.month).zfill(2) + "-01"
        )  # 시작일을 11개월 전 1일로
        df_trailing_dividends = df_preprocessing[
            df_preprocessing["Date"].between(start_date, now_transform)
        ]  # 최근 1년 데이터프레임
        trailing_dividends_yield_rate = (
            df_trailing_dividends["Dividends"].sum() / price_last_close
        ) * 100  # 최근 1년 배당률 구하기
    current_dividends_yield_rate.set(
        f"마지막 종가일({date_lastclose})기준 배당률 : {round(trailing_dividends_yield_rate,1)}%"
    )


# 시각화 함수
def make_graph(df, ticker):
    for widget in frame_3.winfo_children():  # 그래프 매번 새로 그리기
        widget.destroy()

    # 한글 폰트 설정
    plt.rcParams["font.family"] = "Malgun Gothic"  # Windows의 맑은 고딕
    plt.rcParams["axes.unicode_minus"] = False  # 마이너스 기호 깨짐 방지

    fig, ax1 = plt.subplots(figsize=(12, 6))

    # 그래프1
    ax1.set_xlabel("년도")  # 수정. x축 label
    ax1.set_ylabel("배당률", color="red")  # 수정. 그래프1 y축 label
    (line1,) = ax1.plot(
        df["Year"],
        df["Dividends yield rate"],
        "ro--",
        markersize=4,
        label="배당률",
        color="red",
    )  # 그래프1 그리기
    ax1.tick_params(axis="y", labelcolor="red")  # 그래프1 y축 색상 지정

    # 그래프1 숫자표시 : x축, y축 좌표에 값을 텍스트로 표시
    for i, v in enumerate(
        df.index
    ):  # x축 리스트에서 (인덱스, x축 리스트[인덱스]에 해당하는 value)를 가져온다.
        plt.text(
            v,
            df["Dividends yield rate"][i],
            round(
                df["Dividends yield rate"][i], 2
            ),  # x축 리스트[인덱스] : x축 좌표, y축 리스트[인덱스] : y축 좌표, y축 리스트[인덱스] : 표시할 숫자
            fontsize=9,  # 텍스트 크기
            color="red",  # 텍스트 색깔
            horizontalalignment="center",  # 수평 위치 (left, center, right)
            verticalalignment="bottom",  # 수직 위치 (top, center, bottom)
            # rotation=70 # 텍스트 각도
        )

    plt.xticks(rotation=45, fontsize=8)  # 그래프 2 그리기 이전에 해야 x축 45도 돌아감

    # 그래프2
    ax2 = ax1.twinx()  # 두 번째 y축 생성
    ax2.set_ylabel("배당금", color="blue")  # 수정. 그래프2 y축 label
    line2 = ax2.bar(
        df["Year"], df["Dividends"], label=ticker + "_배당금", color="blue"
    )  # 그래프2 그리기
    ax2.tick_params(axis="y", labelcolor="blue")  # 그래프2 y축 색상 지정

    # 그래프2 숫자표시
    for i, v in enumerate(df.index):
        plt.text(
            v,
            df["Dividends"][i],
            round(df["Dividends"][i], 2),
            fontsize=9,
            color="blue",
            horizontalalignment="center",
            verticalalignment="center",
            # rotation=45
        )

    # 제목 및 범례 추가
    lines_labels = line1.get_label(), line2.get_label()
    handles = [line1, line2]
    plt.legend(handles, lines_labels, loc="upper left", fontsize=10, frameon=True)
    ax1.set_zorder(
        ax2.get_zorder() + 10
    )  # 그래프1 zorder를 그래프2 zorder보다 무조건 높게. zorder 낮을수록 먼저 그려짐
    ax1.patch.set_visible(False)  # 그래프1 레이어 투명하게
    plt.title(f"배당률 & 배당금 : {ticker}")  # 수정. 그래프 제목
    fig.tight_layout()  # 레이아웃 조정

    canvas_graph = FigureCanvasTkAgg(fig, frame_3)
    canvas_graph.get_tk_widget().pack(side=LEFT, fill=BOTH)


def save_graph():  # 그래프 저장 위치 정하기
    filename = filedialog.asksaveasfilename(
        initialfile="Untitled.png",
        defaultextension=".png",
        filetypes=[("All Files", "*.*"), ("Portable Graphics Format", "*.png")],
    )
    plt.savefig(filename)


def save_excelfile():  # 엑셀 파일 저장 위치 정하기
    filename = filedialog.asksaveasfilename(
        initialfile="Untitled.xlsx",
        defaultextension=".xlsx",
        filetypes=[("All Files", "*.*"), ("Excel 통합 문서", "*.xlsx")],
    )
    df_savefile.to_excel(filename, index=True, index_label="Year")


# 윈도우 생성
window = Tk()
window.title("Search Dividends History")  # 윈도우 이름
window.geometry("1200x600")  # 윈도우 크기

# tkinter 폰트 맑은 고딕으로
gui_font = font.Font(family="맑은 고딕", size=25)

# 프레임 생성
frame_1 = Frame(
    master=window, relief=RAISED
)  # 티커 검색 버튼, 엑셀 저장 버튼, 그래프 저장 버튼 프레임
frame_2 = Frame(master=window)  # 마지막 종가 기준 배당률 출력
frame_3 = Frame(master=window)  # 그래프 출력 프레임

# 레이블 위젯 : 티커를 입력하세요
label_ticker = Label(master=frame_1, text="Tiker : ", font=gui_font)

# 레이블 위젯 : 마지막 종가 기준 배당률 출력
current_dividends_yield_rate = StringVar()
label_current_dividends_yield_rate = Label(
    master=frame_2, textvariable=current_dividends_yield_rate, font=gui_font
)

# 엔트리 위젯 : 티커 입력. 테스트로는 x의 몇 제곱인지
entry_ticker = Entry(
    master=frame_1, fg="black", bg="white", width=20, justify=CENTER, font=gui_font
)

# 버튼 위젯 : 검색, 엑셀 파일 저장, 그래프 저장
button_search = Button(
    master=frame_1,
    text="검색",
    bg="white",
    fg="black",
    width=10,
    height=2,
    command=scraping_stock_data,
)  # 검색 버튼

button_download_excel = Button(
    master=frame_1,
    text="엑셀 파일 저장",
    bg="white",
    fg="black",
    width=20,
    height=2,
    command=save_excelfile,
)  # lambda: print("엑셀 파일이 저장되었습니다.")) # 엑셀 파일로 저장하는 버튼

button_download_graph = Button(
    master=frame_1,
    text="그래프 저장",
    bg="white",
    fg="black",
    width=20,
    height=2,
    command=save_graph,
)  # 이미지 저장하는 버튼


# 처음에 빈화면 띄워놓기
fig, ax = plt.subplots(figsize=(8, 6))
canvas_graph = FigureCanvasTkAgg(fig, frame_3)
canvas_graph.get_tk_widget().pack(side=LEFT, fill=BOTH)

# 프레임 배치
frame_1.pack()  # 티커 검색, 그래프, 엑셀 저장 버튼
frame_2.pack()  # 마지막 종가 기준 배당률
frame_3.pack()  # 역대 배당률, 배당금 시각화

# 라벨, 엔트리 배치
label_ticker.pack(side=LEFT)  # 티커 검색 라벨 배치
entry_ticker.pack(side=LEFT)  # 티커 검색 엔트리 배치
label_current_dividends_yield_rate.pack(side=LEFT)  # 마지막 종가 기준 배당률 라벨 배치

# 버튼 배치
button_search.pack(side=LEFT)
button_download_excel.pack(side=LEFT)
button_download_graph.pack(side=LEFT)

window.mainloop()

– 코드에 대한 설명은 깃허브 참고, 기타 문의틑 티스토리 연락 부탁

프로그램 배포

1. 파이썬 파일

– 파이썬 파일이 업로드가 안돼서 구글 드라이브 링크에 업로드

2. exe 파일

– 파이썬이 없는 사람은 다음 구글 드라이브 링크에서 프로그램을 다운로드받아 실행 가능하다. 하지만 아래 이유로 웬만하면 파이썬 파일을 다운로드하거나 코드를 그대로 복붙해서 사용하는 걸 추천한다.

  1. 맥에서 사용 불가능. Pyinstaller로 exe 파일 만들었는데, 윈도우에서 만든 exe 파일은 윈도우에서만 작동함
  2. exe 파일 용량이 너무 큼 (거즘 100mb… 위의 파이썬 파일은 단 10kb이다…)
  3. 너무 느림. Yfinance 가 원래도 엄청 빠르진 않지만, tkinter로 gui 만들고, exe 파일로 만드니까 프로그램 켜지는 데만 1분 넘게 걸린다

업데이트

250304(화)

  1. 1. tkinter 폰트 맑은 고딕으로 변경
  2. 배당률 계산 방법 변경 (기존 : 배당률 = 당해 배당금 합 / 당해 종가 평균, 변경 : 배당률 = 당해 배당금 합 / 당해 배당락일 종가 평균) : 당해 배당금을 당해 종가 평균으로 나누는 건 정확성이 떨어지고 무의미할 것 같다는 의견을 받음
  3. 마지막 종가 기준 최근 1년 배당률 계산하여 출력하는 기능 추가

250311(화)

  1. 이번 달 배당이 지급된 경우, 마지막 종가일 기준 배당률 출력이 제대로 되지 않는 오류 수정
  • sum -> sum()
  • bewteen -> between