PYTHON/Kiwoom Open API

키움증권 Open API - 관심종목 관리하기 (3)

  • -

 

 

지난 게시글에서는 기존에 설정한 조건검색식이 찾아낸 종목코드를 얻어오는 방법까지 확인했다. 다만 관심종목에 추가하기 위해서는 해당 종목의 데이터가 필요하기 마련히고 더군다나 해당 종목을 매수할 계획이 있다면 매수를 진행할 가격까지도 사전에 계산해서 관심종목 데이터 안에 입력해주어야 한다.

아직은 이 프로세스가 어렵게 느껴질 수 있겠지만, 엄밀히 따지면 반드시 필요한 절차에 해당하는 부분이므로 이해가 가지 않는다고 하더라도 그냥 그대로 따라하면 큰 어려움 없이 프로그램을 만들 수 있다.

일단 본격적인 내용을 설명하기 전에 앞서, 조건검색식을 통해 종목을 관리할 수 있는 두 가지 방법에 대해 알아보자.

  • ① 조건검색 종목코드를 일자별로 데이터베이스에 저장 
  • ② 종목코드를 관심종목 데이터베이스에 저장

여기서 첫 번째 방법의 경우에는 이전 게시글에서 살펴봤던 종목코드를 내용으로, 그리고 해당 종목코드를 조회한 일자를 하나의 데이터베이스명으로 설정해서 데이터를 저장할 수 있으며 거래를 시작할 때 일자별 데이터베이스를 가져와서 해당 종목이 아직 유효한 종목인지 아닌지를 구분한 후 거래를 진행하는 방법이다.

두 번째 방법의 경우에는 관심종목 데이터베이스에 별도로 저장을 한 후에, 계속해서 관심종목으로의 유효성을 검사하고 유효성을 잃어버린 경우에는 데이터베이스에서 삭제하는 방식으로 거래를 진행하는 방법이다.

사실상 따지고 보면 두 방법 간의 큰 차이점은 없으나, 본인이 직접 관리하기 쉬운 방법으로 데이터를 구축하고 거래를 진행하면 된다. 본인의 경우에는 첫 번째 방법과 두 번째 방법을 모두 사용한다.

 

첫 번째 방법. 조건검색 종목코드를 일자별로 데이터베이스에 저장하기

지난 게시글에서 OnReceiveTrCondition() 함수 내에서 조건검색식이 검색한 종목코드가 담긴 codelist 변수를 self.code_list 변수 내에 입력해주었기 때문에, 우리는 self.code_list 변수를 호출해서 종목코드 리스트를 확인할 수 있다.

그렇다면 이 종목코드 리스트를 바탕으로 데이터베이스에 저장할 것인데, 본인은 MySQL을 이용할 것이다. 만약 MySQL과 연결되어있지 않다면 아래의 게시글을 참조하길 바란다.

파이썬과 MySQL이 연결되어 있다고 가정하고 이제 이 self.code_list 변수를 일자별로 DB(엄밀히 말하자면 테이블)를 생성해서 해당 DB 내에 종목코드 리스트를 입력해주고자 한다. 데이터베이스(DB)와 테이블 간의 관계가 어렵게 느껴질 수도 있지만 따지고 보면 데이터베이스 내에 여러 개의 테이블을 둘 수 있고, 각 테이블마다 데이터를 저장할 수 있다. 

데이터베이스(DB) 테이블(Table) 데이터
동물 육식 동물 호랑이, 사자, 표범 등···
초식 동물 기린, 사슴, 토끼 등···
식물 부엽 식물 등등 ···
수중 식물 등등 ····

이처럼 각 데이터베이스 내에서 세분화된 테이블을 둘 수 있으며, 각 테이블 내에는 여러 가지 데이터들을 정리할 수 있는 것이다. 그렇다면 여기서 우리는 일별 조건검색 데이터를 다음과 같은 틀을 기반으로 분류해서 저장할 수 있다.

