PYTHON/Kiwoom Open API

키움증권 Open API 전체 종목 리스트 불러오기

  • -

이번 포스팅에서는 지난 글에서 이야기했듯이 종목 코드를 불러오고 그 종목들 중에서 필요없는 종목들을 걸러낸다거나 또는 그 종목의 특성을 확인한 후에 얻고자 하는 최종적인 결과물에서 제외하는 방식으로 코드를 제작하고자 한다. 

 

KOSPI · KOSDAQ 전체 종목 코드 불러오기

일단 필요 없는 종목들을 걸러내기 위해서는 그에 앞서 상장된 종목들의 코드는 어떻게 되는지를 확인할 필요가 있는데, 그는 바로 GetCodeListByMarket 함수를 통해 불러올 수 있다. 개발 가이드 내에서의 설명은 다음과 같다.

원형 GetCodeListByMarket(Market)
설명 시장구분에 따른 종목코드를 반환한다.
입력값 Market : 시장구분
반환값 종목코드 리스트, 종목간 구분은 ';'이다.
비고 0:장내, 3:ELW, 4:뮤추얼펀드, 5:신주인수권, 6:리츠, 8:ETF, 9:하이일드펀드, 10:코스닥, 30:K-OTC, 50:코넥스

그렇다면 이제 이를 바탕으로 전체 종목 코드를 불러와보도록 하자. 앞서 차트를 조회할 때 사용했던 CommRqData의 경우에는 OnReceiveTrData 이벤트를 발생시키지만 지금 사용하는 GetCodeListByMarket()함수는 이벤트를 발생시키지 않으므로 아래와 같이 제작하면 결과값을 얻어올 수 있다. 일단 본인의 경우에는 0번과 10번을 통해 장내(코스피)와 코스닥 종목 리스트를 불러오도록 하겠다. 클래스 아래에 GetCodeListByMarket이라는 함수를 아래와 같이 새롭게 제작해주면 된다.

def GetCodeListByMakret(self, market):
	codelist = self.kiwoom.dynamicCall("GetCodeListByMarket(QString)", market)
	return codelist

 

그렇다면 제대로 작동하는지 확인하기 위해 if문 맨 마지막 부분에 다음과 같은 코드를 추가해주도록 하자.

kospi = trade.GetCodeListByMakret('0')
kosdaq = trade.GetCodeListByMakret('10')

print(kospi)
print(kosdaq)

 

그 후에 코드를 실행해보면 지난 시간에 제작해두었던 차트 데이터가 모두 제작된 이후에 코스피와 코스닥 시장의 종목이 시장 별로 분리되어 출력되는 것을 확인해볼 수 있다.

 

종목 코드를 바탕으로 종목명 조회하기

조회한 종목이 필요한 종목인지 아닌지를 확인하기 위해서는 그 종목의 이름이 무엇인지 알아야 그 종목이 우선주인지 또는 스팩주인지 등을 확인할 수 있다. 종목코드를 바탕으로 종목명을 반환하는 함수는 GetMasterCodeName 함수로, 마찬가지로 개발가이드 내에 서술되어 있는 내용을 확인하고 제작하도록 하자.

원형 GetMasterCodeName(trCode)
설명 종목코드의 한글명을 반환한다.
입력값 trCode : 종목코드
반환값 종목 한글명
비고 장내외, 지수선옵, 주식선옵 검색 가능

이제 클래스 내에 종목코드를 변수로 입력받아 종목명을 반환하는 함수를 제작한 후에, 마찬가지로 if문 밖에서 해당 함수를 실행시켜보도록 하자.

# 클래스 하단에 제작
def GetMasterCodeName(self, code):
	code_name = self.kiwoom.dynamicCall("GetMasterCodeName(QString)", code)
	return code_name

 

# if문 하단에 제작
codename = trade.GetMasterCodeName('005930')
print(codename)

그 후에 해당 코드를 실행시켜보면 맨 밑에 삼성전자라는 결과값이 반환되는 것을 보니 정상적으로 작동하고 있다는 것을 확인할 수 있다.

 

전체 종목의 종목명 조회하기

