2. 해외선물/2-1. 해외선물 자동매매 연구

(키움증권 해외선물 자동매매 파이썬) (3) 실시간 데이터 받아오기

봄이오네 2023. 11. 5. 08:03
반응형

 

목 차
1. 들어가며
2. 사전설명
   1) 실시간 데이터 받는 원리
   2) realtype 및 fid 값의 개념
3. 코드설명
4. 전체코드
5. 마치며

 

1. 들어가며

지난 글에서는 키움증권 OpenAPI-W를 통해 해외선물의 "실시간 데이터 받아오는 원리"에 대해 알아보았다. 코드를 어떻게 짜야될지 막막할 때는 WKOA Studio에 종목코드 등을 입력하고 "조회"를 누르면, 수신받는 데이터의 태형(type)이나 결과물(현재가, 체결량 등) 등을 확인할 수 있어서 좋은 것 같다.
 
이번 글에서는 "실시간 데이터 받기"에 관한 코드를 작성해보자. "非실시간 데이터 받기"에 익숙해졌다면, 비교적 쉽게 접근가능할 것이라 생각된다. 천천히 살펴보자.
 


2. 사전설명

1) 실시간 데이터 받는 원리

< 그림1 >은 실시간 데이터 받는 원리를 설명하였다. 데이터입력(①) - 데이터요청(②)까지는 절차가 똑같다. 차이가 난다면, 실시간 연결(⑤) 및 실시간 수신(⑥)에서 차이가 난다.
 

그림1. 실시간 데이터 받는 원리

 

2) realtype 및 fid 값의 개념

realtype는 무엇인가? fid 값들의 모음이라고 생각하면 된다. fid 값을 찾을 때는 realtype을 먼저 찾고, fid 값을 찾는다. < 그림2-1 >에서는 realtype 이름이 "해외선물시세(②)" 아래에 있는 현재가(fid 14)를 찾을 수 있다. < 그림2-1 >에서 출력화면에서 realtype이 "해외선물시세"인 것을 확인할 수 있다.
 
fid 값은 각 단어에 붙는 "고유한 값"이라고 생각하면 된다. < 그림2-1 >에서 체결시간은 20, 현재가는 140, 전일대비는 11이 각각의 고유한 fid 값이다.
 
※ 국내주식 OpenAPI에서는 fid값들의 모음을 "realtype"이라고 하며, 해외선물에서는 "realname"로 부르고 있긴하다. 필자는 realtype으로 설정했는데 에러없이 잘 돌아간다. 사용자 판단에 따라 realtype의 코드이름을 정하자.
 

그림2-1. realtype 이름이 "해외선물시세"인 fid 값을 확인할 수 있다.

 
< 그림2-2 >는 realtype이 "해외선물호가"인 fid 값(②)을 확인할 수 있다.

그림2-2. realtype 이름이 "해외선물호가"에 해당하는 fid 값들을 확인할 수 있다.

 


3. 코드설명

활용될 라이브러리, 로그인 등의 내용은 설명을 생략한다.
 

그림3-1. 라이브러리 및 로그인에 관한 내용

1줄~35줄 : 활용될 라이브러리, 로그인 등의 내용은 설명을 생략한다.
13줄 : 이 글의 첫번째 핵심이다. 실시간 데이터를 받으려면 데이터 입력-요청을 통해 키움증권에서 이벤트가 발생하는데, 사용자가 수신받을 함수(46줄)와 이벤트를 실시간 연결한다.
 

그림3-2. 데이터 입력-요청 및 수신을 정의하는 화면

 
38줄~43줄 : 74줄에서 전달해준 2개 변수(NQZ23, 1분)을 키움증권에 입력(SetInputValue) 및 요청(CommRqData)한다.
46줄~68줄 : 이 글의 두번째 핵심이다. 실시간 수신받을 함수를 선언한다.키움증권 서버에서 이벤트가 발생하면 11줄(OnReceiveRealData)와 연결되어서, 데이터를 실시간 수신(GetCommRealData)받는다.
 
46줄 : 키움서버에서 전달받은 종목, realtype(fid 값 모음), realdata(활용하지 않음)의 3가지 정보를 수신받는 함수를 정의한다.
48줄 : print 문을 주석처리하지 않았다. 전달받은 realtype가 무엇인지 알아야, 향후 코드 짜는게 편할 것이다. "전달받은 realtype"를 확인하였다면, 주석처리해도 된다.
 
50줄 : 키움증권에서 전달받은 realtype가 "해외선물시세"이면,
51줄~52줄 : 현재가격과 체결량을 실시간으로 가져와라.
54줄~55줄 : 지금은 주석처리를 했지만, 필요하면 주석을 풀고 실행해도 된다. "현재가격과 체결량"을 실시간으로 계속 가져온다.
 
57줄~64줄 : 키움증권에서 전달받은 realtype가 "해외선물호가"이면, "호가시간", "최우선매도호가", "최우선매수호가"의 데이터를 출력한다. (지금은 주석처리)

