키움증권 Open API + MySQL, 차트 데이터 저장하기
지난 포스팅에서 이제 Open API 관련 코드가 저장되어 있는 파일(본인의 경우 posting)에서 데이터베이스의 생성과 존재 여부를 자동으로 처리하도록 하는 방법까지 모두 확인했었다. 이번 포스팅에서는 일봉 차트 데이터를 본인의 데이터베이스로 입력하도록 한 후에 그를 확인하는 방법 그리고 불러오는 방법에 대해 다룰 계획이다.
차트 데이터 잘 불러와지는지 확인하기
이전에 차트 데이터를 DataFrame 형태로 구축하는 과정에서, 우리는 if문 아래에 print(trade.df_day_data)를 통해 차트 데이터를 불러왔었다. 기억이 나지 않는다면 해당 포스팅을 다시 보도록 하고, 그 때 작성했던 코드를 다시 가져와보면 아래와 같다.
if __name__ == "__main__":
app = QApplication(sys.argv)
trade = system_trading()
trade.rq_chart_data("005930", "20210606", 1)
df_day_data = pandas.DataFrame(trade.day_data, columns=['date', 'open', 'high', 'low', 'close', 'volume', 'trade_volume'])
print(df_day_data)
즉, rq_chart_data에 종목코드와 일자를 입력하게 되면 알아서 차트 데이터를 조회하면서 그 데이터들을 바탕으로 df_day_data라는 최종 결과값을 받아오는 코드였다. 이제 이를 실행해보면 아래와 같은 결과물을 확인할 수 있다.
open high low close volume trade_volume
date
20210604 82700 82700 81500 82200 18112259 1487791
20210603 81300 83000 81100 82800 29546007 2438123
20210602 80400 81400 80300 80800 16414644 1327714
20210601 80500 81300 80100 80600 14058401 1135462
20210531 80300 80600 79600 80500 13321324 1065281
... ... ... ... ... ... ...
19850109 126 126 122 123 324837 1
19850108 129 129 127 127 845098 4
19850107 129 130 128 129 771895 3
19850105 129 129 128 128 108497 0
19850104 130 130 129 129 111765 0
[9637 rows x 6 columns]
데이터베이스와 연결하기
예전 포스팅에서 create_engine에 대해 서술했었는데, 그 부분을 기억할지 모르겠어서 잠깐 설명하고 넘어가고자 한다. 일단 지난 포스팅에서 제작했던 check_db 함수가 포함되어 있는 파일(본인의 경우 posting_mysql) 안에 아래와 같은 코드를 제작해주도록 하자.
def connect_to_sqlal(db_name):
engine_all = create_engine('mysql+mysqldb://root:PASSWORD@127.0.0.1:3306/'+ db_name, encoding = 'utf8')
connection_all = engine_all.connect()
return engine_all
위의 코드 안에 보면 PASSWORD라는 부분과 db_name이라는 부분이 있는데, PASSWORD 자리에는 mysql에 접속할 때 입력하는 비밀번호를 입력하고 DB NAME에는 본인의 일봉 차트 데이터를 저장할 데이터베이스의 이름을 입력하면 된다. 만약 그 데이터베이스가 없다면, 지난 포스팅에서 다루었듯이 check_db 함수를 통해 만들어주면 된다. 본인은 일봉 차트 데이터를 저장할 데이터베이스의 이름을 day_data로 하도록 하겠다.
그렇다면 지금 posting_mysql 파일 안에 위와 같은 코드가 제작되어 있고, 해당 변수를 사용하기 위해서는 우리가 지난 포스팅에서 작성했던 posting_mysql.check_db()처럼, posting_mysql.connect_to_sqlal을 사용하면 된다. 즉, 방금 제작한 connect_to_sqlal이라는 함수에 사용하고자 하는 db_name인 day_data를 입력하게 되면 해당 데이터베이스와 연결한 이후에 engine_all을 반환받게 되는데, 이 engine_all을 반환받는 이유는 pandas의 to_sql에서 사용되는 것이기 때문이다.
to_sql 사용하기
to_sql 역시 이전 포스팅에서 설명했었는데, to_sql의 가장 기본적인 코드 형태는 아래와 같다.
DATA_NAME.to_sql(name=, con=, index=, if_exists=)
각각 하나씩 설명해보자면 DATA_NAME의 경우에는 to_sql이라는 함수를 통해 전송하고자 하는 데이터의 이름이다. 이 경우에는 앞에서 살펴봤듯이 df_day_data가 전송할 데이터가 되는 것이다. 다음으로 name의 경우에는 해당 데이터베이스 내에서 사용할 table_name을 의미하는 것이다. 다음으로 con의 경우에는 connection의 약자로, 바로 앞에서 설정했던 engine이 들어가는 부분이며 index는 True 또는 False 형태의 Bool 형식으로 입력하게 되는데, 굳이 사용하지 않아도 된다면 index= 부분을 제거해도 된다. 마지막으로 if_exists=는 '만약 존재한다면'이라는 의미로 replace를 사용하게 되면 '삭제-입력'의 절차를 거치고 append를 사용하게 되면 '입력'의 절차를 거친다. 그렇다면 이를 바탕으로 코드를 작성해보면 아래와 같다.
df_day_data.to_sql(name='s005930', con=engine, index=False, if_exists='replace')
[주의] 데이터베이스 내에서 데이터베이스의 이름이나 또는 테이블 명(위의 경우 's005930'를 가리킴)은 숫자로 시작하면 오류가 발생한다. 하지만 종목 코드는 모두 숫자로 이루어져 있기 때문에 앞에 알파벳 아무거나 입력해주도록 하자.
지금까지의 제작한 if문 하단의 코드
if __name__ == "__main__":
app = QApplication(sys.argv)
trade = system_trading()
trade.rq_chart_data("005930", "20210606", 1)
df_day_data = pandas.DataFrame(trade.day_data, columns=['date', 'open', 'high', 'low', 'close', 'volume', 'trade_volume'])
print(df_day_data)
df_day_data.to_sql(name='s005930', con=engine_all, index=False, if_exists='replace')
print("저장이 완료되었습니다.")
이제 코드를 실행해보면 아래와 같은 결과물을 확인할 수 있다.
[DB 관리] day_data DB가 이미 존재합니다.
mysqldb 연결
연결되었습니다..
로그인에 성공하였습니다.
open high low close volume trade_volume
date
20210604 82700 82700 81500 82200 18112259 1487791
20210603 81300 83000 81100 82800 29546007 2438123
20210602 80400 81400 80300 80800 16414644 1327714
20210601 80500 81300 80100 80600 14058401 1135462
20210531 80300 80600 79600 80500 13321324 1065281
... ... ... ... ... ... ...
19850109 126 126 122 123 324837 1
19850108 129 129 127 127 845098 4
19850107 129 130 128 129 771895 3
19850105 129 129 128 128 108497 0
19850104 130 130 129 129 111765 0
[9637 rows x 6 columns]
저장이 완료되었습니다.
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import pandas
import posting_mysql
from sqlalchemy import create_engine
posting_mysql.check_db('day_data')
engine_all = create_engine('mysql+mysqldb://root:a9985623@127.0.0.1:3306/day_data', echo=False)
connection = engine_all.connect()
print("mysqldb 연결")
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.OnReceiveConditionVer.connect(self.OnReceiveConditionVer)
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 GetConditionLoad(self):
self.kiwoom.dynamicCall("GetConditionLoad()")
def OnReceiveConditionVer(self, iRet, Msg):
print("OnReceiveConditionVer")
if iRet == 1:
print("조건 검색식이 로컬 서버에 저장되었습니다.")
else:
print("조건 검색식 저장 실패")
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", "20210606", 1)
df_day_data = pandas.DataFrame(trade.day_data, columns=['date', 'open', 'high', 'low', 'close', 'volume', 'trade_volume'])
print(df_day_data)
df_day_data.to_sql(name='s005930', con=engine_all, index=False, if_exists='replace')
print("저장이 완료되었습니다.")
WorkBench 확인해보기
이제 MySQL WorkBench를 열어서 데이터가 잘 저장되었는지 확인해보도록 하자.
WorkBench 사용 방법에 대해서는 별도로 언급하지 않았었는데, 여기서 간략하게 설명해보도록 하겠다. 일단 가장 먼저 사용하고자 하는 데이터베이스를 활성화시켜야 한다. 아래 사진을 보면 day_data가 굵은 글씨로 되어 있는데 그것이 바로 활성화된 상태라는 것을 의미한다. 활성화된 상태와 활성화되지 않은 상태에서 사용해야 하는 방법은 각각 다른데, 아래의 코드를 보면 어렵지 않게 이해할 수 있다.
# 비활성화 상태
SELECT * FROM 'day_data'.'s005930'
# 활성화 상태
SELECT * FROM s005930
즉, 활성화 상태에서는 검색 대상을 해당 데이터베이스 안으로 한정지었기 때문에 별도의 데이터베이스 명을 입력하지 않아도 되지만, 비활성화 상태에서는 특정된 데이터베이스가 없기 때문에 'day_data'라는 부분을 추가로 입력해주어야 한다는 것이다.
그 후 위와 같은 코드를 실행하는 방법은 바로 해당 코드의 윗 부분에 있는 번개 모양을 클릭하는 것이다. 물론 우리는 이게 아니라 단축키를 사용할 것이다. 단축키는 실행하고자 하는 코드를 한 번 클릭한 후에 Ctrl + Enter 키이다. 클릭된 부분은 아래의 사진처럼 회색 배경으로 나오게 되는데, 그 회색 배경으로 변한 부분만 실행하게 된다. 그렇게 실행이 되면 바로 아래의 Result Grid 안에 우리가 입력했던 데이터들이 포함되는 것을 확인할 수 있다.
여기까지의 내용을 바탕으로 종목 별 데이터를 DataFrame화 시켜서 DB에 저장할 수도 있고, 해당 내용을 불러옴으로써 백테스팅을 한다거나 또는 거래 전략을 검증한다거나 하는 등의 여러 가지 작업들을 수행할 수 있다. 키움증권 Open API와 연결하는 방법에 대해서는 모두 다루었으며, 다루지 않았던 몇 가지 이벤트들에 대해서는 이전 포스팅에서 다루었던 것과 같은 방식으로 제작하면 얼마든지 처리가 가능하다.
'AUTO TRADE > [키움증권] Kiwoom Open API' 카테고리의 다른 글
거래일 기준 최신 일자 조회하기 (0) | 2021.06.07 |
---|---|
키움증권 Open API - 3분봉 차트 조회 (3) | 2021.06.07 |
파이썬 + MySQL, 데이터베이스 자동 처리하기 (3) | 2021.06.06 |
파이썬 + MySQL, 파이썬으로 연동하기 (0) | 2021.06.03 |
파이썬 + MySQL, 설치와 설정 방법 (0) | 2021.06.03 |
소중한 공감 감사합니다