본문 바로가기
python/금융데이터분석

python 증권데이터 분석 - 네이버금융 일별 시세 조회 API 만들기

by orangecode 2022. 12. 25.
728x90
일별 시세 조회 API 만들기

기존 KRX 한국거래소에서 가져온 종목 코드를 이용해 네이버금융 일별 시세를 가져와 maribDB로 넣었다.

 

지금 제작하려는 일별 시세 조회 API는 직접 구축한 mariaDB에서 일별 시세를 직접 조회하는 API를 제작해보고자 한다.


네이버 일별 시세 API stub 코드 : 기능이 구현되어 있지않은 인터페이스 껍데기 코드

# 네이버 일별 시세 API

class MarketDB:
    # 생성자 : MariaDB 연결 및 종목코드 딕셔너리 생성
    def __init__(self):

    # 소멸자 : MariaDB 연결 해제
    def __del__(self):

    # company_info 테이블에서 읽어와 codes에 저장
    def get_comp_info(self):

    # KRX 종목의 일별 시세를 데이터프레임 형태 변환
    def get_daily_price(self, code, start_date=None, end_date=None):

 

생성자, 소멸자로 DB 연결 관리하기
    # 생성자 : MariaDB 연결 및 종목코드 딕셔너리 생성
    def __init__(self):
        self.conn = pymysql.connect(host='localhost', port='3307', user='root', \
                                    password='mariadb', db ='INVESTAR', charset='utf8')
        self.codes = {}
        self.get_comp_info()

    # 소멸자 : MariaDB 연결 해제
    def __del__(self):
        self.conn.close()

 

KRX 종목 일별 시세를 데이터 프레임 형태로 변환해주기
    # KRX 종목의 일별 시세를 데이터프레임 형태 변환
    def get_daily_price(self, code, start_date=None, end_date=None):
        # 1. pandas read_sql()함수를 이용해 SQL 구문의 결과를 데이터 프레임으로 가져온다.
        # 데이터 프레임으로 가져오면 정수형 인덱스가 별도로 생성됨
        sql = "SELECT * FROM daily_price WHERE code = '{}' and date >= '{}' and date <= '{}'".format(code, start_date,end_date)
        df = pd.read_sql(sql, self.conn)
        
        # 2. 데이터프레임의 인덱스를 df의 'date'칼럼으로 새로 설정된다.
        df.index = df['date']
        
        return df

 

get_daily_price()함수에서 read_sql()함수를 이용해 SELECT 구문의 결과를 dataframe으로 변형한다.

 

1. pandas read_sql()함수를 이용해 SQL 구문의 결과를 데이터 프레임으로 가져온다.
데이터 프레임으로 가져오면 정수형 인덱스가 별도로 생성된다.

 

2. 데이터프레임의 인덱스를 df의 'date'칼럼으로 새로 설정된다.

 

기본 인숫값 처리하기

 

조회 시작일과 조회 종료일을 인수로 넘겨주지 않을 때, 기본 인숫값으로 처리하는 함수가 필요하다.

 

    # 1. 인수(argument)=None 형식을 사용하면 인숫값이 주어지지 않을 때, 기본값으로 처리한다. 
    def get_daily_price(self, code, start_date=None, end_date=None):
        # 2. 조회 시작일로 넘겨받은 인수가 None이면 인수가 입력되지 않은 경우
        if start_date is None:
            one_year_ago = datetime.today() - timedelta(days=365)
            # 1년 전 오늘날짜로 '%Y-%m-%d' 형식의 문자열 처리한다.
            start_date = one_year_ago.strftime('%Y-%m-%d')
            print("start_date is initialized to '{}".format(start_date))

get_daily_price의 argument(인수) = None 형식이 사용 되었을 때 기본 인수 값으로 처리가 필요하다.

 

if start_date is None: 은 조회 시작일로 넘겨받은 인수가 None이라면 인수가 입력되지 않았다면

 

1년전 오늘 날짜로 '%Y-%m-%d' 형식의 문자열 처리한다.

 

 

정규표현식으로 연-월-일 분리하기

사용자가 조회 시작일을 입력할 때 날짜를 '연-월-일' 형식으로 고정해주는 것이 좋다.

 

날짜 형식은 '2020-01-01', '2020.01.30', '2020-1-30' 등 다양한 형식으로 입력할 수 있다.

 

정규표현식(regular expression)을 사용하면 연, 월, 일 세 숫자를 분리하여 사용자가 어떤 숫자를 입력해도 제대로 처리가 가능하다.

 

import re

start_date = "2020 year 1/30"
start_lst = re.split('\D+', start_date)

start_year = int(start_lst[0])
start_month = int(start_lst[1])
start_day = int(start_lst[2])

