너무 오래 코딩 관련 글은 안 올린거 같아 코드 약간 올립니다.
기본적으로 람다식과 확장 메서드를 사용합니다.
docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/lambda-expressions
람다 식 - C# 참조
람다 식에 대해 알아봅니다. 식이 본문으로 포함된 식 람다 또는 문 블록이 본문으로 포함된 문 람다가 있습니다.
docs.microsoft.com
docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
확장 메서드 - C# 프로그래밍 가이드
C#에서 확장명 메서드를 사용하면 새 파생 형식을 만들거나 다시 컴파일하거나 원래 형식을 수정하지 않고도 기존 형식에 메서드를 추가할 수 있습니다.
docs.microsoft.com
아래는 OnReceiveTrData 처리 예시 입니다.
// key 값으로 "사용자 구분 명"을 사용하고 value 값으로 callback action으로 쓰는 Dictionary
public Dictionary<string, Action<object, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent>> recieveAction = new Dictionary<string, Action<object, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent>>();
public Form1()
{
axKHO.OnReceiveTrData += OnReceiveTrData;
}
public void OnReceiveTrData(object sender, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e)
{
// callback이 등록 되어 있는 경우
if (recieveAction.ContainsKey(e.sRQName))
{
if (recieveAction[e.sRQName] != null) recieveAction[e.sRQName](sender, e);
recieveAction.Remove(e.sRQName);
return;
}
// 다른 고정으롷 받아 처리 해야 할 것들...
////
////
}
// callback 등록 예시
public bool SendAccountEvaluation(string account, Action<object, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent> action = null) // 계좌평가현황요청 (게좌번호, callback action)
{
if (string.IsNullOrEmpty(account)) return false;
axKHO.SetInputValue(KOAEX.ACCOUNT, account);
axKHO.SetInputValue(KOAEX.PASSWORD, "");
axKHO.SetInputValue(KOAEX.PASSWORD_INPUT, "0");
axKHO.SetInputValue(KOAEX.SEARCH_CLASSIFY, "00");
if (!Error.IsError(axKHO.CommRqData(KOAEX.STOCK_EVALUATION_INFO, KOAEX.STOCK_EVALUATION_CODE, 0, "5000")))
{
Log(string.Format("{0} 요청 실패) ({1})", KOAEX.STOCK_EVALUATION_INFO, Error.GetErrorMessage()));
return false;
}
if (action != null) recieveAction.Add(KOAEX.STOCK_EVALUATION_INFO, action); // 등록
Log(string.Format("{0} 성공", KOAEX.STOCK_EVALUATION_INFO));
return true;
}
아래 코드는 위에 SendAccountEvaluation을 사용 하는 방법이고 여기서 람다식을 사용합니다.
// 사용 코드
SendAccountEvaluation("************", (o, e) => {
AxKHOpenAPI api = FormMain.GET.API;
autoHas.Clear();
hasStock.Clear();
dataGridViewHas.Rows.Clear();
int cnt = api.GetRepeatCnt(e.sTrCode, e.sRQName);
for (int i = 0; i < cnt; ++i)
{
try
{
StockEvaluation eval = new StockEvaluation();
eval.code = api.GetCode(e, i);
eval.volume = api.GetCommDataDouble(e, i, KOAEX.HAS_VOLUME); // Extension Function으로 정의해서 사용
eval.close = api.GetCommDataDouble(e, i, KOAEX.CUR_PRICE);
eval.rate = api.GetCommDataDouble(e, i, KOAEX.EARN_RATE);
eval.average = api.GetCommDataDouble(e, i, KOAEX.AVERAGE_PRICE);
eval.buy_total = api.GetCommDataDouble(e, i, KOAEX.BUY_TOTAL);
autoHas.Add(api.GetCommDataStr(e, i, KOAEX.STOCK_NAME));
dataGridViewHas.Rows.Add(eval.code, FormMain.GET.GetStockName(eval.code), eval.volume, eval.average
,eval.buy_total, eval.close, eval.rate);
if (!hasStock.ContainsKey(eval.code)) hasStock.Add(eval.code, eval);
}
catch (Exception ex)
{
FormMain.GET.Log(ex.Message);
}
}
});
키움 OpenAPI를 사용 해보시던 분들은 위에 코드를 보시면 "KOAEX"와 "GetCommDataDouble" 등등 못보던 것들이 있을 텐데요 이건 이전에 소개 했던 Extension Method를 이용해 확장해서 사용하거나 const string으로 Define 해서 사용하는 것들입니다. (아래 코드를 참조해주세요)
// Extension Function
public static class KOAEX
{
public const string STOCK_EVALUATION_INFO = "계좌평가현황요청";
public const string STOCK_EVALUATION_CODE = "opw00004";
public const string ACCOUNT = "계좌번호";
public const string PASSWORD = "비밀번호";
public const string SEARCH_CLASSIFY = "조회구분";
public const string PASSWORD_INPUT = "비밀번호입력매체구분";
public const string CODE = "종목코드";
public const string STOCK_NAME = "종목명";
public const string HAS_VOLUME = "보유수량";
public const string CUR_PRICE = "현재가";
public const string EARN_RATE = "손익률";
public const string AVERAGE_PRICE = "평균단가";
public const string BUY_TOTAL = "매입금액";
public static string GetCode(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index)
{
string value = api.GetCommData(e.sTrCode, e.sRQName, index, CODE).Trim();
if (!char.IsNumber(value[0])) return value.Substring(1, value.Length - 1);
return value;
}
public static string GetCommDataStr(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index, string itemName)
{
return api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim();
}
public static int GetCommDataInt(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index, string itemName, bool unsigned = true)
{
string value = unsigned ? api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim().Replace("-", "") : api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim();
int nValue = 0;
if (int.TryParse(value, out nValue)) return nValue;
return 0;
}
public static long GetCommDataLong(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index, string itemName, bool unsigned = true)
{
string value = unsigned ? api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim().Replace("-", "") : api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim();
long nValue = 0;
if (long.TryParse(value, out nValue)) return nValue;
return 0;
}
public static decimal GetCommDataDecimal(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index, string itemName, bool unsigned = true)
{
string value = unsigned ? api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim().Replace("-", "") : api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim();
decimal nValue = 0;
if (decimal.TryParse(value, out nValue)) return nValue;
return 0;
}
public static double GetCommDataDouble(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index, string itemName, bool unsigned = true)
{
return (double)GetCommDataDecimal(api, e, index, itemName, unsigned);
}
public static float GetCommDataFloat(this AxKHOpenAPILib.AxKHOpenAPI api, AxKHOpenAPILib._DKHOpenAPIEvents_OnReceiveTrDataEvent e, int index, string itemName, bool unsigned = true)
{
string value = unsigned ? api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim().Replace("-", "") : api.GetCommData(e.sTrCode, e.sRQName, index, itemName).Trim();
float nValue = 0;
if (float.TryParse(value, out nValue)) return nValue;
return 0;
}
}
이렇게 했을 때 장점은 보내는 시점의 코드에서 처리 까지 모두 구현 가능하게 되면서 코드를 여러 곳을 찾아 다닐 필요가 없으며
키움OpenAPI에서 "sRQName" (사용자 구분 명)이 같아도 콜백을 Action을 사용하기 때문에 명확하게 호출 한 곳에서 처리가 가능해집니다.
또 한 함수(OnReceiveTrData)에서 너무 많은 처리로 복잡해 지지 않을 수 있습니다.
전 개인적으로 빈번하게 오가는 정보는 같은 정보를 요청 하더라도 증가하는 Serial 값을 포함한 "사용자 구분명"을 사용하고 있습니다.
(물론 같은 사용자 구분 명에 Action 들을 Queue나 List에 담아 처리 할 수도 있습니다. 편하신 방법으로 사용 하시면 됩니다.)
아! 그리고 openApi 에러 처리 부분은 키움 OpenAPI 예제 솔루션에 있는 걸 사용 했기 때문에 따로 코드를 올리지 않겠습니다.
'주식 > 자동 추천 프로그램' 카테고리의 다른 글
주식분석프로그램 수정(20210309) (0) | 2021.03.09 |
---|---|
키움 OpenAPI 정보 요청시 주의 점 (0) | 2021.03.04 |
주식분석프로그램 수정(20210225) (0) | 2021.02.25 |
자동 매매 기능 추가 중 (0) | 2021.02.24 |
추천 알고리즘 수정(2021년 2월 23일) (0) | 2021.02.23 |
댓글