L-ATS는 한순간에 한 사람의 사용자(하나의 계정)만 사용할 수 있도록 운영할 예정이니 프로그램이 종료되는 시점까지 '사용자 정보'는 하나의 데이터 셋만 생성될 것이다. 

 

그럼 프로그램이 동작하는 동안 사용자는 어떤 정보들을 관리하여야 할까?

 

우선 기본 사용자 정보는 아래 두 가지 형태로 구분할 수 있다. 

  • L-ATS 사용자 정보
  • 이베스트 증권 사용자 정보

좀 더 Detailed 정보들을 나열해 보자.


[ L-ATS 사용자 정보 ]

 

 1. L-ATS 가입자 ID : 비밀번호 찾기와 연계하기 위해 E-Mail Type으로 정함.

 2. 가입자 Pricing Grade : L-ATS 가입자의 가격 정책 등급 정보.

 3. Auto Trading 여부 : 현재 자동 매매 처리 중인지 여부 정보.

 4. 현재 접속 단말 MAC : 한 계정당 한 명만 사용할 수 있도록 MAC 인증을 위한 정보.

 

[ 이베스트 증권 사용자 정보 ]

 

 1. 현재 접속 HTS 정보 : '모의투자' 또는 '실투자' 접속 서버 정보와 현재 접속 여부 정보.

 2. 이베스트 가입자 ID : 이베스트 증권 ID 정보.

 3. 현재 계좌 : 증권 사용자는 여러 개의 계좌를 가질 수 있기 때문에, 1개의 계좌를 선택하여 사용할 수 있음.


우선 이정도면 될 것 같다. 

 


 

사용자 정보는 프로그램이 시작된 순간부터 종료될 때까지 단 하나(!)만 존재하게 되므로 프로그램 어디에서도 사용할 수 있도록 Global 형태로 선언했다.

 

아래는 사용자 정보를 관리하는 Class다.

public class User_Info
{
    public static User_Info gUser_Info = new User_Info();

    public bool IsUserLogin;            // 프로그램 로그인 여부
    public bool IsAutoTrading;          // 자동매매 진행 여부

    private string Id;                  // L-ATS 사용자 ID
    private string Grade;               // 회원등급
    private string MacAddr;             // 현재 접속 단말 MAC Address

    public DateTime Last_Login;

    //---------------------------------------------------------------

    public bool IsHTSLogin;             // HTS 로그인 여부

    private string EbestId;             // 이베스트 ID
    private string UserName;            // 사용자 이름
    private string CurAccount;          // 현재 사용 계좌 번호

    // 생성자
    public User_Info()
    {
        IsUserLogin = false;
        IsAutoTrading = false;
        IsHTSLogin = false;
        
        Id = string.Empty;
        Grade = string.Empty;
        MacAddr = string.Empty;

        EbestId = string.Empty;
        UserName = string.Empty;
        CurAccount = string.Empty;
    }

    // 소멸자
    ~User_Info()
    {

    }
    
    ... 생략 ...
}

bool Type의 변수를 제외한 모든 정보들은 private으로 선언하여 Set/Get 함수를 사용하도록 했다. 

참고로 나는 변수의 초기화를 매우 중요하게 생각하는 개발자다. 

 

그리고 언제든 바로 프로그램 상의 사용자 상태 정보를 확인할 수 있는 메뉴를 만들었다. 즉, 위의 User_Info() 클래스의 정보를 확인하는 메뉴이다. 


 

'메인메뉴 > 사용자 정보'  메뉴에서 User_Info() 클래스 정보 표시

 

먼저 L-ATS 로그인 후 사용자 정보를 보면 아래처럼 알 수 있는 정보만 표시가 된다.

 

L-ATS 로그인 후 User_Info() 정보

 

회원가입시 입력했던 Database의 정보들과 로그인 시에 입력한 HTS Server 정보 (모의투자/실투자)가 표시된다. 

 

이베스트 HTS를 로그인하고 보유계좌까진 선택한 후 '사용자 정보'를 확인해 보자.

이베스트 HTS 로그인. 모의투자기 때문에 공인인증번호는 입력하지 않아도 된다.
이베스트 HTS 로그인이 정상 처리되면, 보유한 계좌의 목록을 확인할 수 있다.

 

이제 계좌의 목록 중 하나를 골라서 '선택' 버튼을 클릭하면 비로소 User_Info()의 모든 정보가 완성된다.

 

모든 정보가 채워진 User_Info(). 하단의 StatusBar에도 함께 표시하여 사용자 인지성을 향상시켰다.

 