그렇다면 이제 앞서 KOSPI와 KOSDAQ에 상장되어 있는 전체 종목의 종목코드를 바탕으로 종목명을 조회해보도록 하자. 일단 종목코드로 반환된 값들을 보면 ';' 표시로 구분되어 있음을 알 수 있는데, 그렇다면 우리는 이 ; 표시를 기준으로 나누어서 각각 하나의 값이 되도록 만들어주면 된다. 특정 문자열에서 특정 값을 기준으로 잘라주도록 하는 것은 파이썬 내장 함수인 .split()이다. 괄호 안에 각 문자열 별로 나누어줄 기준점(이 경우에는 세미콜론 ';' )을 입력해주면 된다. 이를 코드로 제작하면, 아래와 같은 형태이다.

kospi = trade.GetCodeListByMakret('0').split(';')
kosdaq = trade.GetCodeListByMakret('10').split(';')

 

split()을 추가하기 전에는 세미콜론(;)을 기준으로 앞뒤로 줄비하게 있었던 종목코드들이, 이제는 쉼표(,)로 나누어진 결과값이 출력된다는 점을 확인할 수 있다. 그리고 결과값의 맨 앞과 맨 뒤 부분을 보면 []와 같이 대괄호가 씌어 있는 것을 볼 수 있는데, 이것이 바로 해당 문자열의 타입이 '리스트'형태임을 의미한다. 그렇기 때문에 인덱싱을 통해 각각의 문자열에 접근할 수 있다. 예를 들어 kospi[0], kospi[1], kospi[-1] 등과 같은 형태를 통해 각각의 코드 하나만을 불러올 수 있다는 것이다.

