'주식'에 해당되는 글 2건

  1. 2021.11.07 [Trading] xingAPI - 데이터 표시하기 4
  2. 2020.06.21 [Trading] 프로그램 로그인

바쁘다는 핑계로 오랫만에 포스팅을 한다.

핑계인거 같지만 정말 바쁘긴 했다. (우리회사가 드디어 11월에 코스닥 입성을 하게 됐음.)

고통의 결실이 달달하길 바랄뿐이다.

 

앞 포스팅을 보니 정확히 1년전 게시물이므로...

1년만에 다시 포스팅을 한다.

 

이번 포스팅은 xingAPI를 통해서 가져온 내 계좌의 종목 정보를 

C#의 DataGridView에 Binding하여 미려한 표 형식으로 표시하고, 실시간으로 변하는 [현재가] 를 스스로 갱신하면서

보여주도록 해보자.

 

이전 포스팅에 보면 계좌 잔고 정보가 TEXT 타입의 표로 심플하게 표시되어 있다.

 


2020.10.10 - [Trading] - [Trading] xingAPI - 반복 데이터 조회

 

[Trading] xingAPI - 반복 데이터 조회

이번 포스팅은 단일 데이터 조회에 이어 xingAPI의 [반복 데이터 조회]에 대해 포스팅 해본다. 단일 데이터 조회처럼 C#으로 코드를 짤 것이며, xingAPI COM 개발 가이드에는 엑셀 VBA를 이용한 프로그

brandon-dev.tistory.com


사실, 바빠서 포스팅을 못했을 뿐 계획했던 자동 매매 프로그램은 그 당시 완성하여 (내가 당장 필요한 핵심 기능만 구현하였지만...) 계속적으로 사용하고 있었다.

당시 내가 필요했던 기능은 종목 매수는 직접하고, 설정해 놓은 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해 준다.

그리고 DataGridView에 Binding 해주면 끝!!

...
    gBalanceList.Add(new Balance_List(expcode, hname, price, pamt, janqty, mamt, appamt, dtsunik, sunikrt));
...
	dataGridView1.DataSource = gBalanceList;
...

이제 결과물을 한번 보자.

이렇게 간단하게 미려한 표 형태의 데이터 표현이 가능하다니 이보다 편리하고도 편리할 수가 없다....

로그 찍는 부분의 어설픈 표형태(?)의 데이터만 봐도 서버 개발자들은 자괴감이 든다...

 

