지난 게시글(클릭 시 이동)을 통해 대신증권의 CYBOS PLUS 내 통신 방식은 어떠한 방식으로 이루어지는지에 대해 알아보았다. 다시 한 번 정리하자면, 대신증권의 통신 방식은 크게 요청/응답 방식과 구독/생산 방식으로 구분할 수 있으나, 그 구분은 어디까지나 단발성 데이터 요청인지 아니면 실시간 주가 데이터 수신과 같은 지속성 데이터 요청인지에만 기준을 두면 되고, 근본적으로 데이터를 입력하는 방식은 `SetInputValue()`를 통해서 이루어지며 데이터 입력 후의 데이터 요청은 `Request()`나 `BlockRequest()` 또는 `Subscribe()` 함수를 통해 데이터를 요청하고 `GetHeaderValue()`나 `GetDataValue()` 함수를 통해 데이터를 불러온다. 이제 사례를 들어 어떠한 데이터를 수집하고자 할 때 어떠한 방식으로 데이터를 요청해야 하는지에 대해 살펴보도록 하자.
데이터 입력: SetInputValue(Type, Value)
이 함수는 앞선 게시글에서 살펴보았듯이, 우리가 어떤 데이터를 조회하고자 할 때 그 데이터의 종류와 값을 서버로 전달하는 기능을 수행한다. 예를 들어, 삼성전자(005930) 종목에 대한 데이터를 받고자 할 때에는 아래와 같이 코드를 구현하면 된다는 것이다.
## 데이터를 하나 입력할 때
SetInputValue("종목코드", "005930")
마치 위의 코드 한 줄만으로 삼성전자의 모든 것들을 불러올 수 있을 것만 같지만, 이는 하나의 단어를 입력한 것에 불구하다. 예를 들어 우리가 삼성전자의 일봉 차트 데이터가 궁금하다고 할 때, 우리는 서버에 '삼성전자의 일봉 차트 데이터'라는 데이터를 요청해야 한다. 이 때, 우리는 서버에 "005930(`Value`)이라는 종목코드(`Type`)를 가진 종목의 일봉(`Value`)이라는 차트 데이터(`Type`)"를 입력해야 하는데 이 문장을 코드로 정리하면 아래와 같다.
## 데이터를 두 개 이상 입력할 때
SetInputValue("종목코드", "005930")
SetInputValue("차트데이터", "일봉")
하지만, `SetInputValue()`는 어디까지나 우리가 서버에 요청할 모든 정보들을 사전에 입력해놓는 기능만을 수행한다. 즉, '나는 배가 고프다.'라는 단어를 떠올린 정도에 불과한 것이다. 그렇다면 우리는 '나는 배가 고프다.'라는 말을 입 밖으로 내뱉어야 하는데, 그게 바로 데이터를 요청하는 `Request()`와 `BlockRequest()`, `Subscribe()`이다.
통신 요청: Request()와 BlockRequest(), Subscribe()
위의 세 함수는 '통신 요청'이라는 내용만 봐도 알겠지만, 앞서 `SetInputValue()` 함수를 통해 우리 머릿속에 떠올린 단어들을 입 밖으로 내뱉는 함수다. 또한 '통신 요청'을 통해 알 수 있듯이, 이 함수들은 특정한 결과값들을 반환하는데, 대신증권에서는 친절하게 그 반환값들을 정리해두었다.
시세 관련 데이터 통신 요청 시 반환값 목록: `BlockRequest()`
0: 정상 요청
1: 통신요청 실패
3: 그 외의 내부 오류
주문 관련 데이터 통신 요청 시 반환값 목록: `Request()`, `BlockRequest()`
0: 정상 요청
1: 통신요청 실패
2: 주문확인 창에서 발생한 취소
3: 그 외의 내부 오류
4: 주문요청 제한 개수 초과
5 : 주식종목코드 오류, 거래정지종목 매매, (신규 매수도 주문 시)주문 수량이 없음, 주문단가가 상하한가 범위를 넘어섬
예를 들어 `Request()` 함수를 사용한다고 했을 때에는 그 결과 데이터를 아래의 코드와 같이 `return_value`와 같은 변수를 생성해서 해당 변수를 통해 통신 결과를 관리하는 것이 가능하다.
SetInputValue("종목코드", "005930")
SetInputValue("차트데이터", "일봉")
return_value = Request()
print(return_value)
0
더 나아가 사용자의 실수 등의 요인으로부터 서버와 다른 이용자를 보호하기 위하여 시세 요청과 실시간 요청에 대하여 몇 가지 제한을 두고 있다. 키움증권 역시 Open API를 통해 서버와 통신할 때에는 time.sleep()을 활용하여 TR 요청 간 시간 간격을 확보하고 그 내용을 바탕으로 1시간에 몇 건 이하의 데이터를 요청하도록 설정하여야만 멈춤 없이 데이터를 조회할 수 있었는데, 대신증권의 장점이라 함은 그 시간 간격이 키움증권에 비해 널널하다는 것이다.
기본적으로 `Request()`와 `BlockRequest()` 함수가 해당하는 요청/응답(RQ/RP) 방식의 경우에는 시세 관련 데이터 통신은 15초에 최대 60건으로 초당 4건의 조회를 요청할 수 있으며, 해당 기준을 초과하여 데이터를 요청할 경우에도 대신증권은 첫 요청으로부터 15초가 지날 때까지 내부적으로 이벤트루프(`EventLoop()`)를 걸어두어 프로그램이 기다리도록 하두었다. 또한 요청/응답(RQ/RP) 방식을 통한 주문 관련 데이터 통신 요청 시에는 15초에 최대 20건으로 초당 1건 정도만 조회할 수 있으며, 시간 간격이 너무 짧은 문제로 인해 데이터를 수신할 수 없는 상황에서는 앞 전에 살펴봤던 내용과 동일하게 첫 데이터 통신 시점으로부터 15초가 지날 때까지 해당 요청 함수(`Request()`, `BlockRequest()`)로부터 4라는 값이 반환된다.
데이터 수신: Received()
데이터 수신 시에 발생하는 `Received()` 함수는 '이벤트'로, 실제로 파이썬 내에서는 해당 이벤트가 발생했을 경우 해당 이벤트를 특정 함수에 연결한 후, 연결된 함수 내에서 아래의 데이터 획득 함수를 사용하여 통신 요청의 결과로 수신받은 데이터 값을 사용할 수 있게 된다.
※ 이벤트 처리와 관련하여 대신증권은 키움증권과는 다소 다른 방식으로 처리해야 한다. 이 부분은 추후에 구현할 예정
이 `Received()` 함수는 단순하게 데이터가 수신되었음을 알려주는 하나의 이벤트로 특정 데이터를 전달해줘야 그 결과값을 받을 수 있다거나 하는 건 아니고, 단순하게 '이벤트가 발생했을 때 실행시킬 함수와 연결'만 해주면 된다. 간단하게 데이터가 수신됐음을 알려주는 함수라고 생각하면 된다.
obj.Received().connect(self.Received)
def Received():
"""여기서 데이터 수집 함수 구현"""
pass
데이터 획득: GetHeaderValue(Type), GetDataValue(Type, Index)
데이터 수신이 된 후에는(=`Received()` 이벤트가 발생한 후에는) 서버로부터 전달받은 데이터를 우리가 얻어서(Get) 그 데이터를 또 다른 변수에 저장하여 활용할 수 있다. 예를 들어, 아래와 같이 `now`라는 변수에 해당 데이터값을 저장한 후 추후에 `now` 변수를 활용하여 "005930" 종목의 현재가 데이터를 활용할 수 있게 된다.
데이터를 가져올 때 우리가 전달해줘야 할 `Type`이나 `Index` 등과 같은 정보는 추후에 관련 데이터를 획득하는 기능들을 구현하면서 자세히 살펴보도록 하자.
SetInputValue("종목코드", "005930")
SetInputValue("차트데이터", "일봉")
Request() ## Received() 이벤트를 발생시킴
obj.Received().connect(self.Received)
def Received():
now = GetHeaderValue("현재가")
print(now)