오늘은 xingAPI를 사용한 자동매매 프로그램을 만들기 위한 기본 작업들에 대한 포스팅을 하려 한다. 

이 내용은 이베스트 투자증권의 xingAPI 소개자료에도 동일하게 소개되어 있는 내용이지만, 엑셀 VBA를 사용한 개발 소스로 정리되어 있다. 

 

내가 정리하는 내용은 C# COM 방식의 xingAPI 개발 방법이다. 

 


앞서 포스팅한 내용처럼 이베스트 xingAPI의 경우, HTS 서버 연결과 HTS 로그인이 별개로 동작한다. 

2020/07/08 - [Trading] - [Trading] HTS 로그인 설계, 구현

 

[Trading] HTS 로그인 설계, 구현

이베스트에서 제공하는 XingAPI에 의한 로그인 기능은 키움API에서 제공하는 기능과는 사뭇 다르다. 먼저 키움API의 경우에는 로그인 할때 키움 HTS에서 로그인하는 것과 같은 방식을 제공한다. 로��

brandon-dev.tistory.com

때문에 이베스트 투자증권 보유계좌를 활요하기 위해서는 아래의 단계를 거쳐야 한다. 

 

[서버연결] - [로그인] - [보유계좌선택]

 

위의 단계가 완료된 이후부터 종목을 검색하고, 내가 가진 종목 확인을 하고, 주식 주문을 할 수 있다. 

자동매매 프로그램 시작의 3단계를 간단한 테스트 프로그램으로 구현해보자.

 

xingAPI Test 프로그램 시작 화면

 

테스트 프로그램이므로 화면 구성은 매우 간단하다. 

서버 연결을 할 수 있는 버튼, 아이디/비밀번호를 입력할 수 있는 텍스트 박스와 로그인할 수 있는 버튼, 로그인 후 불러오기가 완료된 보유 계좌 목록 캄보 박스와 사용할 계좌를 선택할 수 있는 버튼.

 

그리고 이후 포스팅하게 될 [단일데이터 조회]용 자산현황 조회 버튼과 [반복데이터 조회]용 계좌잔고 조회 버튼이 있다.

실제로 xingAPI를 이용한 자동매매 프로그램을 설계해보면 위의 기능들외의 데이터 조회 기능을 굳이 사용하지 않아도 충분하다는 것을 깨달을 것이다.

 

아래는 계좌 선택까지의 Full 소스다.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using XA_SESSIONLib;
using XA_DATASETLib;

namespace xingAPI_Test
{
    public partial class xingAPI_Test : Form
    {
        // Global 변수
        bool gIsConnHTS = false;
        string gAccount = string.Empty;

        // xingAPI Session 클래스 선언
        XASessionClass myXASessionClass;

        public xingAPI_Test()
        {
            InitializeComponent();
            // xingAPI Session 객체 생성
            myXASessionClass = new XASessionClass();
        }

        // Form Load될 때 초기화
        private void xingAPI_Test_Load(object sender, EventArgs e)
        {
            Realtime_Log.Items.Add("====== xingAPI Test 프로그램 시작 ======");
            // HTS 로그인 핸들러 등록
            myXASessionClass._IXASessionEvents_Event_Login += new XA_SESSIONLib._IXASessionEvents_LoginEventHandler(myXASessionClass_Login);
        }
        
        // HTS 서버 접속
        private void Btn_Conn_Server_Click(object sender, EventArgs e)
        {
            string serverType = "hts.ebestsec.co.kr";
            gIsConnHTS = myXASessionClass.ConnectServer(serverType, 20001); // 20001은 HTS 서버 고정 Port 값
            if (gIsConnHTS == true)
            {
                Realtime_Log.Items.Add("이베스트 실투자 서버 접속 완료");
            }
            else // 연결 실패했을 경우
            {
                int ErrCode = myXASessionClass.GetLastError();
                var ErrMsg = myXASessionClass.GetErrorMessage(ErrCode);
                string err_log = String.Format("[서버연결] ERR - {0}({1})", ErrMsg, ErrCode);
            }
        }

        // HTS 서버 로그인
        private void Btn_Login_Click(object sender, EventArgs e)
        {
            if (gIsConnHTS == true) // HTS 서버 로그인은 서버 접속이 완료된 이후에만 가능.
            {
                bool reqLogin = ((XA_SESSIONLib.IXASession)myXASessionClass).Login(TB_User_ID.Text, TB_User_Passwd.Text, 
                    "", 0, false); // 세번째 인자는 공인인증비번을 입력하는 칸이지만 보안을 위해 입력 받지 않고 보안 프로그램에서 입력하는 것이 좋다.
                // reqLogin의 결과 값은 로그인 완료 여부가 아닌, 서버에 로그인 요청을 전송 완료 여부이다. 때문에 Login Handler 함수를
                // 별도 추가하여 요청한 로그인 정보가 정상적으로 성공했는지 확인해야한다.
                if (reqLogin == true)
                {
                    Realtime_Log.Items.Add("HTS 로그인 요청 완료");
                }
                else
                {
                    Realtime_Log.Items.Add("HTS 로그인 요청 실패");
                    int ErrCode = myXASessionClass.GetLastError();
                    var ErrMsg = myXASessionClass.GetErrorMessage(ErrCode);
                    string err_log = String.Format("[로그인] ERR - {0}({1})", ErrMsg, ErrCode);
                }
            }
        }

        // HTS 서버 로그인 결과
        void myXASessionClass_Login(string code, string msg)
        {
            if (code == "0000") // code 0000은 성공을 의미
            {
                Realtime_Log.Items.Add("HTS 로그인 완료");

                // 보유 계좌 정보 Loading
                int AccountCount = myXASessionClass.GetAccountListCount();
                for (int i = 0; i < AccountCount; i++)
                {
                    string AccountNo = string.Empty;
                    string AccountName = string.Empty;
                    string AccountDetailName = string.Empty;
                    string AccountNickName = string.Empty;
                    string AccountFinal = string.Empty;

                    AccountNo = myXASessionClass.GetAccountList(i);
                    AccountName = myXASessionClass.GetAccountName(AccountNo);
                    AccountDetailName = myXASessionClass.GetAcctDetailName(AccountNo);
                    AccountNickName = myXASessionClass.GetAcctDetailName(AccountNo);
                    AccountFinal = AccountNo + "(" + AccountName + ")";
                    Realtime_Log.Items.Add(AccountFinal);
                    CB_Account_List.Items.Add(AccountNo);
                }
                Realtime_Log.Items.Add("보유 계좌 정보 Loading 완료");
            }
        }

