서론
C#을 통해 키움증권 Open API 활용한 프로그램 구현하기 포스팅 4탄입니다. 이번 포스팅에서는 키움증권 계정에 설정된 조건식을 읽어와서, 조건식 실시간 검색을 구현하려고 합니다. 조건식은 Open API를 통해 설정할 수 없고 키움증권 HTS (영웅문4)를 통해 설정해야 합니다.
C# 키움증권 Open API (3) - 예수금 및 종목 정보 조회 링크
C# 키움증권 Open API (3) - 예수금 및 종목 정보 조회
서론 C# 으로 키움증권 Open API 활용하여 프로그램을 구현하는 방법에 대한 포스팅 3탄입니다. 이번엔 좀 더 응용된 기능을 구현해보려고 합니다. 우선 선택한 계좌의 예수금 조회 를 추가하려고 합니다. 이후 주..

1. 조건식 가져오기

💡
조건식은 우선 키움증권 HTS(영웅문4)에서 설정할 수 있습니다. 화면 번호 “0150”에서 조건식을 본인이 원하는 조건에 맞게 설정하면 해당 조건에 맞는 종목들을 검색할 수 있습니다.
💡
저는 일단 인터넷에 찾은 시가갭 종목을 가져오는 조건식을 설정했습니다.

💡
이제 프로그램에서 키움증권 계정에 설정한 조건식을 읽어올 차례입니다. 조건식을 읽어올 ComboBox, Button을 추가합니다.
// 조건식 조회
btnGetCondition.Click += (s, e) =>
{
if (axKHOpenAPI1.GetConnectState() == 0)
{
ShowMessageBox("Open API 연결되어 있지 않습니다.", MessageBoxIcon.Information);
return;
}
WriteLog("[조건식 조회]");
if (axKHOpenAPI1.GetConditionLoad() == 1)
WriteLog(" 조건식 파일 저장 - 성공");
else
WriteLog(" 조건식 파일 저장 - 실패");
};
💡
조건식을 읽어오기 위해서 Button에 Click 이벤트를 추가합니다.
💡
조건식을 가져오기 위해서 GetConditionLoad() 을 통해 조건식을 파일로 저장합니다. 해당 파일은 Open API 연결이 해지되는 시점에 자동 삭제됩니다.
class ConditionInfo
{
public int Index { get; set; }
public string Name { get; set; }
public DateTime? LastRequestTime { get; set; }
}
private List<ConditionInfo> conditionInfo = new List<ConditionInfo>();
// KHOpenAPI Control Events
axKHOpenAPI1.OnReceiveConditionVer += (s, e) =>
{
if (e.lRet != 1) return;
cboCondition.Items.Clear();
conditionInfo.Clear();
string[] arrCondition = axKHOpenAPI1.GetConditionNameList().Trim().Split(';'); // ';' 구분
foreach (var cond in arrCondition)
{
if (string.IsNullOrEmpty(cond)) continue;
var item = cond.Split('^'); // '^' 구분 ex) 001^조건식1
conditionInfo.Add(new ConditionInfo
{
Index = item[0].ToInt(),
Name = item[1]
});
}
cboCondition.Items.AddRange(arrCondition);
if (cboCondition.Items.Count > 0) cboCondition.SelectedIndex = 0;
WriteLog(" 조건식 조회 - 성공");
};
💡
조건식을 파일로 저장하게되면 OnReceiveConditionVer 이벤트가 호출됩니다.
💡
추후 조건식을 활용한 일반 검색, 실시간 검색을 위해 Class ConditionInfo를 추가합니다.
💡
파일에 저장된 조건식을 읽어오는 동작은 GetConditionNameList()을 통해 구현할 수 있습니다. 결과값은 서로 다른 조건식은 ‘;’로 구분되고, 조건식 번호와 조건식 이름은 ‘^’으로 구분되어 가져옵니다.
ex) 001^조건식1;002^조건식2;003^조건식3;

💡
정상적으로 조건식을 읽어온 결과입니다.
2. 조건식 일반 검색

