파이썬으로 PER 밴드 차트 그리기 2 - 수정주가 가져와서 선형으로 그래프 그리기
본문 바로가기
파이썬으로 만든 것들/PER밴드

파이썬으로 PER 밴드 차트 그리기 2 - 수정주가 가져와서 선형으로 그래프 그리기

by Squat Lee 2022. 9. 7.

2022.09.05 - [취미로 하는 파이썬/투자 실험실 with 파이썬] - 파이썬으로 PER 밴드 만들기 1

파이썬으로 PER 밴드 만들기 1

파이썬으로 PER 밴드를 만들어 보았습니다. https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=kbomb2000&logNo=221295759491 PER band 그리기 해당 글에도 언급이 되어 있듯이 피터 린치는..

dotsnlines.tistory.com


지난번 포스트에서 만들어 본 PER 밴드를 좀 더 다듬어 보겠습니다.(지난번 포스트는 위의 링크를 참고하세요)

지난번 포스트에서 PER 밴드를 만드는 과정에서 2가지 고민할 부분이 있었습니다.

첫번째, 주가를 PER x EPS로 구해서 제대로 된 주가가 표시되지 않았습니다.
두번째, EPS 기준으로 PER 밴드를 만드니 계단형 그래프가 나왔습니다.

이번 포스트에서는 이 2가지 사항을 개선해 보도록 하겠습니다.



우선 전체 코드는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import pandas_datareader.data as web
from datetime import datetime
from pykrx import stock
import matplotlib.pyplot as plt
import pandas as pd
 