start_date = f{start_year:04d}-{start_month:02d}-{start_day:02d}
print("start_date: "m, start_date

 

위 정규표현식 코드를 이용하면, 

 

정규표현식 \D+로 분리하여 연, 월, 일에 대한 숫자만 남길 수 있다.

 

분리된 연, 월, 일 숫자를 다시 {:04d} - {:02d} - {:02d}의 문자열 형식으로 구성하면 DB에 저장된 날짜 형식과 같게 구성할 수 있다.

 

- {04d} : 4자리 숫자로 표시하되 앞자리가 비었다면 0으로 채우라는 의미

- {02d} : 2자리 숫자로 표시하되 앞자리가 비었다면 0으로 채우라는 의미

 

 

728x90

 

 

회사명으로 종목코드 조회하기

codes 의 딕셔너리 key는 종목코드이고 value 값은 회사명을 가지고 있다.

 

종목코드 005930에 해당하는 삼성전자를 찾기 위해서는 codes['005930']으로 조회가 가능하지만

 

반대인 '삼성전자' value로 '005930'이라는 key 값을 찾기는 쉽지 않다.

 

딕셔너리에서 value로 key를 조회해야 한다면, 애초에 딕셔너리가 아닌 데이터프레임 같은 다른 자료형을 사용하는 걸 추천한다.

 

굳이 딕셔너리에서 value로 key를 조회한다면 이렇게 코드를 작성할 수 있다.

 

    # 생성자 : MariaDB 연결 및 종목코드 딕셔너리 생성
    def __init__(self):
        self.conn = pymysql.connect(host='localhost', port='3307', user='root', \
                                    password='mariadb', db ='INVESTAR', charset='utf8')
        self.codes = {}
        
        # 1. codes 딕셔너리에서 key를 뽑아 key(종목코드) 리스트 생성
        codes_keys = list(self.codes.keys())
        
        # 2. codes 딕셔너리에서 value를 뽑아 value(회사명) 리스트 생성
        codes_values = list(self.codes.values())
		
        # 3. 사용자가 입력한 code 값이 005930이라서 key(종목코드)에 리스트가 존재한다면 그대로 사용
        if code in codes_keys:
            pass
            
        # 4. 사용자가 입력한 code 값이 삼성전자라면
        # 값(회사명)에서 삼성전자의 인덱스(key-종목코드)를 구한뒤 key(종목코드) 리스트가 존재하는지 확인
        elif code in codes_values:
            idx = codes_values.index(code)
            code = codes_keys[idx]
        else:
            print(f"ValueError: Code({code}) doesn't exist.")
        self.get_comp_info()

 

전체 코드
import pandas as pd
import pymysql
from datetime import datetime
from datetime import timedelta
import re

class MarketDB:
    def __init__(self):
        """생성자: MariaDB 연결 및 종목코드 딕셔너리 생성"""
        self.conn = pymysql.connect(host='localhost', user='root', 
            password='myPa$$word', db='INVESTAR', charset='utf8')
        self.codes = {}
        self.get_comp_info()
        
    def __del__(self):
        """소멸자: MariaDB 연결 해제"""
        self.conn.close()

    def get_comp_info(self):
        """company_info 테이블에서 읽어와서 codes에 저장"""
        sql = "SELECT * FROM company_info"
        krx = pd.read_sql(sql, self.conn)
        for idx in range(len(krx)):
            self.codes[krx['code'].values[idx]] = krx['company'].values[idx]

    def get_daily_price(self, code, start_date=None, end_date=None):
        """KRX 종목의 일별 시세를 데이터프레임 형태로 반환
            - code       : KRX 종목코드('005930') 또는 상장기업명('삼성전자')
            - start_date : 조회 시작일('2020-01-01'), 미입력 시 1년 전 오늘
            - end_date   : 조회 종료일('2020-12-31'), 미입력 시 오늘 날짜
        """
        if start_date is None:
            one_year_ago = datetime.today() - timedelta(days=365)
            start_date = one_year_ago.strftime('%Y-%m-%d')
            print("start_date is initialized to '{}'".format(start_date))
        else:
            start_lst = re.split('\D+', start_date)
            if start_lst[0] == '':
                start_lst = start_lst[1:]
            start_year = int(start_lst[0])
            start_month = int(start_lst[1])
            start_day = int(start_lst[2])
            if start_year < 1900 or start_year > 2200:
                print(f"ValueError: start_year({start_year:d}) is wrong.")
                return
            if start_month < 1 or start_month > 12:
                print(f"ValueError: start_month({start_month:d}) is wrong.")
                return
            if start_day < 1 or start_day > 31:
                print(f"ValueError: start_day({start_day:d}) is wrong.")
                return
            start_date=f"{start_year:04d}-{start_month:02d}-{start_day:02d}"

        if end_date is None:
            end_date = datetime.today().strftime('%Y-%m-%d')
            print("end_date is initialized to '{}'".format(end_date))
        else:
            end_lst = re.split('\D+', end_date)
            if end_lst[0] == '':
                end_lst = end_lst[1:] 
            end_year = int(end_lst[0])
            end_month = int(end_lst[1])
            end_day = int(end_lst[2])
            if end_year < 1800 or end_year > 2200:
                print(f"ValueError: end_year({end_year:d}) is wrong.")
                return
            if end_month < 1 or end_month > 12:
                print(f"ValueError: end_month({end_month:d}) is wrong.")
                return
            if end_day < 1 or end_day > 31:
                print(f"ValueError: end_day({end_day:d}) is wrong.")
                return
            end_date = f"{end_year:04d}-{end_month:02d}-{end_day:02d}"
         
        codes_keys = list(self.codes.keys())
        codes_values = list(self.codes.values())

        if code in codes_keys:
            pass
        elif code in codes_values:
            idx = codes_values.index(code)
            code = codes_keys[idx]
        else:
            print(f"ValueError: Code({code}) doesn't exist.")
        sql = f"SELECT * FROM daily_price WHERE code = '{code}'"\
            f" and date >= '{start_date}' and date <= '{end_date}'"
        df = pd.read_sql(sql, self.conn)
        df.index = df['date']
        return df

 

 

 

 

반응형

댓글