데이터베이스(DB) 테이블(Table) 데이터
Condition s20220101 005930, 001010, 002020 ···
s20220102 005030, 010290, 019300 ···
s20220103 049230, 039490, 032850 ···

 

① to_sql 사용해보기

to_sql의 경우에는 기본적으로 데이터프레임 형태여야 MySQL 내 데이터베이스에 하나의 테이블과 데이터로 하여 저장할 수 있다. 따라서 저장하고자 하는 데이터들을 데이터프레임으로 만들어주어야 하는데, 이는 판다스(pandas)를 이용해서 간편하게 만들 수 있다. 아래와 같이 튜플 형태(condition_data)의 자료를 생성해준 후에 해당 튜플에 데이터들을 입력하고, 네 번째 줄의   pd.DataFrame()  을 통해 데이터프레임으로 자료를 변환하는 것이다.

import pandas as pd

condition_data = {'item_code':[], 'date':[], 'open':[], 'high':[], 'low':[], 'close':[]}
condition_dataframe = pd.DataFrame(condition_data, columns=['item_code', 'date', 'open',' high', 'low', 'close'])

 

이제 이 condition_dataframe이라는 하나의 데이터프레임 형태의 자료형에 to_sql을 사용해서 MySQL에 저장할 수 있는데, 여기서는 engine을 지정해주어야 한다. 이 엔진은 위의 '파이썬 + MySQL, ② 파이썬으로 연결하기' 편에서 자세히 설명하고 있으니 해당 게시글을 참고하시길 바란다. 여기서는 사용법만 간단하게 살펴볼 예정이다.

코드를 살펴보기 전에 앞서 본인의 경우 Condition이라는 데이터베이스가 존재하며, 그 안에는 일자별로 검색된 조건검색식 데이터들이 저장되어 있다. 다시 말해 Condition이라는 데이터베이스에 연결하는 engine의 이름은 engine_condition이며, name=""에 입력되는 것은 테이블 이름이 되고 condition_dataframe이 곧 데이터가 된다.

condition_dataframe.to_sql(name="", con=engine_condition, if_exists="replace")

 

만약 위의 코드에서 name="s20220401"과 같이 입력한 후 코드를 실행한다면 아래의 이미지와 같은 형태로 데이터베이스와 테이블, 그리고 그 안에 있는 데이터가 생성된다.

워크벤치(WorkBench) 데이터베이스 화면

 


728x90

 

② 저장할 데이터 생성하기

앞서 살펴봤듯이, to_sql을 사용하기 위해서는 저장할 데이터를 데이터프레임으로 제작해주어야 한다. 그렇다면 우리는 거래를 진행하는 데에 있어 필요한 데이터들을 저장하고자 하는 변수에 입력해주어야 한다는 것을 알 수 있다.