def per_band(start, end, stock_code):
 
    # 수정주가 가져오기
    sy = start[:4]
    sm = start[4:6]
    sd = start[-2:]
 
    ey = end[:4]
    em = end[4:6]
    ed = end[-2:]
 
    date_start = datetime(int(sy), int(sm), int(sd))
    date_end = datetime(int(ey), int(em), int(ed))
 
    df_p = web.DataReader(stock_code+'.KS''yahoo', date_start, date_end)
    df_p = df_p[['Adj Close']].astype(int)
 
    #펀더멘털 구하기
    df_f = stock.get_market_fundamental(start, end, stock_code, freq='d')
 
    #EPS가 계단형으로 나오기에 선현으로 변경하기
    li_eps = df_f['EPS'].unique() #년도별 EPS를 리스트로 만들기
    c_eps = list(df_f['EPS'].value_counts())#각 EPS별 개수
 
    combined_eps = []
 
    #EPS 각 값과, 개수를 리스트로 합치기
    for n in range(len(li_eps)):
            combined_eps.append([li_eps[n], c_eps[n]])
 
    NEW_EPS = []
 
    for num, e in enumerate(combined_eps): #EPS와 월별 개수를 합친 리스트를 반복
        if num < len(combined_eps)-1#마지막까지 반복하면 에러가 나므로 마지막 전까지 반복
            for c in range(combined_eps[num][1]): #EPS별 개월수를 반복하기(0 to n-1)
                new_eps = int(combined_eps[num][0+ ((combined_eps[num+1][0]-combined_eps[num][0])*c)/
                              (combined_eps[num][1+ 1)) # 올해EPS + (내년EPS - 올해EPS) X (해당개월수 / 전체개월수)
                NEW_EPS.append(new_eps)
        else#마지막 리스트 개체
            for c in range(combined_eps[-1][1]):#마지막 리스트 반복하기
                new_eps = int(combined_eps[-1][0* (c+1/ combined_eps[-1][1])
                NEW_EPS.append(new_eps)
 
    df_eps = pd.DataFrame(data=NEW_EPS, columns=['N_EPS'], index=df_f.index)
    df_f = pd.merge(df_f, df_eps, left_index=True, right_index=True)
 
    per_med = int(df_f['PER'].median())  # 중간값
    per_max = int(df_f['PER'].max() + 1)  # 최대값
    per_min = int(df_f['PER'].min() - 1)  # 최소값
    per_25 = int(df_f['PER'].quantile(0.25))  # 25%
    per_75 = int(df_f['PER'].quantile(0.75))  # 75%
 
    df_f['PER_' + str(per_min)] = df_f['N_EPS'* per_min
    df_f['PER_' + str(per_25)] = df_f['N_EPS'* per_25
    df_f['PER_' + str(per_med)] = df_f['N_EPS'* per_med
    df_f['PER_' + str(per_75)] = df_f['N_EPS'* per_75
    df_f['PER_' + str(per_max)] = df_f['N_EPS'* per_max
 
    df_f = df_f[['PER_' + str(per_min), 'PER_' + str(per_25), 'PER_' + str(per_med), 'PER_' + str(per_75),
                 'PER_' + str(per_max)]]
 
    df_t = pd.merge(df_p, df_f, left_index=True, right_index=True)
    df_t = df_t.astype(int)
 
    name = stock.get_market_ticker_name(stock_code)
 
    plt.figure(figsize=(188))
    plt.plot(df_t['Adj Close'], 'red')
    plt.plot(df_t['PER_' + str(per_min)], linestyle='--', color='black')
    plt.plot(df_t['PER_' + str(per_25)], linestyle='--', color='gray')
    plt.plot(df_t['PER_' + str(per_med)], linestyle='--', color='blue')
    plt.plot(df_t['PER_' + str(per_75)], linestyle='--', color='pink')
    plt.plot(df_t['PER_' + str(per_max)], linestyle='--', color='green')
    plt.title(name)
    plt.legend(df_t.columns)
    plt.show()
 
    return df_t
 
df = per_band('20180101''20220731''005380')
 
print(df)
 
cs

수정주가를 가져오기 위해서는 import pandas_datareader.data as web 라이브러리를 가져왔습니다.

# 수정주가 가져오기
    sy = start[:4]
    sm = start[4:6]
    sd = start[-2:]
 
    ey = end[:4]
    em = end[4:6]
    ed = end[-2:]
 
    date_start = datetime(int(sy), int(sm), int(sd))
    date_end = datetime(int(ey), int(em), int(ed))

pykrx와 날짜 데이터를 입력하는 형식이 달라서 입력된 날짜를 슬라이싱 해서 일일이 시계형 데이터로 변환했습니다.


계단형으로 되어있는 데이터를 선형으로 만들기 위해서 많은 고민을 했습니다.

KRX에서 가져온 데이터(EPS)는 일정하지가 않아서 매년 EPS가 업데이트가 되는 날이 달랐습니다. 그리고 제가 입력하는 날짜에 따라서 선형으로 그래프가 그려져야 하니 이 부분 또한 구현하기가 어려웠습니다.

#EPS가 계단형으로 나오기에 선현으로 변경하기
    li_eps = df_f['EPS'].unique() #년도별 EPS를 리스트로 만들기
    c_eps = list(df_f['EPS'].value_counts())#각 EPS별 개수
 
    combined_eps = []
 
    #EPS 각 값과, 개수를 리스트로 합치기
    for n in range(len(li_eps)):
            combined_eps.append([li_eps[n], c_eps[n]])
 
    NEW_EPS = []
 
    for num, e in enumerate(combined_eps): #EPS와 월별 개수를 합친 리스트를 반복
        if num < len(combined_eps)-1#마지막까지 반복하면 에러가 나므로 마지막 전까지 반복
            for c in range(combined_eps[num][1]): #EPS별 개월수를 반복하기(0 to n-1)
                new_eps = int(combined_eps[num][0+ ((combined_eps[num+1][0]-combined_eps[num][0])*c)/
                              (combined_eps[num][1+ 1)) # 올해EPS + (내년EPS - 올해EPS) X (해당개월수 / 전체개월수)
                NEW_EPS.append(new_eps)
        else#마지막 리스트 개체
            for c in range(combined_eps[-1][1]):#마지막 리스트 반복하기
                new_eps = int(combined_eps[-1][0* (c+1/ combined_eps[-1][1])
                NEW_EPS.append(new_eps)
 

우선 같은 EPS 별로 만들어서 "li_eps" 의 리스트 변수에 넣고, 중복되지 않은 EPS 개수를 구해서 "c_eps" 리스트 변수에 넣었습니다.

개념은 EPS 값 별 개수를 센 다음 첫번째 EPS 부터 마지막 EPS 값까지 차등을 주는 겁니다.

예를들어 2008년 8월부터 같은해 12월까지 EPS가 100으로 동일하고, 2009년 1월 EPS가 200 이라고 가정한다면, 2008년 "8월은 (100 + (200-100)*0/6), 9월은 (100 + (200-100)*1/6), 10월은 (100 + (200-100)*2/6)... 2009년 1월은 100 + (200-100)*2/6)"이 됩니다.

전체 개월수는 동일한 EPS의 개수 +1을 했습니다. 왜냐하면 다음해 EPS 값이 바뀔때까지 포함한 개수이기 때문입니다.

중복되지 않은 EPS와 각 동일한 EPS의 개수는 "combined_eps" 리스트 변수에 튜플 형태로 넣었습니다.(제가 기초가 약해서 리스트 형태가 사용하기 편해서 튜플 대신에 리스트를 사용해서 해당 위치를 인덱싱 하는 방법을 사용했습니다.)

조금 헷갈리지만 이렇게 각 EPS 값을 선형으로 만들어 준 다음 " NEW_EPS" 리스트를 만들었습니다.

df_eps = pd.DataFrame(data=NEW_EPS, columns=['N_EPS'], index=df_f.index)
df_f = pd.merge(df_f, df_eps, left_index=True, right_index=True)

만들어준 새로운 EPS 리스트인 "NEW_EPS"를 데이터프레임으로 만든 후 펀더멘털, 즉 PER을 가져온 데이터프레임(df_f)와 합쳐 줍니다.

df_t = pd.merge(df_p, df_f, left_index=True, right_index=True)
df_t = df_t.astype(int)

가져온 수정주가를 다시 df_f와 한 번 더 합치고, 데이터프레임의 데이터 타입을 정수로 바꿔줍니다.

정수로 바꿔주는 이유는 계산과정에서 실수 형태가 나와서 그래프를 그리면 소수점까지 지저분하게 보여지기 때문에 깔끔하게 보여주기 위함입니다.


실행을 해 보면 위의 그림과 같이 잘 나옵니다.

증권사나 fnguide에서 제공하는 PER 밴드와 다소 차이는 있을 수 있으나 거의 흡사하게 나온 것 같습니다.

처음에 PER밴드를 아주 간단하게 그릴려고 했는데, 하다보니 개선할 부분이 계속 보이네요.

다음 포스트에는 미래의 예측치 EPS를 가져와서 컨센서스까지 보여지도록 해 보겠습니다.

그리고 매번 파이참을 열고, 그려 보기에 번거로움이 있으니 qtdesinger와 PyQt5로 UI도 만들어 보도록 하겠습니다.

728x90
반응형

댓글