current_price = abs(float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 140).strip()))
volume = float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 15).strip())
※ 여기서 잠깐!!! GetCommRealData 함수를 활용하는 방법
GetCommRealData 함수는 2가지 변수를 받는다. 종목코드(scode)와 fid 값이다.
GetCommRealData을 통해 전달받은 데이터의 태형은 문자형(str)이다. 현재가격 및 체결량의 경우 실수(float)형으로 만들었다.

 
66줄~68줄 : 키움증권에서 전달받은 realtype가 "잔고"이면,
68줄 : 종목코드를 출력하라. 실제 "종목코드"는 실행되지 않는다. 왜? 키움증권은 "해외선물시세" 및 "해외선물호가"의 2가지 realtype만 제공하는 것 같다. ㅠㅠ
 

그림3-3. 시스템을 실행한다.

 
74줄 : 74줄에서 "종목코드, 분봉"을 넣은 후 38줄의 함수에 변수를 넣어서 실행한다.
 


4. 전체코드

< 접은글 >의 코드를 실행하려면 키움증권에 월 시세 이용료($185)를 지불하여야 한다.
 

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

class btlg_system():
    def __init__(self):
        self.kiwoom = QAxWidget("KFOPENAPI.KFOpenAPICtrl.1")
        print("로그인 시작!")

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

        self.kiwoom.OnReceiveRealData.connect(self.tr_realdata_get)

        self.kiwoom.dynamicCall("CommConnect(1)")  # CommConnect() : 괄호 안에 자동(1)을 넣는다.
        self.login_event_loop = QEventLoop()  # from PyQt5.QtCore import * : qtcore가 임포트되어야 함
        self.login_event_loop.exec_()

        self.kiwoom.dynamicCall("GetCommonFunc(QString, QString)", "ShowAccountWindow", "")  # 계좌번호 입력창을 띄우는 내부함수

        ########### 국내주식과는 달리 setrealreg를 사용하지 않는다 #################
        # self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", "1746", "NQZ23", 15, "0")   # 해선에서는 사용하지 않는 코드이다.
        # self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", "2746", "NQZ23", 15, "1")   # 해선에서는 사용하지 않는 코드이다.
        ######################################################################

        #### 해외선물의 실시간 데이터 조회는, 차트조회(opc10002) 등을 통해 commrqdata를 조회하면, OnReceiveTrData에 연결되며 GetCommRealData로 데이터를 수신받는다.
        #### 해당종목이 실시간 자동등록되어, OnReceiveRealData로 수신되는 구조

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

    ######## 키움서버에 TR 요청하는 함수 모음 ##########
    def rq_data_opc10002(self, stock_code_num, time_unit):
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "종목코드", stock_code_num)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "시간단위", time_unit)
        self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opc_10002", "opc10002", "", "5000")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    ########## OnReceiveRealData을 통해 수신받은 데이터 함수  ##########
    def tr_realdata_get(self, scode, realtype, realdata):
        # print(scode)
        print(realtype)

        if realtype == "해외선물시세":
            current_price = abs(float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 140).strip()))
            volume = float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 15).strip())

            # print(current_price)
            # print(volume)

        elif realtype == "해외선물호가":
            hoga_time = float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 21).strip())
            first_sell_hoga = float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 27).strip())
            first_buy_hoga = float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 28).strip())

            # print(hoga_time)          # 호가시간
            # print(first_sell_hoga)    # 최우선매도호가
            # print(first_buy_hoga)     # 최우선매수호가

        elif realtype == "잔고":      # "잔고"는 제공하지 않는다.
            code = float(self.kiwoom.dynamicCall("GetCommRealData(QString, int)", scode, 9203).strip())
            print(code)

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

    btl.rq_data_opc10002("NQZ23", "1")

    # btl.tr_realdata_get("NQZ23", "해외선물시세", "")        # 14줄의 OnReceiveRealData와 '실시간 데이터 수신(tr_realdata_get)'가 연결(connect)되어야 실시간 데이터를 수신받을 수 있다.

    app.exec_()

 
 


5. 마치며

실시간 데이터 받는 코드를 알아보았다. "데이터입력-요청-연결-수신"의 "非실시간 데이터 받기" 틀에서 크게 벗어나지 않았다. 최초에 어떻게 접근해야할지, 그 부분에서 고민이 길었던 것 같다. WKOA Studio에서 실시간 데이터 받기가 되니, 혹시나 몰라 opc10002를 통해 데이터 입력(SetInputValue) 및 요청(CommRqData)를 넣어 코드를 작성해서 실행해보니, 운이 좋게도 실시간 데이터를 받아올 수 있었다.
 
본문에서도 언급하였지만, 필자의 실력부족 때문인지 해외선물시세, 해외선물호가 등 2개 realtype만 받아올 수 있었다.
 
'이렇게 데이터를 받아올 수 있구나' 정도로 생각하자. 어떤 조회방법을 선택하든 수익만 나면 훌륭한 매매이다.
 
 

반응형