사실 '사용자 정보' 메뉴는 디버깅 용으로 만들었던 건데... 메뉴가 되어 버렸다.

'Trading' 카테고리의 다른 글

[Trading] XingAPI - DevCenter 사용하기  (0) 2020.08.14
[Trading] L-ATS 사용자 시나리오  (0) 2020.08.02
[Trading] HTS 로그인 설계, 구현  (0) 2020.07.08
[Trading] 프로그램 로그인  (0) 2020.06.21
[Trading] 두번째 프로그램  (0) 2020.06.07
Posted by [ 브랜든 ]
,

이베스트에서 제공하는 XingAPI에 의한 로그인 기능은 키움API에서 제공하는 기능과는 사뭇 다르다.

 

먼저 키움API의 경우에는 로그인 할때 키움 HTS에서 로그인하는 것과 같은 방식을 제공한다.

로그인과 동시에 HTS 서버에 접속하고 업데이트 버전이 있을 경우 업데이트를 진행하고 그 다음 로그인까지

완료하는 과정을 거친다.

 

L-ATS v1.0을 통해 확인해 보자.

 

'로그인' API를 호출하면 위와 같이 키움 HTS 로그인 화면을 제공한다. 

 

이베스트 XingAPI는 'HTS 서버 연결'과 'HTS 로그인'이 별개로 동작한다.

즉, HTS 서버 연결 상태를 고려하여 로그인/로그아웃 처리를 진행해야 한다. 

물론 HTS 서버에 연결이 되어야 로그인이 될 것이다. 아래 두 경우는 분명 다르게 동작해야 한다.

 

  • 서버가 연결되어 있지 않은 상태에서의 로그인/로그아웃 시도
  • 서버가 연결되어 있는 상태에서의 로그인/로그아웃 시도

간단해 보이지만 쉽게 생각할 수 없는 부분이다. 

 

이베스트 API의 경우 사용자 Form을 이용해서 별도의 로그인이 필요하다.


[ 대략적인 동작 과정 ]

 

1. 프로그램 실행 후 먼저 프로그램 로그인을 시도한다.

 

2. '이베스트 HTS 서버' 연결을 시도한다.

   2-1. 연결이 완료 될 경우, 해당 연결 서버의 정보를 표시한다.

   2-2. 연결이 되지 않을 경우, 환경 설정 값에 의해 서버 연결을 재시도 한다.

      2-2-1. 환경 설정 값에 의한 재연결 시도 횟수 초과 시 자동으로 프로그램을 종료한다.

 

3. '이베스트 HTS 서버' 연결을 별도로 관리한다.

   3-1. HTS 서버와의 연결 상태를 주기적으로 확인하고, 연결 유실 시 재연결을 시도하도록 관리한다.

   3-2. 화면 한편에 현재 HTS 서버와의 연결 상태를 한번에 확인 할 수 있도록 표시한다.

 

LOGIN Flow Chart

 

가장 신중하게 생각해야할 부분은 'Connection 관리' 부분이다. 

 

네트워크 개발을 하던 첫 회사에서 'Connection 관리'가 생각보다 단순하지 않고 쉽지 않다는 것을 많이 느꼈다.

지금 로직을 구성한다 해도 100% 완벽하게 할 수 있을지 의문이다.

 

Connection 관리는 아래 두 개의 환경 설정 값에 의해 동작하도록 해야한다.

  • Connection Check Interval (확인 시간 간격)
  • Connection Check Count (최대 확인 횟수)

솔직히 Connection 관리는 실시간(Realtime)성이 가장 중요한 시스템에서 필수적인 것이지만

Trading 시스템에서는 크게 중요하지 않을 수도 있다. 

때문에 'HTS 로그인' 상태에서 'HTS 서버' Connection이 끊길 경우(네트워크 장애 등) 프로그램을 자동 종료하여도 크리티컬하진 않을 것이다.

 

'L-ATS 로그인 -> HTS 접속 -> HTS 로그인' 까지 완료된 화면.

 

보유 계좌 리스트 조회 완료.

다음 포스팅은 '자산현황' 조회와 '잔고현황' 불러오기가 될 예정이다.

'Trading' 카테고리의 다른 글

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

C언어만 사용하던 나는 솔직히 OOP 개념이 익숙하거나 크게 와 닿지 않는다.

 

중복된 기능의 함수나 DB Handler, Logger 등의 기능 단위를 구분할 수 있는 것들을 모듈화하여

Library 형태로 만들어 컴파일 할 때 추가하여 사용하곤 했다. 

 

