[xingAPI][T1857] T1857 e종목검색(조건검색)(DLL버전)

반응형

예스트레이더, 메타트레이더가 아닌 시중에 자동매매 프로그램이라고 판매 중인 증권사 API 기반의 프로그램들은 사용자가 직접 코딩을 할 수 없는 대신 HTS에서 사전에 설정한 조건검색을 기반으로 매수/매도를 하도록 되어 있습니다.

그만큼 API를 이용한 자동매매에는 조건검색 기능이 가장 핵심입니다.

이번 포스팅에서는 이베스트증권의 xingAPI를 활용하여 조건검색을 하는 방법을 알아보겠습니다.

 

 

1. xingAPI의 조건검색 TR T1857 사용전 알아야 할 점 

키움증권 API는 실시간 종목검색을 5개까지 등록가능하고, OnMsg()에 조건명을 파라미터로 넘겨주기 때문에

어떤 조건에 의한 종목인지 처리하기가 쉽습니다. 

하지만 이베스트투자증권 xingAPI는 실시간 종목검색을 2개까지 등록가능하고(작성일 기준)

키움증권 API보다 사용하기 어렵습니다.

아래의 TR 속성만 봐서는 일반 TR과의 차이점을 찾아볼 수 없습니다만,

T1857(e종목검색) TR 정보

T1857은 TR이긴 하지만 다른 TR들과 다르게 부가서비스로 별도 분류됩니다.

그래서 일단 TR 호출시 사용하는 Request()가 아닌 RequestService()로 호출해야 하는데 이베스트에서 제공하는 설명서만 보고 개발하다 보면 헤매게 되는 부분이 2020.11.29. 기준으로 몇 개 있어서 이 부분 먼저 설명드립니다.

 

1.1 RequestService() 내의 WM_USER 값 하드코딩

xingAPI에서는 아래와 같이 9개의 MSG를 사용하는데 원래는 WM_USER 보다 큰 값이면 아무 값이나 BASE 값으로 설정할 수 있습니다. BASE값은 초기 서버 연결 단계에서 Connect()함수 호출 시 파라미터로 넘겨주게 되는데, RequestService() 내부에서는 그 값을 무시하고 윈도 기본 WM_USER값을 사용합니다. 그래서 다른 값으로 MSG BASE를 설정하셨으면 WM_USER로 바꾸셔야 응답을 받으실 수 있는데 이 내용은 2년 전부터 있던 내용인데 언급이 없어서 기본 코드값이 아닌 임의의 값으로 변경하면 응답 메시지를 못 받습니다.

xingAPI MSG ID

1.2 Worker thread에서 RequestService() 호출시 응답 MSG 받지 못함

MFC에서 DLL을 이용하여 멀티스레드 프로그램을 개발하면 스레드를 UI thread 또는 Worker thread 중 적절한 thread 타입을 선택하게 됩니다. RequestService()를 Worker thread에서 호출하면 응답 MSG가 어딘가에 STUCK 상태가 되어 처리할 수 없으므로 UI thread에서 호출되도록 해야 합니다. 그래서 worker thread로 생성된 SendLoop로 패킷을 보내기 직전에 RequestService() 호출이 필요한 부가서비스의 경우에는 별도 처리를 하였습니다.

RequestService 별도 호출

1.3 t1857OutBlock과 t1857OutBlock1 memory chunk 임의 해제

이베스트 TR 스펙에 따르면, 실시간응답이 아닌 OutBlock의 경우에는 사용자가 ReleaseRequestData()를 호출해야만 메모리 해제가 되고, 호출하지 않을 경우 Request ID가 고갈되어 더 이상 Request가 불가능해진다고 명시되어 있습니다.  t1857OutBlock과 t1857OutBlock1은 응답 포맷은 TR 포맷을 따르고 있지만 사실상 실시간 응답이기 때문에 라이브러리에서 ReleaseData Msg를 응답으로 던짐과 동시에 메모리를 해제해 버립니다. 그래서 t1857의 경우에는 OnMsg() 함수 내에서 Memory Copy를 수행하여 OutBlock을 복제하여 사용해야 합니다. 그래서 아래와 같이 TR 응답에 사용하는 OnMsgReceiveData와 부가서비스에서 사용할 OnMsgReceiveDataMemcpy를 별도 분리하였습니다.

