대신증권 크레온 API로 매수/매도 주문 넣고 확인하기

2021-06-06 • quantpython, creon, 크레온, realtime, 실시간, 주문, 매수, 매도 • 3 min read

이번 포스트에서는 대신증권 크레온 API로 주식 주문을 내고 실시간으로 체결을 확인하고 주문 내역을 조회하는 방법을 다룹니다.

매수/매도 주문 내기

먼저 매수 또는 매도 주문을 내는 방법을 알아보겠습니다. 주문은 크레온 API의 CpTrade.CpTd0311 모듈을 사용하면 됩니다.

다음과 같이 order() 함수를 선언하고, action, code, amount를 매수/매도 구분, 주문할 종목 코드, 주식 수로 각각 입력 받도록 했습니다. 물론 더 다양한 입력을 받을 수 있게 이 함수를 수정할 수 있습니다.

import win32com.client


class Creon:
    def __init__(self):
        self.orderevent_handler = None

    def order(self, action, code, amount):
        if not code.startswith('A'):
            code = 'A' + code
        account_no, account_gflags = self.init_trade()
        self.obj_CpTrade_CpTd0311.SetInputValue(0, action)  # 1: 매도, 2: 매수
        self.obj_CpTrade_CpTd0311.SetInputValue(1, account_no)  # 계좌번호
        self.obj_CpTrade_CpTd0311.SetInputValue(2, account_gflags[0])  # 상품구분
        self.obj_CpTrade_CpTd0311.SetInputValue(3, code)  # 종목코드
        self.obj_CpTrade_CpTd0311.SetInputValue(4, amount)  # 매수수량
        self.obj_CpTrade_CpTd0311.SetInputValue(8, '03')  # 시장가
        result = self.obj_CpTrade_CpTd0311.BlockRequest()
        if result != 0:
            print('order request failed.', file=sys.stderr)
        status = self.obj_CpTrade_CpTd0311.GetDibStatus()
        msg = self.obj_CpTrade_CpTd0311.GetDibMsg1()
        if status != 0:
            print('order failed. {}'.format(msg), file=sys.stderr)

    def buy(self, code, amount):
        return self.order('2', code, amount)

    def sell(self, code, amount):
        return self.order('1', code, amount)

매수할 때는 buy() 함수를 호출해서 action2로 지정하고, 매도할 때는 sell() 함수를 호출해서 action1로 지정하도록 했습니다.

여기서는 시장가로 매수/매도를 하도록 했는데 지정가, 최유리지정가, 최우선지정가 등으로 변경할 수 있습니다. 이 부분은 CpTrade.CpTd0311 문서에서 SetInputValue8 - (string) 주문호가구분코드 항목을 함고하시기 바랍니다.

이렇게 매수 또는 매도 주문을 넣고 체결 되었을 때 다음과 같이 이벤트를 받을 수 있습니다.

class Creon:
    def subscribe_orderevent(self, cb):
        obj = win32com.client.Dispatch('Dscbo1.CpConclusion')
        handler = win32com.client.WithEvents(obj, OrderEventHandler)
        handler.set_attrs(obj, cb)
        self.orderevent_handler = obj
        obj.Subscribe()

    def unsubscribe_orderevent(self):
        if self.orderevent_handler is not None:
            self.orderevent_handler.Unsubscribe()
            self.orderevent_handler = None


class OrderEventHandler(EventHandler):
    def OnReceived(self):
        item = {
            '계좌명': self.obj.GetHeaderValue(1),
            'name': self.obj.GetHeaderValue(2),
            '체결수량': self.obj.GetHeaderValue(3),
            '체결가격': self.obj.GetHeaderValue(4),
            '주문번호': self.obj.GetHeaderValue(5),
            '원주문번호': self.obj.GetHeaderValue(6),
            '계좌번호': self.obj.GetHeaderValue(7),
            '상품관리구분코드': self.obj.GetHeaderValue(8),
            '종목코드': self.obj.GetHeaderValue(9),
            '매매구분코드': self.obj.GetHeaderValue(12),
            '체결구분코드': self.obj.GetHeaderValue(14),
            '체결구분코드': self.obj.GetHeaderValue(14),
            '체결구분코드': self.obj.GetHeaderValue(14),
            '현금신용대용구분코드': self.obj.GetHeaderValue(17),
        }
        self.cb(item)