[ 전체 소스 ]

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;

        // xingAPI TR 클래스 선언
        XAQueryClass CSPAQ12200;        // 자산현황 조회
        XAQueryClass t0424Occurs;       // 잔고조회 - 종목리스트

        List<Balance_List> gBalanceList = new List<Balance_List>(); // 잔고 클래스 리스트

        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);
            // xingAPI TR 이벤트 등록
            CSPAQ12200 = new XAQueryClass();
            CSPAQ12200.ResFileName = @"C:\eBEST\xingAPI\Res\CSPAQ12200.res";  // xingAPI 서버로부터 수신한 응답 메시지를 COM 버전의 API에서 사용할 수 있도록 Format을 맞춰주는 파일
            CSPAQ12200.ReceiveData += CSPAQ12200ReceiveData;  // xingAPI 서버로부터 CSPAQ12200의 응답 메시지를 수신할 함수 등록

            // xingAPI 반복 데이터 조회 TR 이벤트 등록
            t0424Occurs = new XAQueryClass();
            t0424Occurs.ResFileName = @"C:\eBEST\xingAPI\Res\t0424.res";
            t0424Occurs.ReceiveData += t0424ReceiveData;
        }
        
        // HTS 서버 접속
        private void Btn_Conn_Server_Click(object sender, EventArgs e)
        {
            string serverType = "demo.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)
        {
            // CSPAQ12200 TR의 InBlock 데이터 세팅
            CSPAQ12200.SetFieldData("CSPAQ12200InBlock1", "RecCnt", 0, "00001");
            CSPAQ12200.SetFieldData("CSPAQ12200InBlock1", "MgmtBrnNo", 0, " ");
            CSPAQ12200.SetFieldData("CSPAQ12200InBlock1", "AcntNo", 0, gAccount);
            CSPAQ12200.SetFieldData("CSPAQ12200InBlock1", "Pwd", 0, "실제계좌비번");
            CSPAQ12200.SetFieldData("CSPAQ12200InBlock1", "BalCreTp", 0, "0");

            // TR 요청
            var result = CSPAQ12200.Request(false);
            if (result > 0)
                MessageBox.Show("CSPAQ12200 요청 완료");
            else
                MessageBox.Show("CSPAQ12200 요청 실패");
        }

        private void CSPAQ12200ReceiveData(string trCode)
        {
            // 요청한 CSPAQ12200 TR에 대한 응답 메시지 수신
            // CSPAQ12200 TR의 OutBlock2에서 필요한 데이터 추출
            // 1. 예탁자산 총액
            var temp_value = CSPAQ12200.GetFieldData("CSPAQ12200OutBlock2", "DpsastTotamt", 0);
            decimal estimated_assets = Convert.ToDecimal(temp_value);
            // 2. D+1 예수금
            temp_value = CSPAQ12200.GetFieldData("CSPAQ12200OutBlock2", "D1Dps", 0);
            decimal d1_deposit = Convert.ToDecimal(temp_value);
            // 3. D+2 예수금
            temp_value = CSPAQ12200.GetFieldData("CSPAQ12200OutBlock2", "D2Dps", 0);
            decimal d2_deposit = Convert.ToDecimal(temp_value);
            // 4. 손익률
            temp_value = CSPAQ12200.GetFieldData("CSPAQ12200OutBlock2", "PnlRat", 0);
            double valuation_rate = Convert.ToDouble(temp_value);
            // 5. 잔고평가금액
            temp_value = CSPAQ12200.GetFieldData("CSPAQ12200OutBlock2", "BalEvalAmt", 0);
            decimal evaluation_amounts = Convert.ToDecimal(temp_value);

            string text = String.Format("[예탁자산총액 : {0:#,0}] [D+1 예수금 : {1:#,0}] [D+2 예수금 : {2:#,0}] [손익률 : {3:f2}%] [잔고평가금액 : {4:#,0}]",
                estimated_assets, d1_deposit, d2_deposit, valuation_rate, evaluation_amounts);
            MessageBox.Show(text);
        }

        // 계좌잔고 조회 [반복데이터]
        private void Btn_Get_Items_Click(object sender, EventArgs e)
        {
            // 잔고 리스트 조회
            string inform = String.Format("-- 잔고 리스트 조회");
            Realtime_Log.Items.Add(inform);

            t0424Occurs.SetFieldData("t0424InBlock", "accno", 0, gAccount);
            t0424Occurs.SetFieldData("t0424InBlock", "passwd", 0, "실제계좌비번");
            t0424Occurs.SetFieldData("t0424InBlock", "prcgb", 0, "1");
            t0424Occurs.SetFieldData("t0424InBlock", "chegb", 0, "2");
            t0424Occurs.SetFieldData("t0424InBlock", "dangb", 0, "0");
            t0424Occurs.SetFieldData("t0424InBlock", "charge", 0, "0");
            t0424Occurs.SetFieldData("t0424InBlock", "cts_expcode", 0, " ");
            int result = t0424Occurs.Request(false);
            if (result < 0)
            {
                inform = string.Format("[보유계좌] 계좌 잔고리스트(t0424) 요청이 실패했습니다. ret={0}", result);
                Realtime_Log.Items.Add(inform);
            }
        }

        private void t0424ReceiveData(string trCod)
        {
            // 전체 잔고 종목 횟수 가져오기
            var count = t0424Occurs.GetBlockCount("t0424OutBlock1");
            string inform = String.Format("잔고조회 종목 리스트 갯수 : {0}", count);
            Realtime_Log.Items.Add(inform);

            Realtime_Log.Items.Add("-------------------------------------------------------------------------------------------------------------------------------");           
            string title = String.Format("| {0,20} | {1,10} | {2,10} | {3,10} | {4,10} | {5,10} | {6,10} | {7,10} | {8,10} |", "종목명", "종목코드", "현재가", "매입가", "잔고수량", "매입금액", "평가금액", "평가손익", "수익률");
            Realtime_Log.Items.Add(title);
            Realtime_Log.Items.Add("-------------------------------------------------------------------------------------------------------------------------------");

            // 가져온 종목 횟수 만큼 반복하면서 실제 종목 정보 가져오기
            for (int index = 0; index < count; ++index)
            {
                var temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "expcode", index); // 종목코드
                string expcode = Convert.ToString(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "hname", index);       // 종목명
                string hname = Convert.ToString(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "price", index);       // 현재가
                decimal price = Convert.ToDecimal(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "pamt", index);        // 매입가
                decimal pamt = Convert.ToDecimal(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "janqty", index);      // 잔고수량
                int janqty = Convert.ToInt16(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "mamt", index);        // 매입금액
                decimal mamt = Convert.ToDecimal(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "appamt", index);      // 평가금액
                decimal appamt = Convert.ToDecimal(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "dtsunik", index);     // 평가손익
                decimal dtsunik = Convert.ToDecimal(temp_value);
                temp_value = t0424Occurs.GetFieldData("t0424OutBlock1", "sunikrt", index);     // 수익율
                double sunikrt = Convert.ToDouble(temp_value);

                string contents = String.Format("| {0,20} | {1,10} | {2,10} | {3,10} | {4,10} | {5,10} | {6,10} | {7,10} | {8,10} |",
                    expcode, hname, price, pamt, janqty, mamt, appamt, dtsunik, sunikrt);

                gBalanceList.Add(new Balance_List(expcode, hname, price, pamt, janqty, mamt, appamt, dtsunik, sunikrt));

                Realtime_Log.Items.Add(contents);
            }
            Realtime_Log.Items.Add("-------------------------------------------------------------------------------------------------------------------------------");

            // Data Binding
            dataGridView1.DataSource = gBalanceList;
        }
    }
}

 

