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

(키움증권 해외선물 OpenAPI-W) 미체결내역 조회 (opw30002)

봄이오네 2023. 1. 25. 08:02
반응형
목 차
1. 들어가며
2. 사전설명
   1) WKOA Studio에서 TR목록 확인
   2) 영웅문G에서 미체결 주문번호 확인
   3) 받아오는 주문번호 형태 확인하자.
3. 코드설명
4. 전체 코드
5. 마치며

 

1. 들어가며

해외선물을 사용자가 주문을 하는 경우, 100% 주문이 들어가는 것은 아니다. 혹은 취소주문을 하고 싶을 때가 있다.

취소를 하고 싶을 때는 당초 제출한 주문번호를 알아야 그 주문을 취소할 수 있다.

 

개인적으로, 이번 opw30002 TR 데이터를 받는 방법이 상당히 까다롭다는 생각이 들었다. 어렵다는 표현보다는 input새로운 유형으로 헤맸다는 표현이 맞을 것이다. 입력정보(input)에 전체 : space 등 새로운 유형을 어떻게 처리해야 할지 고민했다.

 

국내주식 OpenAPI에서는 볼 수 없는 유형이어서 상당히 헤맸는데... 역시 코딩은 시간과의 싸움이라고 해야할까?

이렇게.. 저렇게.. 바꾸어보고 시간을 쏟아부어서 원하는 결과를 얻을 수 있었다.

 

이 글에서는 당초 제출한 주문번호미체결수량 등 미체결내역을 받는 방법을 알아볼 것이다.

→ WKOA Studio 의 "화면 목록"에서 확인하면, 영웅문G와 WKOA Studio의 필요 내용을 알 수 있다.

  • 영웅문G 기준, 화면번호는 4543(해외선물 실시간 미체결)에서 미체결 수량 등을 확인할 수 있다. (그림2)
  • WKOA Studio 기준, TR요청번호는 opw30002(미체결내역 조회)에서 확인할 수 있다. (그림3)

그림1. WKOA Studio의 화면번호 등을 확인한다.

 

여기서는 23년 3월물 엔화(6JH23)를 통해 미체결 내역 등을 받아오는 방법을 알아보자.

그림2. 엔화('23.3월물)의 매수(long) 1주문이 체결되지 아니한 화면이다.


2. 사전설명

1) WKOA Studio에서 TR목록 확인

  • TR목록의 opw30002에서 input / output 정보를 확인한다.
  • input : 5가지(계좌번호, 비밀번호 등) 입력한다.
  • output : 14가지(주문번호, 종목코드, 미체결 수량, 상태구분 등)의 데이터를 받는다.
  • 수신되는 데이터는 < 그림3 >에서 확인한다.

①의 SetInput 함수에서 입력값으로 "전체"는 "space 처리"를 하라고 한다. 무슨말일까???

인터넷에서 검색을 해도 안 나온다. 흑~

결국 ②에서 빈칸으로 설정(아무것도 입력안함)하고 조회를 하니.. 조회가 되었다. 후후훗~

③은 아래에서 설명한다.

④미체결수량은 0인 붙은 000....1의 형태이다.

 

그림3. opw30002을 통해 수신받는 데이터 내역

 

2) 영웅문G에서 미체결 주문번호 확인

영웅문G에서 원주문번호를 조회할 수 있는 화면을 못 찾아서, < 그림4 >에서 확인하였다.;;;

< 그림4 >에서 종목코드와 당초 제출한 주문번호를 확인할 수 있다.

그림4. 원주문번호를 확인하자.

 

3) 받아오는 주문번호 형태 확인하자.

받아오는 데이터의 문자 형태를 확인하자.

  • < 그림3 >의 ③의 주문번호 형태는 000....117013051 인데,
  • < 그림4 >에서의 주문번호 형태는 117013051이다.

흠... 파이썬 코드 작성시, < 그림3 >의 ③의 왼쪽인 000을 없애주어야 할 것 같다. ( lstrip 함수 )

strip() 함수 : 문자 양옆의 빈칸을 제거한다.

lstrip("문자") : 수신받은 데이터의 왼쪽에 있는 "문자"를 제거한다. (아래에서 설명한다)


3. 코드설명

필요 모듈의 임포트 및 로그인 등 중복적으로 언급되는 내용은 생략한다.

 

그림5-1. 필요모듈 및 로그인 방법

로그인 정보는 링크에서 확인한다.

 

그림5-2. 키움서버에 데이터 입력/요청/수신받는 방법

 

 

33줄~42줄 : 키움서버에 데이터를 입력(SetInputValue 함수) 및 데이터를 요청(CommRqData 함수)한다.

33줄 : 80줄에서 변수를 받아 실행할 수 있도록 임의의 함수를 선언한다.

※ 80줄에서 33줄의 함수를 실행시키는 방법을 알아보자.

 

34줄~38줄 :  80줄의 함수를 실행할 때, 키움서버에 입력할(SetInputData) < 그림3 >의 5가지 데이터이다. (계좌번호, 비밀번호, 종목코드, 통화코드, 매도수구분)