subscribe_orderevent() 함수를 호출해서 체결 이벤트를 실시간으로 받아올 수 있습니다. 매수/매도 주문이 체결되면 지정된 콜백 함수 cb로 체결 정보를 전송합니다. 이를 위해서 OrderEventHandler 클래스를 선언했고, 이 클래스의 OnReceived() 함수에서 체결 정보를 cb에 전송합니다.

다음과 같이 지금까지 주문한 내역을 확인할 수도 있습니다.

class Creon:
    def __init__(self, account_no=''):
            self.obj_CpTrade_CpTd5341 = win32com.client.Dispatch('CpTrade.CpTd5341')

    def get_trade_history(self):
        account_no, account_gflags = self.init_trade()
        self.obj_CpTrade_CpTd5341.SetInputValue(0, account_no)
        self.obj_CpTrade_CpTd5341.SetInputValue(1, account_gflags[0])  # 상품구분

        _fields = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 22, 24]
        _keys = [
            '상품관리구분코드', '주문번호', '원주문번호', '종목코드', '종목이름', 
            '주문내용', '주문호가구분코드내용', '주문수량', '주문단가', '총체결수량', 
            '체결수량', '체결단가', '확인수량', '정정취소구분내용 ', '거부사유내용', 
            '채권매수일', '거래세과세구분내용', '현금신용대용구분내용', '주문입력매체코드내용', 
            '정정취소가능수량', '매매구분',
        ]

        result = self.request(self.obj_CpTrade_CpTd5341, dict(zip(_fields, _keys)), cntidx=6)
        return result

주문 이후에 get_trade_history() 함수를 호출하면 다음과 같이 매수/매도 주문에 대한 정보들을 받아올 수 있습니다. 여기서는 삼성전자를 10주씩 매수->매도->매수한 다음 get_trade_history() 함수를 호출한 결과 입니다.

{'data': [{'상품관리구분코드': '10', '주문번호': 6095, '원주문번호': 0, '종목코드': 'A005930', '종목이름': '삼성전자', '주문내용': '정규장현금매수', '주문호가구분코드내용': '시장가', '주문수량': 10, '주문단가': 0, '총체결수량': 10, '체결수량': 10, '체결단가': 82430, '확인수량': 0, '정정취소구분내용 ': '정상주문', '거부사유내용': '', '채권매수일': '', '거래세과세구분내용': '', '현금신용대용구분내용': '', '주문입력매체코드내용': 'C8', '정정취소가능수량': 0, '매매구분': '매수'}, {'상품관리구분코드': '10', '주문번호': 6099, '원주문번호': 0, '종목코드': 'A005930', '종목이름': '삼성전자', '주문내용': '정규장현금매도', '주문호가구분코드내용': '시장가', '주문수량': 10, '주문단가': 0, '총체결수량': 10, '체결수량': 10, '체결단가': 82500, '확인수량': 0, '정정취소구분내용 ': '정상주문', '거부사유내용': '', '채권매수일': '', '거래세과세구분내용': '', '현금신용대용구분내용': '', '주문입력매체코드내용': 'C8', '정정취소가능수량': 0, '매매구분': '매도'}, {'상품관리구분코드': '10', '주문번호': 6121, '원주문번호': 0, '종목코드': 'A005930', '종목이름': '삼성전자', '주문내용': '정규장현금매수', '주문호가구분코드내용': '시장가', '주문수량': 10, '주문단가': 0, '총체결수량': 10, '체결수량': 10, '체결단가': 82500, '확인수량': 0, '정정취소구분내용 ': '정상주문', '거부사유내용': '', '채권매수일': '', '거래세과세구분내용': '', '현금신용대용구분내용': '', '주문입력매체코드내용': 'C8', '정정취소가능수량': 0, '매매구분': '매수'}]}

퀀티랩 systrader 프로젝트에서 전반적인 시스템 트레이딩 코드를 공개하고 있습니다. 앞으로 시스템 트레이딩에 필요한 주요 기능들을 퀀티랩 Github systrader 리포지토리에서 오픈소스로 개발해 나갈 예정입니다.

SysTrader의 주요 TODO 태스크는 다음과 같습니다.

  • 주요 시스템 트레이딩 기능 구현
  • 딥러닝 모델 기반 자동 매매
  • PyQt5 기반의 GUI 개발
  • 매매 현황 리포팅

관심 있으신 모든 분들께서 이 프로젝트에 참여해 주시면 감사하겠습니다. SysTrader는 모든 개미들을 위한 견고한 시스템 트레이딩 솔루션이 되는 것을 목표로 합니다.