1. 국내주식/1-2. 키움 OpenAPI (사용)

(주식 자동 매매) 키움증권 OpenAPI - SendOrder함수를 이용하여 매수/매도하기

봄이오네 2022. 10. 1. 10:11
반응형

1. 들어가며

이번 시간에는 키움증권 OpenAPI를 활용하여,
주식의 매수/매도하는 방법을 알아보고자 한다.

"매수는 기술이지만, 매도는 예술이다" 라는 주식 명언이 있듯이
주식 자동매매로 수익을 내려면
매수/매도하는 방법을 파이썬에서 구현할 수 있어야 한다.

급하게 코드를 작성해서 매수/매도를 위해
오류없이 코드 작성하는게 생각보다 시간이 많이 걸렸고,
장 종료시간(18:00)은 다가오고...
캡쳐를 제대로 찍지 못한 점에 대해,
미리 양해의 말씀을 드린다.

이 글에서는 매매를 위한 함수인 SendOrder을 통해
주식의 매수/매도의 과정을 설명하고
카카오(035720) 매수와 신일전자(002700) 매수/매도의 과정을 알아볼 것이다.


2. 사전 준비

1) SendOrder 함수 검색 및 실행방법 찾기

KOA StudioSA에서는 아래와 같이 SendOrder 함수를 설명하고 있다.
KOA StudioSA에 접속하여 "개발가이트" 탭 선택
② 주문과 잔고처리 > 관련함수 > SendOrder 함수를 찾을 수 있다.
SendOrder 함수는 9개의 인자를 준비해야 한다. -_-;;;
④ 시장가 주문시 주문가격은 0으로 설명한다.
⑤ 모의계좌에서는 지정가(00), 시장가(03)로만 실행할 수 있다.

< 그림1. KOA Studio에서 찾은 SendOrder 함수 >

2) SendOrder 함수의 9가지 인자(변수) 설명
SendOrder 함수를 실행하기 위해서는 9가지 변수를 넣어준다.

  • 사용자 구분명(sRQName) : 사용자가 함수 내 인수를 구분하게 위해 임의로 짓는 변수
  • 화면번호(sScreenNo) : 사용자가 임의로 선언해도 되는 변수(4자리)
  • 계좌번호 10자리(sAccNo) : 사용자의 모의투자 계좌 10자리
  • 주문유형(nOrderType) : 1.신규매수, 2.신규매도, 3.매수취소, 4.매도취소, 5.매수정정
  • 종목코드(sCode) : 종목코드(6자리)
  • 주문수량(nQty) : 주문수량
  • 주문가격(nPrice) : 주문가격
  • 거래구분(혹은 호가구분)(sHogaGb) : 00지정가, 03(시장가), 05(조건부지정가) 등
  • 원주문번호(sOrgOrderNo) : 원주문 번호이며, 신규주문에는 공백 입력(" "), 정정/취소시 입력

3) SendOrder 함수의 활용
그림1의 9가지 변수 중 3가지가 매수/매도/취소 등의 관건이다.
핵심은 4번째 변수인 "주문유형"이다.

SendOrder 함수를 일단 선언해 두고,
그림1의 "주문유형(nOrderType)" 번호에 따라 필요할 때마다,
신규매수, 신규매도, 매수취소 등으로 활용할 수 있다.


3. 코드 구현

그간 필자가 SendOrder 함수를 너무 어렵게 생각했나보다. -_-;;
코드를 간략하게 작성해도 매수 주문이 잘 들어가서, 카카오 10주를 매수했다.

< 그림2. 키움서버에 접속하여 카카오(035720) 1주를 매수하는 코드 >


위 그림1에서 SendOrder 함수는 아래 3가지와 관련있다.
17줄~18줄 : 계좌번호 전역변수 선언한다.
37줄에서 모의계좌 10자리를 넣어주어야 한다.

27줄~30줄 : 키움에서 안내한 대로 9가지 변수가 들어갈 수 있도록,
SendOrder 함수 만들어 준다. (37줄에서 활용하기 위함)

37줄 : 종목코드, 주문물량 등을 입력하여 SendOrder 함수 실행
①~② 사용자 구분명, 화면번호는 임의로 이름을 정했고
③ 계좌번호는 17줄~18줄에서 전역변수로 선언된 str형태인 deposit_num을 넣어준다.
- deposit_num은 17~18줄에서 계좌번호가 쌍 따옴표(" ")로 묶여서
이미 문자형(QString)으로 선언되었다.
- 37줄에서 문자형으로 선언된 deposit_num에 양 따옴표(" ")
④ 주문유형은 신규매수이므로 int형태인 숫자 1을 넣어준다.
⑤ 종목코드는 문자형(QString)으로 카카오의 035720을 양 따옴표(" ")로 묶어준다.
⑥ 주문수량은 키움증권 OpenAPI에서 안내한 인수형(int)인 숫자 10을 넣어준다.
⑦ 그림1에서 설명하였듯이 시장가 주문은 0을 넣어준다.
⑧ 거래구분은 시장가 이므로, 문자형(QString)인 "03" 넣어준다.
⑨ 원주문번호는 신규주문이므로 공백(" ")을 입력한다

※ 9개 변수 중 첫번째(sRQName) ~ 두번째 변수(sScreenNo)는
   함수를 만들 때(27줄)는 키움 KOA Studio에서 안내한대로 함수를 작성하되,
   함수를 실행할 때(37줄)는, 사용자 마음대로 이름을 지어도 된다.
   필자는 첫번째 변수는 "신규매수1", 두번째 변수는 "4949"로 화면번호를 임의로 지었다.

 

※ 로그인 방법은 아래에서 설명한다.