        // 보유 계좌 선택
        private void Btn_Input_Account_Click(object sender, EventArgs e)
        {
            if (CB_Account_List.Text != "")
            {
                gAccount = CB_Account_List.Text;
                string str_log = String.Format("[보유계좌선택] {0} 선택완료.", gAccount);
                Realtime_Log.Items.Add(str_log);
            }
        }

        // 자산현황 조회 [단일데이터]
        private void Btn_Get_Balance_Click(object sender, EventArgs e)
        {




        }

        // 계좌잔고 조회 [반복데이터]
        private void Btn_Get_Items_Click(object sender, EventArgs e)
        {

        }
    }
}

 

이제 한 단계씩 프로그램을 실행해 보자.

 

STEP 1. 서버 연결

에러 발생 없이 정상적으로 이베스트 HTS 실투자 서버에 접속했다.

 

STEP 2. 로그인

아이디와 비밀번호를 입력하고 로그인을 누르면, HTS 로그인 요청이 완료되었다는 문구와 함께 공인인증서 비밀번호를 입력하는 팝업이 하나 뜬다. 많은 xingAPI 자동매매 프로그램들이 아이디/비밀번호와 함께 공인인증서 비밀번호를 입력하도록 만들어져 있는 걸 보았는데 이렇게 보안 프로그램에서 입력하는 것이(조금 번거로울 수 있겠지만...) 더 안전하겠다.

 

STEP 3. 보유 계좌 선택

로그인이 성공하면 보유계좌 목록에 보유한 계좌들이 저장되게 되고, 그중에서 하나를 골라 선택 버튼을 누르면 비로소 해당 계좌에 대해 자동 매매를 진행할 준비가 완료된다.

 

 

이 이후부터는 xingAPI의 TR들을 적절히 조합하여 자신만의 자동 매매 프로그램을 만들어 적용하면 된다.

다음은 선택한 계좌의 자산현황을 조회하는 기능으로 xingAPI의 [단일데이터 조회] 기능을 구현해본다.

Posted by [ 브랜든 ]
,

xingAPI를 사용해서 실전 코드를 만들기 전에 xingAPI 소개자료의 일부를 짚고 넘어가야겠다.

이베스트 투자증권에서 제공하는 [xingAPI 도움말]은 생각보다 꽤 자세하게(?)는 아니고 개발을 어느 정도 해본 사람에겐 한번 훑어보고 이해가 되는 정도는 되는 수준 같았다. 

 

이베스트 투자증권에서 제공하는 xingAPI 도움말

목차를 보면 생각보다 매우 세분화되어 있고 자세히 설명되어 있을 것만 같다. 

하지만 실제로 열어보면~ 비개발자가 보기엔 쉽게 이해하기 어려운 부분들이 많이 있다. 게다가 [프로그램 개발 가이드]는 엑셀 VBA로 개발하는 과정만이 소개되어 있다. (생각해보니...비개발자는 저것만 보고 엑셀 VBA로 개발하면 되긴 하겠다....굳이 다른 개발언어 쓸 필요없이...)

 


xingAPI 데이터 조회 종류

위의 [xingAPI 도움말] 을 보면 5가지의 데이터 조회 방식이 보인다.

  • 일반 데이터 조회
  • 단일 데이터 조회
  • 반복 데이터 조회 (Occurs)
  • 연속 데이터 조회
  • 실시간 데이터 조회

위에서 실제로 사용할 수 있게끔 설명되어 있는 것은 '일반 데이터 조회'를 제외한 나머지다.

아래는 xingAPI 소개자료의 데이터 조회에 대한 내용이다.

 

일단 데이터 조회

xingAPI에서의 데이터 조회는 미리 정해진 형식의 입력값을 서버로 전송하고 그에 대한 데이터를 응답받는 것을 의미.

 

1) TR (Transaction)

xingAPI에서의 Transaction은 서버로부터 데이터를 주고받는 행위. 

예를 들어, 종목코드를 입력해서 현재가를 받아오는 것, 계좌번호를 입력해서 잔고를 받아오는 것.

 

2) TR Code

TR 요청할 경우 서버 입장에서 어떤 종류의 데이터를 요청하는 TR인지 구별할 수 있도록 TR코드를 같이 입력해 주어야 함. TR코드는 5자리인 경우와 10 자린 경우가 있음.

 

3) TR Layout

TR 요청을 통한 데이터 조회는 미리 정의된 입력값과 결과값들로 구성되어 있는데 이 구성된 형태를 TR Layout이라고 부름. 이베스트 xingAIP의 DevCenter를 이용하여 확인 가능.

Layout은 크게 [InBlock] 입력값 - [OutBlock] 결과값으로 구성되어 있다. 사용자는 InBlock을 만들어 서버에 요청하면, 서버는 OutBlock 형태로 결과값을 돌려줌.

 

4) RES

서버에서 보낸 OutBlock 형태는 당연히 그들만의 Protocol Interface에 의한 데이터 형식으로 이루어져 있음. 이 결과값을 COM 버전의 xingAPI에서 인식할 수 있는 형식으로 변경한 구조를 의미. 

때문에 COM 버전 사용 시 반드시 Local PC에 사용한 TR Code의 RES 파일이 저장되어 있어야 함. (DevCenter에서 다운로드할 수 있음. - 다음 포스팅에서 자세히...)

 

 

나머지 데이터 조회 방식들은 하나씩 실제 구현 소스와 함께 포스팅하는 게 좋을 것 같다.

Posted by [ 브랜든 ]
,

이베스트 XingAPI는 COM과 DLL의 두 가지 방식의 API를 제공한다.

(키움증권의 API도 마찬가지다...)

 

