서론
C#을 통해 키움증권 Open API 활용한 프로그램 구현하기 포스팅 4탄입니다. 이번 포스팅에서는 키움증권 계정에 설정된 조건식을 읽어와서, 조건식 실시간 검색을 구현하려고 합니다. 조건식은 Open API를 통해 설정할 수 없고 키움증권 HTS (영웅문4)를 통해 설정해야 합니다.
C# 키움증권 Open API (3) - 예수금 및 종목 정보 조회 링크
1. 조건식 가져오기
// 조건식 조회
btnGetCondition.Click += (s, e) =>
{
if (axKHOpenAPI1.GetConnectState() == 0)
{
ShowMessageBox("Open API 연결되어 있지 않습니다.", MessageBoxIcon.Information);
return;
}
WriteLog("[조건식 조회]");
if (axKHOpenAPI1.GetConditionLoad() == 1)
WriteLog(" 조건식 파일 저장 - 성공");
else
WriteLog(" 조건식 파일 저장 - 실패");
};
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(" 조건식 조회 - 성공");
};
2. 조건식 일반 검색
// 일반 검색
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(" 조건식 일반 검색 - 실패");
};
// 조건식 일반 검색
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());
};
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;
}
};
3. 조건식 실시간 검색
// 실시간 검색
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(" 조건식 실시간 검색 - 실패");
};
// 조건식 실시간 검색
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;
}
};
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;
}
};
// 실시간 중단
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());
};
마무리
C#을 통해 키움증권 Open API를 활용한 프로그램을 구현하는 방법에 대한 4번째 포스팅이 끝났습니다. 이제 어느정도 실제 투자에 활용할 수 있는 프로그램 기능이 점점 구현이 되고 있습니다. 하지만 조건식을 통해 종목 검색을 하는 경우는 HTS로 보다 더 정확하고 빠르게 읽어올 수 있습니다. 즉 종목을 읽어오기만 할 뿐 아니라 추가적인 자동 매매 기능을 구현해야 의미가 있다고 할 수 있습니다. 다음 포스팅에서는 자동 매매에 대한 기능을 구현하고자 합니다.
C# 키움증권 Open API (3) - 예수금 및 종목 정보 조회 링크
Uploaded by N2T