https://springcoming.tistory.com/17?category=1048804 

 

(주식 자동 매매) 키움증권 OpenAPI 로그인

1. 들어가며 키움증권에서 제공하는 OpenAPI를 통해 키움서버에 접근하기 위해서는, 파이썬을 통해 OpenAPI에 접속하여 로그인할 수 있어야 한다. 당초 로그인 후 예수금 받기까지 진행하려고 했으

springcoming.tistory.com


4. 매수/매도 화면

1) 카카오(035720) 10주 매수 결과

그림3-1에서는 계좌에 카카오 주식이 없다.

< 그림3-1. 계좌에 카카오 종목이 없음 >


그림3-2는 위의 함수를 실행하여 카카오 10주를 매수한 후의 화면이다.

< 그림3-2. 카카오 주식 10주 매수 >

 

< 그림3-3. 카카오 10주를 매수한 결과가 계좌에 있는 화면 >

 

2) 신일전자(002700) 10주 매수/매도하는 화면

그림2의 37줄의 함수에서 종목코드만 바꾸어주면 10주를 매수한다.

그림4-1은 신일전자(002700)을 매수 전 계좌현황이다.

< 그림4-1. 계좌에 신일전자가 없다 >



그림2를 신일전자(002700) 10주 매수 명령어로 바꾸어 실행하면,
그림4-2처럼 체결될 때 "체결내역통보"가 된다.

< 그림4-2. 신일전자(002700) 10주를 매수하는 코드 >
< 그림4-3. 신일전자 10주 매수한 체결내역통보 결과 >


신일전자(002700) 10주가 체결되고 나서의 계좌 잔고이다.
금일 매수 10주가 눈에 띈다.

< 그림4-4. 금일 매수한 신일전자 10주가 계좌에 있는 화면 >


이제 매수한 신일전자 10주를 매도해 보자.
매수코드에서 4번째 변수인 주문유형(nOrderType)을 "2.신규매도"로 바꾸기 위해,
4번째 인수에 "2"를 넣었다.
※ 첫번째(사용자 구분명, sRQName)과 화면번호(sScreenNo)는, 매수코드와 똑같이 해도 상관없다.
(여기서는 매도코드를 강조하기 위해, 첫번째~두번째 인수를 바꾼 것이다)

< 그림4-5. 신일전자 10주 매도하는 코드 >


매도가 제대로 키움서버에 요청되었다면, 미체결 주문이 올라가 있다.
(현재가로 팔려고 했는데, 다른 주문들이 먼저 처리되어야 해서,
필자의 주문이 대기하고 있는 상태이다.)

< 그림4-6. 신일전자 10주 매도주문이 체결되기 위해 대기 중인 화면 >


신일전자 10주 매도를 걸었는데. 1주가 먼저 체결되고,
미체결 주문이 9개 남았다.

< 그림4-7. 신일전자 1주가 먼저 매도되고, 미체결 9주 화면 >


당초 미체결 9주가 다 팔린 모습이다.
미체결 주문내역에는 "미체결 내역"이 없고,
체결내역통보에 10주가 다 팔린 걸 확인할 수 있다.

※ 한가지 의문인건, 분명 현재가(1,710원)에 매도 주문을 넣었는데,
모의계좌라서 그런가?
바로 체결이 안된 부분이 의문이긴 하다.

< 그림4-8. 신일전자 10주 매도 체결내역통보 결과 >


모의계좌에서 매도한 신일전자(002700) 없는 화면이다.

< 그림4-9. 계좌에 신일전자가 매도되고 난 후의 화면 >


 

5. 전체 코드

import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

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

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

        self.kiwoom.dynamicCall("CommConnect()")
        self.login_event_loop = QEventLoop()
        self.login_event_loop.exec_()

        global deposit_num
        deposit_num = "계좌번호 10자리 넣는 곳"

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

    def sendorder_trading(self, rqname, scrno, accno, ordertype, scode, qty, price, hogagb, orgorderno):
        print("키움서버에 매매 요청하였습니다.")
        self.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
                                [rqname, scrno, accno, ordertype, scode, qty, price, hogagb, orgorderno])


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

    btl.sendorder_trading("신규매수1", "4949", deposit_num, 1, "035720", 10, 0, "03", "")  # 카카오

    app.exec_()

6. 마치며

매매를 위한 함수인 SendOrder 함수를 통해
카카오(035720) 매수와 신일전자(002700) 매수/매도의 과정을 알아보았다.

물론 if 조건문이나 for반복문을 쓰면 더 복잡해 지겠지만.
SendOrder 함수의 작동 원리가 생각보다 간단하다는 감각만 있으면 될거 같다.

4번째 변수인 주문유형(nOrderType)을 1번(매수), 2번(매도)로 바꾸어주면
제대로 작동한다.

미체결 내역을 취소하는 것까지 생각을 했으나,
장이 종료(18:00)되어, "취소 주문"이 자동으로 삭제되어 버렸다. ㅠㅠ

다음에는 "미체결 내역"에 대해 삭제하는 코드를 알아보고자 한다.
일부 주식만 매수 체결될 경우 매수주문 취소하는 방법과
매수/매도 주문 내역을 전체 취소하는 방법을 검토 중인데,

일부 주식만 매수체결된 경우, 그 경우가 쉽게 올 것 같지 않다.ㅠㅠ
주식이 워낙 순식간에 체결되기 때문에
그 상황이 먼저 발생해야 하고, 오류없이 코드를 작성하고,
일부체결 안되는 내용을 취소하려는데, 쉽지 않은 여건임을 감안하면,
어쩔 수 없이, 매수/매도 주문 내역 전체를 취소하는 방법을 검토할 것이다.

반응형