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

(키움증권 해외선물 OpenAPI-W) 주문가능수량 조회 (opw30011, 주문가능수량요청)

봄이오네 2023. 1. 23. 08:04
반응형
1. 들어가며
2. 사전설명
1) 주문가능수량 및 청산가능수량 개념 및 화면번호
2) WKOA Studio 설명
3. 코드설명
4. 전체 코드
5. 마치며

 

1. 들어가며

지난 글에서는 opw30009(예탁금및증거금요청) TR요청을 통해 주문가능금액을 조회해 보았다.
이번 글에서 설명하겠지만, 국내주식과 달리 해외선물의 주문가능금액은 주문을 할 때 쓰이지는 않는다.
왜냐하면, 해외선물에서는 주문가능수량을 구할 수 있기 때문이다. 국내주식은 주문가능금액을 구할 필요가 있다. 왜냐하면 (주문가능금액 ÷ 현재가)를 통해 주문가능수량을 구한다.

이번 글에서는 opw30011(주문가능수량요청)을 통해 주문가능수량, 청산가능수량, 주문가능금액을 조회해 보도록 하자.
여기서 구한 주문/청산가능수량은 향후 주문(sendorder)에서 쓰이는 수량인 만큼 차분히 읽어보시길 권한다.
아래 예시는 S&P 500 (ESH23)으로 설명할 예정이다.


2. 사전설명

1) 주문가능수량 및 청산가능수량 개념 및 화면번호

① 주문가능수량 : long/short 진입을 위해 계산된 수량
② 청산가능수량 : long/short 청산을 위해 계산된 수량

영웅문G에서 화면번호는 4560(주문가능수량요청)이다.

더보기
그림1. 영웅문G에서 확인가능한 E&P 500의 주문가능수량은 2개이다.



※ 주문을 넣을 때 쓰이는 함수의 형태

 

더보기

아래는 키움증권 개발가이드에 나온 주문(send order)에 관한 내용이다.

진입/청산 시 적용되는 함수는 동일하다. 주문/청산가능수량이 6번째 nQty에 들어간다.

def sendorder_func(self, rqname, scr, acc, ordertype, futures_code, qty, futures_price, s_stop, hogagb, orgno):
    self.kiwoom.dynamicCall(
        "SendOrder(Qstring, Qstring, Qstring, int, Qstring, int, Qstring, Qstring, Qstring, Qstring)",
        [rqname, scr, acc, ordertype, futures_code, qty, futures_price, s_stop, hogagb, orgno])

"""
    1) BSTR sRQName,
    2) BSTR sScreenNo,
    3) BSTR sAccNo,
    4) LONG nOrderType, *주문유형(1: 신규매도, 2: 신규매수, 3: 매도취소, 4: 매수취소, 5: 매도정정, 6: 매수정정)
    5) BSTR sCode,
    6) LONG nQty,
    7) BSTR sPrice,
    8) BSTR sStop,      * stop 단가
    9) BSTR sHogaGb,    * 거래구분(1: 시장가, 2: 지정가, 3: STOP, 4: STOP LIMIT)
    10) BSTR sOrgOrderNo)
        *(예시)  ## openApi.SendOrder("RQ_1", "1000", "5077000072", 1, "6AZ20", / 1, "0.7900", "0",        "2",  "");       // 지정가 매도
                   openApi.SendOrder("RQ_1", "1000", "5077000072", 1, "6AZ20", / 1, "0",      "0",        "1",  "");       // 시장가 매도
                ## openApi.SendOrder("RQ_1", "1000", "5077000072", 1, "6AZ20", / 1, "0",       "0.7900", "3", "");       // STOP 매도
                ## openApi.SendOrder("RQ_1", "1000", "5077000072", 1, "6AZ20", / 1, "0.7850", "0.8000",   "4", "");       // STOP LIMIT 매도
                ## openApi.SendOrder("RQ_1", "1000", "5077000072", 5, "6AZ20", / 1, "0.7850", "0",       "2", "500060"); // 정정 매도
                ## openApi.SendOrder("RQ_1", "1000", "5077000072", 3, "6AZ20", / 1, "0",     "0",       "2", "500060"); // 취소 매도
"""

 

2) WKOA Studio 설명

opw30011(주문가능수량조회)를 통해 얻는 주요 데이터는 3가지이다. (주문/청산가능수량, 주문가능금액)
입력해야 할 데이터는 7가지인데, 아래에서 설명한다.

 