DataGridView는 데이터가 없는 초기상태에서 회색 바탕의 빈칸이 덩그러니~ 표시된다. (디자인적으로 보기 싫게....)

개인 취향에 맞게 초기에 일정 갯수의 빈칸을 넣고, 실제 데이터가 발생시에 교체하거나 아니면 새로 다시 그리거나 나는 등의 방식으로 개발하면 좀 더 미려한 프로그램이 될 것이다.

 

 

최근 회사의 상장 준비 때문에 바빠서 (드디어 11월에 코스닥 입성!!! 갬동 ㅠㅠ) 퇴근 후라도 조금씩 잡았던 개발을 손놨었는데 이거 조금 손댔다고 다시 개발이 하고 싶어진다.....

현재 사용하고 있는 프로그램의 단점이 하나 있어 새로운 프로그램을 설계 중인데

다음 포스팅에 새로운 프로그램 개발의 시작과 함께 그 내용을 다뤄 봐야 겠다.

 

Posted by [ 브랜든 ]
,

앞서 말한바와 같이 이베스트의 XingAPI는 서버 연결과 HTS 연결이 별로도 동작한다. 

때문에 L-ATS 프로그램을 사용하기 위해서는 아래 두 가지 로그인 절차를 거치게 설계했다.

  • Step 1. L-ATS 프로그램 로그인
  • Step 2. 이베스트 HTS 로그인

번거로울 수 있을 것이라 생각할 수도 있겠지만, 이베스트 XingAPI의 로그인 구조와

내가 추구하는 (아무나 내프로그램을 사용할 수 없는...?) 방향과 딱 맞아 떨어졌다 생각할 수 있겠다.

 