우리는 이 형태를 응용하여 for문을 작성할 것이다. for문을 통해 하나 하나의 종목 코드를 GetMasterCodeName이라는 함수로 전송하고 그 결과값을 받아오도록 하는 것이다. for code in kospi는 어떠한 절차를 거쳐 작동하는지에 대해 잠깐 설명해보자면 일단 kospi의 맨 앞 부분을 보면 ['000020', '000040', '000050', '000060', '000070', '000075', '000080' ····와 같은 형태로 이루어져 있는데, 여기서 code = 000020이라는 값을 가진 채로 for 문 아래의 코드를 수행하고 그 다음에는 code = 000040이라는 값을 가진 채로 for 문 아래의 코드를 수행하고, 그 다음에는 code = 000050이라는 값을 가진 채로 for문 아래의 코드를 수행하게 된다. 

# if문 아래에 제작
for code in kospi:
	print(code)

 

이렇게 제작한 후에 프린트를 해보면 이전에는 한 줄로 주르륵 나왔던 종목 코드들이 이제는 한 줄 한 줄 출력된다는 점을 확인할 수 있다. 즉, code = 000020에서 print(code)를 하였으니 000020만 출력되고, 그 다음에는 code = 000040에서 print(code)를 하였으니 000040만 출력되는 것이다. 이런 식으로 반복 작업을 수행하는 것이 for문이다.

그렇다면 이제 각각의 코드들을 출력하였으니, 그 값들을 GetMasterCodeName이라는 함수에 변수로 전달해준 후에 그 반환된 값을 종목 코드와 함께 출력하도록 해보자.

# if문 아래에 제작
for code in kospi:
	codename = trade.GetMasterCodeName(code)
	print("종목명:", codename, " 종목코드:", code)

 

이제 코드를 실행해보면 아래와 같이 종목명과 종목 코드가 함께 출력되는 모습을 확인할 수 있다. 

종목명: 미래에셋 미국 항공우주 ETN(H)  종목코드: 590009
종목명: 미래에셋 레버리지 S&P500 ETN(H)  종목코드: 590010
종목명: 미래에셋 인버스 S&P500 ETN(H)  종목코드: 590011
종목명: 미래에셋 미국 리츠 ETN(H)  종목코드: 590013
종목명: 미래에셋 중국 심천 100 ETN  종목코드: 590018
종목명: 미래에셋 글로벌 리츠 ETN(H)  종목코드: 590012
종목명: 하나 KRX300 ETN  종목코드: 700002
종목명: 하나 KRX BBIG K-뉴딜 ETN  종목코드: 700003
종목명: 하나 코스피 변동성추세 추종 양매도 ETN  종목코드: 700001
종목명: 엘브이엠씨홀딩스  종목코드: 900140
종목명:   종목코드: 

 


728x90

 

필요 없는 종목 코드 걸러내기

일단 뒤에 있는 ETN, ETN(H) 등을 보면 거래할 실익이 종목들이라는 것을 육안으로는 한 눈에 확인할 수 있는데, 이를 제거하기 위한 코드는 어떻게 구축해야 할까? 이와 관련하여 키움증권 내에서 해당 종목의 특성에 대한 정보를 제공하지 않고 있다는 것은 정말 아쉬운 부분이다. 따라서 약간의 노가다와 함께 이러한 종목들을 제거할 수 있다.

일단 for문 구축함으로써 각 종목명을 불러왔으니, 해당 종목명 안에 어떤 내용이 포함되어 있다면 출력하지 않도록 하는 코드를 먼저 제작해보자. 일단 codename을 기준으로 [-3:]은 뒤에서부터 시작하여 세 번째 자리까지의 문자열을 반환하라는 의미이고, 그 반환된 문자열이 "ETN"이라면 출력하지 않고 pass하라는 의미이다. 그리고 or 뒤에는 codename을 기준으로 뒤에서부터 여섯 번째 자리까지의 문자열을 반환한 후에, 그 문자열이 "ETN(H)"라면 이 역시 pass하라는 의미이다.

for code in kospi:
	codename = trade.GetMasterCodeName(code)

	if codename[-3:] == "ETN" or codename[-6:] == "ETN(H)":
		pass
	else:
		print("종목명:", codename, " 종목코드:", code)

 

이제 이를 실행하면 아래와 같은 결과물이 출력된다는 점을 확인할 수 있다. 바로 위에 있는 결과물과 비교해보면 맨 밑에 있는 엘브이엠씨홀딩스를 제외하고 그 위에 있넌 하나 뭐시기, 미래에셋 뭐시기는 없어지고 TRUE 뭐시기가 출력되는 차이점을 확인할 수 있다. 

종목명: TRUE 유로선물 ETN B  종목코드: 570037
종목명: TRUE 인버스 유로선물 ETN B  종목코드: 570038
종목명: TRUE 엔선물 ETN B  종목코드: 570034
종목명: TRUE 레버리지 엔선물 ETN B  종목코드: 570035
종목명: TRUE 인버스 엔선물 ETN B  종목코드: 570036
종목명: TRUE 인버스 HSCEI ETN(H) B  종목코드: 570028
종목명: 엘브이엠씨홀딩스  종목코드: 900140
종목명:   종목코드: 

 

이런 식으로 꼴보기 싫은 코드들을 하나하나 조건문(if문)을 구축해서 제거하다보면 결과적으로는 일반적인 종목의 코드만을 얻어올 수 있다. 

 

필요 없는 종목 코드를 제거하는 함수 제작하기

여기까지의 내용을 바탕으로, class 내에 별도의 함수를 제작함으로써 번거로운 절차를 거치지 않아도 필터링된 종목 코드를 반환해주는 함수를 제작해보도록 하자. 

def filtered_code(self):
	kospi = trade.GetCodeListByMakret('0').split(';')
	kosdaq = trade.GetCodeListByMakret('10').split(';')
	all_list = kospi+kosdaq

	code_list = []

	for code in all_list:
		codename = trade.GetMasterCodeName(code)

		if codename[-3:] == "ETN" or codename[-6:] == "ETN(H)":
			pass
		else:
			code_list.append(code)
			print("종목명:", codename, " 종목코드:", code)
        
	return code_list

 

함수 이름은 filtered_code로 설정하였고, 그 안에서는 kospi와 kosdaq의 종목 코드를 모두 불러온 후에 all_list라는 변수에 모두 넣어주었다. 그 후에 for문의 경우 원래는 for code in kospi 였으나, 이 kospi 부분을 all_list라는 변수로 바꿔줌으로써 전체 종목 코드를 대상으로 for문을 돌도록 제작하였고, 그 아래에 있는 부분은 동일하다. 다만 else: 아래에 보면 code_list.append(code)라는 부분이 추가되었는데, 이는 제거 대상이 아닌 종목의 경우에는 code_list라는 변수에 입력하라는 것이다. 이 코드는 for문 안에 있기 때문에 모든 종목들을 하나 하나 체크하면서 조건에 맞는 종목 코드만을 code_list라는 변수 안에 입력하게 되므로 우리는 code_list만 사용하면 된다. 추후에 제거하고 싶은 종목이 있다면 이 filtered_code 함수 내에서 조건문을 추가함으로써 해당 종목을 걸러내면 된다. 

이제 if문 아래에서 kopsi = ~~ kosdaq = ~~ 이하의 모든 부분을 제거한 후에, 다음과 같이 제작해주도록 하자. 

code_list = trade.filtered_code()
print(code_list)

 

이러한 방식을 바탕으로, if문 아래에서 code_list를 대상으로 전 종목의 차트 데이터를 불러오도록 설정할 수도 있다. 다음 포스팅에서는 영웅문 내에 저장되어 있는 조건검색식을 기반으로 하여 검색된 종목을 불러오는 방법에 대해 포스팅할 예정이다.

 

지금까지 제작한 코드 현황

더보기
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import pandas

class system_trading():
    def __init__(self):
        self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
        print("연결되었습니다..")

        self.kiwoom.OnEventConnect.connect(self.OnEventConnect)

self.kiwoom.dynamicCall("CommConnect()")
        self.login_event_loop = QEventLoop()
        self.login_event_loop.exec_()

        self.kiwoom.OnReceiveTrData.connect(self.OnReceiveTrData)

        self.day_data = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'volume':[], 'trade_volume':[]}

        self.df_day_data = pandas.DataFrame(self.day_data, columns=['date', 'open', 'high', 'low', 'close', 'volume', 'trade_volume'])

    def GetMasterCodeName(self, code):
        code_name = self.kiwoom.dynamicCall("GetMasterCodeName(QString)", code)
        return code_name

    def GetCodeListByMakret(self, market):
        codelist = self.kiwoom.dynamicCall("GetCodeListByMarket(QString)", market)
        return codelist

    def OnEventConnect(self, err_code):
        if err_code == 0:
            print("로그인에 성공하였습니다.")
        else:
            print("로그인에 실패하였습니다.")
        self.login_event_loop.exit()

    def OnReceiveTrData(self, scrno, rqname, trcode, recordname, prenext, unused1, unused2, unused3, unused4):

        if prenext == '2':
            self.remained_data = True
        elif prenext != '2':
            self.remained_data = False


        if rqname == 'hello':
            self.opt10081()

        try:
            self.tr_event_loop.exit()
        except AttributeError:
            pass

    def opt10081(self):
        getrepeatcnt = self.kiwoom.dynamicCall("GetRepeatCnt(QString, QString)", "opt10081", "주식일봉차트조회요청")

        for i in range(getrepeatcnt):
            item_code = self.GetCommData("opt10081", "주식일봉차트조회요청", 0, "종목코드").strip()
            date = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "일자").strip()
            open = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "시가").strip()
            high = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "고가").strip()
            low = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "저가").strip()
            close = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "현재가").strip()
            volume = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "거래량").strip()
            trade_volume = self.GetCommData("opt10081", "주식일봉차트조회요청", i, "거래대금").strip()

            self.day_data['date'].append(date)
            self.day_data['open'].append(open)
            self.day_data['high'].append(high)
            self.day_data['low'].append(low)
            self.day_data['close'].append(close)
            self.day_data['volume'].append(volume)
            self.day_data['trade_volume'].append(trade_volume)


    def rq_chart_data(self, itemcode, date, justify):
        self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", itemcode)
        self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)
        self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", justify)
        self.kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "hello", "opt10081", 0, "0101")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

        while self.remained_data == True:
            self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", itemcode)
            self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)
            self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", justify)
            self.kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "hello", "opt10081", 2, "0101")
            self.tr_event_loop = QEventLoop()
            self.tr_event_loop.exec_()

    def GetCommData(self, trcode, recordname, index, itemname):
        result = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", trcode, recordname, index, itemname)
        return result

    def filtered_code(self):
        kospi = trade.GetCodeListByMakret('0').split(';')
        kosdaq = trade.GetCodeListByMakret('10').split(';')
        all_list = kospi+kosdaq

        code_list = []

        for code in all_list:
            codename = trade.GetMasterCodeName(code)

            if codename[-3:] == "ETN" or codename[-6:] == "ETN(H)":
                pass
            else:
                code_list.append(code)
                print("종목명:", codename, " 종목코드:", code)

        return code_list


if __name__ == "__main__":
    app = QApplication(sys.argv)
    trade = system_trading()

    trade.rq_chart_data("005930", "20210530", 1)

    print(trade.df_day_data)
    print(trade.day_data)

    code_list = trade.filtered_code()
    print(code_list)



 

 


728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.