성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# XingAPI - ACF 파일을 이용한 퀀트 종목 찾기(t1857)</h1> <p> 지난 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# XingAPI - 주식 종목에 따른 PBR, PER, ROE, ROA 구하는 방법(t3320, t8430 예제) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13034'>https://www.sysnet.pe.kr/2/0/13034</a> C# XingAPI - 주식 종목에 따른 PBR, PER, ROE 구하는 방법(t3341 예제) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13045'>https://www.sysnet.pe.kr/2/0/13045</a> </pre> <br /> 여러 가지 지표를 구해봤는데요, 그런데 이러한 조건식을 일일이 코딩까지 하려니 좀 귀찮습니다. 바로 이런 경우에 잘 써먹을 수 있는 방법이 "eBEST PRO"에서 제공하는 "종목검색" / "e종목검색" 기능입니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='acf_search_1.png' src='/SysWebRes/bbs/acf_search_1.png' /><br /> <br /> 위의 화면을 보면, UI를 통해 "조건설정 및 추가"를 할 수 있고, 그러한 조건을 조합해 자신만의 "전략작성"을 하는 것이 가능합니다. 그다음, "결과조회" 화면의 "검색" 버튼을 눌러 해당 조건을 만족하는 주식을 찾을 수 있습니다.<br /> <br /> 그러니까, 사실 여러 지표를 통한 종목 검색을 하는 것이라면 굳이 힘들게 C# 프로그램을 만들 필요 없이, 그냥 기존의 HTS나 MTS에서 제공하는 "검색" 기능을 이용하는 것이 더 좋습니다. <br /> <br /> 갑자기... 프로그래밍 할 필요가 없어졌군요. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 그래도 프로그래밍을 해볼 만한 이유가 있습니다. 제가 해당 HTS 검색 기능을 잘 몰라서이기도 한데, 가령 위의 경우 PCR, PBR, PSR, PER 4가지 수치를 단순히 검색해서 보여주는 것에 그칩니다. 더 나아가서 그 4가지를 종합해서 개별 순위를 점수로 매겨 평균 점수가 낮은 정렬을 하지 못하고 있습니다.<br /> <br /> 이런 경우라면, (물론 엑셀을 이용해도 되지만) 프로그래밍을 통해 쉽게 해결할 수 있을 텐데요. ^^ 위의 화면에서 우측 하단에 있는 "API 보내기"를 통해 해당 전략을 ACF 파일로 내보내기 할 수 있습니다. 예를 들어, 그 조건식 파일을 "D:\temp\4factor.ACF" 파일로 저장했다고 가정해보겠습니다.<br /> <br /> 그럼, 이제 저 ACF 파일로 검색을 수행하는 XingAPI인 t1857을 이용해 다음과 같은 식으로 검색 조건에 일치하는 종목들을 가져올 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > foreach (var item in GetACFSearchResult(@"d:\temp\4factor.ACF")) { Console.WriteLine($"[{item.hname}({item.shcode})"); } private IEnumerable<StockInfo> GetACFSearchResult(string acfFilePath) { using (XQt1857 xQT = new XQt1857()) { XQt1857InBlock inBlock = new XQt1857InBlock { sRealFlag = '0', sSearchFlag = 'F', query_index = acfFilePath, }; xQT.SetBlock(inBlock); if (xQT.Request(null, true) < 0) // Request의 2번째 인자에 true를 주면 내부적으로 XAQueryClass.RequestService를 호출 { yield break; } var block = xQT.GetBlock(); if (block.IsValidData == false) { yield break; } foreach (var item in xQT.GetBlock1s()) { yield return new StockInfo(item); } } } </pre> <br /> 그런데, 아쉽게도 저 쿼리 결과에는 쿼리 조건에서 요청한 PCR, PBR, PSR, PER 값은 누락됩니다. (어찌 보면 당연한 결과인데요, 정형화된 API 반환값에 사용자가 다양하게 조합할 수 있는 쿼리의 결과를 포함한다는 것이 부담스러웠을 것입니다.)<br /> <br /> 그래서, 다시 해당 결과를 기반으로 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13034'>지난번 재무정보를 가져오는 t3320 API를 이용</a>해야 합니다. 하지만, t3320의 경우 PBR, PER 값만 가져오기 때문에 나머지는 계산을 통해 가져와야 합니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PCR = <a target='tab' href='https://blog.naver.com/pureriver777/222533144587'>주가 / CPS</a> PSR = <a target='tab' href='https://uppity.co.kr/serial/?bmode=view&idx=6673292'>시가 총액 / 연간 총매출액</a> = <a target='tab' href='https://blog.naver.com/pureriver777/222529133939'>주가 / SPS</a> </pre> <br /> (제대로 구한 것인지는 모르겠으나 ^^;) 다행히, SPS와 CPS의 값을 t3320에서 주기 때문에 다른 API를 호출할 필요는 없습니다. 결국 이를 바탕으로 다음과 같이 코딩을 할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > List<StockInfo> list = new List<StockInfo>(GetACFSearchResult(@"d:\temp\4factor.ACF")); using (XQt3320 query = new XQt3320()) { query.QueryPerTime = 3 * 1000; foreach (var item in list) { query.SetFieldData(XQt3320InBlock.BlockName, XQt3320InBlock.F.gicode, 0, item.shcode); int resultCode = query.Request(); if (resultCode < 0) { Console.WriteLine($"[{resultCode}] XQt3320: request failed at {item.hname}"); break; } XQt3320OutBlock1 outBlock = query.GetBlock1(); if (outBlock.IsValidData == false) { Console.WriteLine($"XQt3320: invalid block at {item.hname}"); break; } item.CPS = outBlock.cps; item.SPS = outBlock.sps; item.PBR = outBlock.pbr; item.PER = outBlock.per; Console.WriteLine(item); } } public class StockInfo : XQt1857OutBlock1 { public float CPS { get; set; } public float SPS { get; set; } public float PBR { get; set; } public float PER { get; set; } public float PCR { get { return this.price / this.CPS; } } public float PSR { get { return this.price / this.SPS; } } public StockInfo(XQt1857OutBlock1 block) { block.CopyTo(this); } public override string ToString() { return $"{hname:F2}({shcode}): PCR={PCR:F2}, PBR={PBR:F2}, PER={PER:F2}, PSR={PSR:F2}, CPS={CPS:F2}, SPS={SPS:F2}, Price={price}"; } } </pre> <br /> Console.WriteLine으로 찍힌 결과를 HTS에서 반환한 결과와 비교해 보면 PSR과 PCR의 계산 결과가 다소 다른 것을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 메리츠화재(000060): PCR=7.42, PBR=1.98, PER=8.05, PSR=0.53, CPS=5967.27, SPS=83229.11, Price=44250 삼양홀딩스(000070): PCR=3.02, PBR=0.42, PER=3.12, PSR=2.14, CPS=28097.03, SPS=39708.13, Price=84900 두산(000150): PCR=4.19, PBR=0.46, PER=4.70, PSR=2.00, CPS=21546.49, SPS=45180.48, Price=90200 ...[생략]... 유수홀딩스(000700): <span style='color: blue; font-weight: bold'>PCR=-82.49</span>, PBR=0.62, <span style='color: blue; font-weight: bold'>PER=-59.14</span>, PSR=26.06, CPS=-76.37, SPS=241.71, Price=6300 ...[생략]... 크리스탈신소재(900250): <span style='color: blue; font-weight: bold'>PCR=∞, PBR=0.00, PER=0.00, PSR=∞, CPS=0.00, SPS=0.00</span>, Price=1210 ...[생략]... </pre> <br /> 계산 수식과 시기에 약간의 차이점이 있을 것을 감안하면 그래도 어느 정도는 비슷한 결과가 나오긴 하는데요, 그래도 2가지 정도 문제가 있습니다.<br /> <br /> 1) 검색 조건에서는 모든 값들이 0에서 10 사이에 해당하도록 했고 실제로 HTS의 출력 화면에 따르면 "유수홀딩스"의 경우 PCR 값이 5.18이라고 나오는데, 저 쿼리 결과에서는 -82.49가 나왔습니다. 계산이 잘못되었을 수도 있겠지만, 해당 결과에 보면 계산이 필요 없는 PER도 음수입니다. (확인을 위해, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13034'>t3320</a>을 사용해도 PER 값이 음수가 나오는 반면, HTS의 기업 정보 화면에는 양수였습니다.)<br /> <br /> 2) 또한 "크리스탈신소재" 값도 이상합니다. 전부 (PCR과 PSR이 무한대인 것은 CPS와 SPS가 0이므로.) 0으로 나오는데요, 실제로 HTS를 이용해 해당 기업 정보를 봐도 PER, PBR 등의 정보들이 없습니다. 차트를 보면 2016년도부터 주가 데이터가 나오는데... 뭐 어쨌든, 데이터가 없다고 가정했을 때 그렇다면 애당초 ACF 검색 결과에서 이런 종목은 필터링이 되었어야 합니다. 상식적으로, 데이터가 null인 종목을 0이라고 가정하고 검색에 끼워 준다는 것이 잘못된 것입니다.<br /> <br /> 문제군요, 설령 결과가 정확해도 (t3320의 연속 쿼리 제한인) 3초마다 한 번씩 쿼리를 해야 하는 지루함을 견뎌야 하는 마당에... 음... <a target='tab' href='https://www.sysnet.pe.kr/2/0/12134#pro'>여러모로 편리한 API이긴</a> 하지만... 도대체 신뢰를 줄 수 없는 방식으로 동작하고 있습니다. 아니, 금융을 다루는 API가 저래서야, 어느 누가 이런 불확실성을 믿고 자신의 소중한 자산을 투자할 수 있겠습니까? ^^; (아이러니하게도, API를 알면 알수록 자신감이 생기는 것이 아니고... 불안감이 가중된다는!)<br /> <br /> 과연 이 API를 계속 다뤄야 할지... 심히 고민이 됩니다. ^^;<br /> <br /> (이 글의 <a target='tab' href='https://github.com/stjeong/XingAPI/tree/master/DevCenterSample/TR/%EC%97%85%EC%A2%85/t1857'>예제 코드는 github에 올려</a> 두었습니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9824
(왼쪽의 숫자를 입력해야 합니다.)