💡
이제 읽어온 조건식에 맞는 종목을 일반 검색할 차례입니다. 일반 검색을 위해 Button을 추가합니다.
// 일반 검색
btnCondSearch.Click += (s, e) =>
{
if (string.IsNullOrEmpty(cboCondition.SelectedItem.ToString())) return;
string[] condition = cboCondition.SelectedItem.ToString().Split('^');
var condInfo = conditionInfo.Find(f => f.Index == condition[0].ToInt());
if (condInfo == null) return;
if (condInfo.LastRequestTime != null && condInfo.LastRequestTime >= DateTime.Now.AddSeconds(-60))
{
int second = 60 - (DateTime.Now - condInfo.LastRequestTime.Value).Seconds;
WriteLog($"{second}초 후에 조회 가능합니다.");
return;
}
WriteLog("[일반 검색]");
condInfo.LastRequestTime = DateTime.Now;
int result = axKHOpenAPI1.SendCondition(GetScreenNo(), condition[1], condition[0].ToInt(), 0);
if (result == 1)
WriteLog(" 조건식 일반 검색 - 성공");
else
WriteLog(" 조건식 일반 검색 - 실패");
};
💡
하나의 조건식에 대한 검색은 60초 마다 가능합니다. 즉 조건식1에 대한 검색을 한번 수행하면 60초 동안 재검색을 불가능합니다. 이를 위해 LastRequestTime을 기억하고 있다가 비교하여 Log를 남기도록 구현합니다.
💡
조건식에 맞는 종목 검색은 SendCondition를 통해 구현합니다. 파라미터는 화면 번호, 조건식 이름, 조건식 번호, 조회 구분 입니다. 조회 구분의 경우 0은 일반 검색, 1은 실시간 검색입니다.
// 조건식 일반 검색
axKHOpenAPI1.OnReceiveTrCondition += (s, e) =>
{
string code = e.strCodeList.Trim();
if (string.IsNullOrEmpty(code)) return;
if (code.Length > 0) code = code.Remove(code.Length - 1);
int codeCount = code.Split(';').Length;
axKHOpenAPI1.CommKwRqData(code, 0, codeCount, 0, "조건일반검색", GetScreenNo());
};
💡
SendCondition을 통해 조건식을 보내면 OnReceiveTrCondition 이벤트가 호출됩니다.
💡
종목은 많은 수의 종목을 읽어올 예정이기 때문에 CommKwRqData을 통해 읽어옵니다. 첫번째 파라미터는 종목 코드(’;’로 구분한 string), 종목 코드 개수, Request 이름, 화면 번호가 필요합니다.
private DataTable dtCondStock = new DataTable();
// Receive Data
axKHOpenAPI1.OnReceiveTrData += (s, e) =>
{
switch (e.sRQName)
{
case "조건일반검색":
DataTable dataTable = new DataTable();
dataTable.Columns.Add("종목코드", typeof(string));
dataTable.Columns.Add("종목명", typeof(string));
dataTable.Columns.Add("현재가", typeof(string));
dataTable.Columns.Add("전일대비", typeof(string));
dataTable.Columns.Add("등락율", typeof(string));
dataTable.Columns.Add("거래량", typeof(string));
int count = axKHOpenAPI1.GetRepeatCnt(e.sTrCode, e.sRQName);
for (int i = 0; i < count; i++)
{
dataTable.Rows.Add(
axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, i, "종목코드").Trim(),
axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, i, "종목명").Trim(),
string.Format("{0:#,##0}", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, i, "현재가").Trim().ToInt()),
string.Format("{0:#,##0}", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, i, "전일대비").Trim().ToInt()),
string.Format("{0:#,##0.00}%", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, i, "등락율").Trim().ToDecimal()),
string.Format("{0:#,##0}", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, i, "거래량").Trim().ToDecimal())
);
}
dtCondStock = dataTable;
dataGridView1.DataSource = dtCondStock;
break;
}
};
💡
CommKwRqData를 통해 Request를 보내면 OnReceiveTrData 이벤트가 호출됩니다.
💡
종목코드, 종목명, 현재가, 전일대비, 등락율, 거래량을 읽어온 뒤 DataGridView에 바인딩합니다.

💡
DataGridView에 정상적으로 조건식에 맞는 종목 정보가 읽어온 결과입니다.
3. 조건식 실시간 검색