C#에서는 하나의 프로젝트 안에 여러 클래스를 생성하여 사용하는 것 같으니 방법을 정리해 보자.

 

delegate 테스트했던 프로젝트에 추가해서 진행하면 좋을것 같다.

2020/06/20 - [C#] - [C#] delegate, 폼 간의 DATA 공유

[C#] delegate, 폼 간의 DATA 공유

Trading 시스템을 내가 아닌 제 3자의 사용자적 입장에서 사용하도록 시뮬레이션 해 보았을 때 자식 Form을 새로 띄워서 값을 입력 받거나 확인할 수 있게 하는 작업이 많이 필요하단 것을 알게 되��

brandon-dev.tistory.com

Form2에서 입력했던 '아이디'와 '비밀번호'를 Form1에서 바로 출력하지 않고

새로운 클래스 파일(.cs)의 객체에 저장한 후에 그 값을 읽어와서 출력하는 방법으로 진행해 보자.

 

오른쪽 솔루션 탐색기에서 프로젝트 명의 오른쪽 클릭 후 [ 추가(D) ] - [ 새 항목(W) ]을 선택한다.

 

 

프로젝트 오른쪽 클릭 - [ 추가(D) ] - [ 새 항목(W) ]

 

 

클래스를 선택 후에 입력할 사용자 정보를 관리할 'User_Info.cs' 파일을 추가한다.

 

 

User_Info.cs 파일 추가

 

User_Info Class는 ID와 Password, 두 개의 변수를 갖고, 각각의 값을 Set/Get하는 함수를 갖게 될 것이다.

 

ID와 비밀번호는 사용하는 Class에서 직접 접근할 수 없도록 'private' 접근 제한자를 사용하고, 각각의 값을 Handling 할 수 있는 'public' 함수를 만들어 사용할 수 있도록 한다. (이게 객체지향 방법이라는데...나는 익숙치 않아서....Anyway)

 

[ User_Info.cs ]

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

namespace delegate_test
{
    class User_Info
    {
        private string Id;             // 사용자 ID
        private string Password;       // 사용자 비밀번호

        // 생성자
        public User_Info()
        {
            Id = string.Empty;
            Password = string.Empty;
        }

        public void SetId(string Id)
        {
            this.Id = Id;
        }

        public string GetId()
        {
            return this.Id;
        }

        public void SetPassword(string Password)
        {
            this.Password = Password;
        }

        public string GetPassword()
        {
            return this.Password;
        }
    }
}

참고로 나는 변수의 초기화를 굉~~장히 중요하게 생각하는 개발자다. (생성자에서 변수 초기화는 무조건 필수!!)

 

이제 User_Info Class를 어떻게 사용하는가...

두 가지의 방법이 있는데, 하나는 User_Info Class 내에 static 객체를 하나 생성해두고 다른 곳에서 호출해서 쓰는 방식이고 다른 하나는 User_Info Class를 사용할 곳에서 객체를 직접 생성해서 사용하는 방식이 있다. 

 

< 방법 1> - Static 객체

[User_Info.cs]

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

namespace delegate_test
{
    class User_Info
    {
        // static 객체
        public static User_Info gUser_Info = new User_Info();

        private string Id;             // 사용자 ID
        private string Password;       // 사용자 비밀번호

        // 생성자
        public User_Info()
        {
            Id = string.Empty;
            Password = string.Empty;
        }

        public void SetId(string Id)
        {
            this.Id = Id;
        }

        public string GetId()
        {
            return this.Id;
        }

        public void SetPassword(string Password)
        {
            this.Password = Password;
        }

        public string GetPassword()
        {
            return this.Password;
        }
    }
}

어디서든 접근 가능하도록 public으로 만든 static 객체를 만들어 둔다.

 

사용하는 방법은 간단하다.

User_Info 객체를 사용하고 싶은 곳에서 [클래스명].[객체명].[함수/변수]와 같은 형태로 사용하면 된다.

[Form1.cs]

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;

namespace delegate_test
{
    public delegate void LoginGetEventHandler(string id, string password); // 자식 Form EventHandler

    public partial class Form1 : Form
    {
        static string static_id = "brandon";
        Form2 Child = new Form2(static_id);

        public Form1()
        {
            InitializeComponent();
            Form2.LoginGetEvent += new LoginGetEventHandler(this.Login_Process); // EventHandler 등록
        }

        private void 로그인ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Child.Owner = this;
            Child.ShowDialog();                     
        }

        private void Login_Process(string id, string password) // Event 발생 시, 처리할 함수 선언
        {
            // User_Info 객체에 입력 받은 정보 저장
            User_Info.gUser_Info.SetId(id);
            User_Info.gUser_Info.SetPassword(password);


            // User_Info 객체에서 값 가져오기
            string test_result = string.Empty;
            //test_result = String.Format("ID : {0}, PW : {1}", id, password);
            test_result = String.Format("ID : {0}, PW : {1}", User_Info.gUser_Info.GetId(), User_Info.gUser_Info.GetPassword())
            textBox1.Text = test_result;  // 자식 Form에서 수신한 값 표시
        }
    }
}

 

이전과 동일하게 비밀번호를 'asdf1234!@#$'로 입력 후 로그인 버튼 클릭

 

전과 동일하게 아이디랑 비밀번호가 출력되면 정상적으로 User_Info Class를 사용하는 셈.

 

 

자식 Form에서 입력한 값을 부모 Form에서 받아 User_Info 객체에 Set/Get 후 출력 

 

 

사용자 정보의 경우, 프로그램내에서 단 하나만(!) 존재하는 것이기에 위의 예제처럼 static 객체로 선언해서 쓰는것이 좋을 것 같다. 상황에 따라 여러개의 객체가 필요한 경우는 아래처럼 사용하면 된다.

 

[Form1.cs]

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;

namespace delegate_test
{
    public delegate void LoginGetEventHandler(string id, string password); // 자식 Form EventHandler

    public partial class Form1 : Form
    {
        // User_Info 객체 생성
        User_Info gUser_Info = new User_Info();

        static string static_id = "brandon";
        Form2 Child = new Form2(static_id);

        public Form1()
        {
            InitializeComponent();
            Form2.LoginGetEvent += new LoginGetEventHandler(this.Login_Process); // EventHandler 등록
        }

        private void 로그인ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Child.Owner = this;
            Child.ShowDialog();                     
        }

        private void Login_Process(string id, string password) // Event 발생 시, 처리할 함수 선언
        {
            // User_Info 객체에 입력 받은 정보 저장
            gUser_Info.SetId(id);
            gUser_Info.SetPassword(password);


            // User_Info 객체에서 값 가져오기
            string test_result = string.Empty;
            //test_result = String.Format("ID : {0}, PW : {1}", id, password);
            test_result = String.Format("ID : {0}, PW : {1}", gUser_Info.GetId(), gUser_Info.GetPassword());
            textBox1.Text = test_result;  // 자식 Form에서 수신한 값 표시
        }
    }
}

 

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 [ 브랜든 ]
,