일반 TR은 DLL의 Memory Pointer를 Callback Func에 전달하고

2. AlertNum 값에 대한 설명

T1857을 실시간모드로 요청시 Outblock1으로 들어오는 AlertNum 값을 별도 저장해두어야 합니다.

이후 RealDataSearch로 종목명이 들어올 때 AlertNum과 비교해야 어떤 조건에 의한 종목인지(급등/급락 여부)를 파악할 수 있습니다.

부가서비스의 Outblock은 Callback Func에 복제된 메모리 영역을 전달합니다.

T1857을 실시간 모드로 등록할 경우에는 OnMsgReceiveRealDataSearch()로 데이터가 들어오며 실시간패킷은 OnMsg() 반환과 동시에 메모리가 해제됩니다. 프로그램구조상 인터럽트핸들러(OnMsg())에서 여러 로직을 실행하는 것은 바람직하지 않은 구조라서 메모리만 그대로 복제하여 Queue에 담고 별도 처리 thread에서 해당 데이터를 처리하도록 하였습니다. LPARAM이 RECEV_REAL_PACKET이고 RECEV_REAL_PACKET::pszData에 t1857OutBlock1 포인터를 연결해 주면 됩니다.

memorycopy callback
T1857 Cb 실행이 완료되면 위와 같이 할당의 역순으로 메모리를 해제해 줍니다.

3. RealDataSearch로 들어온 t1857Outblock1의 조건식명 알아내기

키움증권 API에는 OnMsg 파라미터로 조건식명을 종목명과 같이 주기 때문에 처리가 편리한데

xingAPI는 RECEV_REAL_PACKET의 szRegKey와 등록당시 회신받은 AlertNum값을 비교해야 합니다.

사양서에는 szRegKey인지 szKeyData인지 명시가 안되어 있고 수신된 종목을 저장만 할 뿐 어떤 조건의 종목인지 알아내는 방법이 없습니다.

KEY 등록여부 조회는 hashMap을 사용하는걸 권장드리고 KEY 조회에 사용할 문자열은 TRIM처리를 해주세요.
현재 들어온 종목이 A라는 KEY 조건식에 이미 있는 종목이면 JobFlag(N:신규,O:이탈)값만 없데이트 하고, 신규 종목이면 메모리를 새로 할당합니다.

 

그래서 실제 확인결과 아래와 같이 szRegKey 또는 szKeyData 둘 중 아무거나 사용해도 되는 것을 확인하였습니다.

szKeyData와 szRegKey는 동일한 값을 가지고 있네요.

 

xingAPI 예제에는 Request(), Receive()의 매우 기본적인 내용만 나와있습니다.

실제 프로그램에서 사용하기 위해서는 별도 자료구조를 정의하여 단순 STRING을 의미있는 VALUE로 변환 저장해 두어야 합니다.

 

4. RemoveService() 호출

T1857을 실시간으로 요청해 사용하다가 프로그램 종료나 기타 이유로 실시간을 멈춰야 하면

RemoveService()를 호출해줘야 합니다. RemoveService() 호출 하지 않아도 LogOut()호출시 자동으로 해지가 된다고 하는데 명시적으로 호출하지 않으면 프로그램 종료시점에 메모리 오류 경고도 뜨고 50번 정도 프로그램 비정상 종료되면(개발단계에서) 당일에는 더 이상 실시간등록요청이 안됩니다. 

일괄 해지시 AlertNum으로 해지요청 하지만, RUNTIME에서 User 관점에서는 조건식명으로 해지하는게 편하므로 조건식명으로 AlertNum을 조회하도록 하였습니다.

5. 기타 사항

*2020.12.02. DLL 메모리 버그 발견

T1857 실시간등록모드 사용시 좀 지나면 DLL 쪽에서 아래와 같은 예외와 함께 프로그램이 터집니다.

 

Assertion Capture

 

이상으로 xingAPI에서 조건검색이 가능한 T1857 사용방법에 대해 알아보았습니다.

반응형