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

(키움증권 해외선물 OpenAPI-W) 일괄청산 (1) 해외파생지정청산대상조회(opw30023)

봄이오네 2023. 2. 12. 11:42
반응형
목 차
1. 들어가며
2. 사전설명
1) 지정청산(일괄청산)을 위해 필요한 항목
2) WKOA Studio의 해외파생지정청산주문
3. 코드설명
4. 전체코드
5. 마치며

1. 들어가며

해외선물을 하다보면 일괄청산(지정청산)을 해야 할 때가 있다. 금리인상 등의 악재로 시장이 급격히 방향을 바꿀 때 가지고 있는 모든 종목을 한번에 청산해야 하는 경우가 드물게 있다.

필자도 청산시 sendorder 함수를 사용하려고 하는데 잘 안되어서, 지난 주까지 모의계좌에서 일괄청산을 사용하였다. 종목조회를 안해서 sendorder 함수의 청산이 안 되었음을 발견하였기 때문이다. 물론 지금은 sendorder 함수를 사용한다.

진입은 sendorder 함수 1개만 있지만, 청산은 sendorder 함수와 일괄청산(opw10007) 2개가 있다. 일괄청산(지정청산)을 하려면 인수를 많이 입력해주어야 하기 때문에 손이 많이 가긴 하지만, 일괄청산으로 포지션 정리를 할 수 있다는 것만 알아두자.

  • 진입 1가지 : sendorder 함수
  • 청산 2가지 : sendorder 함수+일괄청산(opw10007)


이 글에서는 opw30023(해외파생지정청산대상조회) 조회를 통해 받아온 데이터를 opw10007(지정청산)의 input으로 활용하여 "일괄청산"하는 방법을 알아본다. 즉, opw30023의 결과물을 opw10007에 전달하는 과정을 설명할 예정이다.

< 그림1 >에서 확인가능하듯이, 영웅문G의 화면번호는 4541이며, 종목선택(①) 후 일괄청산(②)를 누르면, 비밀번호 입력창이 나오고, "조회"버튼을 누르면 일괄청산된다.

그림1. 영웅문G에서 일괄청산 화면번호는 4541이다.


※ 장 종료로 인해 실제 처리되는 결과는 보여드릴 수 없는 점에 대해 양해 부탁드린다. ^^

글 쓰기 목차
(1) 해외파생지정청산대상조회(opw30023)
(2) 해외파생지정청산주문(opw10007)


키움측 설명을 보면 여러종목을 가지고 있어도 API 지정청산시 1종목 청산된다고 한다. 영웅문G에서 수기로 모든 종목을 청산하는 내용은 아니다. 흑~

그림4. 지정청산주문시 1건씩만 청산된다는 내용


2. 사전설명

1) 지정청산(일괄청산)을 위해 필요한 항목

< 그림2-1 >에서 확인하였듯이 지정청산(opw10007)을 위해 필요한 인수는 18가지이다. @.@
계좌번호 등은 알겠는데, FCM코드, 에러코드, 처리메세지는 무슨 말인지 모르겠다. -_-+
우리에게 필요한 것은 지정청산의 실행이다. opw30023에서 받아온 데이터를 opw10007의 input에 넣어주면 된다.

그림2-1 지정청산 input 현황

2) WKOA Studio의 해외파생지정청산주문(opw30023)

해외파생 지정청산대상조회(opw30023)에서 input 정보는 계좌번호, 비밀번호, 통신주문구분 등 4가지이다. 설명서에 나온대로 사용자의 계좌번호/비밀번호/00을 입력하고, 통신주문구분도 < 그림2-2 >에서 설명한대로 "AP"만 입력하면 된다. ;;

여기서 중요한 것은 OUTPUT이다. 위의 < 그림2-1 >에서 확인하였듯이 FCM코드, 매도수구분, 에러코드, 처리메시지 등을 받아온 다는 것을 확인하자.

그림2-2. 해외파생지정청산대상조회(opw30023)의 input / output 내역

3. 코드 설명

활용 모듈 및 로그인 관련 설명은 생략한다. 그림에서 짤린 부분은 4번(전체코드)에서 확인하자.

그림3-1. 키움서버에 데이터를 입력/요청하는 화면