L-ATS 첫 실행 화면

지난 '두번째 프로그램' 초기화면 설계와 조금 달라진 모습을 볼 수 있다. 

 

우선, 프로그램 실행과 동시에 'L-ATS 로그인' 화면을 제공하여 프로그램 사용자 로그인과 동시에 

이베스트 HTS 서버 연결을 선택하도록 했다. (덕분에 프로그램 사용자 정보도 관리해야할 것 같다.)

 

먼저 구현된 각 화면의 표시 정보를 보자. (기능이 구현되는 순으로 계속 설명을 써야겠다.)

  1. 사용자 정보 : L-ATS 등록 사용자 정보
  2. HTS : 실시간 이베스트 HTS 연동 상태 표시. 빨 : Disconnect, 녹 : Connected. 
  3. 시작 버튼 : HTS 로그인 후 계좌를 선택해야 하므로, ATS의 모든 기능은 이 버튼이 눌린 시점부터 시작된다.
  4. 알림 : 프로그램에서 Event 발생 시, 텍스트로 표시해 준다. (예] 로그인, 매수, 매도 등)
  5. 하단 Status Bar : 이베스트 HTS 사용자 정보 표시

우선 로그인을 해보자.

내 계정은 이미 등록이 되어 있는 상태이며, 추후 사용자 관리관련 포스팅 예정이다.

ID와 비밀번호를 입력 후, 모의투자/실투자 선택.

그리고 로그인 버튼을 누르면 다음과 같은 L-ATS 시작 화면이 된다.

L-ATS 사용자 로그인 및 이베스트 HTS 접속 상태.

L-ATS 프로그램 사용자의 정보가 왼쪽 상단에 표시되고, 실시간 이베스트 HTS 접속 상태가 'HTS' 부분에 빨간색->초록색으로 표시가 된다. '모의투자'를 선택했을 경우 '모투', '실투자'를 선택했을 경우 '실투'라고 표시된다.

그리고 '알림' 부분에 현재 발생한 로그인 이벤트에 대한 내용이 표시된다. 

여기까지는 아직 이베트스 HTS에 로그인 하긴 전 상태라 계좌정보, 자산현황 등을 알 수 없다. (다음 게시물 예정.)

 

자동 매매 프로그램에 있어서 HTS 연결은 매우 중요하다. 

정전, 네트워크 장애 등등. 어떠한 이유에 있어 프로그램이 돌아가고 있는 상태에서 HTS 연결만 끊기는 경우가 발생할 수 있으므로, HTS 연결 상태를 지속적으로 확인(Heart-Beat)하고 재연결(Reconnect)를 할 수 있도록 개발해야하는 것이 기본이다. 이건 Database 서버와의 연결도 마찬가지이다. 

 

L-ATS v1.0 때보다 많은 기능이 설계에 들어가고 있어...매우 정신이 없다...

 

그래도 재밌는걸 어떡하나 싶다...

 

만들다보니 회원가입 절차가 필요해져서 '비밀번호'는 당연히 입력 즉시(나도 알 수 없도록) 암호화하여 DB에 저장하도록 해야겠다 생각했지만, 이베스트 XingAPI 특징상 HTS 로그인시에 아이디/비번/공인인증서비번을 입력 받도록 되어있다.

 

보안에 굉장히 취약할 수도 있는 부분이라 이 부분을 어떻게 해야 사용자들이 안심하고 사용할 수 있을지 고민이다. 

아니면....내가 예상하는 것과 다를 수도 있고....

'Trading' 카테고리의 다른 글

[Trading] L-ATS 사용자 시나리오  (0) 2020.08.02
[Trading] 사용자 정보 구조  (0) 2020.07.18
[Trading] HTS 로그인 설계, 구현  (0) 2020.07.08
[Trading] 두번째 프로그램  (0) 2020.06.07
[Trading] 첫번째 프로그램  (0) 2020.05.24
Posted by [ 브랜든 ]
,