2. 해외선물/2-4. 해외선물 API (사용)

(키움증권 해외선물 OpenAPI-W) RSI 값 구하기 (1) 연속조회 및 과거데이터 받기

봄이오네 2023. 1. 31. 08:02
반응형
목 차
1. 들어가며
2. 사전설명
   1) 국내주식 연속조회 방법
   2) 해외선물 연속조회 방법
   3) GetRepeatCnt 함수
3. 연속조회하는 방법
4. 전체 코드
5. 마치며

 

1. 들어가며

지난 글에서는 볼린저밴드의 상한값/하한값 구하는 방법을 알아보았다. 볼린저밴드는 현재가를 포함하여 총 20개 데이터의 평균 및 표준편차(신뢰구간 95%)를 이용하여 계산해 보았다.

 

이번 글에서는 RSI 값을 구하는 방법을 알아보려고 했으나, 그 전에 OpenAPI-W에서 2페이지가 있는 경우 연속조회하는 방법과 과거데이터 받는 방법을 먼저 알아볼 것이다.

 

이유는? 키움증권에서 제공하는 RSI 값을 도출하기 위해 활용하는 데이터가 600개가 넘기 때문이다.

키움증권의 WKOA Studio에서 확인해보면 알겠지만, CommRqData 함수를 통해 데이터를 요청해서 수신받는(GetCommDATA) 데이터는 600개이다.

 

그.런.데...

600개로 도출하는 RSI 값과 키움증권에서 보이는 RSI 값이 다르다는 것을 확인하게 되었고,

키움증권의 RSI 도출을 위해 필요한 데이터는 650개~700개 정도가 된다는 것을 테스트를 통해 알게 되었다.

결론은 종가 700개 정도가 필요하고, 연속조회를 통해 종가 데이터를 받아와서 RSI를 산출할 필요가 있다.

 

이 글에서는 연속조회를 하는 방법을 알아보자.


2. 사전설명

국내주식 OpenAPI와 해외선물 OpenAPI-W에서 연속조회하는 방법은 서로 다르다.

결론부터 말하면, 국내주식 연속조회가 훨씬 쉽다!!. 해선인들의 슬픔이여... 흑~

 

1) 국내주식 연속조회 방법

KOA StudiIo에서 확인해보면, < 그림1 >과 같이 조회할 수 있음을 확인할 수 있다.

CommRqData에서 3번째 인자를 숫자 "2"로 넣어주면 끝이다. 

  • (확인 경로) KOA Studio > 개발가이드 > 자주 묻는 질문 > 키움OpenAPI 개발/구현 중입니다.

그림1. KOA Studio에서 연속조회하는 방법

 

2) 해외선물 연속조회 방법

< 그림2 >의 답변은 "연속조회를 할 때, 데이터가 없다는 것을 확인하는 방법"에 대한 답변이다.

기본적으로 연속조회하는 방법은 WKOA Studio에 설명되어 있지 않다.

결국 하나씩 수작업을 해보면서 연속조회하는 방법을 찾아봐야 하는 수 밖에 없다. ㅠ_ㅠ

 

그림2. sPrevNext 값을 이용하여 연속조회하는 방법

 

3) GetRepeatCnt 함수

키움측에서 제공하는 함수이며, WKOA Studio의 설명에 의하면 수신메이터 반복횟수를 반환한다고 한다.

설명이 어렵다.

간단하게 이야기하면, 데이터를 요청한 경우 페이지별로 저장이 되어 있을 것이다.

1페이지에 있는 데이터 갯수를 반환하며, 대개는 600개이므로,

물론 예외도 있겠지만, GetRepeatCnt를 통해 불러오는 데이터 갯수는 600으로 알고 있자.

그림3. GetRepeatCnt 함수에 대한 설명


3. 연속조회 및 과거데이터 받는 방법

연속조회를 통해 1분봉 종가데이터 700개를 수신받는 방법을 알아보자.

이번 코드에서는 print를 의도적으로 넣었다. 코드 실행순서를 확인할 필요가 있기 때문이다.

(모듈 및 로그인 정보에 대한 설명은 생략한다)

 

그림4-1. 필요 모듈 및 로그인 관련 정보

 

1줄 ~ 28줄 : 모듈 및 로그인에 관한 내용이다.

20줄 : 50줄과 71줄에서 추가(append)한 데이터를 담는 리스트를 선언한다.

 

그림4-2. 데이터 입력/요청/수신받는 함수 내역

 

31줄~43줄 : 데이터를 입력/요청하는 내역이다.

31줄~36줄 : 현재가 포함, 600개 데이터를 받기 위해 요청하는 함수이다.

38줄~43줄 : 601개~700개 데이터(+100개)를 받기 위해 요청하는 함수이다.

 

34줄과 41줄이 짤렸는데, 차이는 아래와 같다.

