1. 국내주식/1-2. 키움 OpenAPI (사용)

(주식 자동 매매) 키움증권 OpenAPI - 가격급등락요청(opt10019)

봄이오네 2023. 2. 11. 08:01
반응형
목 차
1. 들어가며
2. 사전설명
1) 영웅문S에서 급등주 조회화면(0164)
2) KOA Studio의 TR (opt10019)
3. 코드 설명
4. 전체 코드
5. 마치며

1. 들어가며

주식을 시작하면서 한가지 다짐한건, "급등주" 매매는 하지 말자는 것이었다. 상한가로 한번은 수익을 내더라도, 나중에는 그런 "상한가"만 찾게되고, 결국 고점에 물려 수습하지 못할 지경까지 갈 수 있기 때문이다.

그렇지만, ①단기간 거래량이 많고, ②가격변화가 심하고 ③단타를 할만한 투자 중 하나로 급등주 매매가 있는것 같아서 일단 모의계좌로 투자 테스트를 진행할 예정이다.

이번 글을 쓰는 것은 상당히 조심스럽다. 급등주 1위~200위를 받아오는 방법은 의외로 간단한데, 급등주 투자로 수익을 내는 것은 별개의 내용이기 때문이다. 또한, 급등주 투자는 투자 환기/과열종목, 투자경고종목 등을 고려하지 않고 검색을 하기 때문에 주의하여야 한다.결론은, 이 글도 어느 정도 걸러서 읽으셨으면 한다. ^^;

아래 링크는 4번(전체코드)를 실행한 결과물이다.

updown_stock_data.xlsx
0.02MB

그림1. 2023년 2월 10일(금) 앤씨앤(092600)의 하루 1봉 차트


2. 사전설명

1) 영웅문S에서 급등주 조회화면

키움증권 영웅문S에서의 화면번호는 0164(가격급등/급락)에서 조회가 가능하다. 2023년 2월 10일(금) 급등주 1위 종목인 앤씨앤(092600)이다. 분봉 기준이다. 기준가(2,205원) 대비 종가(2.390원)으로 급등률은 8.39%이다.

그림2-1. 가격급등/급락 조회 화면(화면번호 0164)

2) KOA Studio의 TR은 opt10019

TR목록은 opt10019(가격급등락요청)이다. input으로 9가지(시장구분, 등락구분, 시간구분 등)이며, output은 11가지(종목코드, 등락률, 급등률 등)이다. 매매시스템에 적용할 데이터는 output의 11가지에서 사용할 데이터는 2가지(종목코드, 급등률)이며, 적용 방법은 향후 설명할 예정이다.

그림2-2. KOA Studio에서 input 및 output 데이터


3. 코드 설명

활용 모듈과 로그인 등 중복되는 내용은 생략한다. 또한, 그림에서 짤린 내용은 4번(전체코드)에서 확인 가능하다.

그림3-1. 활용 모듈 및 로그인 정보 관련 내역


1줄~30줄 : 활용 모듈 및 로그인에 관한 내용이며, 설명이 중복되므로 여기서는 생략한다.
20줄 : 급등주 종목을 엑셀에 보내기 위해, 데이터를 담아줄 딕셔너리를 선언한다. (63줄~75줄, append)
23줄 : 계속 조회를 위해 48줄 for문의 마지막 범위를 정하는 self.updown_stock_ending_data를 200으로 설정한다.

32줄 : 98줄에서 9개의 인수를 담은 함수를 실행하면, 32줄의 함수가 실행된다.
33줄~41줄 : SetInputValue 함수를 통해 9개 인수(시장구분, 등락구분 등)를 키움서버에 입력한다.
43줄 : CommRqData 함수를 통해 9개 인수를 키움서버에 요청한다.

그림3-2. 수신받는 데이터 및 딕셔너리에 수신받은 데이터를 추가(append)한다.


47줄 : 42줄에서 요청한 CommRqData 함수로 키움서버에서 이벤트가 발생한 rqname이 'opt_10019'이면,
48줄~61줄 : 11가지 데이터(급등률 등)을 받아온다.
63줄~75줄 : 받아온 데이터를 20줄 self.updown_stock의 딕셔너리에 추가(append)한다.

그림3-3. 급등률이 높은 주식을 엑셀시트에 담는 화면


81줄 : 20줄(self.updown_stock)의 딕셔너리를 판다스의 데이터프레임 형태에 담기 위해 coulmns를 설정한다.
85줄 : 바탕화면(Desktop)에 파일 경로를 설정한다.
87줄~89줄 : 바탕화면에 85줄의 파일이 있으면, 데이터를 추가하라.
90줄~92줄 : 바탕화면에 85줄의 파일이 없으면, 파일을 만들고 데이터를 추가하라.
98줄 : 9개 인수(시장구분, 등락구분 등)를 넣어 32줄의 함수를 실행하라.
100줄~110줄 : KOA Studio에 있는 설명이며, 사용자에 따라 필요한 내용을 참고하여 98줄의 인수를 수정한다.

그림3-4. 파이참에 출력된 결과물


4. 전체 코드

전체코드는 아래와 같다. 98줄에 필요한 인자 9개를 사용자 편의에 따라 수정하여 사용하자.

더보기
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import pandas as pd
import os