39줄 : 34줄~38줄에서 키움서버에 입력한 5가지 데이터를, 키움서버에 요청(CommRqData)한다.

  • 요청한 데이터로 키움서버에서 이벤트가 발생하는데,
  • 그 이벤트를 45줄의 GetCommData 함수와 연결시켜주는 것이 13줄의 OnReceive 함수이다.

※ 이벤트라는 말이 상당히 어려울 수 있는데, 몰라도 대세에 지장은 없다.

    (여기서는 "데이터를 반환해 주세요"라고 이해하면 된다.)

 

46줄 : 39줄에서 키움서버에 요청한 데이터는 13줄의 OnReceive를 통해 46줄 if rqname == 'opw30002'에 해당하면,

47줄~74줄 : 데이터를 받아오고(GetCommData), 출력(print)한다.

47줄 : 여기서 주의한다.

< 그림3 >에서 확인하였듯이, 원주문번호는 000...117013051 의 형태로 받아온다.

원주문번호 앞에 있는 000을 없애기 위해 lstrip("0")으로 데이터 왼쪽의 000을 없앤다.

 

그림5-3. 33줄의 함수를 80줄에서 실행한다.

 

80줄 : 이 글의 핵심이다.

첫번째, < 그림3 >의 ①에서 확인하였듯이, 종목코드 설정시, "전체: space 처리"가 어떤 의미인가?

"(빈칸있음)"으로 해야 하는 것인지, "(빈칸없음)"으로 해야하는지 한참을 고민했다.

실험 결과... "(빈칸없음)"으로 설정하니, 데이터를 받아왔다. 쌍따옴표 2개를 붙여서 설정하자.

통화코드도 마찬가지로 쌍따옴표 2개를 붙여서 설정해주자.

 

두번째, 매도수구분에서 살짝 고민했다. 무엇이 1인고, 무엇이 2인지... ㅠㅠ

제출한 주문 형태가 매도(short)이면 " 1 "이고, 매수(long)이면 " 2 "로 설정하라는 것이다.

문자형태이므로, "1", "2"로 넣어준다.

long/short에서 주문시, 각각 미체결 내역을 조회해 주어야 하는 번거러움이 있다.

 


4. 전체 코드

 

22줄에 사용자의 계좌번호 10자리 입력후 실행하자.

전체 코드는 아래와 같다.

 

더보기
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)")
        self.login_event_loop = QEventLoop()
        self.login_event_loop.exec_()

        self.kiwoom.dynamicCall("GetCommonFunc(QString, QString)", "ShowAccountWindow", "")

        ##### 계좌 번호, 종목코드, 익절/손절 타점 설정 #####
        self.deposit_num = "계좌번호 10자리를 기재한다."

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

    ########## 키움서버에 TR 요청하는 함수 모음 ##########
    def rq_data_opw30002(self, deposit_num_1, password_2, futures_codes_3, currency_code_4, sell_buy_gubun_5):
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "계좌번호", deposit_num_1)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "비밀번호", password_2)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "종목코드", futures_codes_3)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "통화코드", currency_code_4)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "매도수구분", sell_buy_gubun_5)
        self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opw_30002", "opw30002", "", "3002")
        time.sleep(0.5)
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    ########## 키움서버에 TR 수신받는 함수 모음 ##########
    def trdata_get(self, scrno, rqname, trcode, recordname, prenext):
        if rqname == "opw_30002":
            order_num = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "주문번호").lstrip("0").strip()
            futures_code_30009 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "종목코드").strip()
            order_type = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "주문유형").strip()
            sell_buy_gubun = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "매도수구분").strip()
            not_trading_qty = int(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "미체결수량").strip())
            order_price = float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "주문표시가격").strip())
            order_state = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "상태구분").strip()
            order_date = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30002", "opw30002", 0, "주문시각").strip()

            self.order_num = order_num
            self.futures_code_30009 = futures_code_30009
            self.order_type = order_type
            self.sell_buy_gubun = sell_buy_gubun
            self.not_trading_qty = not_trading_qty
            self.order_price = order_price
            self.order_state = order_state
            self.order_date = order_date

            print(self.order_num)
            print(self.futures_code_30009)
            print(self.order_type)
            print(self.sell_buy_gubun)
            print(self.not_trading_qty)
            print(self.order_price)
            print(self.order_state)
            print(self.order_date)

            self.tr_event_loop.exit()

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

    btl.rq_data_opw30002(btl.deposit_num, "0000", "", "", "2")

    app.exec_()
    
    
# (expected result)
# 117013051
# 6JH23
# 2
# 2
# 1
# 7802.5
# 1
# 01/17 11:43:08

 


5. 마치며

opw30002를 통해 해외선물 거래시 미체결내역을 알아보았다.

생각보다 시간이 많이 걸렸다. "전체: space 처리"를 어떻게 처리해야 할지 몰라 헤맨것 같다.

 

기 제출한 주문을 취소하려면 주문번호가 필요한데, 이 때 주문번호를 조회할 때 유용하게 사용될 것이다.

반응형