1줄~29줄 : 활용모듈 및 로그인 관련 내용은 여기에서는 생략한다. 21줄에 사용자 계좌 10자리를 넣어준다.
32줄~39줄 : 82줄에서 전달해줄 인수를 받아줄 함수를 선언한다. < 그림2-2 >에서 확인하였듯이 계좌번호, 비밀번호, 비밀번호입력매체, 통신주문구문의 4가지이다.

그림3-2. 수신받은 데이터 내역


언뜻 길게보이지만, 61줄~73줄은 print문임을 감안하면, 생각보다 간단하니 가벼운 마음으로 코드를 확인하자.
41줄~56줄 : 37줄 CommRqData 함수를 키움서버에 데이터를 요청하면, (자료를 수신해줄) 이벤트가 발생하는데 12줄의 OnReceive 함수의 연결을 통해 데이터를 41줄~56줄에서 수신받는다. FCM코드, 에러코드, 처리메세지 등이 무엇인지는 몰라도 된다. 우리는 opw10007에 받아온 데이터를 전달해주면 된다.

82줄 : 32줄의 함수를 실행한다. 이 때 입력하는 인수는 계좌번호, 비밀번호, 비밀번호입력매체, 통신주문구문 4가지이다.


4. 전체 코드

아래 코드는 종목을 보유하고 있는 상태에서 23줄과 82줄에 계좌번호 10자리, 82줄에 계좌비밀번호 4자리를 입력해야 결과물이 반환되니 실행시 참고하자.

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

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_()

        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_opw30023(self, deposit_num1, password_2, password_enter3, tongsin_gubun_4):  # 계좌 현재가, 매도수구분, 보유수량, 매입단가, 계좌현재가, 평가손익
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "계좌번호", deposit_num1)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "비밀번호", password_2)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "비밀번호입력매체", password_enter3)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "통신주문구분", tongsin_gubun_4)
        self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opw_30023", "opw30023", "", "3023")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    def trdata_get(self, scrno, rqname, trcode, recordname, prenext):
        if rqname == "opw_30023":  # 계좌 현재가, 매도수구분, 보유수량, 매입단가, 계좌현재가, 평가손익
            self.fcm_code_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "FCM코드").strip()
            self.futures_code_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "종목코드").strip()
            self.futures_code_name_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "종목명").strip()
            self.sell_buy_gubun_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "매도수구분").strip()
            self.buy_price_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "매입표시가격").strip()
            self.buy_date_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "매입일자").strip()
            self.order_type_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "해외주문유형").strip()
            self.order_qty_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "주문수량").strip()
            self.order_price_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "주문표시가격").strip()
            self.stop_price_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "STOP표시가격").strip()
            self.order_num_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "주문번호").strip()
            self.error_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "에러코드").strip()
            self.message_30023 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "처리메세지").strip()
            self.pl_check_30023 = float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30023", "opw30023", 0, "평가손익").strip()) / 100

            # print(self.fcm_code_30023)
            self.tr_event_loop.exit()

            print("=======================================================")
            print(self.deposit_num)
            print(self.deposit_num)
            print(self.fcm_code_30023)
            print(self.futures_code_30023)
            print(self.futures_code_name_30023)
            print(self.sell_buy_gubun_30023)
            print(self.buy_price_30023)
            print(self.buy_date_30023)
            print(self.order_type_30023)
            print(self.order_qty_30023)
            print(self.pl_check_30023)
            print("=======================================================")

            # print(self.sell_buy_gubun_30023)    # 진입이 매수(매도수구분 2)일 때, sell_buy_gubun_30023은 2가 반환


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

    btl.rq_data_opw30023(btl.deposit_num, "계좌비밀번호 4자리", "00", "AP")  # 통신주문은 WKOA Studio에서 "AP"로 입력

    app.exec_()


5. 마치며

사실 위의 해외파생지정청산조회(opw30023) 단독으로는 그렇게 큰 의미를 지니지 못한다. 다음글에서 설명한 해외파생지정청산주문(opw10007)과 결합되어 활용되었을 때 의의가 커질 것이다. 다음 글에서는 지정청산(일괄청산, opw10007)에 대해 알아보자.

반응형