실제 API 사용하여 기능을 테스트해보기 앞서 xingAPI에 대한 이베스트 증권의 소개 자료를 간략히 정리해보자.

 

1. COM과 DLL의 비교

나는 C/C++이 손에 더 익숙하지만, GUI 환경의 프로그램을 개발해보겠다는 욕심에 C#을 선택했으니 COM 방식의 API를 사용하기로 결정했다.

 

COM 방식의 API의 경우 DLL 방식의 API와 완전히 별개의 구성이 아니라 DLL 방식의 API를 사용자들이 쉽게 사용할 수 있도록 변경한 것이므로 실제로는 xingAPI의 DLL 파일들이 필요하다. xingAPI COM 방식의 객체 구성을 보면 알 수 있다.

 

C#에서 위의 DLL 파일들을 사용하는 방법은 간단하다.

 

먼저 이베스트 투자증권에 접속해서 xingAPI 패키지를 PC에 설치해야 한다. 이전 글의 DevCenter 설치와 동일하니 참고한다.

 

2020/08/14 - [Trading] - [Trading] XingAPI DevCenter 사용하기

 

프로젝트 생성 후, 오른쪽의 [솔루션 탐색기] - [참조] 우클릭 - [참조 추가] 실행

COM 추가를 위한 참조 추가

중간 즈음 .dll 파일들의 이름을 갖는 eBest 형식 라이브러리가 보인다. 

DataSet과 Session Lib를 클릭 후 [확인] 버튼을 누르면 [참조] 하단에 두 개의 라이브러리가 추가되고, 이제 객체를 선언해서 사용하기만 하면 된다.

eBest xingAPI COM 방식의 라이브러리 참조

이제 프로젝트의 솔루션 탐색기에서 [참조]에 xsingAPI 라이브러리가 추가되었는지 확인해보자.

참조 추가가된 xingAPI 라이브러리

xingAPI 라이브러리가 정상적으로 추가되었다.

그럼 이제 xingAPI를 이용하여 개발할 준비가 모두 끝난셈이다. 

 

++ xingAPI 라이브러리를 사용하려고 할때 아래와 같은 Error가 발생하는 경우가 있다.

이때는 참조 속성에서 [Interop 형식 포함] 항목을 False로 바꿔주면 된다.


분량조절 실패로...'단일데이터조회'는 다음 포스팅에...

Posted by [ 브랜든 ]
,

학창 시절 C++ 시간에 배웠던 OOP, 그중에서도 Class의 강점에 대해 귀에 박히도록 들었던 말은...

'은닉성'

Encapsulation이라고도 하는 그놈이다. 

은닉성을 가장 쉽게 구현하는 방법은 class의 멤버는 외부에서 직접 접근 가능하지 못하도록 private 또는 protected로 선언하고 class에서 지정한 방법으로만 접근 가능하도록 public 함수를 두는 것이다. 

 

그러면 외부 사용자는 class의 멤버 변수들을 원하는 방식으로 조작할 수 없을 것이고, 그로 인한 장애나 버그도 발생할 일이 없을 것이다. 

 

class에 선언된 멤버 변수에 대해 값을 설정하고, 또 값을 얻어오는 setter와 getter에 대한 함수를 class 내부에 별도로 선언해 놓는 것이 일반적이다. 

class Test
{
	private int value;
    ...
    
    public void setValue(int value)
    {
    	if(value > 0)
            this.value = value;
    }
    
    public int getValue()
    {
    	return this.value;
    }
}

integer 멤버 변수 value에 대해 setter와 getter가 선언된 OOP적인(?) class의 모습이다. 

물론 C#에서도 위 형태의 class 사용이 가능하나...만약 class 내부에 변수가 30개면 어떻게 될까....?

setter와 getter가 각각 30개씩...엄청난 노가다와 가독성(readability)이 현저히 떨어질 것이다. 

 

그래서 C#에서는 속성(Property)라는 아주 좋은 기능을 제공한다.


C# Property

 

같은 기능을 하도록 Property를 사용해 보자. 아래처럼 변경하게 되면, 사용 방법이 바뀌는데 구조체 사용하듯이 사용하면 된다. 

class Test
{
    private int value;
    public int _value
    {
        get { return value; }
        set { this.value = value; }
    }
}

void main()
{
    Test test = new Test();
    
    test.value = 10;
    Console.WriteLine(test.value);
}

훨씬 간결해 지고 간단해졌다. 

 

C# 자동구현 Property

C#에서 제공하는 정말 간결하게 하는 방법이 있다. 

이 Property의 단점을 꼽자면...바로 '접근 제한자'가 public이어야 한다는 것 같다. 

class Test
{
    public int value { get; set; }
}

void main()
{
    Test test = new Test();
    
    test.value = 10;
    Console.WriteLine(test.value);
}

 

MS Visual Studio에서는 Property 작성을 쉽게 할 수 있는 방법을 제공한다.

 

[prop]을 입력하고 [Tab] 키를 두 번 누르면 자동구현 Property 코드가 입력된다.

[propfull]을 입력하고 [Tab] 키를 두 번 누르면 C# Property 코드가 입력된다.

namespace WinFormTest
{
    public partial class Form1 : Form
    {
        // prop + Tab 두번
        public int MyProperty { get; set; }

        // propfull + Tab 두번
        private int myVar;

        public int MyProperty
        {
            get { return myVar; }
            set { myVar = value; }
        }
    }
}

 

입력된 코드의 class 명, 변수명만 원하는 값으로 변경해서 사용하면 끝.

 

Posted by [ 브랜든 ]
,

C#에 대한 공부를 하면서 L-ATS v2.0 개발을 진행하다보니 생각했던대로 되지 않거나 내 설계 방향으로는 구현이 불가능한 부분들이 종종 발생한다. 

 

앞서 포스팅한 '주식종목'에 대한 클래스(객체) 사용을 객체 배열(Array)을 사용하였는데...(단순히 C/C++ 개발자로써 구조체 배열을 생각했기에...) 사실, 데이터의 빠른 접근 때문에 배열(Array)자체를 매우 선호하는 편이다. 

