성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
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'>eBEST XingAPI의 C# 래퍼 버전 - XingAPINet Nuget 패키지</h1> <p> C# 래퍼 버전을 만들어 NuGet에 배포했습니다. 얼마나 사용 방법이 쉬운지 ^^ 한 번 작성해 볼까요?<br /> <br /> 예제를 위해 .NET Framework 4.0 이상의 프로젝트를 만들고, Target Platform을 "AnyCPU"에서 "x86"으로 맞춰줍니다. (왜냐하면, 증권사 API가 64비트를 지원하지 않습니다.)<br /> <br /> <img alt='how_to_use_xing_1.png' src='/SysWebRes/bbs/how_to_use_xing_1.png' /><br /> <br /> 그다음, NuGet으로부터 XingAPINet 라이브러리를 참조 추가하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a href='https://www.nuget.org/packages/XingAPINet/'>https://www.nuget.org/packages/XingAPINet/</a> Install-Package XingAPINet </pre> <br /> 끝입니다! ^^<br /> <br /> 그런데... 제가 주식 분야는 거의 몰라서 ^^ 할 줄 아는 게 없으니 그냥 종목 코드를 가져오는 예제를 작성해 보겠습니다. XingAPI에서 종목 코드를 구할 수 있는 API는 t8430이므로, 따라서 다음과 같이 코딩을 하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LoginInfo user = LoginInfo.FromPlainText("...id...", "...password...", "...인증서 password..."); // 만약 demo 서버 접속이라면 인증서 계정은 필요 없습니다. using (XingClient xing = new XingClient(true)) // true == demo.etrade.co.kr, false == hts.etrade.co.kr { if (xing.ConnectWithLogin(user) == false) // 서버 접속 { Console.WriteLine(xing.ErrorMessage); return; } // Query이므로 t8430에 "XQ" 접두사가 붙은 타입을 사용 XQt8430OutBlock [] items = XQt8430.Get('1'); // 1 == 코스피 종목 foreach (var item in items) { Console.WriteLine(item.uplmtprice); // 상한가 출력 // item.Dump(Console.Out, DumpOutputType.Inline80Cols); } } </pre> <br /> 오~~~ 엄청 간단합니다. 하는 김에 t8425로 가져오는 테마주도 다뤄볼까요? ^^ 위의 코드에 다음과 같은 코드만 추가하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using (XingClient xing = new XingClient(true)) { // ...[생략]... XQt8425OutBlock [] items2 = XQt8425.Get(); foreach (var item in items2) { item.Dump(Console.Out, DumpOutputType.Inline); } } </pre> <br /> 테마주의 목록이 화면에 출력됩니다. 게다가 모든 타입에 속성으로 API의 필드까지 모두 포함되어 있어 문자열을 하드 코딩할 필요 없이 인텔리센스의 도움을 받아 가며 작업할 수 있습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 프로그램을 빌드하면 출력 폴더로 eBEST의 xingAPI 패키지에 포함된 바이너리가 함께 출력됩니다.<br /> <br /> <img alt='how_to_use_xing_2.png' src='/SysWebRes/bbs/how_to_use_xing_2.png' /><br /> <br /> 또한, 예전에 키움 OpenAPI처럼,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 키움 Open API+ 사용 시 Registry 등록 없이 KHOpenAPI.ocx 사용하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12129'>https://www.sysnet.pe.kr/2/0/12129</a> </pre> <br /> app.manifest 파일을 추가해 다음의 내용으로 채워주면 말 그대로 XCopy 배포 방식도 가능합니다. 즉, COM 객체 등록도 필요가 없는 것입니다. (만약 이 파일을 추가하지 않으면 빌드 출력 폴더의 reg.bat 파일을 관리자 권한으로 실행해야 합니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <?xml version="1.0" encoding="utf-8"?> <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> </application> </compatibility> <dependency> <dependentAssembly asmv2:dependencyType="install" asmv2:codebase="XA_Session.dll.manifest" asmv2:size="1035"> <assemblyIdentity name="XA_Session.dll" version="1.0.0.1" processorArchitecture="x86" type="win32" /> </dependentAssembly> </dependency> <dependency> <dependentAssembly asmv2:dependencyType="install" asmv2:codebase="XA_DataSet.dll.manifest" asmv2:size="1654"> <assemblyIdentity name="XA_DataSet.dll" version="1.0.0.1" processorArchitecture="x86" type="win32" /> </dependentAssembly> </dependency> </asmv1:assembly> </pre> <br /> 이 때문에, eBEST의 데모 서버에 접속하는 용도라면 인증서까지도 필요 없기 때문에 아무 PC에나 .NET Frmaework 4.0만 설치되어 있다면 그대로 복사해 실행하는 것이 가능합니다. 물론, 실 서버에 접속한다면 공인 인증서가 설치된 PC이기만 하면 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='pro'></a> <br /> 직접 해보니, 키움 API보다 eBEST의 XingAPI가 더 좋습니다. 왜냐하면 API 단에서 로그인할 수 있는 방법도 제공하기 때문에 "NT 서비스" 형식의 프로그램도 만들 수 있어 컴퓨터에 수동으로 직접 로그인하지 않아도 시스템 트레이딩 작업이 가능합니다. (혹은 autohotkey 같은 꼼수나 기타 UAC를 꺼서 보안 위협이 될 만한 조치가 전혀 필요 없습니다.) 여전히 Unicode 지원이 되지 않는다는 정도의 단점은 있지만 그래도 이 정도면 키움에 비해서는 훌륭합니다. ^^<br /> <br /> 또한, 해당 API들의 명세를 "RES" 확장자 파일로 제공하는데 제법 규격화가 잘 되어 있습니다. 예를 들어 아래는 t0167.res 파일의 내용인데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > BEGIN_FUNCTION_MAP .Func,서버시간조회(t0167),t0167,block,headtype=A; BEGIN_DATA_MAP t0167InBlock,기본입력,input; begin id,id,id,char,8; end t0167OutBlock,출력,output; begin 일자(YYYYMMDD),dt,dt,char,8; 시간(HHMMSSssssss),time,time,char,12; end END_DATA_MAP END_FUNCTION_MAP </pre> <br /> 제가 만든 <a target='tab' href='https://github.com/stjeong/XingAPI/tree/master/Res2Query'>Res2Query 프로젝트</a>는 위의 RES 파일을 읽어들여 다음과 같은 형식의 C# 소스 코드 파일을 생성합니다. (그리고 이 파일은 XingAPINet 바이너리에 포함됩니다.)<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using XingAPINet; namespace XingAPINet { public partial class XQt0167InBlock : XingBlock { /// <summary> /// t0167InBlock /// </summary> public const string _blockName = "t0167InBlock"; /// <summary> /// 기본입력 /// </summary> public const string _blockDesc = "기본입력"; /// <summary> /// input /// </summary> public const string _blockType = "input"; /// <summary> /// false /// </summary> public const bool _hasOccurs = false; /// <summary> /// t0167InBlock /// </summary> public override string GetBlockName() => _blockName; /// <summary> /// t0167InBlock /// </summary> public static string BlockName => _blockName; /// <summary> /// 기본입력 /// </summary> public string BlockDesc => _blockDesc; /// <summary> /// input /// </summary> public string BlockType => _blockType; /// <summary> /// false /// </summary> public bool HasOccurs => _hasOccurs; /// <summary> /// id /// </summary> [XAQueryFieldAttribute("id")] public string id; public static class F { /// <summary> /// id /// </summary> public const string id = "id"; } public static string[] AllFields = new string[] { F.id, }; public override Dictionary<string, XAQueryFieldInfo> GetFieldsInfo() { Dictionary<string, XAQueryFieldInfo> dict = new Dictionary<string, XAQueryFieldInfo>(); dict["id"] = new XAQueryFieldInfo("char", id, id, "id", (decimal)8); return dict; } public override void SetFieldValue(string fieldName, XAQueryFieldInfo fieldInfo) { switch (fieldName) { case "id": this.id = fieldInfo.FieldValue.TrimEnd('?'); break; } } public bool VerifyData() { if (id?.Length > 8) return false; // char 8 return true; } } public partial class XQt0167OutBlock : XingBlock { /// <summary> /// t0167OutBlock /// </summary> public const string _blockName = "t0167OutBlock"; /// <summary> /// 출력 /// </summary> public const string _blockDesc = "출력"; /// <summary> /// output /// </summary> public const string _blockType = "output"; /// <summary> /// false /// </summary> public const bool _hasOccurs = false; /// <summary> /// t0167OutBlock /// </summary> public override string GetBlockName() => _blockName; /// <summary> /// t0167OutBlock /// </summary> public static string BlockName => _blockName; /// <summary> /// 출력 /// </summary> public string BlockDesc => _blockDesc; /// <summary> /// output /// </summary> public string BlockType => _blockType; /// <summary> /// false /// </summary> public bool HasOccurs => _hasOccurs; /// <summary> /// 일자(YYYYMMDD) /// </summary> [XAQueryFieldAttribute("일자(YYYYMMDD)")] public string dt; /// <summary> /// 시간(HHMMSSssssss) /// </summary> [XAQueryFieldAttribute("시간(HHMMSSssssss)")] public string time; public static class F { /// <summary> /// 일자(YYYYMMDD) /// </summary> public const string dt = "dt"; /// <summary> /// 시간(HHMMSSssssss) /// </summary> public const string time = "time"; } public static string[] AllFields = new string[] { F.dt, F.time, }; public override Dictionary<string, XAQueryFieldInfo> GetFieldsInfo() { Dictionary<string, XAQueryFieldInfo> dict = new Dictionary<string, XAQueryFieldInfo>(); dict["dt"] = new XAQueryFieldInfo("char", dt, dt, "일자(YYYYMMDD)", (decimal)8); dict["time"] = new XAQueryFieldInfo("char", time, time, "시간(HHMMSSssssss)", (decimal)12); return dict; } public override void SetFieldValue(string fieldName, XAQueryFieldInfo fieldInfo) { switch (fieldName) { case "dt": this.dt = fieldInfo.FieldValue.TrimEnd('?'); break; case "time": this.time = fieldInfo.FieldValue.TrimEnd('?'); break; } } public static XQt0167OutBlock FromQuery(XQt0167 query) { XQt0167OutBlock block = new XQt0167OutBlock(); block.IsValidData = true; block.InvalidReason = ""; if (query.QueryResult != null && query.QueryResult.IsSystemError == true) { block.IsValidData = false; block.InvalidReason = query.ReceiveMessage; return block; } try { block.dt = query.GetFieldData(block.GetBlockName(), "dt", 0).TrimEnd('?'); // char 8 block.time = query.GetFieldData(block.GetBlockName(), "time", 0).TrimEnd('?'); // char 12 } catch (InvalidDataFormatException e) { block.IsValidData = false; block.InvalidReason = $"FieldName == {e.DataFieldName}, FieldData == \"{e.DataValue}\""; } return block; } public bool VerifyData() { if (dt?.Length > 8) return false; // char 8 if (time?.Length > 12) return false; // char 12 return true; } } /// <summary> /// 서버시간조회(t0167) /// </summary> public partial class XQt0167 : XingQuery { /// <summary> /// t0167 /// </summary> public const string _typeName = "t0167"; /// <summary> /// 서버시간조회(t0167) /// </summary> public const string _typeDesc = "서버시간조회(t0167)"; /// <summary> /// /// </summary> public const string _service = ""; /// <summary> /// A /// </summary> public const string _headType = "A"; /// <summary> /// /// </summary> public const string _creator = ""; /// <summary> /// /// </summary> public const string _createdDate = ""; /// <summary> /// false /// </summary> public const bool _attr = false; /// <summary> /// true /// </summary> public const bool _block = true; /// <summary> /// false /// </summary> public const bool _encrypt = false; /// <summary> /// false /// </summary> public const bool _signature = false; /// <summary> /// t0167 /// </summary> public string TypeName => _typeName; /// <summary> /// 서버시간조회(t0167) /// </summary> public string TypeDesc => _typeDesc; /// <summary> /// /// </summary> public string Service => _service; /// <summary> /// A /// </summary> public string HeadType => _headType; /// <summary> /// /// </summary> public string Creator => _creator; /// <summary> /// /// </summary> public string CreatedDate => _createdDate; /// <summary> /// false /// </summary> public bool Attr => _attr; /// <summary> /// true /// </summary> public bool Block => _block; /// <summary> /// false /// </summary> public bool Encrypt => _encrypt; /// <summary> /// false /// </summary> public bool Signature => _signature; public XQt0167() : base("t0167") { } public static XQt0167OutBlock Get(string id = default) { using (XQt0167 instance = new XQt0167()) { instance.SetFieldData(XQt0167InBlock.BlockName, XQt0167InBlock.F.id, 0, id); // char 8 if (instance.Request() < 0) { return null; } var outBlock = instance.GetBlock(); if (outBlock.IsValidData == false) { return null; } return outBlock; } } public bool SetBlock(XQt0167InBlock block) { if (block.VerifyData() == false) { return false; // throw new ApplicationException("Failed to verify: " + block.BlockName); } _xaQuery.SetFieldData(block.GetBlockName(), "id", 0, block.id); // char 8 return true; } public XQt0167OutBlock GetBlock() { XQt0167OutBlock instance = XQt0167OutBlock.FromQuery(this); return instance; } } } </pre> <br /> 그렇기 때문에 여러분들은 하드 코딩할 필요 없이 다음과 같은 식으로 In/Out 데이터를 다룰 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // XQt0167OutBlock outBlock = XQt0167.Get(); var outBlock = XQt0167.Get(); Console.WriteLine("dt == " + outBlock.dt); Console.WriteLine("time == " + outBlock.time); </pre> <br /> 심지어 해당 Block의 필드 자체도 클래스의 필드로 추가해 두었기 때문에 다음과 같이 하드 코딩을 없앨 수 있습니다. (제가 인텔리센스 광팬입니다. ^^)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Console.WriteLine(XQt0167OutBlock.F.dt + " == " + outBlock.dt); Console.WriteLine(XQt0167OutBlock.F.time + " == " + outBlock.time); </pre> <br /> 취향에 따라, Get 정적 메서드를 사용하지 않고 이를 풀어서 다음과 같이 사용하는 것도 가능합니다. (업데이트 2021-02-24: 현재 Get 버전의 내부에서 사용하는 XAQuery COM 개체에 메모리 누수가 있으므로 하루 종일 켜두면서 초당 몇 번씩 호출하는 식의 응용 프로그램에서는 사용하지 않는 것이 권장됩니다. 따라서 그런 경우에는 아래의 방법으로 query 인스턴스를 하나 생성해 두고 Request 호출을 반복적으로 처리하는 것이 좋습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using (XQt0167 query = new XQt0167()) { query.SetFieldData(XQt0167InBlock.BlockName, XQt0167InBlock.F.id, 0, ""); if (query.Request() < 0) { Console.WriteLine("Failed to send request"); } XQt0167OutBlock outBlock = query.GetBlock(); if (outBlock.IsValidData == true) { outBlock.Dump(Console.Out, DumpOutputType.FormattedKeyValue); } else { Console.WriteLine($"Invalid: {outBlock.InvalidReason}"); } } </pre> <br /> 이 정도면... 좀 쓸만하겠죠? ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1547&boardid=331301885'>첨부 파일은 t0167 예제 코드 및 app.manifest까지 구성한 완전한 예제 프로젝트</a>입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 유튜브에 보니까, python으로 xingAPI를 강의하는 것이 있습니다. 일단 21강 정도까지는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [21강] xingAPI를 이용한 데이타 수신 : 종목코드조회(t8430) ; <a target='tab' href='https://www.youtube.com/watch?v=loO2isn8OXk'>https://www.youtube.com/watch?v=loO2isn8OXk</a> </pre> <br /> 제가 만든 C# API로 무리 없이 따라 할 수 있었습니다. 말인즉, XingAPINet 라이브러리가 완벽하지 않을 있다는 점을 감안하시고 혹시 개선해야 할 점이 있다면 소스 코드가 모두 공개되었으니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > stjeong / XingAPI ; <a target='tab' href='https://github.com/stjeong/XingAPI'>https://github.com/stjeong/XingAPI</a> </pre> <br /> PR을 날리셔도 좋겠고, "<a target='tab' href='https://github.com/stjeong/XingAPI/issues'>Issues</a>" 게시판을 이용해 개선점을 남기시면 최대한 시간 날 때 반영해 보겠습니다. 물론, 저도 틈나는 대로 위에서 소개한 Youtube 강의를 보면서 그에 대응하는 예제를 직접 C#으로 만들어 추가하고 있습니다. 그래서 현재 다음의 경로에 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > XingAPI/DevCenterSample/TR/업종 ; <a target='tab' href='https://github.com/stjeong/XingAPI/tree/master/DevCenterSample/TR/업종'>https://github.com/stjeong/XingAPI/tree/master/DevCenterSample/TR/업종</a> </pre> <br /> t0167, t1475, t1511,... 등의 코드가 들어있으니 이를 참고해 코딩하셔도 무방합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1048
(왼쪽의 숫자를 입력해야 합니다.)