Trading 시스템을 내가 아닌 제 3자의 사용자적 입장에서 사용하도록 시뮬레이션 해 보았을 때

자식 Form을 새로 띄워서 값을 입력 받거나 확인할 수 있게 하는 작업이 많이 필요하단 것을 알게 되었다. 

(기능적으로 그렇지만 보기에, 사용하기에 신규Form을 보여주는 것이 더 깔끔한...)

 

  • 부모 Form : Form1 (Delegate Test) 

'로그인' 버튼과 결과를 출력해줄 TextBox를 갖는 부모 Form

  • 자식 Form : Form2 (Login)

'아이디'와 '비밀번호'를 입력할 수 있는 TextBox와 로그인 버튼을 갖는 자식 Form

 

테스트 구현 방식은

부모 Form(Form1)에서 아이디 값을 넘겨주어 자식 Form(Form2)에서는 아이디가 자동으로 입력되고

자식 Form에서 비밀번호 입력 후 '로그인' 버튼을 누르면 

자식 Form이 사라짐과 동시에 부모 Form의 TextBox에 입력받은 비밀번호를 출력하도록 한다. 

 

그러면 두 Form 간에 데이터를 주고 받는 테스트가 완료!

 

1. 부모 Form -> 자식 Form

 

여러 다른 방법이 있는지는 모르겠지만, 내가 구현한 방법은 생각보다 간단하다.

 

[ 부모 Form ]

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;

namespace delegate_test
{
    public partial class Form1 : Form
    {
        static string static_id = "brandon";          // 자식 Form에 넘겨줄 ID 값
        Form2 Child = new Form2(static_id);           // 자식 Form 생성자

        public Form1()
        {
            InitializeComponent();

        }

        private void 로그인ToolStripMenuItem_Click(object sender, EventArgs e)  // 로그인 버튼을 눌렀을 때
        {
            Child.Owner = this;
            Child.ShowDialog();                     
        }
    }
}

[ 자식 Form ]

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;

namespace delegate_test
{
    public partial class Form2 : Form
    {
        string global_id = string.Empty;