다만 지난 게시글에서 우리는 이미   self.code_list  라는 변수 안에 codelist라는 변수(종목 코드들이 담겨 있는 변수로 조회할 당시 '013700'과 ''258830' 두 종목이 입력되어 있었다.)를 입력하고 넘어왔기 때문에, 이제 self.code_list를 대상으로   for 문  을 돌면서 데이터들을 입력해주면 된다.

다만 그 전에, 앞서 ①번 단계에서 살펴봤듯이 데이터프레임으로 만들기 위해서는 그 기반이 되는 튜플 형태의 자료형이 필요하다. 따라서 아래와 같이 함수가 호출됐을 때 곧바로 튜플 형태의 변수를 생성하도록 하자. 그리고 그   for 문   안에서 하나의 종목 코드(  item_code  )를 튜플 형태의 변수(  temporary_tuple  ) 안의 'item_code'에 데이터를 입력해보자.

 

def make_condition_dataframe():
	self.temporary_tuple = {'item_code':[]}
	for item_code in self.code_list:
		self.temporary_tuple['item_code'].append(item_code)
		print(item_code)


>>> '013700'
>>> '028830'

 

이렇게 튜플 형태의 자료형에 종목코드는 입력했는데, 종목코드 이외에 어떠한 종류의 데이터들을 입력해주어야 할까? 그리고 그 데이터는 어디서 받아와야 할까? 첫 번째 질문에 대한 답은 간단하다. 기본적으로 본인이 진행하고자 하는 매매에 있어 필요한 모든 데이터들을 입력해주면 된다. 이와 관련하여 본인은 단순하게 해당 종목별 시가, 고가, 저가, 종가, 거래량, 거래대금만 입력해볼 것이다. 일단 튜플 변수의 내용을 아래의 두 번째 줄과 같이 수정해주도록 하자.

def make_condition_dataframe():
	self.temporary_tuple = {'item_code':[], 'open':[], 'low':[], 'high':[], 'close':[], 'volume':[], 'tvolume':[]}
	for item_code in self.code_list:
		self.temporary_tuple['item_code'].append(item_code)

 

그렇다면 이제 종목별 주가 데이터와 거래량, 거래대금 등의 데이터를 받아와야 하는데, 이는 opt10007 : 시세표성정보요청을 통해 얻어올 수 있다. 아래의 사진 자료에 나와 있는 데이터 외에도 정말 많은 데이터를 확인할 수 있다.

 

그렇다면 이제 item_code를 하나의 변수로 하여 데이터를 전달해주면 되는데, 아래와 같이 작성할 수 있다. 즉, 데이터를 요청하는 부분은 아래의 6, 7번째 줄에 해당하는 것이다.

def make_condition_dataframe():
	self.temporary_tuple = {'item_code':[], 'open':[], 'low':[], 'high':[], 'close':[], 'volume':[], 'tvolume':[]}
	for item_code in self.code_list:
		self.temporary_tuple['item_code'].append(item_code)
		
		self._setinputvalue("종목코드", item_code)
		self._commrqdata("rq_opt10007", "opt10007", 0, "0101")

 

키움증권 Open API를 한 번이라도 사용했었다면 알겠지만, CommRqData를 사용하면   OnReceiveTrData   이벤트가 발생하게 되고 그 안에서 RequestName(이 경우 "rq_opt10007")을 바탕으로 이벤트를 처리해주어야 한다. 이 이벤트 처리의 경우 아래의 게시글을 참고해서 제작한 후에 넘어오는 것이 좋다.

 

위의 게시글을 통해 이벤트를 처리하기 위한   self.kiwoom.OnReceiveTrData.connect(self.receive_trdata)  라는 코드를 작성했다면 이제   self.receive_trdata  에 해당하는 함수를 생성해주어야 한다. 즉, OnReceiveTrData라는 이벤트가 발생했을 때 self.receive_trdata로 연결(connect)되도록 작성했으니, self.receive_trdata가 실행됐을 때 처리할 코드를 입력해주어야 한다는 것이다.

아래와 같이 두 번째 줄에서 요청된 요청명(  rqname  )이 'rq_opt10007'이라면   opt10007   함수를 실행하도록 작성했다.

def receive_trdata(self, scrno, rqname, trcode, recordname, prenext, unused1, unused2, unused3, unused4):
	if rqname == "rq_opt10007":  
		self.opt10007(trcode, recordname)

 

그럼 이제 opt10007 함수를 생성한 후에 데이터를 받아와서, 그 데이터를 앞서 제작한 temporary_tuple 안에 입력해주도록 하자. 해당 변수에 입력할 데이터로는 종목코드(item_code), 시가(open), 고가(high), 저가(low), 종가(close), 거래량(volume), 거래대금(tvolume)의 7개 데이터이지만 아래의 함수에서는 6개만 입력했다. 그 이유는 바로 앞서 make_condition_dataframe()이라는 함수에서 종목코드를 입력했기 때문이다.

def opt10007(self, trcode, recordname):
	open = int(self._getcommdata(trcode, recordname, 0, "시가").strip('+- '))
	high = int(self._getcommdata(trcode, recordname, 0, "고가").strip('+- '))
	low = int(self._getcommdata(trcode, recordname, 0, "저가").strip('+- '))
	close = int(self._getcommdata(trcode, recordname, 0, "현재가").strip('+- '))
	vol = str(self._getcommdata(trcode, recordname, 0, "거래량").strip('+- '))
	tvol = str(self._getcommdata(trcode, recordname, 0, "거래대금").strip('+- '))

	self.temporary_tuple['open'].append(open)
	self.temporary_tuple['high'].append(high)
	self.temporary_tuple['low'].append(low)
	self.temporary_tuple['close'].append(close)
	self.temporary_tuple['volume'].append(vol)
	self.temporary_tuple['tvolume'].append(tvol)

 

 

지금까지의 절차를 간단하게 정리해보자면 아래와 같이 요악할 수 있다.


[종목코드별 for문 진행]
첫 번째 조회 종목의 종목코드 : '013700'

+ self.temporary_tuple 데이터 입력

데이터 입력 함수 : self._setinputvalue()

데이터 요청 함수 : self._commrqdata()


데이터 수신 함수 : self.receive_trdata()
함수 연결 : rqname=="opt10007"


데이터 조회 함수 : self.opt10007()

+ self.temporary_tuple[''] 데이터 입력

[종목코드별 for문 진행]
두 번째 조회 종목의 종목코드 : '258830'

+ self.temporary_tuple 데이터 입력

데이터 입력 함수 : self._setinputvalue()

데이터 요청 함수 : self._commrqdata()


데이터 수신 함수 : self.receive_trdata()
함수 연결 : rqname=="opt10007"


데이터 조회 함수 : self.opt10007()

+ self.temporary_tuple[''] 데이터 입력


 

 

③ 저장할 데이터, 데이터프레임으로 만들기

이제 다시   make_condition_dataframe  으로 돌아와보자. 아래의 코드 중 for문 안에서   self.temporary_tuple  안에 item_code를 입력했고, _commrqdata를 통해 opt10007 함수 내에서는 item_code를 제외한 나머지 데이터를 모두 입력했다. 그렇다면 이제 for문을 나온 부분에서는 self.temporary_tuple을 하나의 데이터프레임으로 제작해주어야 하는데, 그 방법은 앞서 살펴봤던 방법과 동일하다.

def make_condition_dataframe():
	self.temporary_tuple = {'item_code':[], 'open':[], 'low':[], 'high':[], 'close':[], 'volume':[], 'tvolume':[]}
	for item_code in self.code_list:
		self.temporary_tuple['item_code'].append(item_code)
		
		self._setinputvalue("종목코드", item_code)
		self._commrqdata("rq_opt10007", "opt10007", 0, "0101")

	## 데이터프레임화 시키기
	condition_dataframe = pd.DataFrame(self.temporary_tuple, columns=['item_code', 'open', 'high', 'low', 'close', 'volume', 'tvolume'])
	
	## 데이터 저장하기
	condition_dataframe.to_sql(name="s20220401", con=engine_condition, if_exists="replace")

 

 

이와 같은 방식을 통해 조건검색식을 통해 검색된 종목들을 일자별로 하나의 데이터로 제작한 후 MySQL에 저장할 수 있으며, 추후 일자별 데이터를 불러와서 거래에 활용할 수 있기도 하다.

키움증권 Open API의 경우 엄밀히 따져보면, 매일매일 종목별 주가 데이터를 받아와서 매일매일 그 데이터를 기반으로 종목을 검색하고, 그를 거래에 활용하기에는 제한이 많다. 무엇보다도 데이터 요청 횟수가 초당 5회(로 알고 있음) 정도로 제한되어 있다는 점이 가장 큰 이유이다. 제한에 아무런 조회가 없다면 매일매일 데이터를 받아 거래를 진행하는 것도 좋겠지만, 궁극적으로 데이터를 받지 않고도 영웅문 내에서 제공하는 조건검색식을 활용해서 해당 종목에 대한 데이터만 받으면 된다는 점을 극도로 메리트가 높은 데이터 관리 방식이자 거래 방식이다.

 

 


728x90
반응형
Contents

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

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