객체 배열을 DatagridView에 Binding하는 과정에서 C#에서는 Collection(콜렉션)이란 강력한 도구를 제공한다는 것을 알았다. C# 기본서와 구글링을 통해 알게 된것은 C언어에서처럼 구조체 배열과 같은 형태의 데이터 구조는 DataGridView에 Binding하기에 적합하지 않은것 같다.

 

C#에서 제공하는 ArrayList와 Collection, 그리고 Generic Collection에 대해 짚고 넘어가야겠다. 

이미 '주식종목' 부분의 설계와 개발이 거의 끝나가고 있는데...뒤집어 엎어야 할거 같다....


컬렉션(Collection)

   - 같은 Type의 데이터를 모아서 처리하는 데이터 구조.

   - 배열(Array)은 컬렉션 중의 하나라고 할 수 있음.

   - 컬렉션은 Generic Collection 과 Non-Generic Collection이 있음.

 

ArrayList

   - 배열과 유사한 자료구조.

   - 배열과 달리 그 크기를 미리 지정하지 않고 동적으로 할당 가능.

   - 모든 타입의 데이터가 저장될 수 있음. (Parameter의 Type이 Object임)

   - 관리하는 데이터가 많아질 수록 속도가 느려짐.

 

수행할 작업 Generic Collection Non-Generic Collection
키별로 빠르게 접근할 수 있도록 키/값의 쌍으로 저장 Dictionary(Tkey, TValue) Hash Table
인덱스 별로 항목 액세스 List<T> Array
ArrayList
FIFO 방식으로 항목 사용 Queue<T> Queue
LIFO 방식으로 항목 사용 Stack<T> Stack
순서대로 항목 액세스 LinkedList<T> -
컬렉션에 항목을 추가하거나 삭제할 떄 알림 표시 ObserverbleCollection<T> -
정렬된 컬렉션 SortedList<t> SortedList
수학 함수용 집합 HashSet<T>
SortedSet<T>
-

위의 내용을 보면...Generic Collection의 경우 C#에서만 제공하는 Optimized 도구들인것 같은 느낌이다. 

Non-Generic Collection의 경우는 구식 도구들인것 같은 느낌...C#에서는 두 가지 형태 모두 제공한다는 것이고.

사실 Generic Collection은 컴파일 타입에 Type-Safe(형식이 안전한)하기에 Non-Generic Collection보다 성능이 좋다고 한다. 

 

Dictionary는 Python에서도 자주 사용하는 Data 구조로 그 활용성이 매우 좋음을 알고 있기에, 내가 만들려는 '주식종목' 목록에 대해 Dictionary의 TValue가 Object 형식으로 저장이 가능하다면 Dictionary를 사용해야겠다.

 


Dictionary(딕셔너리)

'사전'. 사전을 펼쳐보면 가장 마지막 부분에 Index가 있다. 몇몇의 [키워드]들이 나열되어 있고, 그 키워드에 대한 [내용]이 몇 페이지에 존재하는지 표시되어 있는...

때문에 찾기 원하는 내용의 [키워드]를 알고 있으면 빠르게 [내용]을 찾을 수 있다. 

Dictionary의 원리는 이것과 같다. 

Array(배열)도 마찬가지다. 원하는 데이터가 저장되어 있는 Index(배열의 Index)를 알면 바로 데이터를 가져올 수 있다. 

Dictionary는 Index 대신, 특정 Key 값(사용자가 원하는)을 사용할 수 있기에 보다 인지하기 쉽고 가독성(Readability)이 좋다.

< 주의 사항 >
1. 중복된 키(Tkey)를 사용할 수 없음. 'ArgumentException' 발생.
2. 존재하지 않는 키(Tkey)로 검색 시 'KeyNotFoundException' 발생.
3. Array와 같이 순차적인 숫자 Index 사용 불가.

 