        public Form2(string id)           // 자식 Form의 생성자 - 부모 Form으로부터 값을 전달 받음
        {
            InitializeComponent();
            global_id = id;
        }

        private void Form2_Load(object sender, EventArgs e) // Form2가 보여질때 설정
        {
            textBox1.Text = global_id;    // Form2에 부모 Form으로부터 받은 id 값을 미리 설정
        }

        private void button1_Click(object sender, EventArgs e)
        {

        }
    }
}

[ 결과 ]

자식 Form(Login)이 표시됨과 동시에 부모 Form에서 받은 'brandon'이란 ID 값이 자동으로 입력됨.

 

2. 자식 Form -> 부모 Form

 

이 경우에는 delegate라는것을 사용하는데, C++에서의 함수포인터와 비슷한 개념이다.

'대리자'라는 뜻의 이 delegate는 메소드를 대신해서 호출해 주는 역할을 한다고 생각하면 된다. 

 

[ 부모 Form ]

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;

namespace delegate_test
{
    public delegate void LoginGetEventHandler(string id, string password); // 자식 Form EventHandler

    public partial class Form1 : Form
    {
        static string static_id = "brandon";
        Form2 Child = new Form2(static_id);

        public Form1()
        {
            InitializeComponent();
            Form2.LoginGetEvent += new LoginGetEventHandler(this.Login_Process); // EventHandler 등록
        }

        private void 로그인ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Child.Owner = this;
            Child.ShowDialog();                     
        }

        private void Login_Process(string id, string password) // Event 발생 시, 처리할 함수 선언
        {
            string test_result = string.Empty;
            test_result = String.Format("ID : {0}, PW : {1}", id, password);
            textBox1.Text = test_result;  // 자식 Form에서 수신한 값 표시
        }
    }
}

[ 자식 Form ]

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;

namespace delegate_test
{
    public partial class Form2 : Form
    {
        public static LoginGetEventHandler LoginGetEvent;  // 부모 Form에서 등록한 EventHandler 선언

        string global_id = string.Empty;

        public Form2(string id) 
        {
            InitializeComponent();
            global_id = id;
        }

        private void Form2_Load(object sender, EventArgs e) 
        {
            textBox1.Text = global_id;
        }

        private void button1_Click(object sender, EventArgs e) // 로그인 버튼을 눌렀을 때
        {
            this.Close();
            LoginGetEvent(textBox1.Text, textBox2.Text);  // 입력된 ID와 PASSWORD 값을 EventHandler에 전달
        }
    }
}

[ 결과 ]

비밀번호를 'asdf1234!@#$'로 입력 후 로그인 버튼 클릭
자식 Form에서 입력한 값을 부모 Form에서 받아서 출력

 

나도 C#을 처음 해보는 것이기에 더 좋고, 간단하며, 오류 없는 방법이 있을지는 모르겠다. 

Trading 시스템의 '사용자 로그인' 부분을 구현하면서 필요한 기능이었기에 어떻게 활용하였는지는

아래 링크에서 확인해 보자. 

Posted by [ 브랜든 ]
,

[C#] MAC Address 가져오기

C# 2020. 6. 15. 23:51

Trading 시스템을 만들면서 사용자 인증방식을 지정된 단말로만 가능하도록

'MAC 인증' 방식을 택했다.

 

물론 내가 C#을 처음써서인 것도 있겠지만, MAC 주소를 읽어올때 마다 값이 다른 것을 확인했다.

이유인 즉, PC에 설치된 VPN...가상 환경 때문이었다.

 

구글링 결과 C#에서 기본적인 단말 MAC 주소를 가져오는 로직은 NetworkInterface들중에 0번째 것을 가져오도록 하는 소스가 대부분이었다. 

 

[ 방법 1 ]

string mac = string.Empty;
mac = NetworkInterface.GetAllNetworkInterfaces()[0].GetPhysicalAddress().ToString();

가상 환경이 설치되어 있는 PC의 경우, 해당 가상환경 사용중인 여부에 따라 값이 다르게 읽어지는 것을 확인했다.

 

그래서 찾은 방법은 현재 상태가 사용중인 네트워크 어댑터의 MAC 주소를 가져오는 것.

 

[ 방법 2 ]

var macAddr =
    (
        from nic in NetworkInterface.GetAllNetworkInterfaces()
        where nic.OperationalStatus == OperationalStatus.Up
        select nic.GetPhysicalAddress().ToString()
    ).FirstOrDefault();

OperationalStatus가 "Up"으로 설정되어 있는 놈을 가져오는 것이다. 

위의 경우에도 현재 시스템의 상태에 따라 정확하게 MAC 주소를 가져오는 것을 확인했다.

실제 물리적 네트워크 어댑터만 사용 중

[ 방법 1 ]을 사용했을 때는 '이더넷 3'인 가상 네트워크 어댑터의 MAC주소를 가져왔지만, [ 방법 2 ]를 사용했을 경우에는 현재 사용중인 '이더넷 2' 네트워크 어댑터의 MAC주소를 가져왔다.

 

하지만, 가상환경도 함께 연결되어 있는 경우, 보다 정확하게 하기 위해 현재 데이터 송/수신량까지 확인하여 가장 많이 사용되고 있는 네트워크 어댑터의 MAC 주소를 가져오는 방식을 사용하는 방법도 있다. 

가상환경 네트워크 어댑터 동시 사용 중

[ 방법 3 ]

Dictionary<string, long> macAddresses = new Dictionary<string, long>();
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
    if (nic.OperationalStatus == OperationalStatus.Up)
        macAddresses[nic.GetPhysicalAddress().ToString()] = nic.GetIPStatistics().BytesSent + nic.GetIPStatistics().BytesReceived;
}

