[키움API]python 샤프지수를 이용한 종목선정1: 코스피 종목 종가데이터 입수
코스피 종목 데이터 찾기: KRX데이터시스템
[키움API]파이썬 주식 종목별 종가정보 불러오기5: UI파일 화면구성(Qt Designer) 및 프로그램module과 CLASS구성까지는 키움에서 제공하는 주식목록입수 API 함수인 GetCodeListByMarket 을 사용해서 약 15,000 개 가량의 종목을 대상으로 입수 작업을 했다. 큰 기업 위주로 투자하겠다는 계획이면 사실 코스피 주식들만 분석해 보면 되기 때문에 약 15,000 개가 되는 데이터 전부를 DB에 저장할 필요는 없다.
코스피 종목은 아래 그림 1과 같이 KRX정보데이터시스템(http://data.krx.co.kr/)에서 지수 -> 주가지수 -> 지수구성종목 항목에서 지수명에 "코스피"를 입력하면 볼 수 있으며 그림 1에 표시된 버튼을 누르면 엑셀로 다운받을 수 있다.
코스피 종목 리스트 입수 함수: loadkospilist
다운받은 엑셀 파일은 먼저 내가 작업하고 있는 프로젝트 폴더로 옮겨준다. 그 뒤 파일명을 내가 원하는 대로 바꾸고(여기서는 kospi.xlsx) pandas의 read_excel 함수를 아래와 같이 사용한 뒤 불러온 엑셀 데이터를 데이터프레임에 저장하면 아래 그림 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 함수로 대체하면 된다.
결국 기존에 만들어 둔 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에서 확인할 수 있다.