다음은 C# 기본서에 있는 Dictionary 예제이다. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dictionary
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, string> colorTable = new Dictionary<string, string>();

            colorTable.Add("Red", "빨간색");
            colorTable.Add("Green", "초록색");
            colorTable.Add("Blue", "파란색)");

            foreach (var v in colorTable)
                Console.WriteLine("colorTable[{0}] = {1}", v.Key, v.Value);

            // 동일 Key 값을 추가하는 경우
            try
            {
                colorTable.Add("Red", "빨강");
            }
            catch(ArgumentException e)
            {
                Console.WriteLine(e.Message);
            }

            // 없는 Key 값을 찾는 경우
            try
            {
                Console.WriteLine("Yellow => {0}", colorTable["Yellow"]);
            }
            catch(KeyNotFoundException e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

Add() 함수가 쉽게 Dictionary Item을 추가할 수 있도록 해주며, Tkey의 위치의 영문값이 Key가 되어 빠르게 TValue에 해당하는 값을 읽어올 수 있는 구조이다. 

 

Dictionary<string, string>은 TKey와 TValue를 모두 'string' Type으로 지정하여 Dictionary를 생성하였으므로, 객체를 사용할 결루 Dictionary<string, object명>을 사용하면 될 것 같다. 

그럼...TValue 쪽에 해당하는 객체(object)는...DataGridView에 어떻게 또 Binding을 시킬 수 있을지...걱정이 앞서긴 하지만...다음 C# 포스팅은 Dictionary를 DataGridView에 Binding하는 걸 해야겠다. 

 

 

 

 

Posted by [ 브랜든 ]
,

하나의 시스템/프로그램을 설계하는 것은 상당히 고난도의 작업이다. 

Architecture-Design에 속하는 이 작업은 Front-End의 User Interface(UI)적 편의성과 간결함, 그리고 정확성을 바탕으로 하는 사용자 친화적 경험 및 지식이 필요하며, Back-End의 Call-Flow(메시지 흐름)와 가장 중요한 Data Structure가 가능해야한다. 

 

현재 L-ATS Flex v2.0은 약 50% 정도 완성이 되었지만, 실제 게시글은 그때마다 작성하지 못했다. 이유는 위에서 '가장 중요한'이라고 말한 Data Structure가 불안정했기 때문이다. 

처음 사용해 보는 이베스트 투자증권의 xingAPI에서 얻을 수 있는 Data가 생각보다 제한적이고 API의 사용성 또한 제한적이라 프로그램이 만들어지면서 데이터 구조가 많이 변경되었다. 

 

지금 시점은 xingAPI로부터 얻을 수 있는 데이터가 모두 파악되고, 그 데이터들을 어떤 부분에 사용하고 응용할지 정리가 되었기에, 잠시 개발을 멈추고 글을 작성한다. 


1. L-ATS 화면 구성

L-ATS Flex v2.0 화면 구성

크게 총 15개의 구역으로 구분이 되며, 각 구역 마다의 설명은 아래 정리되어 있다.

L-ATS v1.0과 가장 큰 차이점은 '매수' 기능이 없어져 그에따른 '미체결' 관련 화면 구역이 없어졌다. 그리고 L-ATS v2.0의 핵심 기능인 '스탑로스 Flex' 관련 설정 및 결과 창이 생겼다. 

실은 '스탑로스 Flex'보다 '스탑로스'가 더 표시할 정보가 많이 있는데(설정 조건이 복잡해서...)...결과 창의 위치를 바꾸어야 할 수도 있을 것 같다.

 

2. L-ATS 상세 화면 구성

1~15개의 구역을 가진 L-ATS v2.0

 

① L-ATS v2.0의 주 메뉴 Bar

② L-ATS v2.0 프로그램 사용자 정보 

    : L-ATS v2.0을 사용하기 위한 사용자 가입 정보.

③ 이베스트 투자증권 HTS 서버 접속 정보

    : 접속 (초록색), 미접속 (빨간색)

④ 자동 매매 시작 버튼

    : 이 버튼이 눌리지 않는한 자동매매가 시작되지 않음.

⑤ 프로그램 알림 Bar

    : 프로그램 동작 중 사용자에게 알림이 필요한 수준의 메시지가 표시됨.

⑥ 사용자 계좌 목록

    : 사용자가 보유한 이베스트 투자증권의 계좌 목록 표시. HTS 로그인이 완료되어야 표시됨.

⑦ 실시간 지수 정보

    : 실시간으로 증권 시장의 코스피/코스닥 지수를 표시. 약 3초마다 데이터 수신. HTS 로그인이 완료되어야 표시됨.

⑧ 실시간 매매 처리 종목 현황

    : L-ATS 프로그램 상에서 처리되고 있는 종목들의 갯수를 실시간으로 표시.

     (기존 잔고건수 + 신규 매수 건수) = (거래 완료 건수 + 스탑로스 건수 + 스탑로스 Flex 건수)

⑨ 사용자 계좌의 실시간 자산 현황

⑩ 사용자 계좌의 실시간 잔고 종목 목록

    : 현재 사용자가 보유한 종목들의 목록. 이 목록에서 종목별 스탑로스 처리 여부를 정할 수 있음.

⑪ 자동 스탑로스 환경 설정

    : 자동 매매 시작 이후 적용될 스탑로스 설정 값.

⑫ 스탑로스 처리 종목 목록

    : 실시간 잔고에서 스탑로스 처리를 선택한 종목들에 대한 목록 표시.

⑬ 거래 완료 종목 목록

    : 스탑로스 또는 스탑로스 Flex에서 매도처리가 완료된 종목들에 대한 목록 표시.

⑭ 스탑로스 Flex 처리 종목 목록

    : 실시간 잔고에서 스탑로스 Flex 처리를 선택한 종목들에 대한 목록 표시.

⑮ 이베스트 투자증권 사용자 정보

    : HTS 로그인 후 표시되는 사용자의 이베스트 증권 사용자 정보.

 

3. xingAPI 사용 구역

위의 15개 구역 중 xingAPI를 통해 얻은 데이터를 표시하는 구역에 대한 설명이다.

L-ATS v2.0 화면 내의 xingAPI 사용 구역

생각보다 xingAPI를 사용하는 부분은 많지 않다. 그리고 단순하다. 

실시간으로 계속 데이터를 요청하고 수신하여 반영해야하는 '실시간 잔고' 부분을 제외하고는 단순 Request-Response 처리이기에 크게 어려운 부분은 없다. 

 

xingAPI에서 제공하는 TR처리 기능 중 한번 Binding 해두고 값의 변화가 있을 때마다 서버 측에서 데이터를 내려주는 TR 처리 방식이 있는데 실시간으로 변화되는 잔고 정보를 갱신하는 데에 매우 유용할 것 같지만, 경험상 굳이 세션 유지를 신경 쓰며 보다 세세한 예외처리를 하기 위한 노력을 기울이는 것보다 주기적으로 정보를 요청하여 처리하는 것이 더 심플할 것 같다. 프로그램의 기능은 심플한 것이 최고다. 

 

다음은 실제로 구현한 'HTS 로그인' 부분 포스팅이다.

Posted by [ 브랜든 ]
,

키움증권 API도 마찬가지지만 이베스트 XingAPI에서도 개발할 때 유용한 도구를 제공한다.

 

이베스트 XingAPI에서는 'DevCenter'라는 것인데, XingAPI에서 제공하는 다양한 TR(Transaction)들을 실제로 내가 보유한 계좌를 이용해서 시뮬레이션해볼 수 있도록 해준다. 

 

예를 들면, 계좌 조회를 하는 기능을 개발하고 싶을 경우 어떤 XingAPI TR을 사용해야 하는지, 그리고 해당 TR에 어떤 값들을 입력해서 요청을 해야하는지, 마지막으로 TR에 대한 응답들은 어떤 변수에 어떤 값들이 포함되어 오게 되는지 등이다. 

 

따라서, DevCenter와 HTS/MTS에서 실제 내 계좌의 정보를 비교해가며, 프로그램 개발하면서 내가 필요한 값들이 어떤 것들인지 확인하고 골라내가며 개발을 수월하게 도와준다. 

 


STEP 1. XingAPI 설치

 

우선 XingAPI를 사용하기 위해서는 이베스트 증권에서 제공하는 XingAPI 패키지를 설치해야 한다.

http://www.ebestsec.co.kr/

 

이베스트투자증권

 

www.ebestsec.co.kr

 

xingAPI 다운로드는 이베스트증권 회원가입이 되어있어야 한다.

 

xingAPI 선택

 

xingAPI 패키지 소개부분의 '최신 버전 설치' - 'PC'를 선택하여 설치한다.

 

 

xingAPI 패키지 다운로드

 

설치가 완료되면 바탕화면에 다음과 같은 아이콘이 생긴다. 

 

xingAPI 패키지 설치 후 사용 가능한 프로그램

 

 

STEP 2. XingAPI 실행

 

개발에 필요한 TR 테스트 프로그램으로 'DevCenter'를 사용하면 된다. 그럼 프로그램을 실행시켜 보자.

프로그램을 실행하면 다음과 같은 이베스트 증권 로그인 화면이 뜬다.

 

DevCenter 로그인 화면. 실제 본인 계정, 계좌 정보와 동일하다.

 

접속 서버는 '실서버'와 '모의투자'를 선택할 수 있는데, '모의투자'를 선택할 경우 미리 이베스트 투자증권 사이트에서 본인 계정에 대해 '모의투자' 참여 신청을 완료해야 한다.

 

'DevCenter' 첫 화면. 왼쪽에 다양한 TR 목록을 확인할 수 있다. 

 

첫 로그인 후에 로그 상세, 로그 속성, TR 속성 등이 화면에 표시되지 않을 수 있다. 이때는 메뉴의[보기]-[도구 모음 및 도킹 창]에서 필요한 화면들을 선택, 체크하면 된다.

 

그럼 이제 특정 TR 하나를 선택해 보자. 처리할 수 있는 TR의 종류로는 '단일 데이터', '연속 데이터', '반복 데이터'가 있는데 이 중에서 '단일 데이터'와 '반복 데이터'를 하나의 TR로 제공하는 '[t0424] 주식 잔고 2' TR을 살펴보자.

 

'[t0424] 주식 잔고2' TR의 상세 정보

 

왼쪽 TR 목록에서 '[t0424] 주식 잔고2' TR을 선택하면 위와 같이 해당 TR의 요청 메시지(t0424 inBlock) 그리고 응답 메시지(t0424 OutBlock)에 사용될 각 Data Field 들에 대한 정보를 볼 수 있다. 

 

간략하게 보자면 '[t0424] 주식 잔고 2' TR을 사용하려면 입력에 계좌번호, 비밀번호 등을 넣어야 하고, 수신한 응답에서는 추정 순자산, 실현손익, 매입금액 등의 값을 얻을 수 있는 것으로 되어 있다.

 

또 하나 중요한 것은 바로 'TR 속성' 창의 정보이다. 이곳에 어마어마하게 중요한! 어찌 보면 가장 중요한 것 정보 중의 하나가 있는데 바로 [ 초당 전송 수 ]이다. 

 

'[t0424] 주식 잔고2'의 TR 속성

 

분명하게 '1초당 2건'이라고 명시되어 있다. 

이것은 사용자들이 개발할 프로그램에서 '[t0424 주식 잔고2]' TR을 사용할 경우, 반드시 1초에 최대 2건만 요청할 수 있도록 개발해야 한다는 것이다. (실제로 약 0.1초간 요청을 보내게 하면... 하면 당연히 안된다... 계정 정지당함...)

 

사용자의 프로그램에서 해당 TR을 요청하는 메시지를 만들어야 하는데 그 부분이 바로 't0424InBlock'에 해당된다. 

여러 가지 잘 모르는 것들을 입력해야 하는데 어떤 값을 입력해야 할지 모를 때는 해당 Field를 클릭하면 오른쪽 'TR 속성' 화면의 'Field 정보'에 표시된다.

계좌번호는 사용자의 계좌번호를 입력하면 되고, 비밀번호는 계좌의 비밀번호 4자리... 이외 나머지 것들은 어떤 값을 입력해야 하는지... 당연히 알 수 없다. 단가 구분을 눌러보자.

 

t0424 TR의 '단가구분' Field 상세 정보

 

[TR속성]-[Field 속성] 부분에 '1@평균단가', '2@BEP단가' 라고 쓰여있다. 즉, 평균단가로 잔고를 조회하고 싶으면 '1'이란 값을, BEP단가로 잔고를 조회하고 싶으면 '2'란 값을 넣어야 한다는 것이다. 

다른 Field 들도 위와 같은 방법으로 어떤 값을 넣어야 하는지 확인하면 된다. 

 

또 하나 유심히 봐야 하는 것은 해당 TR이 '단일데이터' 처리인지 '반복데이터'처리인지 확인하는 것이다. 

't0424'를 잘 살펴보면 OutBlock, 즉, 응답 메시지가 두 가지가 존재하는 것을 확인할 수 있다. 

't0424OutBlock'이 단일데이터 응답 메시지이고, 't0424OutBlock1 - OCCURS'가 반복데이터 처리 응답 메시지이다. 

 

풀어 얘기하자면 [t0424 주식 잔고2] TR은 't0424InBlock' 메시지로 계좌번호/비밀번호/단가구분/체결구분/단일가구분/제비용포함여부/CTS_종목번호 값들을 입력받아, 't0424OutBlock'으로 계좌의 일반 정보들을 전달하고, 't0424OutBlock1'로 이 계좌에 존재하는 매수된 종목 리스트(여러개일 수 있으니 동일한 형태의 반복적 데이터들임.)를 전달한다. 는 얘기다.

 

STEP 3. xingAPI TR 실행

xingAPI는 위에서 살펴본 TR에 대해 실제로 테스트해볼 수 있는 기능을 제공한다. 

위에서처럼 사용할 TR을 선택한 다음, 메뉴바 밑의 다양한 아이콘들 중에 푸른 오른쪽 세모(재생 버튼 모양)를 클릭하면 별도의 'TR 확인 창'이 뜬다.

 

[TR 확인 창] 초기 화면

 

TR 확인 창을 실행하면 아까 보았던 InBlock과 OutBlock들의 Field 값들을 볼 수 있고, [In Block]에 해당되는 값들을 입력해서 TR을 실행해 볼 수 있다.

이제 내 계좌의 정보를 입력하고 '조회' 버튼을 클릭하여 이베스트증권 서버로부터 수신한 정보를 확인해 보자.

 

실제 t0424 TR 실행 결과

 

내 실계좌에 대한 잔고 정보 수신이 완료되었다. Out Block의 값들은 실제 내 계좌정보와 동일하니 바로 이베스트증권 HTS/MTS에 접속하여 하나하나 비교하면서 정확한지 확인해보면 된다. 자동매매 프로그램을 만들면서 이 작업이 중요한 이유는... 아무래도 Field의 이름들이 모두 한눈에 어떤 것을 의미하는지 알아보기 힘들고, TR 정보창의 Field 설명이 나와있지만 그 또한 내가 생각한 이름과 다른 게 쓰여있는 것들이 있어 값을 비교해보고 내가 필요한 값과 동일한 값을 가진 Field를 적어 두었다가 사용하면 꽤 효율적이다. 

 

이제 개발을 시작해 보자.

Posted by [ 브랜든 ]
,

C언어를 주로 사용하더 나에게는 이 Class 라는 것이 이해하기 쉬우면서도 이해하기 어려운 부분이었다.

흔히 Class와 Sturct(구조체)를 비교하는데 Struct는 평소에 아주 많이 사용하는 놈이기 때문이었다.

 

C에서의 구조체의 정의는 너무도 쉽다. 

함께 쓰고 싶은 다양한 데이터 Type 변수들의 집합. 모음. 모둠..? 

그리고 OOP를 흉내낼 수 있도록 해준 '함수 포인터'도 멤버로 가질 수 있다. (C#에서는 함수를 가질수 있다는 점이 가장 큰 차이점일까.)

 

구조체와 클래스의 차이점에 대해 정리되어 있는 것은 이보다 더 많은 자료들을 찾아볼 수 있다.

구분 구조체 (STRUCT) 객체 (CLASS)
형식 Value (값 형식) Reference (참조 형식)
생성영역 Stack Heap
인스턴스 new () 선언
상속 X O
생성자 X O
인터페이스 O O
STATIC X O

 


 

구조체의 경우 구조체 배열을 사용하여 동일한 형태의 여러 데이터들의 집합을 관리할 수 있다. 

이 기능은 프로그램 내의 데이터 관리 측면에 있어서 엄청난 효율성을 가져다 준다. 

 

게다가 배열이다. Index를 사용해서 가장 빠른 속도로 원하는 데이터에 접근할 수 있으니, 개발자의 입장에서는 최고의 조합이다. C#의 클래스도 같은 구조의 기능을 지원할까.

 

 

나는 아래와 같이 객체 배열을 만들어서 사용하고 싶었다. 

 

 

제작중인 자동매매 프로그램의 '종목'관련 Class 상속 관계

 

 

부모 클래스의 변수 및 함수를 상속받은 자식이 사용하도록, 그것도 외부 클래스에서 사용하도록 하기 위해 부모클래스의 변수에 대해 '접근 제한자'를 사용하도록 하는 데에도 몇번의 시행착오를 겪었다. 

 

부모 클래스의 변수를 상속받은 자식 클래스에서만 접근이 가능하도록 'protected'로 선언하였는데 아래 표의 설명이 많은 도움이 되었다.

 

 C# 접근 제한자
public
클래스의 내부와 외부 모든 곳에서 접근이 가능하다.
protected
클래스의 내부와 해당 클래스를 상속 받은 자식 클래스에서만 접근이 가능하다.
private
해당 클래스 내부에서만 접근이 가능하다.

 

 

그리고 외부 클래스에서 자식 클래스인 Stock_RTB를 객체 배열로 선언하여 사용하기 위해 C# 기초 책과 많은 포스팅에서 아래와 같이 간단하게 적용했는데....

이상하게 계속 NullReference Exception이 발생하는 것이다. 

using System;

namespace Program
{
	public partial class MainClass : Form
    {
    	... 생략 ...
        
        // RTB 클래스 배열 선언
        Stock_Realtime_Balance[] gArrStockRTB;
        
        
        ... 생략 ...
        
        string hname = "이베스트증권";
        
        gArrStockRTB = new Stock_Realtime_Balance[3];
        
        for (int index = 0; index < 3; i++)
        {
            try
            {
                gArrStockRTB[index].setCodeName(hname);
                ... 생략 ...
                
                string line = String.Format("{0}", gArrStockRTB[index].getCodeName());
                ... 생략 ...
            }
            catch (NullReferenceException e)
            {
                Console.WriteLine(e);
            }
        }
         ... 생략 ...
    }
}               
                

와...정말 온갖 부분을 다 예외 처리하고 로그를 찍어서 확인해 봤지만 도저히 찾지를 못했는데, 2일만에 드디어 문제가 됐던 부분을 찾았다. (2일이라면 오래 걸린것 같지만...이 프로그램을 만드는데에 하루에 1.5시간 정도만 투자하고 있으므로 생각보다 빨리 찾은 셈이다.)

using System;

namespace Program
{
	public partial class MainClass : Form
    {
    	... 생략 ...
        
        // RTB 클래스 배열 선언
        Stock_Realtime_Balance[] gArrStockRTB;
        
        
        ... 생략 ...
        
        string hname = "이베스트증권";
        
        gArrStockRTB = new Stock_Realtime_Balance[3];
        for (int i = 0; i < gArrStockRTB.Length; i++)
            gArrStockRTB[i] = new Stock_Realtime_Balance();
        
        for (int index = 0; index < 3; i++)
        {
        	try
        	{
        		gArrStockRTB[index].setCodeName(hname);
                ... 생략 ...
                
                string line = String.Format("{0}", gArrStockRTB[index].getCodeName());
                ... 생략 ...
            }
            catch (NullReferenceException e)
            {
                Console.WriteLine(e);
            }
        }
         ... 생략 ...
    }
}

다시 각 객체 배열의 위치마다 new로 잡아줘야 한다니...객체(Class)의 특성상 당연한 작업인데...

좀 더 세련되게 'Extension Method'를 사용해서 심플하게 처리하는 방법이 있다는데...C# 초보자인 나는 일단 for문을 사용하기로 했다....

 

역시 정확하게 다 이해하지 못한 상황에서 개발부터 진행하면 이렇게 간단한 실수를 하게된다.

프로그래밍은 재밌다.

 

 

Posted by [ 브랜든 ]
,

CentOS7을 설치하면 기본적으로 Python 2.7.5 버전이 설치된다.

 

2.x 버전의 Python도 사용하는데는 그닥 큰 무리가 없지만, 최근 3.x 버전에서 제공하는 신규 모듈들을 사용하기 위해서는 3.x 버전 Python 설치가 필요하다. (예를 들면...최근 3.8에 있는 공유메모리...아니면 AI 모듈들...인공지능 서비스 회사에 근무하는 나에게는 반드시 필요한...)

 

구글링하면 엄청나게 많은 Python 3.x 설치 블로그들이 검색된다. 

이 포스팅도 그저 나를 위한... (CentOS 설치할 때마다 매번 정확한 블로그를 찾기가 불편해서..)

 


CentOS7 설치 후 Python의 버전을 확인해 보자.

CentOS7의 기본 설치된 python 버전

 

설치는 아래 과정을 거친다.

  1. python.org에서 최신 3.x 버전 python 설치 파일을 다운로드 한다.
  2. 다운로드 한 파일을 풀어 make;make install로 설치를 진행한다.

 

설치 진행에 앞서 아래 Package들을 설치해 준다. (make install 할 때 문제가 생긴다고 함.)

 

  • zlib, zlib-devl, libffi-devel

zlib, zlib-devel, libffi-devl 설치

  • openssl, openssl-devel

openssl, openssl-devel 설치

 

설치가 완료되면 이제 python 3.x 버전을 설치해 보자.

https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz

wget을 통해 설치 파일을 다운로드 한다.

다운이 완료된 파일은 아래 명령어로 압축을 푼다.

$ tar -xvzf Python-3.8.1.tgz

파일 압축풀기가 완료되면 아래와 같이 Python-3.8.1 디렉토리가 생성되고, 그 안에 보면 다양한 package 구성파일들을 볼 수 있다.

Python 3.8.1 구성 파일 리스트

이제 설치를 진행해 보자.

 

순서는 configure -> make > make install 을 차례대로 진행한다.

 

Configure 실행

$ ./configure --enable-optimization으로 실행해도 된다.

make, make install 실행 (root 권한 필요)

 

make와 make install이 오류 없이 모두 정상적으로 실행 완료 되었다면 아래와 같이 python 버전을 확인해 본다.

CentOS7에 설치된 python 버전

현재 시스템 기본 파이썬으로 python 2.7.5가 설정되어 있다.

 

새로 설치한 python 3.8.1 버전을 기본 python으로 사용하려면 /bin/ 밑의 python 링크 하나만 변경해주면 된다.

python 2로 되어있던 python 심볼링 링크를 python 3으로 변경한다.

그리고 python 버전과 실제로 접속하면...

Python 3.8.1로 바뀐것을 볼 수 있다.

 

새로 적용된 python 3.8.1

 

 

Posted by [ 브랜든 ]
,

MariaDB 설치 이후 Database 생성과 해당 Database의 사용자 계정을 생성해 보자.

아래 내용은 구글링하면 너무나도 많은 정보들이, 더 정확하고 양질의 정보들이 나오는 내용들이라 굳이 나의 편의성에 의해 기록해 놓는다. 

 

Database 생성

[ STEP 1 ] Database 접속

 

$ mysql -uroot

 root 계정으로 mysql에 접속한다.

root 계정으로 MariaDB 접속

[ STEP 2 ] Database 생성 및 확인

 

MariaDB [(none)]> CREATE DATABASE 데이터베이스명;

MariaDB [(none)]> SHOW DATABASES;

신규 생성된 DATABASE 정보

[ STEP 3 ] Database 선택

 

처음 접속했을 때 'MariaDB [(none)]'에서처럼 'none'으로 어떤 DB도 선택되지 않았음을 확인할 수 있다.

USE 명령어로 사용할 DB를 선택할 수 있으며, 접속하면서 DATABASE를 지정할 수도 있다.

 

MariaDB [(none)]> USE LATS

특정 DATABASE 선택

접속할 때 DATABASE를 선택하고 싶을 경우 DATABASE 명을 입력해 주면 된다.

 $ mysql -u계정 -p비밀번호 LATS

 

사용자 계정 생성

생성한 DATABASE에 대한 사용자 계정을 생성해 보자.

 

[ STEP 1 ] 사용자 계성 생성 및 권한 부여

 

MariaDB [선택DB]> CREATE USER '계정명'@'%' IDENTIFIED BY '비밀번호';

MariaDB [선택DB]> GRANT ALL PRIVILEGES ON DB명.* TO '계정명'@'%';

사용자 계정 생성 및 DB 권한 설정

로컬에서도 접속 가능하도록 localhost를 추가해준다.
MariaDB [선택DB]> CREATE USER '계정명'@'localhost' IDENTIFIED BY '비밀번호';

 

[ STEP 2 ] 사용자 계정 모든 IP 접속 허용

 

MariaDB [선택DB]> GRANT ALL PRIVILEGES ON *.* TO '계정명'@'%' IDENTIFIED BY '비밀번호';

사용자 계정 외부 접속 IP 허용 설정

특정 IP 대역만 허용하고 싶을 경우 아래와 같이 실행한다.

 GRANT ALL PREVILEGES ON *.* TO '계정명'@'192.168.%' IDENTIFIED BY '비밀번호';

 

 

다음 포스팅은 외부 IP를 통해 위의 계정으로 Database에 접근하는 것을 포스팅 하려한다.

참고로 위의 DB는 내부 공유기에 연결되어 있는 라즈베리파이에 설치되어 있으며, 외부접속 시 공유기를 거쳐야 하기때문에 DNS 설정과 Port Forwarding 설정 후 진행해야 한다.

(예전에 사용하던 DNS 서비스가 만료되었는데...무료 DNS 서비스를 찾아봐야지....)

Posted by [ 브랜든 ]
,