파이썬 마법의 공식 - 7. PER, ROA 순위를 매겨서 투자 종목 선정하기
본문 바로가기
파이썬으로 만든 것들/마법의 공식

파이썬 마법의 공식 - 7. PER, ROA 순위를 매겨서 투자 종목 선정하기

by Squat Lee 2022. 8. 15.

이번 포스트는 지난번에 만든 PER, ROA를 가져오는 함수를 이용해서 전 종목의 PER과 ROA의 순위를 매긴다음 조엘그린블란트의 마법의 공식과 비슷하게 20개 종목을 고르는 코드를 작성해 보겠습니다.

 

지난 포스트는 아래 링크를 참고하세요.

2022.07.25 - [집구석 강의/파이썬으로 마법의 공식 구현하기] - 파이썬 마법의 공식 - 6. ROA 가져오기

 

파이썬 마법의 공식 - 6. ROA 가져오기

지난번까지 전체 종목을 가져와서 PER까지 가져오는 방법을 알아보았습니다. 지난번 포스트는 아래 링크를 참고하세요. 2022.07.13 - [집구석 강의/파이썬으로 마법의 공식 구현하기] - 파이썬 마법

dotsnlines.tistory.com


 

def make_magic_df(year, date):
 
      # #KRX 데이터 "종목코드", "종목명", "PER" 가져오기
      codes = per(date)
      codes['종목코드'= codes.index

이번에는 바로 함수를 만들어서 함수 안에서 작성해 보도록 하겠습니다.

 

지난번에 만든 PER 함수를 이용해서 전 종목의 Code를 가져와서 'codes'라는 데이터프레임 형태의 변수에 넣겠습니다.

 

그리고 인덱스를 '종목코드'라는 컬럼으로 따로 만들도록 하겠습니다.

 

# ROA를 가져와서 데이트프레임 만들기
      for num, code in enumerate(codes['종목코드']):
            try:
                  if num == 0:
                        roa_df = get_roa(code, year)
                  else:
                        roa_df = pd.concat([roa_df, get_roa(code, year)])
                  time.sleep(0.01)
            except:
                  pass
 
            print(len(codes['종목코드']) - num, codes['종목명'][num])

지난번에 만든 ROA를 가져오는 함수는 코드를 인자로 일일이 넣어줘야 합니다.

 

2000개가 넘는 종목코드를 일일이 넣어 줄 수는 없기에 codes['종목코드'] 에서 code를 하나씩 빼서 "get_roa" 함수에 넣어줍니다.

 

이때 for 문에서 숫자가 순차적으로 표시되는 enumerate 함수를 사용합니다. 왜냐하면, 첫번째 가져오는 Dataframe은 "roa_df"라는 데이터프레임에 바로 넣어주고, 두번째 부터는 기존에 만든 Dataframe과 새로가져온 Dataframe을 "concat"로 합쳐줘야 하기 때문입니다.

 

예를 들어서 첫번째 삼성전자 ROA를 가져왔다면, 두번째 현대자동차 ROA를 가져와서 이 두 Data를 Dataframe 형태로 합쳐줍니다.

(※ Dataframe은 테이블 형식으로 가져오는 데이터를 말합니다. 좀 더 자세히 알고 싶으시면 "10 min for pandas"를 공부해 보시기 바랍니다.)

 

크롤링을 하는 것이기에 try 함수를 사용하고, 에러가 발생하면 과감하게 pass 하도록 하겠습니다.

 

그리고 실제로 실행되는지 실행창에서 확인해보기 위해 전체종목 숫자(len(codes['종목코드'])-num)를 하나씩 줄여가며, 해당 종목을 프린트 해 봅니다.

 

# 종목코드 칼럼을 index로 바꾸기
      codes = codes.set_index('종목코드')

이제 칼럼으로 뺀 '종목코드'를 다시 index로 바꿉니다.

 

 # ROA, ROA 문자형 데이터를 숫자로 변경
      roa_df['ROA'= pd.to_numeric(roa_df['ROA'], errors='coerce'#문자는 에러를 발생하므로 N/A로 변경
      roa_df = roa_df['ROA'].dropna()

ROA는 문자형태로 가져옵니다. 순위를 구하기 위해서는 숫자형태로 변경할 필요가 있습니다.

 

"to_numeric" 함수의 옵션을 사용해서 "errors='coerce'" 로 공백이나, 숫자로 변경이 안되는 항목은 N/A로 변경합니다.

 

N/A 항목은 행으로 모두 삭제해 줍니다.

 

 # 전체 마법의 공식 데이터 만들기
      total_df = pd.merge(codes, roa_df, left_index=True, right_index=True)
 
# 값이 0인 행 제거
      total_df = total_df[total_df['PER'!= 0]

get_roa 함수로 가져온 ROA 데이터와 get_per 함수로 가져온 '종목코드', '종목명', 'PER'을 합쳐줍니다.

PER 값이 0인 항목은 제거합니다.

 

# PER 순위매기기
      total_df['per_rank'= total_df['PER'].rank()
      total_df['roa_rank'= total_df['ROA'].rank(ascending=False)
      total_df['total'= total_df['per_rank'+ total_df['roa_rank']
      total_df['total_rank'= total_df['total'].rank()
      total_df = total_df.sort_values(by=['total_rank'])

PER은 낮은 순서로 순위를 매기고, ROA는 높은 순서로 순위를 매깁니다. rank 함수를 사용하는데, 옵션에서 asccending=False 값으로 설정하면 높은 숫자부터 순위를 매깁니다.

 

PER 순위와 ROA 순위를 더한 다음 최종 순위를 매겨서 total_df['total_rank'] 컬럼에 표시합니다. 그리고 순위가 낮은 순서로 정렬을 합니다.

      today = datetime.today().strftime('%y-%m-%d')
      total_df.to_excel('Magic(' + today + ').xlsx')
 
      return total_df

파일 이름에 작성한 날짜를 넣기 위해 'today'라는 변수를 만들어주고, 다 만든 Dataframe(total_df)은 엑셀로 변환해 줍니다.

 

전체 코드는 아래와 같으니 참고하시기 바랍니다.

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
90
91
92
93
94
95
from pykrx import stock
import pandas as pd
import requests
import time
from datetime import datetime
 
 
def per(date):
    codes = stock.get_market_ticker_list(date, market='ALL')
    corp = []
 
    for code in codes:
        name = stock.get_market_ticker_name(code)
        corp.append([code, name])
 
    df = pd.DataFrame(data=corp, columns=['종목코드''종목명'])
    df = df.set_index('종목코드')
    df_f = stock.get_market_fundamental_by_ticker(date, market='ALL')
    df = pd.merge(df, df_f, left_index=True, right_index=True)
 
    return df
 
 
def get_roa(code, year):
      url = 'https://comp.fnguide.com/SVO2/ASP/SVD_FinanceRatio.asp?pGB=1&gicode=A'+ code \
            +'&cID=&MenuYn=Y&ReportGB=&NewMenuID=104&stkGb=701'
 
 
      fr_page = requests.get(url)
      fr_tables = pd.read_html(fr_page.text)
 
      temp_df = fr_tables[0]
      temp_df = temp_df.set_index(temp_df.columns[0]) #가져온 칼럼의 첫번째 열을 인덱스로 지정하기
      temp_df = temp_df.loc[['ROA계산에 참여한 계정 펼치기']] #'ROA계산에 참여한 계정 펼치기' 행만 가져오기
      temp_df.index = ['ROA'#인덱스 이름을 'ROA'로 수정하기
 
      temp_df = pd.DataFrame({code:temp_df[year]}) #가져올 시점에 따라 데이터프레임 만들기
      temp_df = temp_df.transpose() #행과 열의 위치를 바꾸기
 
      return temp_df
 
 
def make_magic_df(year, date):
 
      # #KRX 데이터 "종목코드", "종목명", "PER" 가져오기
      codes = per(date)
      codes['종목코드'= codes.index
 
      # ROA를 가져와서 데이트프레임 만들기
      for num, code in enumerate(codes['종목코드']):
            try:
                  if num == 0:
                        roa_df = get_roa(code, year)
                  else:
                        roa_df = pd.concat([roa_df, get_roa(code, year)])
                  time.sleep(0.01)
            except:
                  pass
 
            print(len(codes['종목코드']) - num, codes['종목명'][num])
      # 종목코드 칼럼을 index로 바꾸기
      codes = codes.set_index('종목코드')
 
      # ROA, ROA 문자형 데이터를 숫자로 변경
      roa_df['ROA'= pd.to_numeric(roa_df['ROA'], errors='coerce'#문자는 에러를 발생하므로 N/A로 변경
      roa_df = roa_df['ROA'].dropna()
 
      # 전체 마법의 공식 데이터 만들기
      total_df = pd.merge(codes, roa_df, left_index=True, right_index=True)
 
      # 값이 0인 행 제거
      total_df = total_df[total_df['PER'!= 0]
 
      # PER 순위매기기
      total_df['per_rank'= total_df['PER'].rank()
      total_df['roa_rank'= total_df['ROA'].rank(ascending=False)
      total_df['total'= total_df['per_rank'+ total_df['roa_rank']
      total_df['total_rank'= total_df['total'].rank()
      total_df = total_df.sort_values(by=['total_rank'])
 
      today = datetime.today().strftime('%y-%m-%d')
      total_df.to_excel('Magic(' + today + ').xlsx')
 
      return total_df
 
 
 
 
year = '2022/06'
date = '20220812'
 
print(make_magic_df(year, date))
 
cs

제가 마법의 공식을 흉내내서 코드를 작성하다 보니깐 한 가지 문제점이 있더라구요.

 

fnguide에서 데이터를 가져오다 보니깐, 해당 페이지가 업데이트 되면(새로운 회계 데이터가 업데이트 되면) 에러가 발생하는 경우가 있습니다.

 

최근에 업데이트 된 시점(year 이라는 변수입니다.)으로 검색하셔만 해당 데이터가 잘 나옵니다. 이 점 참고하시기 바랍니다.

728x90
반응형

댓글