long maxValue = 0;
string mac = "";
foreach(KeyValuePair<string, long> pair in macAddresses)
{
   if(pair.Value > maxValue)
    {
        mac = pair.Key;
        maxValue = pair.Value;
    }
}

먼저 딕셔너리에 네트워크 어댑터들의 MAC 주소와 데이터 송/수신량을 쌍(Pair)로 저장 후,

비교하여 송/수신량이 가장 많은 MAC 주소를 가져오는 로직이다. 

 

아래 웹 페이지에 MAC 주소를 가져오는 다양한 방법이 기술되어 있으니, 본인의 개발 방향과 맞는 방법을 사용하면 좋을 것 같다.

 

[ 출처 ]

https://www.it-swarm.dev/ko/c%23/c-%EC%97%90%EC%84%9C-%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-mac-%EC%A3%BC%EC%86%8C%EB%A5%BC-%EC%96%BB%EB%8A%94-%EC%95%88%EC%A0%95%EC%A0%81%EC%9D%B8-%EB%B0%A9%EB%B2%95/957550098/

Posted by [ 브랜든 ]
,

Class...객체지향에서 빼놓을 수 없는.

Class에는 항상 상속, 오버로딩, 오버라이딩 그리고 캡슐화가 항상 붙어 다닌다.

 

절차적 언어인 C언어를 주 언어로 사용하던 나는

솔직히 객체지향 방식이 쉽게 와닿지 않았다. (사실은 지금도 그렇다....)

그래서 Python을 써도 C언어를 주로 쓰던 시절처럼 Library 함수 형태로 만드는 것이 익숙하고

Class 형식의 Library를 만드는 것이 덜 익숙하다. 

 

그래도 Class에 대해 정리를 해두면 필요할 경우 곧잘 사용할 수 있겠지. 

 

C언어를 사용하던 나에게는 구조체(Struct)라는 개념은 아주 많이 익숙하다. 

때때로 C# 책을 만든 저자들은 클래스와 구조체를 크게 구분하지 않으려 하지만 나와 같은 개발자에게는

분명하게 개념상의 차이점이 있다. 

 

  • 클래스 및 구조체에는 데이터와 동작을 나타내는 멤버가 있다. (사실 8년여간 C 프로그래밍을 해오면서 단 한번도 구조체 않에 멤버 함수를 선언해서 사용해 본 적이 없다!!)
  • 클래스가 상속 받았다면, 클래스에서 선언된 모든 멤버와 함께 상속받은 클래스에서 선언된 모든 멤버들(생성자 및 소멸자 제외)도 클래스의 멤버에 포함된다.
멤버 설명
필드 필드는 클래스 또는 구조체에서 직접 선언되는 모든 형식의 변수이다. 필드는 기본 제공 형식 또는 다른 클래스의 인스턴스일 수 있다.
상수 상수는 값이 컴파일 시간에 설정되며, 변경할 수 없는 필드나 속성이다.
속성 해당 클래스의 필드처럼 액세스되는 클래스의 메소드로 객체 모르게 필드가 변경되지 않도록 할 수 있다.
메소드 클레스가 수행하는 작업을 정의한다. 입력으로 매개변수를 사용할 수 있고, 매개변수를 통해 출력 데이터를 반환할 수 있다. 매개변수를 통하지 않고 직접 값을 반환할 수도 있다.
이벤트 이벤트는 버튼 클릭, 성공적인 메소드 완료 등의 사건이 발생했을 때 알림을 다른 객체에 제공한다.
이벤트는 대리자(delegate)를 사용하여 정의 및 트리거 된다.
연산자 오버로드 된 연산자는 클래스 멤버로 간주되며, 클래스에서 'public static' 메소드로 정의한다.
인덱서 인덱서를 사용하면 배열과 유사한 방식으로 객체를 인덱싱할 수 있다. 
생성자 생성자는 객체를 처음 만들 때 호출되는 메소드이다. 보통 객체의 데이터를 초기화하는데 사용한다.
소멸자 C#에서 드물에 사용되며, 메모리에서 객체를 제고할 때 런타임 실행 엔진이 호출하는 메소드이다.

 

