Cybos #3_주식정보가져오기(Request)

*해당 시리즈는 대신증권 cybos api를 활용하여 주식 프로그램을 제작하는 글입니다.

Request

Cybos 에서 주식 정보를 가져오는 두 번째는 Request()를 통해 값을 불러오는 방법이다. 순서는 다음과 같다.

  1. 디스패치를 통해 객체를 생성한다.

    objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")

  2. 객체에 가져오고 싶은 정보를 입력한다.

    objStockMst.SetInputValue(0, code)
    (여기서 code는 종목코드를 담은 변수이다.)

  3. 입력이 완료된 객체를 리퀘스트(혹은 블록리퀘스트)를 한다.

    objStockMst.BlockRequest()
    (이 과정이 실제 데이터를 요청하는 과정이다)

  4. 객체로부터 원하는 정보들을 출력한다.

    item['현재가'] = objStockMst.GetHeaderValue(11)
    ('GetHeaderValue(11)'은 '종가'를 의미한다.)

왜 객체에 input을 하고 request를 하고 다시 get을 하는지 헷갈릴 수 있다. 여기서 input은 내가 찾고자하는 종목코드를 객체에 입력해주는 것이고, 입력한 객체를 실제 Cybos api로 요청을 보내는 것이 request이다. 그리고 마지막으로 그렇게 요청한 객체로부터 우리가 원하는 정보(input으로 넣은 종목에 대한)를 얻어내는 것이 바로 get인 것이다.

요약하자면 instance -> input -> request -> get 순이다.

동작원리에 따라 Request()를 통해 값을 가져올 수 없는 instance들도 있다. 이에 대해서는 도움말에 자세히 기재되어 있으므로 확인해보고 사용하자. 보통 하단에 사용가능 여부가 기재되어 있다.

BlockRequest()와 Request()의 차이는?

위에서는 BlockRequest()를 사용하였다. 그렇다면 BlockRequest()와 Request()의 차이는 무엇일까?

이 부분에 대해서 대신증권 자료실에 예제가 올라와 있다. 한번 확인해보자. 우선 예제 코드의 맨 밑을 보자.

예제 코드

import pythoncom
from PyQt5.QtWidgets import *
import win32com.client
 
import win32event
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
 
StopEvent = win32event.createEvent(None, 0, 0, None)
 
class CpEvent:
    def set_params(self, client, name, caller):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.caller = caller  # callback 을 위해 보관
 
    def OnReceived(self):
        # 실시간 처리 - 현재가 주문 체결
        if self.name == 'stockmst':
            print('recieved')
            win32event.SetEvent(StopEvent)
            return
 
 
class CpCurReply:
    def __init__(self, objEvent):
        self.name = "stockmst"
        self.obj = objEvent
 
    def Subscribe(self):
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, None)
 
 
def MessagePump(timeout):
    waitables = [StopEvent]
    while 1:
        rc = win32event.MsgWaitForMultipleObjects(
            waitables,
            0,  # Wait for all = false, so it waits for anyone
            timeout, #(or win32event.INFINITE)
            win32event.QS_ALLEVENTS)  # Accepts all input
 
        if rc == win32event.WAIT_OBJECT_0:
            # Our first event listed, the StopEvent, was triggered, so we must exit
            print('stop event')
            break
 
        elif rc == win32event.WAIT_OBJECT_0 + len(waitables):
            # A windows message is waiting - take care of it. (Don't ask me
            # why a WAIT_OBJECT_MSG isn't defined < WAIT_OBJECT_0...!).
            # This message-serving MUST be done for COM, DDE, and other
            # Windowsy things to work properly!
            print('pump')
            if pythoncom.PumpWaitingMessages():
                break  # we received a wm_quit message
        elif rc == win32event.WAIT_TIMEOUT:
            print('timeout')
            return
            pass
        else:
            print('exception')
            raise RuntimeError("unexpected win32wait return value")
 
 
code = 'A005930'
 
##############################################################
#1. BlockRequest
print('#####################################')
objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
objStockMst.SetInputValue(0, code)
objStockMst.BlockRequest()
print('BlockRequest 로 수신 받은 데이터')
item = {}
item['종목명']= g_objCodeMgr.CodeToName(code)
item['현재가'] = objStockMst.GetHeaderValue(11)  # 종가
item['대비'] =  objStockMst.GetHeaderValue(12)  # 전일대비
print(item)
 
