카테고리 없음

[Python] [Kiwoom API] 주식 데이터를 필터링하고 랭킹을 매기는 함수: get_universe()

indigenthuman 2024. 9. 21. 13:59

주식 데이터를 필터링하고 랭킹을 매기는 함수: get_universe()

def get_universe():
    df = execute_crawler()

    # 데이터 전처리: 불필요한 문자 제거 및 N/A 값 처리
    mapping = {',':'', 'N/A':'0'}
    df.replace(mapping, regex=True, inplace=True)

    # 분석에 사용할 컬럼 리스트
    cols = ['거래량', '매출액', '매출액증가율', 'ROE', 'PER']

    # 숫자 데이터 타입 변환
    df[cols] = df[cols].astype(float)

    # 조건에 맞는 종목만 필터링
    df = df[(df['거래량'] > 0) & 
            (df['매출액'] > 0) & 
            (df['매출액증가율'] > 0) & 
            (df['ROE'] > 0) & 
            (df['PER'] > 0) & 
            (~df.종목명.str.contains("지주")) & 
            (~df.종목명.str.contains("홀딩스"))]

    # PER의 역수를 구해 1/PER 컬럼 생성
    df['1/PER'] = 1 / df['PER']

    # ROE와 1/PER을 각각 랭킹
    df['RANK_ROE'] = df['ROE'].rank(method='max', ascending=False)
    df['RANK_1/PER'] = df['1/PER'].rank(method='max', ascending=False)

    # 두 랭킹의 평균으로 최종 랭킹 계산
    df['RANK_VALUE'] = (df['RANK_ROE'] + df['RANK_1/PER']) / 2

    # RANK_VALUE에 따라 정렬하고 상위 200개의 종목 선택
    df = df.sort_values(by=['RANK_VALUE'])
    df.reset_index(inplace=True, drop=True)
    df = df.loc[:199]

    # 선택된 종목명 출력 및 엑셀 저장
    df.to_excel('universe.xlsx')
    print(df['종목명'].tolist())

    return df['종목명'].tolist()

1.  주요 기능 설명

  • 데이터 전처리: 크롤링한 데이터에 포함된 ,와 N/A 같은 불필요한 문자나 값을 제거하고, 필요한 컬럼들을 숫자(float) 형식으로 변환합니다.
  • 필터링: 특정 조건을 만족하는 종목만 필터링합니다. 예를 들어, 거래량이나 매출액이 0보다 큰 종목만 남기고, ‘지주’나 ‘홀딩스’가 포함된 종목은 제외합니다.
  • PER의 역수 계산: 저평가된 종목을 찾기 위해 PER(주가수익비율)의 역수를 계산하여 1/PER 컬럼을 만듭니다.
  • 랭킹 계산: ROE(자기자본이익률)와 1/PER 값에 기반한 두 개의 랭킹을 각각 구한 후, 두 랭킹의 평균을 계산하여 종목의 최종 점수를 결정합니다.
  • 상위 200개 종목 선택: 랭킹을 기준으로 상위 200개의 종목을 선택합니다.

2.  메인 실행부

if __name__ == "__main__":
    print('start')
    get_universe()
    print('end')
  • 프로그램이 시작될 때 get_universe() 함수를 호출하여 전체 과정을 실행합니다. start와 end를 출력하여 프로그램의 시작과 끝을 알 수 있습니다.

 

3. 전체 코드 및 실행결과

import pandas as pd
import requests
from bs4 import BeautifulSoup
import numpy as np
import pandas as dp
from datetime import datetime


BASE_URL = 'https://finance.naver.com/sise/sise_market_sum.nhn?sosok='
CODES = [0, 1]
START_PAGE = 1
fields = []

now = datetime.now()
formattedDate = now.strftime("%Y%m%d")

def execute_crawler():
    df_total = []

    for code in CODES:

        res = requests.get(BASE_URL + str(CODES[0]))
        page_soup = BeautifulSoup(res.text, 'lxml')

        total_page_num = page_soup.select_one('td.pgRR > a')
        total_page_num = int(total_page_num.get('href').split('=')[-1])

        ipt_html = page_soup.select_one('div.subcnt_sise_item_top')

        global fields
        fields = [item.get('value') for item in ipt_html.select('input')]

        result = [crawler(code, str(page)) for page in range(1, total_page_num + 1)]

        df = pd.concat(result, axis=0, ignore_index=True)

        df_total.append(df)

    df_total = pd.concat(df_total)
    df_total.reset_index(inplace=True, drop=True)

    df_total.to_excel('NaverFinance.xlsx')

    return df_total

def crawler(code, page):

    global fields

    data = {'menu': 'market_sum',
            'fieldIds': fields,
            'returnUrl': BASE_URL + str(code) + "&page=" + str(page)}

    res = requests.post('https://finance.naver.com/sise/field_submit.nhn', data=data)

    page_soup = BeautifulSoup(res.text, 'lxml')

    table_html = page_soup.select_one('div.box_type_l')

    header_data = [item.get_text().strip() for item in table_html.select('thead th')][1:-1]

    inner_data = [item.get_text().strip() for item in table_html.find_all(lambda x:
                                                                          (x.name == 'a' and
                                                                           'tltle' in x.get('class', [])) or (x.name == 'td' and
                                                                                                              'number' in x.get('class', []))
                                                                          )]

    no_data = [item.get_text().strip() for item in table_html.select('td.no')]
    number_data = np.array(inner_data)

    number_data.resize(len(no_data), len(header_data))

    df = pd.DataFrame(data=number_data, columns=header_data)
    return df

def get_universe():
    df = execute_crawler()


    mapping = {',':'', 'N/A':'0'}
    df.replace(mapping, regex=True, inplace=True)

    cols = ['거래량', '매출액', '매출액증가율', 'ROE', 'PER']

    df[cols] = df[cols].astype(float)

    df = df[(df['거래량'] > 0) & (df['매출액'] > 0) & (df['매출액증가율'] > 0) & (df['ROE'] > 0) & (df['PER'] > 0) & (~df.종목명.str.contains("지주")) & (~df.종목명.str.contains("홀딩스"))]

    df['1/PER'] = 1 / df['PER']

    df['RANK_ROE'] = df['ROE'].rank(method='max', ascending=False)

    df['RANK_1/PER'] = df['1/PER'].rank(method='max', ascending=False)

    df['RANK_VALUE'] = (df['RANK_ROE'] + df['RANK_1/PER']) / 2

    df = df.sort_values(by=['RANK_VALUE'])

    df.reset_index(inplace=True, drop=True)

    df = df.loc[:199]

    df.to_excel('universe.xlsx')
    print(df['종목명'].tolist())
    return df['종목명'].tolist()

if __name__ == "__main__":
    print('start')
    get_universe()
    print('end')

 


4. 결론

이 코드는 Naver Finance에서 코스피 및 코스닥 종목 데이터를 수집하고, 특정 조건에 따라 데이터를 필터링하고 랭킹을 매긴 후, 상위 200개의 종목을 선정하여 엑셀 파일로 저장하는 과정을 수행합니다.

이 코드를 통해 실시간으로 다양한 주식 데이터를 분석하고, 특정 조건에 맞는 종목을 효율적으로 선정할 수 있습니다. Pandas와 BeautifulSoup을 사용한 데이터 크롤링 및 분석의 실용적인 예시라고 할 수 있습니다.