금융퀀트/(퀀트)증권사API활용(키움)

[키움API]python 샤프지수를 이용한 종목선정1: 코스피 종목 종가데이터 입수

문송한투자자 2024. 1. 22. 07:29
반응형

코스피 종목 데이터 찾기: KRX데이터시스템

[키움API]파이썬 주식 종목별 종가정보 불러오기5: UI파일 화면구성(Qt Designer) 및 프로그램module과 CLASS구성까지는 키움에서 제공하는 주식목록입수 API 함수인 GetCodeListByMarket 을 사용해서 약 15,000 개 가량의 종목을 대상으로 입수 작업을 했다. 큰 기업 위주로 투자하겠다는 계획이면 사실 코스피 주식들만 분석해 보면 되기 때문에 약 15,000 개가 되는 데이터 전부를 DB에 저장할 필요는 없다.

코스피 종목은 아래 그림 1과 같이 KRX정보데이터시스템(http://data.krx.co.kr/)에서 지수 -> 주가지수 -> 지수구성종목 항목에서 지수명에 "코스피"를 입력하면 볼 수 있으며 그림 1에 표시된 버튼을 누르면 엑셀로 다운받을 수 있다.

그림1: KRX정보데이터 시스템 코스피 구성 종목 다운

코스피 종목 리스트 입수 함수: loadkospilist

다운받은 엑셀 파일은 먼저 내가 작업하고 있는 프로젝트 폴더로 옮겨준다. 그 뒤 파일명을 내가 원하는 대로 바꾸고(여기서는 kospi.xlsx) pandas의 read_excel 함수를 아래와 같이 사용한 뒤 불러온 엑셀 데이터를 데이터프레임에 저장하면 아래 그림 2와 같은 결과를 얻을 수 있다.

그림2: 코스피 종목 목록 불러오기

엑셀 데이터에서 종목코드는 무조건 6자리가 되어야 하기 때문에 앞자리를 0으로 채우는 기능이 필요하고, 우리가 키움  API 함수인 GetCodeListByMarket 을 사용해서 종목코드의 List를 얻었다는 점을 감안해서 아래와 같이 utils.py 모듀에 Utils class 안에 loadkospilist라는 함수를 만들 수 있다. 

import pandas as pd

class Utils:
    def __init__(self):
        pass
    
    def loadkospilist(self):
    	# kospi.xlsx 파일 불러오기
        kospilist = pd.read_excel("kospi.xlsx")
        # 엑셀 파일에서 불러온 데이터를 데이터프레임화 하기 이 때 컬럼은 종목코드, 종목명, 시가총액
        df = pd.DataFrame(data=kospilist, columns=["종목코드","종목명","상장시가총액"])
        # 종목코드는 6자리 이기 때문에 6자리가 아닌 데이터는 앞에 0을 채워서 6자리 만들기
        df["종목코드"] = df["종목코드"].astype(str).str.zfill(6)
        # 종목코드 컬럼만 뽑아내서 list로 만들기
        return df["종목코드"].tolist()

 

코스피 종목 리스트 종가 DB저장

이제 [키움API]파이썬 주식 종목별 종가정보 불러오기5: UI파일 화면구성(Qt Designer) 및 프로그램module과 CLASS구성에서 만들었던 함수 중 timeseriesdataget 의 GetCodeListByMarket 부분을 아래 그림 3처럼 새로 만든 utils 모듈 Utils class의 loadkospilist 함수로 대체하면 된다.

그림3:  timeseriesdataget 함수 변경

결국 기존에 만들어 둔 uihandle.py 파일을 아래와 같이 수정하면 된다.

from PyQt5.QtWidgets import *
import time
from kiwoomapi import KIWOOMapi
from apihandle import APIhandle
from sqlhandle import SQLhandle
# 새로 만든 module 인 utils module 의 Utils 클래스 참조
from utils import Utils

class UIhandle(QWidget):
    def __init__(self, ocx: object, mainui):
        super().__init__()
        self.mainui = mainui
        self.ocx = ocx
        self.api = KIWOOMapi(ocx)
        self.handle = APIhandle(ocx)
        self.sql = SQLhandle()
        # Utils 클래스를 self.utils 로 객체화
        self.utils = Utils()
        self.timer = time
...(중략)...
	# 함수이름 변경(main.py 에 해당 함수 불러오는 부분도 변경 필요)
    def gettimeseriesdata(self):
    	# Utils 클래스가 객체화 된 self.utils에서 loadkospilist 함수 호출: kospi 종목 리스트 입수 
        stockcodes = self.utils.loadkospilist()
        basedate = self.getdate("date_base").date().toString("yyyyMMdd")
        fromno = int(self.getspinboxvalue("spinBox_from"))
        tono = int(self.getspinboxvalue("spinBox_to"))
        callback = lambda: self.handle.insertstockpricedf("opt10081", "주식일봉차트")
        self.ocx.OnReceiveTrData.connect(callback)
        for stockcode in stockcodes[fromno:tono+1]:
            self.handle.rqstockpriceinfo(stockcode, "opt10081", "주식일봉차트", stockcode, basedate)
            self.timer.sleep(0.5)
            print("{} processed!".format(stockcode))
        self.ocx.OnReceiveTrData.disconnect(callback)
        print("Process Done!")
        self.postresult("Process Done!")

 

기존 sqlhandle.py 모듈의 SQLhandle class의 timeseriesdatasave 함수도 db 저장 시 데이터가 존재하는지 한 줄씩 확인하는 로직 때문에 데이터 저장 속도도 지나치게 느려서 savetimeseriesdata 함수로 아래와 같이 고쳐준다.(apihandle.py의 insertstockpricedf에서 해당 함수를 참조하는데 참조하는 함수명도 같이 수정해 주면 된다.) 

#SQLhandle
import pymysql
import sqlalchemy as db
import pandas as pd
pymysql.install_as_MySQLdb()

class SQLhandle:
    def __init__(self):
 
 ...(중략)...

    def savetimeseriesdata(self, tablename: str, df: pd.DataFrame):
        if len(df)>0:
            conn = self.engine.connect()
            columns = self.getcolumns(tablename)
            columnnames = ", ".join(columns)
            valueslist = []
            for row in df.itertuples():
                values = [f"'{value}'" for value in row[1:]]
                values = ", ".join(values)
                valueslist.append(f"({values})")
            if len(valueslist) > 0:
                valueslist = ", ".join(valueslist)
                query = db.text(f"INSERT INTO {tablename} ({columnnames}) VALUES {valueslist}")
                try:
                    conn.execute(query)
                    conn.commit()
                except Exception as e:
                    print(f"Error executing query: {e}")
            else:
                pass   
            conn.close()
        else:
            pass

 

함수를 모두 수정하고 [키움API]파이썬 주식 종목별 종가정보 불러오기6: 데이터 수집시 주의사항에서 말한 주의사항 대로 200개씩 데이터를 수집하면 아래와 같이 수집된 결과를 MySQL workbench에서 확인할 수 있다.

그림4: SQL Data 입수 최종 결과

 

반응형