개인 프로젝트/C# 키움 주식 패턴 분석

C# - 키움증권 API를 이용한 실시간 체결 분석 프로그램 개발일지 - 실시간 체결정보 DB에 넣기

마스터욱 2023. 3. 30. 01:54
반응형

데이터베이스는 Access DB 를 사용했습니다.

SQL Lite 를 사용하려다가 설치가 좀 복잡해서, 걍 닷넷에서 기본으로 제공하는 Access DB 로 만들었습니다.

 

일단 실시간 체결정보를 땡겨오는

"axKHOpenAPI1_OnReceiveRealData" 함수가 핵심입니다.

1시간만 넣었는데도 2Mbyte 공간을 차지하더군요...

 

다음차례는 이제 쌓여진 체결정보를 바탕으로 체결정보 분석 알고리즘을 적용해 보겠습니다.

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
 
namespace Auto_Stock
{
public partial class MainForm : Form
{
        private string LoginName;
        private string LoginId;
 
        private string accFileName = "stock_db.accdb";
        private string dbPasswd = "admin";
 
        public MainForm()
        {
            InitializeComponent();
 
            /*
            OleDbConnection conn = new OleDbConnection();
            OleDbCommand connCmd = new OleDbCommand();
            Console.WriteLine("mainform start");
            try
            {
                Console.WriteLine("db access start");
                conn.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" + this.accFileName + ";Mode=ReadWrite;Jet OLEDB:Database Password=" + this.dbPasswd;
                conn.Open();
                connCmd.Connection = conn;
                connCmd.CommandText = "INSERT INTO favorite(usr_id) VALUES(@usr_id)";
                connCmd.Parameters.Add(new OleDbParameter("@usr_id", "test"));
                connCmd.ExecuteNonQuery();    // 입력
                connCmd.CommandText = "SELECT * FROM favorite";
                OleDbDataReader oleData = connCmd.ExecuteReader(CommandBehavior.CloseConnection);
                while (oleData.Read())
                {
                    Console.WriteLine(oleData["userId"].ToString());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                conn.Close();
            }
            */
        }
 
        private void database_proc(string sql, Dictionary<stringstring> param)
        {
            OleDbConnection conn = new OleDbConnection();
            OleDbCommand connCmd = new OleDbCommand();
            try
            {
                conn.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" + accFileName + ";Mode=ReadWrite;Jet OLEDB:Database Password=" + dbPasswd;
                conn.Open();
                connCmd.Connection = conn;
 
                //connCmd.CommandText = "INSERT INTO favorite(usr_id) VALUES(@usr_id)";
                connCmd.CommandText = sql;
                foreach (KeyValuePair<stringstring> row in param)
                {
                    //connCmd.Parameters.Add(new OleDbParameter("@usr_id", "test"));
                    connCmd.Parameters.Add(new OleDbParameter(row.Key, row.Value));
                }
                connCmd.ExecuteNonQuery();    // 입력
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception 에러발생 : " + ex.Message);
            }
            finally
            {
                conn.Close();
            }
        }
 