💡
일반 검색의 경우 검색 버튼을 누른 순간에 해당하는 데이터를 읽어오지만, 실시간 검색을 하면 새로운 종목이 조건식에 편입되거나 기존 종목이 조건식에서 이탈되는 경우에 재갱신을 실시간으로 동작할 수 있습니다.
💡
구현을 위해 실시간 검색, 실시간 중단 Button을 추가합니다.
// 실시간 검색
btnCondRealSearch.Click += (s, e) =>
{
if (string.IsNullOrEmpty(cboCondition.SelectedItem.ToString())) return;
string[] condition = cboCondition.SelectedItem.ToString().Split('^');
var condInfo = conditionInfo.Find(f => f.Index == condition[0].ToInt());
if (condInfo == null) return;
if (condInfo.LastRequestTime != null && condInfo.LastRequestTime >= DateTime.Now.AddSeconds(-60))
{
int second = 60 - (DateTime.Now - condInfo.LastRequestTime.Value).Seconds;
WriteLog($"{second}초 후에 조회 가능합니다.");
return;
}
WriteLog("[실시간 검색]");
condInfo.LastRequestTime = DateTime.Now;
int result = axKHOpenAPI1.SendCondition(GetScreenNo(), condition[1], condition[0].ToInt(), 1);
if (result == 1)
WriteLog(" 조건식 실시간 검색 - 성공");
else
WriteLog(" 조건식 실시간 검색 - 실패");
};
💡
실시간 검색 Button의 Click 이벤트입니다. 앞서서 구현한 일반 검색과 동일하게 구현하되 SendCondition의 조회 구분을 1로 전달합니다.
💡
최초 종목 검색을 일반 검색과 동일하게 OnReceiveTrCondition 이벤트가 호출되면서 읽어옵니다.
// 조건식 실시간 검색
axKHOpenAPI1.OnReceiveRealCondition += (s, e) =>
{
switch(e.strType)
{
case "I": // 편입
axKHOpenAPI1.SetInputValue("종목코드", e.sTrCode);
axKHOpenAPI1.CommRqData("조건실시간검색", "OPT10001", 0, GetScreenNo());
break;
case "D": // 이탈
DataRow[] findRows = dtCondStock.Select($"종목코드 = {e.sTrCode}");
if (findRows.Length == 0) return;
dtCondStock.Rows.Remove(findRows[0]);
dataGridView1.DataSource = dtCondStock;
break;
}
};
💡
실시간 검색의 차이점은 실시간으로 종목이 조건식에 편입, 이탈될 때 OnReceiveRealCondition 이벤트가 호출된다는 점입니다.
💡
e.strType이 “I”인 경우 편입, “D”인 경우 이탈입니다. 종목이 조건식에 편입, 이탈될 때 DataGridView의 Row를 추가 및 삭제 하도록 구현합니다.
💡
이탈되어 Row를 삭제하는 경우에는 간단하게 해당 조건식 번호와 일치하는 Row를 Remove합니다.
💡
편입되는 경우에는 CommRqData를 통해 Request를 전달합니다.
axKHOpenAPI1.OnReceiveTrData += (s, e) =>
{
switch (e.sRQName)
{
case "조건실시간검색":
dtCondStock.Rows.Add(
axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "종목코드").Trim(),
axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "종목명").Trim(),
string.Format("{0:#,##0}", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "현재가").Trim().ToInt()),
string.Format("{0:#,##0}", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "전일대비").Trim().ToInt()),
string.Format("{0:#,##0.00}%", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "등락율").Trim().ToDecimal()),
string.Format("{0:#,##0}", axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "거래량").Trim().ToDecimal())
);
dataGridView1.DataSource = dtCondStock;
break;
}
};
💡
CommRqData을 통해 Request를 전달하면 OnReceiveTrData 이벤트가 호출됩니다.
💡
DataGridView에 해당 Row를 추가하여 바인딩합니다.

💡
DataGridView에 정상적으로 조건식에 맞는 종목 정보가 읽어온 결과입니다. 실시간으로 편입, 이탈되는 경우 DataGridView의 데이터가 변경됩니다.
// 실시간 중단
btnCondRealSearchStop.Click += (s, e) =>
{
if (string.IsNullOrEmpty(cboCondition.SelectedItem.ToString())) return;
string[] condition = cboCondition.SelectedItem.ToString().Split('^');
WriteLog("[실시간 중단]");
axKHOpenAPI1.SendConditionStop(GetScreenNo(), condition[1], condition[0].ToInt());
};
💡
주의해야할 점은 조건식에 대한 실시간 검색을 수행하다가 멈추는 경우 실시간 검색 중단을 해야합니다.
💡
실시간 중단 버튼을 통해 SendConditionStop을 호출하여 실시간 검색을 중단해줍니다. 파라미터는 화면 번호, 조건식 이름, 조건식 번호 입니다.
마무리
C#을 통해 키움증권 Open API를 활용한 프로그램을 구현하는 방법에 대한 4번째 포스팅이 끝났습니다. 이제 어느정도 실제 투자에 활용할 수 있는 프로그램 기능이 점점 구현이 되고 있습니다. 하지만 조건식을 통해 종목 검색을 하는 경우는 HTS로 보다 더 정확하고 빠르게 읽어올 수 있습니다. 즉 종목을 읽어오기만 할 뿐 아니라 추가적인 자동 매매 기능을 구현해야 의미가 있다고 할 수 있습니다. 다음 포스팅에서는 자동 매매에 대한 기능을 구현하고자 합니다.
C# 키움증권 Open API (3) - 예수금 및 종목 정보 조회 링크
C# 키움증권 Open API (3) - 예수금 및 종목 정보 조회
서론 C# 으로 키움증권 Open API 활용하여 프로그램을 구현하는 방법에 대한 포스팅 3탄입니다. 이번엔 좀 더 응용된 기능을 구현해보려고 합니다. 우선 선택한 계좌의 예수금 조회 를 추가하려고 합니다. 이후 주..

Uploaded by N2T