class btl_system():
    def __init__(self):
        self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
        print("로그인 시작!")

        self.kiwoom.OnEventConnect.connect(self.login_Connect)
        self.kiwoom.OnReceiveTrData.connect(self.trdata_get)

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

        self.updown_stock = {'stock_code': [], 'stock_sort': [], 'stock_name': [], 'before_plus_minus': [], 'before_daebi_price': [],
                             'updown_per': [], 'standard_price': [], 'current_price': [], 'standard_daebi_price' : [], 'volume_scale': [], 'sudden_updown_per': []}

        self.updown_stock_ending_data = 200

    def login_Connect(self, err_code):
        if err_code == 0:
            print('로그인 성공했습니다!')
        else:
            print('로그인 실패했습니다!')
        self.login_event_loop.exit()

    def rq_data_opt10019(self, market_gubun_1, updown_gubun_2, time_gubun_3, time_4, volume_gubun_5, jongmok_condition_6, credit_condition_7, price_condition_8, updown_concludging_9):
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "시장구분", market_gubun_1)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "등락구분", updown_gubun_2)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "시간구분", time_gubun_3)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "시간", time_4)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "거래량구분", volume_gubun_5)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "종목조건", jongmok_condition_6)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "신용조건", credit_condition_7)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "가격조건", price_condition_8)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "상하한포함", updown_concludging_9)
        self.kiwoom.dynamicCall("CommRqData(QString,QString,int,QString)", "opt_10019", "opt10019", 0, "1019")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    def trdata_get(self, sScrNo, rqname, trcode, recordname, sPreNext, nDataLength, sErrorCode, sMessage, sSplmMsg):
        if rqname == 'opt_10019':
            for i in range(0, self.updown_stock_ending_data):
                stock_code1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "종목코드").strip()
                stock_sort1 =  self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "종목분류").strip()
                stock_name1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "종목명").strip()
                before_plus_minus1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "전일대비기호").strip()
                before_daebi_price1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "전일대비").strip()

                updown_per1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "등락률").strip()
                standard_price1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "기준가").strip()
                current_price1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "현재가").strip()
                standard_daebi_price1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "기준대비").strip()
                volume_scale1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "거래량").strip()

                sudden_updown_per1 = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10019", "가격급등락요청", i, "급등률").strip()

                self.updown_stock['stock_code'].append(stock_code1)
                self.updown_stock['stock_sort'].append(stock_sort1)
                self.updown_stock['stock_name'].append(stock_name1)
                self.updown_stock['before_plus_minus'].append(before_plus_minus1)
                self.updown_stock['before_daebi_price'].append(before_daebi_price1)

                self.updown_stock['updown_per'].append(updown_per1)
                self.updown_stock['standard_price'].append(standard_price1)
                self.updown_stock['current_price'].append(current_price1)
                self.updown_stock['standard_daebi_price'].append(standard_daebi_price1)
                self.updown_stock['volume_scale'].append(volume_scale1)

                self.updown_stock['sudden_updown_per'].append(sudden_updown_per1)

            self.tr_event_loop.exit()

            # print(self.updown_stock)

            df1 = pd.DataFrame(btl.updown_stock, columns=['stock_code', 'stock_sort', 'stock_name', 'before_plus_minus', 'before_daebi_price', 'updown_per', 'standard_price', 'current_price', 'standard_daebi_price', 'volume_scale', 'sudden_updown_per'])

            print(df1)

            dir = r'C:\Users\User\Desktop\updown_stock_data.xlsx'  # 경로 설정

            if os.path.exists(dir):
                with pd.ExcelWriter(dir, mode='a', engine='openpyxl', if_sheet_exists='overlay') as writer:
                    df1.to_excel(writer, header=False, index=False, sheet_name='sheet1', startrow=writer.sheets['sheet1'].max_row)
            else:
                with pd.ExcelWriter(dir, mode='w', engine='openpyxl') as writer:
                    df1.to_excel(writer, index=False, sheet_name='sheet1')

if __name__ == "__main__":
    app = QApplication(sys.argv)
    btl = btl_system()

    btl.rq_data_opt10019("000", "1", "1", "", "00000", "1", "0", "0", "0")

    # opt10019 TR목록의 9가지 인수

    # 시장구분 = 000:전체, 001:코스피, 101:코스닥, 201:코스피200
    # 등락구분 = 1:급등, 2:급락
    # 시간구분 = 1:분전, 2:일전
    # 시간 = 분 혹은 일입력
    # 거래량구분 = 00000:전체조회, 00010:만주이상, 00050:5만주이상, 00100:10만주이상, 00150:15만주이상, 00200:20만주이상, 00300:30만주이상, 00500:50만주이상, 01000:백만주이상
    # 종목조건 = 0:전체조회,1:관리종목제외, 3:우선주제외, 5:증100제외, 6:증100만보기, 7:증40만보기, 8:증30만보기
    # 신용조건 = 0:전체조회, 1:신용융자A군, 2:신용융자B군, 3:신용융자C군, 4:신용융자D군, 9:신용융자전체
    # 가격조건 = 0:전체조회, 1:1천원미만, 2:1천원~2천원, 3:2천원~3천원, 4:5천원~1만원, 5:1만원이상, 8:1천원이상
    # 상하한포함 = 0:미포함, 1:포함

    app.exec_()


5. 마치며

급등주를 조회하는 방법을 알아보았다. 거래량과 변화율이 큰 종목을 찾다보니, 급등주까지 찾게 된것 같다. 이런 투자방법을 구축하고 있는 시스템에 적용할지는 생각해봐야 할 거 같다. 실시간 급등주를 찾고, 거기에 매수로 들어갔을 때, 물리지 않을 것인지... 내 마음처럼 되지 않을 것이다. 어떻게 적용할지는 충분히 고민해보자.

향후 설명하겠지만 급등주 매매를 구축시스템에 적용은 종목코드, 급등률 2가지이다. 급등률이 높은 종목을 찾아서 진입하고, 익절/손절 타점을 잘 잡아서 시뮬레이션을 돌려보자.

반응형