C# 공부를 위해 구매한 책에서 발췌한 내용이다. 

다른 것들은 한번쯤은 들어보거나 사용해봤던 것들인데, '이벤트'와 '인덱서'는 조금 새롭다. 

 

C 언어로 프로그래밍 할 때 '구조체 배열'을 자주 사용했었는데, '인덱서'를 사용하면 비슷한 기능을 구현할 수 있을 것 같다. 

'이벤트' 요소는 .NET 계열에서 특화적으로 사용되는 것으로 생각되는데(그냥 내 생각....), 내가 구현하고자 하는 Trading System에서 아주 많이 사용될 기능으로 보인다. 

 

  1. 필드 (Field)
  • 인스턴스(객체) 필드 : 객체 이름과 함께 사용. public string name과 같이 [접근제한자][자료형][필드명]으로 생성하고 [인스턴스].[필드명]으로 사용.
  • 정적(클래스) 필드 : 클래스 이름과 함께 사용. static 키워드 필수. [클래스이름].[필드명]
  1. 상수
  • 컴파일 시간에 알려진 변경할 수 없는 값. 프로그램 수행되는 공안 갑승ㄹ 변경하지 않는 경우 사용.
  • const 한정자 사용.
  • bool, byte, char, int double, string만 const 사용 가능.
  • Access : [클래스이름].[상수명]

 

 

