두번째 L-ATS 2.0 사용자 화면의 Focus는 '매매 처리중인 종목의 표시' 부분이었다.
세번째 L-ATS 3.0 사용자 화면의 핵심은 이것이다.
캡쳐된 화면 표시와 OCR 처리된 문자열
L-ATS 2.0에서 Flexible 스탑로스를 통한 매도의 자동화를 완성한 상태에서
L-ATS 3.0에서는 매수 행위의 자동화를 구현하여 매매의 완전 자동화 를 완성하는 것이 서비스 기획의 핵심 부분이다.
그렇기 때문에 사용자가 보기 편하도록 캡쳐된 화면과 해당 이미지의 OCR 결과를 실시간으로 표시하는 부분에 Focus를 맞췄다.
3.0 버전의 핵심 기능 부분을 부각시키는 것이 사용자 관점에서 서비스의 장점을 직접적으로 잘 나타낼 수 있기 때문에 그때 그때 이미지 캡쳐한 화면과 OCR 처리된 내용을 즉각적으로 표시해줌으로써 사용자의 만족감을 더 높여 줄수 있다.
① 메인 메뉴
메뉴의 구조는 [HTS 로그인], [시스템 설정], [프로그램 종료]로 구성된다.
HTS 로그인 : 이베스트 증권에 로그인할 수 있도록 기능을 제공한다.
시스템 설정 : 메인 화면에는 보이지 않는 시스템적으로 Critical한 값(예; OCR 엔진 정보, 예외처리 문자열, 문자열 파싱 패턴 등)들을 설정할 수 있는 화면을 제공한다.
프로그램 종료 : 시스템의 안정적인 운영을 위해 비정상 종료 Case를 없애고 이 버튼으로만 종료될 수 있도록 했다.
② 상태 확인
HTS 서버 연동 : 증권사(이베스트 증권) API를 통해 HTS와 연동 상태 표시
서버 연결 : LINUX Server의 L-ATS Main Server와 연동 상태 표시
③ 프로그램 조작
캡쳐 영역 [Button]: 캡쳐할 화면의 영역을 설정할 수 있는 투명한 Form 생성
시작 [Button] : 사용자 설정 값으로 자동 매매를 시작하고 정지하는 버튼
캡쳐타임(초) : 실시간으로 화면을 캡쳐할 시간 간격
[ 캡쳐 타임이 필요했던 이유 ] 자동매매의 최초 서비스 기획의 핵심은 '누군가' 실시간으로 특정 종목을 언급해 주는 조건이 되어야 하는 것이었다. 근데 과연 누가...? 그렇게 찾고 찾아 선택한 것이 카카오톡과 텔레그램의 일명 뉴스방이었다. 무료로 불특정 다수에게 특정 종목의 당일 Good/Bad 뉴스를 실시간으로(?) 알려주는 고마운 곳. 하지만 이 방들은 소프트웨어적으로 개인이 메시지를 받을 수 없었다. (캡쳐 + OCR 방식을 선택한 이유.) 때문에 이 뉴스방에 새로운 뉴스가 올라온 것을 알 수 있는 방법이 없다. 그래서 일정 간격으로 화면을 지속적으로 캡쳐해서 OCR 결과를 처리하는 방식으로 처리했다. (OCR 기술이 많이 발전했듯 이런것도 해결이 가능한 좋은 방향의 방법이 곧 생기길...)
스탑로스(%) : 자동 매도를 위한 손실률 설정
④ 자동매수 설정
보유계좌 : 자동 매매에 사용할 사용자의 증권사 계좌
매수금 : 자동으로 매수할 경우 1회 매수가
1회 매수가가 10만원일 경우, 1주당 10만원 이상인 종목은 매수하지 않는다. 1주당 10만원 이하인 종목은 10만원 이내에서 최대 매수량을 계산하여 매수한다. 예) 1주당 9천원인 종목이면 11주 매수
⑤ 잔고조회
자산총액 : 현재 선택된 사용자 증권사 계좌의 잔고 확인
예수금 : 현재 선택된 사용자 증권사 계좌의 예수금 확인
⑥ 캡쳐 이미지
캡쳐 영역으로 선택된 화면의 캡쳐된 이미지를 표시
⑦ OCR 결과
캡쳐된 이미지의 OCR 변환 결과를 표시
⑧ 실시간 잔고
매수가 완료되서 현재 계좌에 보유하고 있는 종목 리스트 표시
표시되는 종목 리스트들은 실시간으로 현재가를 갱신하여 표시하고 스탑로스를 계산
⑨ 거래 완료
당일 거래 완료된 종목 리스트 표시
정말 이베스트 증권의 거래 완료 종목 표시를 위한 API는 최악이다. HTS나 MTS에서 확인할 수 있는 '잔고 조회'의 화면에서 조회되는 값들과 동일한 값들을 표시하기 위해 정말 많은 시행착오를 겼었다. 이 내용은 따로 포스팅 예정이다.
⑩ 종목 추출 결과
OCR 결과에서 패턴 분석으로 종목명을 추출하고 LINUX Server program으로 전달하여 매수를 위한 해당 종목의 Code 정보를 요청/수신하여 표시
⑪ 실시간 건수
현재 계좌에 보유하고 있는 종목의 갯수와 당일 거래 완료된 종목의 갯수 표시
⑫ 거래 완료
당일 거래 완료된 종목들의 금액 정보 표시
⑬ 접속 정보
사용자의 증권사 계정 접속 정보 표시
L-ATS 시스템이 버전업 되면서 UX/UI는 일부분들이 계승되어 구성되는것 같다.
아무래서 버전 마다 기획하고 설계하고 구현하고 운영하다보면 배우는 것이 있기때문에 버릴건 버리고, 활용할 건 활용하게 되는것 같다.
사실, 바빠서 포스팅을 못했을 뿐 계획했던 자동 매매 프로그램은 그 당시 완성하여 (내가 당장 필요한 핵심 기능만 구현하였지만...) 계속적으로 사용하고 있었다.
당시 내가 필요했던 기능은 종목 매수는 직접하고, 설정해 놓은 StopLoss 기준에 부합할때 자동으로 매도 처리하는 프로그램이었다.
사실 이 프로그램은 찌라시방 용도로 사용하기 위해 설계되었는데 갑자기 급등하는 종목에 대해 최대한의 수익을 끌어내기 위함이었다. 때문에 당일 매시점 갱신되는 최고가 기준으로 StopLoss 기준이 같이 변하도록(Flexible) 해두었기에 'Flex'란 이름을 붙여 만들었다.
DatagridView의 데이터 표시는 이전 예제 프로그램 위에 다시 구현해 보도록 하자.
VIEW 테이블 만들기
먼저 [도구상자]에서 [DataGridView]를 선택하여 화면안에 넣고 원하는 크기로 맞춰 준다.
생성된 DataGridView를 선택하고 오른쪽 하단의 [속성]에 [Columns] 항목 오른쪽 끝의 '...'을 클릭하여 [열 편집] 화면을 띄운다.
속성에 [DataSource] 항목을 통해 프로젝트 내의 특정 데이터 집합을 바로 바인딩하여 표현할 수 있는 것 같은데 이것을 시도하다 번번히 실패하여 하나하나 컬럼을 만들어 진행했다. (나는 C#을 이번에 처음 해보는 10년차 개발자이므로....)
[열 편집] - [열 추가]를 선택하여 표시하고자 하는 데이터의 컬럼을 생성해준다.
당연한 얘기지만 프로그램 내에서 표시하기 위한 위치와 실제 데이터 값의 매칭이 필요한데 DataPropertyName이 그에 해당하는 값이다.
아래 Code라고 지정해 놓은 값은 이후에 만들 잔고 데이터 클래스의 '종목코드'를 저장하는 변수명과 일치시킬 것이다.
속성에 아랫부분으로 내려가면 HeaderText 라는 것이 있는데 이곳에 표시하고 싶은 이름을 입력하면 된다.
그리고 각각의 열에 대해서도 특정 속성을 부여할 수 있다. 예를 들면, 금액일 경우 통화 형태로, 백분율 값일 경우 소수점 자리수 표시 형태로 말이다.
최초 만들어진 DataGridView의 모습은 이렇다.
DataGridView의 속성에서 가장 앞부분의 무의미한 Column을 숨길 수 있고, 위에서 열 편집했던 창에서 각각의 열에 대해 너비 조정이 가능하므로 본인의 입맛에 맞게 모양을 수정한다.
한 화면에 표시되도록 각각의 열에 대해 너비를 조정하였다.
(오른쪽 끝에 일부 공간을 남겨둔건 항목이 많으면 스크롤 바가 생기기 때문.)
VIEW 테이블과 연결할 데이터 구조체(Class) 만들기
화면에 보여줄 VIEW는 준비가 되었으니 이제 VIEW에 Binding할 데이터 구조를 생성해야 한다.
데이터는 [클래스 리스트]를 사용했다
먼저 데이터 클래스(class)를 먼저 만들자.
Balance_List.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace xingAPI_Test
{
class Balance_List
{
public string Code { get; set; } // 종목코드
public string CodeName { get; set; } // 종목명
public decimal CurPrice { get; set; } // 현재가
public decimal PurchPrice { get; set; } // 매입가
public int BalCount { get; set; } // 잔고수량
public decimal PurchAmount { get; set; } // 매입금액
public decimal EvalAmount { get; set; } // 평가금액
public decimal EvalPrice { get; set; } // 평가손익
public double EvalRate { get; set; } // 손익률
public Balance_List(string Code, string CodeName, decimal CurPrice, decimal PurchPrice, int BalCount, decimal PurchAmount, decimal EvalAmount, decimal EvalPrice, double EvalRate)
{
this.Code = Code;
this.CodeName = CodeName;
this.CurPrice = CurPrice;
this.PurchPrice = PurchPrice;
this.BalCount = BalCount;
this.PurchAmount = PurchAmount;
this.EvalAmount = EvalAmount;
this.EvalPrice = EvalPrice;
this.EvalRate = EvalRate;
}
}
}
이제 한 종목에 대한 정보를 담이 이 클래스를 리스트로 저장할 데이터 공간을 만들어 준다.
...
List<Balance_List> gBalanceList = new List<Balance_List>(); // 잔고 클래스 리스트
...
gBalanceList라는 글로벌 변수가 Balance_List라는 하나하나의 객체를 갖는 리스트로 선언되었다.
솔직히 나는 C#을 다루던 개발자가 아니라 이와 같은 기능을 개발할 때 어떤 방식이 가장 최적화된 방식인지 알지 못한다. 다만, C/C++ 개발자였던 경험으로 데이터 구조체가 가장 다루기 쉽기때문에(본인에게...) 클래서 리스트 방식을 선택했다.
클래스 리스트에 데이터를 넣을때는 아래와 같이 객체를 새로 생성하여 리스트에 ADD해 준다.
여기서는 잔고조회 TR을 통해 가져온 결과 데이터 한줄 한줄을 만들때 마다 같이 리스트에 ADD해 준다.
풀어 얘기하자면 [t0424 주식 잔고2] TR은 't0424InBlock' 메시지로 계좌번호/비밀번호/단가구분/체결구분/단일가구분/제비용포함여부/CTS_종목번호 값들을 입력받아, 't0424OutBlock'으로 계좌의 일반 정보들을 전달하고, 't0424OutBlock1'로 이 계좌에 존재하는 매수된 종목 리스트(여러개일 수 있으니 동일한 형태의 반복적 데이터들임.)를 전달한다. 는 얘기다.
STEP 3. xingAPI TR 실행
xingAPI는 위에서 살펴본 TR에 대해 실제로 테스트해볼 수 있는 기능을 제공한다.
위에서처럼 사용할 TR을 선택한 다음, 메뉴바 밑의 다양한 아이콘들 중에 푸른 오른쪽 세모(재생 버튼 모양)를 클릭하면 별도의 'TR 확인 창'이 뜬다.
TR 확인 창을 실행하면 아까 보았던 InBlock과 OutBlock들의 Field 값들을 볼 수 있고, [In Block]에 해당되는 값들을 입력해서 TR을 실행해 볼 수 있다.
이제 내 계좌의 정보를 입력하고 '조회' 버튼을 클릭하여 이베스트증권 서버로부터 수신한 정보를 확인해 보자.
내 실계좌에 대한 잔고 정보 수신이 완료되었다. Out Block의 값들은 실제 내 계좌정보와 동일하니 바로 이베스트증권 HTS/MTS에 접속하여 하나하나 비교하면서 정확한지 확인해보면 된다. 자동매매 프로그램을 만들면서 이 작업이 중요한 이유는... 아무래도 Field의 이름들이 모두 한눈에 어떤 것을 의미하는지 알아보기 힘들고, TR 정보창의 Field 설명이 나와있지만 그 또한 내가 생각한 이름과 다른 게 쓰여있는 것들이 있어 값을 비교해보고 내가 필요한 값과 동일한 값을 가진 Field를 적어 두었다가 사용하면 꽤 효율적이다.
모든 프로그램들은 Call Flow가 존재하며, 사용자 지향적인 프로그램일수록 사용자가 어떻게 프로그램을 사용해 나갈지, 예를 들면, 로그인부터 해서 특정 기능을 사용하기 위해 어떤 것들을 설정해야 하며, 얻어낸 결과를 어떻게 저장하고 프로그램을 종료하는지까지의 시나리오가 존재한다.
프로그램 제작 전에 완성된 내용이지만, 나중에 사용자 매뉴얼 작성 시에 도움이 될 수 있도록 지금 기록을 해놓는 게 좋을 것 같다. 물론 Flow Chart 기반의 시나리오 정리이며, 필요할 경우 지속적으로 업데이트될 것이다.
L-ATS 사용자 시나리오
실은 어떤 프로그램이든 사용자 관점에서 정의하는 시나리오는 복잡하지 않다.
사용자는 프로그램의 기능이 직관적이며 단순해야(단순하다는 어감이 조금 좋지 않은데 Simple하다가 의도하는 바와 가까운 단어다.) 잘 사용하게 된다.
개발자라면 잘 알겠지만 단순해 보이는 하나하나의 과정마다 수가지의 예외처리와 백그라운드 프로세스 작업이 존재한다는 것을 알 것이다.
사용자의 시선에서는 당연히 신경 쓸 필요 없다. 모든 기능이 오류 없이 정상적으로 처리되어야 하니 말이다.
즉, 위의 시나리오는 프로그램의 모든 기능이 정상적일 때의 시나리오이다.
설계된 L-ATS 2.0의 기능은 크게 4가지로 구분된다.
이베스트 증권 계좌 선택
스탑로스 조건 설정
자동 매매 시작
종목별 처리할 스탑로스 종류 선택
실제론 대부분의 자동매매 프로그램들이 1.~3.번 기능이 전부이다. 2.번 기능이 프로그램마다의 특징을 갖는 기능이며, 자동으로 매수하는 기능이 있는 프로그램도 있다. (L-ATS 1.0에도 존재했던 기능이나 2.0에서는 스탑로스만...)
기능 자체는 간단해 보이지만, 돈이 왔다갔다하는 프로그램이기에 얕은 지식으로 간단하게 만들 수 있는 프로그램의 종류는 아니다.
기능 구현보다 Exception 처리에 더 많은 공을 들여야 한다.
사용자는...더구나 GUI 환경에서의 사용자는 개발자가 예측하지 못한 돌발 행동을 많이 한다.