print('')
##############################################################
# 2. Request ==> 메시지 펌프 ==>  OnReceived 이벤트 수신
print('#####################################')
objReply = CpCurReply(objStockMst)
objReply.Subscribe()
 
code = 'A005930'
objStockMst.SetInputValue(0, code)
objStockMst.Request()
MessagePump(10000)
item = {}
item['종목명']= g_objCodeMgr.CodeToName(code)
item['현재가'] = objStockMst.GetHeaderValue(11)  # 종가
item['대비'] =  objStockMst.GetHeaderValue(12)  # 전일대비
print(item)

혼란스러울테니 블록리퀘스트의 경우만 잘라서 보자. 주석으로 설명을 달아놓겠다.
#1. BlockRequest
objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
# objStockMst 라는 인스턴스를 만들었다.
objStockMst.SetInputValue(0, code)
# 인스턴스에 SetInputValue을 통하여 code(종목코드를 넣은 변수)를 입력한다. 
objStockMst.BlockRequest()
# BlockRequest를 통해 api로 요청을 보낸다.
item = {}
# get한 데이터를 딕셔너리로 넣기 위해 빈 딕셔너리를 생성한다.
item['종목명']= g_objCodeMgr.CodeToName(code)
# CodeToName으로 종목명을 가져와 딕셔너리에 넣는다.
item['현재가'] = objStockMst.GetHeaderValue(11)  # 종가
# GetHeaderValue(11)로 종가를 가져와 딕셔너리에 넣는다.
item['대비'] =  objStockMst.GetHeaderValue(12)  # 전일대비
# GetHeaderValue(12)로 전일대비를 가져와 딕셔너리에 넣는다.

이렇듯 BlockRequest()는 일반적인 데이터를 요청하는 가장 간단한 방식이다. 그렇다면 Request()는 언제 사용하는 걸까? 위에 예제코드로 미루어본다면 실시간 데이터(Subscribe)를 받아 이벤트가 발생하면 그때 데이터를 반환해주는 것으로 추측할 수 있다. 즉, Request 호출 후 Received 이벤트로 수신을 받은 데이터를 반환하는 것이다. Request() 코드도 한 번 살펴보자.

# 2. Request ==> 메시지 펌프 ==>  OnReceived 이벤트 수신
objReply = CpCurReply(objStockMst)
# 위에서 생성한 objStockMst 인스턴스를 CpCurReply 클래스에 인수로 넣는다. 
# CpCurReply클래스는 WithEvents매서드를 통해 CpEvent클래스와 
# objStockMst인스턴스를 묶어주어 이벤트가 발생하면 핸들링을 해준다.
code = 'A005930'
# 종목코드를 code 변수에 할당한다.
objStockMst.SetInputValue(0, code)
# 인스턴스에 SetInputValue을 통하여 code(종목코드를 넣은 변수)를 입력한다. 
objStockMst.Request()
# Request를 통해 api로 요청을 보낸다.
MessagePump(10000)
# MessagePump함수로 이벤트를 발생시킨다.
item = {}
# get한 데이터를 딕셔너리로 넣기 위해 빈 딕셔너리를 생성한다.
item['종목명']= g_objCodeMgr.CodeToName(code)
# CodeToName으로 종목명을 가져와 딕셔너리에 넣는다.
item['현재가'] = objStockMst.GetHeaderValue(11)  # 종가
# GetHeaderValue(11)로 종가를 가져와 딕셔너리에 넣는다.
item['대비'] =  objStockMst.GetHeaderValue(12)  # 전일대비
# GetHeaderValue(12)로 전일대비를 가져와 딕셔너리에 넣는다.

정리해보자면 둘의 차이점은 다음과 같다.

BlockRequest()

  • 함수 리턴과 동시에 요청한 데이터를 수신 받는다. 즉, 동기식 통신이다.
  • 이벤트와 상관없이 수신 받을 수 있으며 가장 간단하게 수신 받을 수 있는 통신이다.

Request()

  • 함수는 먼저 리턴하고 이후 이벤트가 발생하면 데이터를 수신 받는다. 즉, 비동기식 통신이다.
  • 이벤트가 발생해야 수신을 받기 때문에 이벤트와 묶어야 한다.


레퍼런스

cybos 도움말 가이드 http://cybosplus.github.io//
파이썬으로 배우는 알고리즘 트레이딩 https://wikidocs.net/book/110
대신증권API를 이용한 트레이딩 시스템 https://minsuksung-ai.tistory.com/26

좋은 웹페이지 즐겨찾기