Posted by [ 브랜든 ]
,
  • 프로그램 명 : L-ATS (Lim's Automatic Trading System)
  • 버전 : 2.0
  • 환경 : Visual C#
  • 주요기능 : HTS/MTS 매수 종목 정보 실시간 수신, 설정값 기반 주식 매도, Flexable 스탑로스
  • API : 이베스트 증권 XingAPI
  • 제작기간 : 2020.05 ~ 

 

1차 화면 구성 초안이 완성 됐다. 

 

v1.0이 있어서 구성은 어렵지 않았지만, v2.0은 실제 다른 여러 사람이 사용할 수 있을 거란(그럴리 없겠지만...;;;)

생각을 전제로 시작부터 많은 기능을 구상했다. 

 

프로그램 사용 절차부터 가입 사용자 로그인 부분과 HTS 접속을 구분하고,

사용자 구분에 따라 스탑로스 사용 범위를 나눌 생각이다. 

그렇게 기본 스탑로스는 모두에게 공개하며, 스탑로스 Flex는 공개되지 않을 예정이다. 

무엇보다 공개 Beta 서비스는 모의 투자만 가능하도록 할 것이다. 

 

스탑로스 자체는 증권사 HTS/MTS에서도 제공되는 기능이지만,

v1.0에서 구현했던 세분화된 스탑로스 조건을 적용할 수 있는 방식을 다시 사용하여 차별화를 둘 예정이다. 

 

설계랑 구현을 동시에 할라니 정신이 없구나...

'Trading' 카테고리의 다른 글

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

WinApp이나 Explorer에서 항상 해보고 싶던 팝업 띄우기.

 

제공하는 라이브러리가 있을테니 별것 아닐거라 생각은 했지만 실제로 해보게 되니

그 또한 즐겁구나.

 

1. 팝업을 띄울 버튼 생성

 

모달, 모달리스 'Button' 생성

 

2. 팝업으로 보여줄 새로운 Form 추가

 

Project > Add Windows Form

새로운 Form2를 생성

 

3. 팝업창 만들기

 

 

4. 팝업을 띄우는 방법

모달 모달리스
1. showDialog()
2. 새로운 Form이 프로그램의 제어권을 독점.
3. 다른 작업 불가능.
4. 중요한 메시지를 표시할 때 사용.
1. show()
2. 새로운 Form이 프로그램의 제어권을 독점하지 않음.
3. 다른 작업 가능.
4. 알림 또는 정보를 표시하는데 사용.
using System.Windows.Forms;

namespace WinFormTest
{
	public partial class Form1 : Form
    {
    	public Form1()
    	{
            InitializeComponent();
        }
        
        // 모달
        private void button1_Click(object sender, EventArgs e) 
        {
            Form2 modal = new Form2();
            modal.showDialog()
        }
        
        //모달리스
        private void button2_Click(object sender, EventArgs e)
        {
            Form2 modalless = new Form2();
            modalless.show();
        }
    }
}

 

5. 창 닫기

 

팝업 화면에 닫기 버튼이 있다면...

using System.Windows.Forms;

namespace WinFormTest
{
    public partial class Form2: Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

 

  • 그럼...팝업의 닫기 버튼으로 Form1까지 종료시킬 수 있을까..?

     - 이벤트를 추가하면 된다는데...?

 

6. 원하는 위치에 보이고 싶을때

 

사용자 입장에서 볼 때, 상위(부모) Form의 가운데에 팝업을 띄우는 것이 가장 보기 좋을 것이다.

방법...?

C#에서 제공해 주는 라이브러리가 없다면....

상위(부모) Form의 크기와 팝업 Form의 크기를 구해서 위치를 변경해주는 수밖에...

이것 저것 해보다 보니 C#에서 제공되는 쉬운 방법이 있었다.

private void button1_Click(object sender, EventArgs e) 
{
    Form2 modal = new Form2();
    modal.StartPosition = FormStartPosition.CenterParent; // 부모 Form의 가운데
    // modal.StartPosition = FormStartPosition.CenterScreen; // 모니터의 가운데
    modal.showDialog()
}

와우...정말 손쉽게 이런 기능을...

C언어 쓰던 자괴감이 든다...

'C#' 카테고리의 다른 글

[C#] delegate, 폼 간의 DATA 공유  (2) 2020.06.20
[C#] MAC Address 가져오기  (0) 2020.06.15
[C#] Class, 클래스, 구조체  (0) 2020.06.08
[C#] 값을 표현하기 위한 방법  (0) 2020.05.28
[C#] 눈에 보이는 재미  (0) 2020.05.24
Posted by [ 브랜든 ]
,

GUI로 표현되는 개발 언어들은 다양하고 편리한 기능들을 제공해준다.

 

예를 들면, 값을 표시해주는 방법 같은 것.

 

1. 그룹 표시자

 

   숫자의 정수부를 표시할 때, 특히 금액을 표시할 때 세자리마다 콤마(,)를 넣는 것이 인식하기 편리하다.

   C#에서 제공하는 표준 형식지정자 중에 N이 그룹 분리자를 표시해준다.

  • 표준 형식 지정자 'N'은 Default로 소수점 아래 두자리까지 표시해 준다. 
double value = 1234.5678;

string.Format("{0:N}", value);      // 출력 : 1,234.57  (Default 소수점 두자리)
string.Format("{0:N0}", value);     // 출력 : 1,235     (소수점 삭제)
string.Format("{0:N3}", value);     // 출력 : 1,234.568 (소수점 세자리)

 

2. String.Format()

 

   String.Format() 메소드는 개발자가 지정한 형식에 따라 객체, 변수, 수식의 값을 문자열로 변환하여 문자열 변수에

   저장해 준다.

  •    통화 (Currency)는 'C'
  •    날짜 (Date)는 'd'
  •    시간 (Time)은 't'
  •    % (Percent)는 'P'
String str;
Decimal ExchangeRate = 1240.00m;
double percent = 1.25d;

str = String.Format("원달러 환율 : {0:C}", ExchageRate);    // W1,240
str = String.Format("원달러 상승률 : {0:P}", percent);      // 1.25%
str = String.Format("날짜 : {0:d}", DateTime.Now);          // 2020-05-28
str = String.Format("시간 : {0:t}", DateTime.Now);          // 오후 8시 12분

 

사용자 지정 형식 문자열은 종류도 많을 것이고 어떻게 활용하냐에 따라 나열하기에는 끝도 없을 것이다.

우선 필요한것 만 정리할 뿐...

'C#' 카테고리의 다른 글

[C#] delegate, 폼 간의 DATA 공유  (2) 2020.06.20
[C#] MAC Address 가져오기  (0) 2020.06.15
[C#] Class, 클래스, 구조체  (0) 2020.06.08
[C#] 팝업창, 새로운 Form 열기, 모달vs모달리스  (0) 2020.06.01
[C#] 눈에 보이는 재미  (0) 2020.05.24
Posted by [ 브랜든 ]
,