3번째 인수인 sPrevNext가 다르다.

31줄의 3번째 인수는 " " (빈칸)이고,

41줄은 self.prenext이다. (뒤에서 설명한다)

34줄 : self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opc_10002", "opc10002", "", "1012")
41줄 : self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opc_10002_1", "opc10002", self.prenext, "1012")

 

46줄~57줄 : 34줄에서 요청한 함수에 대해, 키움증권에서 600개 종가를 보내주는데, 이것을 20줄의 self.price_rsi_total에 넣어준다.

 

61줄~64줄 : 이 글의 핵심이다.

61줄 : 만약 prenext가 비어있지 않다면,

62줄 : prenext를 self.prenext에 담아라.

64줄 : 51줄에서 600개 데이터를 담아주었고, 나머지 100개를 받기 위해 38줄의 함수를 실행한다.

여기서 41줄의 3번째 인수인 self.prenext가 전달되는 것이다.

 

self.prenext를 출력해보면 아래와 같다.

키움측에서는 데이터를 불러오기 위한 시퀀스라고 하는데... 그냥 아래의 내용을 전달해준다고 보면 된다.

나스닥(23년 3월물이고), 뒤에는 날짜이다. 2023-01-27, 20:59분을 의미한다. (from ~ to에서 기준시점인 from을 말한다)

F0NQH23           2023012720590000010000

 

여기서 잠깐!!!

중요한 발견이 될 수 있다.

위에서도 이야기했지만, 202301270590은 조회의 기준시점이다.

이것은, 우리가 못받은 데이터가 있다면, 위의 기준시점을 기준으로 연도와 날짜, 시간의 기준점만 잘 설정한다면,

얼마든지 과거 데이터를 받을 수 있다는 것이다.

 

나중에 WKOA Studio에서 확인해보면 알겠지만,

opc10002(분차트조회)의 "다음"을 누르면, 화면이 계속 넘어가면서 과거 데이터가 계속 화면에 출력된다.

즉, 202391270590에서 숫자를 변경하여 sprevnext의 연속조회에 넣으면 과거데이터가 나온다는 것이다! ^^

 

그림4-3. 데이터 100개를 추가로 받는 내역

 

67즐~78줄 : 데이터(100개)를 추가적으로 반환받는다.

71줄 : 반환받는 데이터를 20줄의 self.price_rsi_total 리스트에 추가(append)한다.

 

84줄 : 나스닥(23년 3월) 및 1분봉을 변수로 넣어 31줄의 함수를 실행한다.


4. 전체 코드

연속조회를 통해 700개 데이터를 받아오는 코드이다.

 

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

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

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

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

        ##### rsi의 현재 1분 데이터의 au/ad 구하기 #####
        self.price_rsi_total = []

    ########## 로그인 관련 함수 ##########
    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", "", "1012")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    def rq_data_opc10002_1(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_1", "opc10002", self.prenext, "1012")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    ########## 키움서버에서 데이터 수신받는 함수 모음 ##########
    def trdata_get(self, scrno, rqname, trcode, recordname, prenext):
        if rqname == "opc_10002":
            getrepeatcnt = self.kiwoom.dynamicCall("GetRepeatCnt(QString,QString)", trcode, recordname)
            for i in range(getrepeatcnt):   # 현재가 → 오래된 값으로 출력된다.
                self.current_price_rsi = float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "현재가").strip())
                self.price_rsi_total.append(self.current_price_rsi)

            print("53줄")
            print(getrepeatcnt)
            print(prenext)
            print(len(prenext))
            print("57줄")

            self.tr_event_loop.exit()

            if prenext != "":
                self.prenext = prenext
                print("63줄")
                btl.rq_data_opc10002_1("NQH23", "1")
                time.sleep(1)

        elif rqname == "opc_10002_1":
            time.sleep(1)
            for i in range(0, 100):
                self.current_price_rsi_1 = float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002_1", "opc10002", i, "현재가").strip())
                self.price_rsi_total.append(self.current_price_rsi_1)

            print("73줄")
            print(self.price_rsi_total)
            print(len(self.price_rsi_total))
            print("76줄")

            self.tr_event_loop.exit()

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

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

    app.exec_()

 


5. 마치며

연속조회 및 과거데이터 조회하는 방법을 알아보았다.

sPrevNext를 통해 과거데이터 조회하는 방법까지 알았다는 것은 상당히 의미있는 일인것 같다.

 

예전같으면 저녁 약속으로 1분봉을 놓치면 못받는줄 알고 굉장히 좌절하였는데,

이제는 sPrevNext만 잘 설정한다면, 얼마든지 데이터를 받을 수 있다는 자신감이 생긴다.

 

이번 시간에는 데이터 700개를 받는 방법을 알아보았고, 다음시간에는 RSI 값을 구하는 방법을 알아보자.

반응형