더보기
그림2. WKOA Studio에서 입력/수신받는 데이터 현황

3. 코드설명

로그인 등 중복된 설명은 최소화할 예정이다.

그림3-1. 로그인, 계좌번호 등 현황


1줄~5줄 : 동시성 처리 등 필요모듈을 임포트한다.
9줄 : 키움증권 레지스트리를 self.kowoom에 넣는다.
12줄~19줄 : 로그인/이벤트를 연결하고, 로그인 화면을 띄운다.
21줄 : 사용자 본인의 계좌 10자리를 입력한다.
24줄~29줄 : 로그인 관련 함수

그림3-2. GetCommData를 통해 데이터를 받아온 내용


32줄~43줄 : SetInput 함수 및 CommRqData 함수를 이용하여 키움증권 OpenAPI-W에 데이터를 입력/요청한다.
37줄 : 매도수구분 관련하여 매도는 1번, 매수는 2번이다. → 매수로 진입할 것이므로 68줄에서는 "2"로 입력함
38줄 : 해외주문유형으로, 1번(시장가), 2번(지정가), 3번(STOP), 4번(Stop Limit)
39줄 : 시장가로 진입한 경우 빈칸으로 데이터를 키움증권에 입력한다. ( " " )

47줄~60줄 : 주문가능수량, 청산가능수량, 주문가능금액, 통화코드 4가지 데이터를 받아와서 출력한다.
68줄 : 32줄의 함수(rq_data_opw30011)을 실행한다. (시장가로 주문하므로 39줄의 주문표시가격은 빈칸으로 둔다)


4. 전체 코드

전체 코드는 아래와 같다.
21줄에는 사용자 본인의 계좌 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 요청하는 함수 모음 (키움 OPENApi-w에서 제공) ##########
    def rq_data_opw30011(self, deposit_num1, password_2, password_enter3, futures_code4, sell_buy_gubunm5, order_type6, order_price7):  # 주문가능수량
        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)", "종목코드", futures_code4)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "매도수구분", sell_buy_gubunm5)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "해외주문유형", order_type6)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "주문표시가격", order_price7)  # "시장가"로 주문할 경우, 함수 실행시 빈칸("")으로 요청
        self.kiwoom.dynamicCall("CommRqData(QString, QString, QString,QString)", "opw_30011", "opw30011", "", "3011")
        time.sleep(0.5)
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    def trdata_get(self, scrno, rqname, trcode, recordname, prenext):
        if rqname == "opw_30011":  # 주문가능수량c
            orderable_qty = int(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30012", "opw30012", 0, "주문가능수량").strip())
            liquidable_qty = int(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30012", "opw30012", 0, "청산가능수량").strip())
            orderable_money_30011 = float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30012", "opw30012", 0, "주문가능금액").strip())
            currency_code = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opw_30012", "opw30012", 0, "통화코드").strip()

            self.orderable_qty = orderable_qty
            self.liquidable_qty = liquidable_qty
            self.orderable_money_30011 = orderable_money_30011 / 100
            self.currency_code = currency_code

            print(self.orderable_qty)
            print(self.liquidable_qty)
            print(self.orderable_money_30011)
            print(self.currency_code)

            self.tr_event_loop.exit()

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

    btl.rq_data_opw30011(btl.deposit_num, "0000", "00", "ESH23", "2", "1", "")

    app.exec_()
    
    
# (expected result)
# 2
# 0
# 29552.25
# USD

 


5. 마치며

주문/청산가능수량은 진입과 청산을 위한 매매를 할 때 상당히 중요한 개념이다.
필자도 실투를 할 때 가끔씩 실수를 한다.
long으로 2개(매수)로 진입해 수익이 많이 났는데,
청산을 하고 싶어 수기로 "매도" 창에서 "주문가능수량"을 눌렀는데 3이 떴고, 매도를 눌렀다.

나중에 알게 되었지만, 이렇게 주문이 된 경우 long 2개는 청산되지만, short 1개가 추가로 진입되어버리는 경우이다.
필자도 수익났다고 좋아했는데, 그 다음날 확인해 보니 손실이 1,000달러 이상이 발생해서, 눈물의 손절을 했다.

진입은 주문가능수량으로, 청산은 "청산가능수량"으로 투자를 해야, 추가 진입을 막을 수 있을 것이다.

 

반응형