        private Dictionary<intDictionary<stringstring>> database_select(string sql, Dictionary<intstring> columns)
        {
            OleDbConnection conn = new OleDbConnection();
            OleDbCommand connCmd = new OleDbCommand();
            Dictionary<intDictionary<stringstring>> list = new Dictionary<intDictionary<stringstring>>();
            try
            {
                Console.WriteLine("select start");
                conn.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" + this.accFileName + ";Mode=ReadWrite;Jet OLEDB:Database Password=" + this.dbPasswd;
                conn.Open();
                connCmd.Connection = conn;
 
                //connCmd.CommandText = "SELECT * FROM favorite";
                connCmd.CommandText = sql;
                OleDbDataReader oleData = connCmd.ExecuteReader(CommandBehavior.CloseConnection);
                int i = 0;
                while (oleData.Read())
                {
                    //Console.WriteLine("usr_id = " + oleData["usr_id"].ToString());
                    Dictionary<stringstring> list_row = new Dictionary<stringstring>();
                    foreach(KeyValuePair<intstring> row in columns)
                    {
                        string column = row.Value;
                        list_row[column] = oleData[column].ToString();
                    }
                    list[i++= list_row;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                conn.Close();
            }
 
            return list;
        }
 
        //로그인 버튼 클릭
        private void ButtonLogin_Click(object sender, EventArgs e)
        {
            if(LoginName != null)
            {
                MessageBox.Show("이미 로그인 하셨습니다.");
                return;
            }
 
            long Result;
            Result = axKHOpenAPI1.CommConnect();
            if (Result != 0)
            {
                Console.WriteLine("로그인창 열기 실패!");
            }
            else
            {
                Console.WriteLine("로그인창 열기 성공!");
            }
        }
 
        //로그인 후 개인정보 가져오기
        private void axKHOpenAPI1_OnEventConnect(object sender, AxKHOpenAPILib._DKHOpenAPIEvents_OnEventConnectEvent e)
        {
            Console.WriteLine("개인정보 가져오기 성공!");
            string UserName = axKHOpenAPI1.GetLoginInfo("USER_NAME");
            string UserId = axKHOpenAPI1.GetLoginInfo("USER_ID");
            //ButtonLogin.Text = UserName + "(" + axKHOpenAPI1.GetLoginInfo("USER_ID") +  ")";
            ButtonLogin.Text = axKHOpenAPI1.GetLoginInfo("USER_ID");
            LoginName = UserName;
            LoginId = UserId;
        }
 
        //전체종목리스트에서 주식 row를 클릭하면 실행
        private void listBoxStock_SelectedIndexChanged(object sender, EventArgs e)
        {
        }
 
        //종목리스트 불러오기
        bool IsListCall = false;
        bool 전체종목리스트_초기체크 = true;
        Dictionary<stringstring> CodeToString = new Dictionary<stringstring>();
        private void ButtonListCall_Click(object sender, EventArgs e)
        {
            if (LoginName == null)
            {
                MessageBox.Show("로그인이 필요합니다.");
            }
            else
            {
                if(IsListCall == true)
                {
                    //MessageBox.Show("이미 호출된 상태입니다.");
                    //return;
                }
 
                Dictionary<stringstring> favorite_list = listVIew1_select();
                //Console.WriteLine(favorite_list);
 
                IsListCall = true;
 
                전체종목리스트_초기체크 = true;
                listView1.Items.Clear();
                ListViewStock.Items.Clear();
                ListViewStock.CheckBoxes = true// 항목옆에 확인란을 표시할지 여부를 선택합니다.
                //ListViewStock.Click += new EventHandler(ListViewCheckClick);
                //ListViewStock.OwnerDraw = true;
 
                //GetCodeListByMarket 메소드는 메소드의 인자로 시장 구분 코드를 문자열로 넘겨주면 메소드의 리턴 값으로 해당 시장에 속해 있는 종목들의 종목 코드 목록을 리턴
                //sMarket – 0:장내, 3:ELW, 4:뮤추얼펀드, 5:신주인수권, 6:리츠, 8:ETF, 9:하이일드펀드, 10:코스닥, 30:제3시장
                Console.WriteLine("종목 불러오기 시작");
                string[] marketList = { "0""10" };
                int InCnt = 0;
                int OutCnt = 0;
                int i = 0;
                foreach (string MNumber in marketList)
                {
                    string result = axKHOpenAPI1.GetCodeListByMarket(MNumber);
                    string[] stockList = result.Split(';');
 
                    if (MNumber == "0")
                    {
                        InCnt = stockList.Count();
                    }
                    else
                    {
                        OutCnt = stockList.Count();
                    }
 
                    int listView1_cnt = 0;
                    foreach (string code in stockList)
                    {
                        if (code != "")
                        {
                            string StockName = axKHOpenAPI1.GetMasterCodeName(code);
 
                            CodeToString[code] = StockName;
 
                            //전체종목리스트 ListView row 만들자
                            ListViewItem item = new ListViewItem(""0);
                            item.Checked = false// 체크박스는 일단 false
                            if (favorite_list.ContainsKey(code))
                            {
                                item.Checked = true;
                                listView1_add(listView1_cnt.ToString(), code, favorite_list[code]); //관심종목추가
                                listView1_cnt++;
 
                                axKHOpenAPI1.SetRealReg("0120", code, "20;15""1");
                            }
                            else
                            {
                                item.Checked = false;
                            }
 
                            //SubItem
                            item.SubItems.Add(i.ToString());
                            item.SubItems.Add(StockName);
                            item.SubItems.Add(code);
 
                            ListViewStock.Items.Add(item);
                            i++;
                        }
                    }
                }
 
                전체종목리스트_초기체크 = false;
 
                //axKHOpenAPI1.SetInputValue("종목코드", 000030.ToString());
                //axKHOpenAPI1.CommRqData("000030_주식기본정보", "opt10001", 0, "0101");
                //axKHOpenAPI1.SetRealReg("0120", "000030", "9001;302;10;11;25;12;13", "1");  // 000030 종목을 실시간 추가등록
            }
        }
 
        //ListView의 이벤트는 [디자인]창에서 속성모드에서 function 명을 추가해서 사용하자.
        //[전체종목리스트] 체크박스 클릭시 발생
        private void ListViewStock_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            //Console.WriteLine("ListViewStock_ItemChecked");
            //Console.WriteLine(e.Item.Checked); //true or false
            if(전체종목리스트_초기체크 == false//초기로딩시에는 실행이 되면 안됨!
            {
                //Console.WriteLine(e.Item.SubItems.Count); //라인의 컬럼개수(4개)
                //Console.WriteLine(e.Item.Index); //라인 row index 번호
                //Console.WriteLine(e.Item.SubItems[2].Text); //유한양행
                if (e.Item.Checked == true)
                {
                    string sql = "INSERT INTO favorite(usr_id, stock_code, stock_name) VALUES(@usr_id, @stock_code, @stock_name)";
                    Dictionary<stringstring> param = new Dictionary<stringstring>();
                    param["@usr_id"= LoginId;
                    param["@stock_code"= e.Item.SubItems[3].Text; //000060
                    param["@stock_name"= e.Item.SubItems[2].Text; //하이트진로
                    database_proc(sql, param);
                }
                else
                {
                    string sql = "DELETE FROM favorite WHERE usr_id = @usr_id AND stock_code = @stock_code";
                    Dictionary<stringstring> param = new Dictionary<stringstring>();
                    param["@usr_id"= LoginId;
                    param["@stock_code"= e.Item.SubItems[3].Text; //000060
                    database_proc(sql, param);
                }
 
                listView1_loading();
            }
        }
 
        private Dictionary<stringstring> listVIew1_select()
        {
            Dictionary<intstring> columns = new Dictionary<intstring>();
            int ci = 0;
            columns[ci++= "stock_code";
            columns[ci++= "stock_name";
            Dictionary<intDictionary<stringstring>> list = database_select("SELECT * FROM favorite WHERE usr_id = '" + LoginId + "'", columns);
            Dictionary<stringstring> favorite_list = new Dictionary<stringstring>();
            foreach (KeyValuePair<intDictionary<stringstring>> row in list)
            {
                Console.WriteLine(row.Value["stock_code"+ " => " + row.Value["stock_name"]);
                favorite_list[row.Value["stock_code"]] = row.Value["stock_name"];
            }
 
            return favorite_list;
        }
 
        private void listView1_loading()
        {
            listView1.Items.Clear();
            Dictionary<stringstring> favorite_list = listVIew1_select();
            int i = 0;
            foreach (KeyValuePair<stringstring> row in favorite_list)
            {
                string number = i.ToString();
                listView1_add(number, row.Key, row.Value);
                i++;
            }
        }
 
        //관심종목 추가
        private void listView1_add(string number, string code, string codeString)
        {
            ListViewItem item2 = new ListViewItem(number, 0);
            item2.SubItems.Add(code);
            item2.SubItems.Add(codeString);
 
            listView1.Items.Add(item2);
        }
 
        //실시간 시세 이벤트라는데 뭔지 잘 몰겠다.
        //이게 보니깐 주기적으로 호출이 된다... 아마도 특정초단위로 알아서 호출되는 건가보다...
        private void axKHOpenAPI1_OnReceiveRealData(object sender, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveRealDataEvent e)
        {
            //Console.WriteLine("sRealKey : " + e.sRealKey);
            //Console.WriteLine("sRealType : " + e.sRealType);
            //Console.WriteLine("sRealData : " + e.sRealData);
 
            //e.sRealKey = 종목코드
            //e.sRealType = 리얼타입 (주식시세, 주식체결 등)
            //e.sRealData = 실시간 데이터 전문
 
            if (e.sRealType == "주식체결")
            {
                string 현재가 = axKHOpenAPI1.GetCommRealData(e.sRealKey, 10);
                string 체결량 = axKHOpenAPI1.GetCommRealData(e.sRealKey, 15);
                string 체결시간 = axKHOpenAPI1.GetCommRealData(e.sRealKey, 20);
 
                Console.WriteLine("=======================" + e.sRealKey + "==================================");
                Console.WriteLine("현재가 : " + 현재가);
                Console.WriteLine("체결량 : " + 체결량);
                Console.WriteLine("체결시간 : " + 체결시간);
 
                /*
                 * =======================000030==================================
                 * 현재가   : +12800
                 * 체결량   : +116
                 * 체결시간 : 121850
                 * 
                 * stock_sign 테이블 스키마
                 * idx, code, code_name, sign_price, sign_cnt, sign_mktime, sign_YmdHis, sign_Ymd
                 */
 
                string sql = "";
                sql += "INSERT INTO stock_sign(code, code_name, sign_price, sign_cnt, sign_mktime, sign_YmdHis, sign_Ymd)";
                sql += "VALUES(@code, @code_name, @sign_price, @sign_cnt, @sign_mktime, @sign_YmdHis, @sign_Ymd)";
                Dictionary<stringstring> param = new Dictionary<stringstring>();
                param["@code"]          = e.sRealKey; //종목코드번호
                param["@code_name"]     = CodeToString[e.sRealKey];
                param["@sign_price"]    = 현재가;
                param["@sign_cnt"]      = 체결량;
                param["@sign_mktime"]   = GetMktime(체결시간).ToString();
                param["@sign_YmdHis"]   = DateTime.Now.ToString("yyyy"+ DateTime.Now.ToString("MM"+ DateTime.Now.ToString("dd"+ " " + 체결시간;
                param["@sign_Ymd"]      = DateTime.Now.ToString("yyyy"+ DateTime.Now.ToString("MM"+ DateTime.Now.ToString("dd");
                database_proc(sql, param);
            }
        }
 
        //분석하기 버튼클릭
        //stock[코드번호][거래량] = stock_sign 배열
        Dictionary<stringDictionary<string, ArrayList>> stock = new Dictionary<stringDictionary<string, ArrayList>>();
        private void btn_ana_Click(object sender, EventArgs e)
        {
            Console.WriteLine("데이터뽑기 시작");
 
            Dictionary<intstring> columns = new Dictionary<intstring>();
            int ci = 0;
            columns[ci++= "code";
            columns[ci++= "code_name";
            columns[ci++= "sign_price";   //현재가
            columns[ci++= "sign_cnt";     //체결량
            columns[ci++= "sign_mktime";  //체결_mktime
            columns[ci++= "sign_YmdHis";
            columns[ci++= "sign_Ymd";
            //string sign_Ymd = DateTime.Now.ToString("yyyy") + DateTime.Now.ToString("MM") + DateTime.Now.ToString("dd");
            string sign_Ymd = "20170118"//일단 테스트를 위해
            Dictionary<intDictionary<stringstring>> list = database_select("SELECT * FROM stock_sign WHERE sign_Ymd = '" + sign_Ymd + "' ORDER BY idx ASC", columns);
            Dictionary<stringstring> stock_list = new Dictionary<stringstring>();
            foreach (KeyValuePair<intDictionary<stringstring>> row in list)
            {
                if(!stock.ContainsKey(row.Value["code"]))
                {
                    stock[row.Value["code"]] = new Dictionary<string, ArrayList>();
                }
 
                if(!stock[row.Value["code"]].ContainsKey(row.Value["sign_cnt"]))
                {
                    stock[row.Value["code"]][row.Value["sign_cnt"]] = new ArrayList();
                }
 
                stock[row.Value["code"]][row.Value["sign_cnt"]].Add(row.Value);
            }
 
            Console.WriteLine("데이터뽑기 종료");
 
            /*
            //자료 잘 들어갔는지 확인
            foreach(Dictionary value in stock["000030"]["-5"])
            {
                string test = value["sign_YmdHis"];
                Console.WriteLine(test);
            }
            */
 
            foreach(KeyValuePair<stringDictionary<string, ArrayList>> row in stock)
            {
                string 종목코드 = row.Key;
                foreach(KeyValuePair<string, ArrayList> row2 in row.Value)
                {
                    string 체결량 = row2.Key;
                    ArrayList 체결량_리스트1 = new ArrayList(row2.Value);
                    ArrayList 체결량_리스트2 = new ArrayList(row2.Value);
 
                    //하나하나씩 비교하자.
                    foreach(Dictionary<stringstring> value1 in 체결량_리스트1)
                    {
                        string value1_mktime = value1["sign_mktime"];
                        체결량_리스트2.RemoveAt(0);
                        foreach (Dictionary<stringstring> value2 in 체결량_리스트2)
                        {
                            string value2_mktime = value2["sign_mktime"];
                            int 갭 = int.Parse(value2_mktime) - int.Parse(value1_mktime);
                        }
                    }
                }
            }
 
            /*
                시총(시가총액)
 
                1000억에 100만원을 기준으로 = 0.00001(시가총액 * 0.00001) 금액을 마지막에 빼는거임.
                100 - 분당 규칙패턴 = 순위가 가장 높은순으로
                2분 간격으로 5번 반복하고 가격이 100만원 이고 10개씩 구매했다면 = 1000만원이 2분 간격으로 5번 반복하는 거임
                그러면 1분에 500만원을 사게 된다는 규칙이 성립되는 것이고
                .....
                ....
                ...
                ..
                .
                계속 반복패턴 찾아서 그 분당 금액을 모두 더한다...
                1분당 100
                1분당 40
                뭐 이렇게 쌓이다가.... 토탈이 900이 나왔으면 900 - 100(0.001) = 800 이 되는거임... 
 
 
                500 - 100(0.001) = 400(순위반영)이 되는거임
 
                기준은 반복 5회이상....
 
 
 
 
                특정시간을 정해서 하면 좋을거 같은데???
                장내,코스닥만???
 
 
                1. 클릭 -> 분당금액 리스트 보여주고... 총합계보여주고 총합계 - (시가총액 * 0.001) => 금액
                2. 
                ....
                ...
                10.
 
 
                분단위 규칙성 합계금액
                ---------------------- *  100 => 제일 높은거
                  시가총액 * 0.00001  
             */
        }
 
        ///////////////////////////////////////////////////////// 자체제작 라이브러리
 
        //타임스템프 => MKTIME
        static DateTime ConvertFromUnixTimestamp(double timestamp)
        {
            DateTime origin = new DateTime(1970119000);
            return origin.AddSeconds(timestamp);
        }
 
        //MKTIME => HHiiss
        static string TimestampToHis(int mktime)
        {
            // 2016-10-10 오후 2:46:16 => 14:46:16
            // 타임 스템프 값을 hhiiss 값으로 변경하기...
            DateTime test = ConvertFromUnixTimestamp(mktime);
            string test2 = test.ToString();
            string[] test3 = test2.Split(' ');
            string[] test4 = test3[2].Split(':');
            string test5 = "";
            int i = 0;
            foreach (string str in test4)
            {
                string ttt = "";
                if (test3[1== "오후" && i == 0)
                {
                    ttt = (int.Parse(str) + 12).ToString();
                }
                else
                {
                    ttt = str;
                }
 
                string str2 = Regex.Replace(ttt, @"d+", n => n.Value.PadLeft(2'0'));
                string add = "";
                if (i < 2)
                {
                    add = ":";
                }
                test5 += str2 + add;
                i++;
            }
 
            return test5;
        }
 
        static int GetMktime(string time_data)
        {
            int year = int.Parse(DateTime.Now.ToString("yyyy"));
            int month = int.Parse(DateTime.Now.ToString("MM"));
            int day = int.Parse(DateTime.Now.ToString("dd"));
            int hour = int.Parse(time_data.ToString().Substring(02));
            int minute = int.Parse(time_data.ToString().Substring(22));
            int second = int.Parse(time_data.ToString().Substring(42));
 
            if (hour < 9)
            {
                return 999999;
            }
 
            DateTime dt = new DateTime(year, month, day, hour - 9, minute, second);
 
            int time;
            try
            {
                time = ConvertDatetimeToInt(dt);
            }
            catch (System.IndexOutOfRangeException e)
            {
                time = 0;
            }
 
            return time;
        }
 
        public static string TimeShow(string second)
        {
            int second_int = int.Parse(second);
            string return_minute = Regex.Replace((second_int / 60).ToString(), @"d+", n => n.Value.PadLeft(2'0'));
            string return_second = Regex.Replace((second_int % 60).ToString(), @"d+", n => n.Value.PadLeft(2'0'));
 
            return return_minute + ":" + return_second;
        }
 
        public static int ConvertDatetimeToInt(System.DateTime dt)
        {
            System.TimeSpan ts = dt.Subtract(new System.DateTime(197011));
            return (int)(ts.Ticks / 10000000L);
        }
    }
}
Colored by Color Scripter
cs

이 게시글은
https://webschool.kr/?v=board_view&board_key=5&idx=41
에서 작성한 글입니다. 소스코드의 경우 해당 블로그에서 이뿌게 노출이 되지 않을 수 있사오니, 위 링크로 들어오셔서 보시길 바랍니다.

반응형