├── .gitignore ├── KHOpenAPICtrl.cpp ├── KHOpenAPICtrl.h ├── KiwoomRestfulCpp.cpp ├── KiwoomRestfulCpp.h ├── KiwoomRestfulCpp.rc ├── KiwoomRestfulCpp.sln ├── KiwoomRestfulCpp.vcxproj ├── KiwoomRestfulCppDlg.cpp ├── KiwoomRestfulCppDlg.h ├── README.md ├── crow_all.h ├── docker ├── Dockerfile ├── README.md ├── ans.iss ├── autostart ├── build.sh ├── entry_point.sh ├── img │ ├── vnc01.png │ ├── vnc02.png │ ├── vnc03.png │ ├── vnc04.png │ ├── vnc05.png │ ├── vnc06.png │ ├── vnc07.png │ ├── vnc08.png │ └── vnc09.png ├── kiwoom.service ├── run_vnc.sh ├── run_vnc_saved.sh └── xinitrc ├── framework.h ├── pch.cpp ├── pch.h ├── res ├── KiwoomRestfulCpp.ico └── KiwoomRestfulCpp.rc2 ├── resource.h ├── targetver.h └── vcpkg.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.opensdf 2 | *.sdf 3 | *.ipch 4 | *.suo 5 | *.exp 6 | *.ilk 7 | *.pdb 8 | *.iobj 9 | *.ipdb 10 | *.aps 11 | *.user 12 | *.tlog 13 | *.obj 14 | *.log 15 | *.res 16 | *.idb 17 | *.pch 18 | *.lastbuildstate 19 | 20 | # Directories 21 | ipch 22 | Debug 23 | Release 24 | .vs 25 | 26 | # Executables 27 | *.exe 28 | *.dll 29 | *.lib 30 | -------------------------------------------------------------------------------- /KHOpenAPICtrl.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/KHOpenAPICtrl.cpp -------------------------------------------------------------------------------- /KHOpenAPICtrl.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/KHOpenAPICtrl.h -------------------------------------------------------------------------------- /KiwoomRestfulCpp.cpp: -------------------------------------------------------------------------------- 1 |  2 | // KiwoomRestfulCpp.cpp: 애플리케이션에 대한 클래스 동작을 정의합니다. 3 | // 4 | #include 5 | #include "pch.h" 6 | #include "framework.h" 7 | #include "KiwoomRestfulCpp.h" 8 | #include "KiwoomRestfulCppDlg.h" 9 | #include 10 | 11 | #ifdef _DEBUG 12 | #define new DEBUG_NEW 13 | #endif 14 | 15 | 16 | // CKiwoomRestfulCppApp 17 | 18 | BEGIN_MESSAGE_MAP(CKiwoomRestfulCppApp, CWinApp) 19 | ON_COMMAND(ID_HELP, &CWinApp::OnHelp) 20 | END_MESSAGE_MAP() 21 | 22 | 23 | // CKiwoomRestfulCppApp 생성 24 | 25 | CKiwoomRestfulCppApp::CKiwoomRestfulCppApp() 26 | { 27 | // InitInstance에 모든 중요한 초기화 작업을 배치합니다. 28 | } 29 | 30 | 31 | // 유일한 CKiwoomRestfulCppApp 개체입니다. 32 | 33 | CKiwoomRestfulCppApp theApp; 34 | 35 | 36 | // CKiwoomRestfulCppApp 초기화 37 | 38 | BOOL CKiwoomRestfulCppApp::InitInstance() 39 | { 40 | // 애플리케이션 매니페스트가 ComCtl32.dll 버전 6 이상을 사용하여 비주얼 스타일을 41 | // 사용하도록 지정하는 경우, Windows XP 상에서 반드시 InitCommonControlsEx()가 필요합니다. 42 | // InitCommonControlsEx()를 사용하지 않으면 창을 만들 수 없습니다. 43 | INITCOMMONCONTROLSEX InitCtrls; 44 | InitCtrls.dwSize = sizeof(InitCtrls); 45 | // 응용 프로그램에서 사용할 모든 공용 컨트롤 클래스를 포함하도록 46 | // 이 항목을 설정하십시오. 47 | InitCtrls.dwICC = ICC_WIN95_CLASSES; 48 | InitCommonControlsEx(&InitCtrls); 49 | 50 | CWinApp::InitInstance(); 51 | 52 | AfxEnableControlContainer(); 53 | 54 | // 대화 상자에 셸 트리 뷰 또는 55 | // 셸 목록 뷰 컨트롤이 포함되어 있는 경우 셸 관리자를 만듭니다. 56 | CShellManager *pShellManager = new CShellManager; 57 | 58 | // MFC 컨트롤의 테마를 사용하기 위해 "Windows 원형" 비주얼 관리자 활성화 59 | CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); 60 | 61 | // 표준 초기화 62 | // 이들 기능을 사용하지 않고 최종 실행 파일의 크기를 줄이려면 63 | // 아래에서 필요 없는 특정 초기화 64 | // 루틴을 제거해야 합니다. 65 | // 해당 설정이 저장된 레지스트리 키를 변경하십시오. 66 | // TODO: 이 문자열을 회사 또는 조직의 이름과 같은 67 | // 적절한 내용으로 수정해야 합니다. 68 | SetRegistryKey(_T("로컬 애플리케이션 마법사에서 생성된 애플리케이션")); 69 | 70 | this->portNumber = 12233; 71 | this->bindAddr = "127.0.0.1"; 72 | if (__argc >= 3) 73 | { 74 | std::wstring addr = __wargv[1]; 75 | this->bindAddr.assign(addr.begin(), addr.end()); 76 | this->portNumber = std::stoi(__wargv[2]); 77 | } 78 | else if (__argc == 2) 79 | { 80 | this->portNumber = std::stoi(__wargv[1]); 81 | } 82 | 83 | CKiwoomRestfulCppDlg dlg; 84 | m_pMainWnd = &dlg; 85 | INT_PTR nResponse = dlg.DoModal(); 86 | if (nResponse == IDOK) 87 | { 88 | // TODO: 여기에 [확인]을 클릭하여 대화 상자가 없어질 때 처리할 89 | // 코드를 배치합니다. 90 | } 91 | else if (nResponse == IDCANCEL) 92 | { 93 | // TODO: 여기에 [취소]를 클릭하여 대화 상자가 없어질 때 처리할 94 | // 코드를 배치합니다. 95 | } 96 | else if (nResponse == -1) 97 | { 98 | TRACE(traceAppMsg, 0, "경고: 대화 상자를 만들지 못했으므로 애플리케이션이 예기치 않게 종료됩니다.\n"); 99 | TRACE(traceAppMsg, 0, "경고: 대화 상자에서 MFC 컨트롤을 사용하는 경우 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS를 수행할 수 없습니다.\n"); 100 | } 101 | 102 | // 위에서 만든 셸 관리자를 삭제합니다. 103 | if (pShellManager != nullptr) 104 | { 105 | delete pShellManager; 106 | } 107 | 108 | #if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS) 109 | ControlBarCleanUp(); 110 | #endif 111 | 112 | // 대화 상자가 닫혔으므로 응용 프로그램의 메시지 펌프를 시작하지 않고 응용 프로그램을 끝낼 수 있도록 FALSE를 113 | // 반환합니다. 114 | return FALSE; 115 | } 116 | -------------------------------------------------------------------------------- /KiwoomRestfulCpp.h: -------------------------------------------------------------------------------- 1 |  2 | // KiwoomRestfulCpp.h: PROJECT_NAME 애플리케이션에 대한 주 헤더 파일입니다. 3 | // 4 | 5 | #pragma once 6 | //#include "crow_all.h" 7 | 8 | #ifndef __AFXWIN_H__ 9 | #error "PCH에 대해 이 파일을 포함하기 전에 'pch.h'를 포함합니다." 10 | #endif 11 | 12 | #include "resource.h" // 주 기호입니다. 13 | #include "KHOpenAPICtrl.h" 14 | #include 15 | 16 | // CKiwoomRestfulCppApp: 17 | // 이 클래스의 구현에 대해서는 KiwoomRestfulCpp.cpp을(를) 참조하세요. 18 | // 19 | 20 | class CKiwoomRestfulCppApp : public CWinApp 21 | { 22 | public: 23 | CKiwoomRestfulCppApp(); 24 | int portNumber; 25 | std::string bindAddr; 26 | 27 | // 재정의입니다. 28 | public: 29 | virtual BOOL InitInstance(); 30 | 31 | // 구현입니다. 32 | 33 | DECLARE_MESSAGE_MAP() 34 | }; 35 | 36 | extern CKiwoomRestfulCppApp theApp; 37 | -------------------------------------------------------------------------------- /KiwoomRestfulCpp.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/KiwoomRestfulCpp.rc -------------------------------------------------------------------------------- /KiwoomRestfulCpp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31424.327 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KiwoomRestfulCpp", "KiwoomRestfulCpp.vcxproj", "{AEF7C594-8318-4A63-AC4E-856F310E1249}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Debug|x64.ActiveCfg = Debug|x64 17 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Debug|x64.Build.0 = Debug|x64 18 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Debug|x86.ActiveCfg = Debug|Win32 19 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Debug|x86.Build.0 = Debug|Win32 20 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Release|x64.ActiveCfg = Release|x64 21 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Release|x64.Build.0 = Release|x64 22 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Release|x86.ActiveCfg = Release|Win32 23 | {AEF7C594-8318-4A63-AC4E-856F310E1249}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {CDDC900C-21E2-4531-BFE4-B05DF276E4CC} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /KiwoomRestfulCpp.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {AEF7C594-8318-4A63-AC4E-856F310E1249} 24 | MFCProj 25 | KiwoomRestfulCpp 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | Dynamic 35 | 36 | 37 | Application 38 | false 39 | v142 40 | true 41 | Unicode 42 | Dynamic 43 | 44 | 45 | Application 46 | true 47 | v142 48 | Unicode 49 | Dynamic 50 | 51 | 52 | Application 53 | false 54 | v142 55 | true 56 | Unicode 57 | Dynamic 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | false 88 | 89 | 90 | false 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Use 111 | Level3 112 | true 113 | WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) 114 | pch.h 115 | C:\src\vcpkg\installed\x86-windows\include;%(AdditionalIncludeDirectories) 116 | 117 | 118 | Windows 119 | 120 | 121 | false 122 | true 123 | _DEBUG;%(PreprocessorDefinitions) 124 | 125 | 126 | 0x0412 127 | _DEBUG;%(PreprocessorDefinitions) 128 | $(IntDir);%(AdditionalIncludeDirectories) 129 | 130 | 131 | 132 | 133 | Use 134 | Level3 135 | true 136 | _WINDOWS;_DEBUG;%(PreprocessorDefinitions) 137 | pch.h 138 | C:\src\vcpkg\installed\x86-windows\include;%(AdditionalIncludeDirectories) 139 | 140 | 141 | Windows 142 | 143 | 144 | false 145 | true 146 | _DEBUG;%(PreprocessorDefinitions) 147 | 148 | 149 | 0x0412 150 | _DEBUG;%(PreprocessorDefinitions) 151 | $(IntDir);%(AdditionalIncludeDirectories) 152 | 153 | 154 | 155 | 156 | Use 157 | Level3 158 | true 159 | true 160 | true 161 | WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) 162 | pch.h 163 | C:\src\vcpkg\installed\x86-windows\include;%(AdditionalIncludeDirectories) 164 | 165 | 166 | Windows 167 | true 168 | true 169 | 170 | 171 | false 172 | true 173 | NDEBUG;%(PreprocessorDefinitions) 174 | 175 | 176 | 0x0412 177 | NDEBUG;%(PreprocessorDefinitions) 178 | $(IntDir);%(AdditionalIncludeDirectories) 179 | 180 | 181 | 182 | 183 | Use 184 | Level3 185 | true 186 | true 187 | true 188 | _WINDOWS;NDEBUG;%(PreprocessorDefinitions) 189 | pch.h 190 | C:\src\vcpkg\installed\x86-windows\include;%(AdditionalIncludeDirectories) 191 | 192 | 193 | Windows 194 | true 195 | true 196 | 197 | 198 | false 199 | true 200 | NDEBUG;%(PreprocessorDefinitions) 201 | 202 | 203 | 0x0412 204 | NDEBUG;%(PreprocessorDefinitions) 205 | $(IntDir);%(AdditionalIncludeDirectories) 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | Create 224 | Create 225 | Create 226 | Create 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /KiwoomRestfulCppDlg.cpp: -------------------------------------------------------------------------------- 1 |  2 | // KiwoomRestfulCppDlg.cpp: 구현 파일 3 | // 4 | #include "pch.h" 5 | #include "framework.h" 6 | #include "KiwoomRestfulCpp.h" 7 | #include "KiwoomRestfulCppDlg.h" 8 | #include "afxdialogex.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define CROW_MAIN 17 | #include "crow_all.h" 18 | 19 | #ifdef _DEBUG 20 | #define new DEBUG_NEW 21 | #endif 22 | 23 | 24 | // trim from start (in place) 25 | static inline void ltrim(std::wstring& s) { 26 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { 27 | return !std::isspace(ch); 28 | })); 29 | } 30 | 31 | // trim from end (in place) 32 | static inline void rtrim(std::wstring& s) 33 | { 34 | s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { 35 | return !std::isspace(ch); 36 | }).base(), s.end()); 37 | } 38 | 39 | // trim from both ends (in place) 40 | static inline void trim(std::wstring& s) 41 | { 42 | ltrim(s); 43 | rtrim(s); 44 | } 45 | 46 | // trim from start (copying) 47 | static inline std::wstring ltrim_copy(std::wstring s) 48 | { 49 | ltrim(s); 50 | return s; 51 | } 52 | 53 | // trim from end (copying) 54 | static inline std::wstring rtrim_copy(std::wstring s) 55 | { 56 | rtrim(s); 57 | return s; 58 | } 59 | 60 | // trim from both ends (copying) 61 | static inline std::wstring trim_copy(std::wstring s) 62 | { 63 | trim(s); 64 | return s; 65 | } 66 | 67 | std::wstring cstring2int2str(const CString &num) 68 | { 69 | if (num == L"") 70 | return L"0"; 71 | int x = std::stoi(std::wstring(num)); 72 | return std::to_wstring(x); 73 | } 74 | 75 | bool startswith(const std::wstring& ss, const std::wstring& prefix) 76 | { 77 | if (ss.size() < prefix.size()) 78 | return false; 79 | 80 | for (int i = 0; i < prefix.size(); ++i) 81 | { 82 | if (prefix[i] != ss[i]) 83 | return false; 84 | } 85 | return true; 86 | } 87 | 88 | 89 | // CKiwoomRestfulCppDlg 대화 상자 90 | 91 | UINT CrowThreadProc(LPVOID Param) 92 | { 93 | crow::SimpleApp* app = (crow::SimpleApp*) Param; 94 | app->multithreaded().run(); 95 | return 0; 96 | } 97 | 98 | CKiwoomRestfulCppDlg::CKiwoomRestfulCppDlg(CWnd* pParent /*=nullptr*/) 99 | : CDialogEx(IDD_KIWOOMRESTFULCPP_DIALOG, pParent) 100 | { 101 | m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 102 | } 103 | 104 | void CKiwoomRestfulCppDlg::DoDataExchange(CDataExchange* pDX) 105 | { 106 | CDialogEx::DoDataExchange(pDX); 107 | 108 | // 이거 안 하면 winocc.cpp line 377 에서 에러 뜸 109 | DDX_Control(pDX, IDC_KHOPENAPICTRL1, this->kiwoom); 110 | } 111 | 112 | BEGIN_MESSAGE_MAP(CKiwoomRestfulCppDlg, CDialogEx) 113 | ON_WM_PAINT() 114 | ON_WM_QUERYDRAGICON() 115 | ON_WM_DESTROY() 116 | END_MESSAGE_MAP() 117 | 118 | 119 | // CKiwoomRestfulCppDlg 메시지 처리기 120 | 121 | void CKiwoomRestfulCppDlg::initCrowHandlers(void *voidCrowApp) 122 | { 123 | crow::SimpleApp* crowApp = (crow::SimpleApp*)voidCrowApp; 124 | 125 | CROW_ROUTE((*crowApp), "/")([]() 126 | { 127 | return "Kiwoom Restful"; 128 | }); 129 | 130 | CROW_ROUTE((*crowApp), "/balance") 131 | .methods("POST"_method) 132 | ([this](const crow::request& req) 133 | { 134 | auto x = crow::json::load(req.body); 135 | if (!x) { 136 | return crow::response(400); 137 | } 138 | 139 | this->reqno++; 140 | std::wstring str_reqno = std::to_wstring(this->reqno); 141 | std::wstring rqname = std::wstring(L"req") + str_reqno; 142 | //std::string accno_str = std::string(x["accno"].s().begin()); 143 | std::wstring accno; 144 | // wstr to str, thanks to https://wendys.tistory.com/40 145 | accno.assign(x["accno"].s().begin(), x["accno"].s().end()); 146 | 147 | // Delayed connectedness check for faster debug of the crow part 148 | if (!this->kiwoomConnected) { 149 | return crow::response{ "{\"error\": \"Kiwoom not connected yet\"}" }; 150 | } 151 | 152 | this->kiwoom.SetInputValue(L"계좌번호", (LPCTSTR)accno.c_str()); 153 | this->kiwoom.SetInputValue(L"비밀번호", L""); 154 | this->kiwoom.SetInputValue(L"상장폐지조회구분", L"1"); 155 | this->kiwoom.SetInputValue(L"비밀번호입력매체구분", L"00"); 156 | int errCode = this->kiwoom.CommRqData((LPCTSTR)rqname.c_str(), L"OPW00004", 0, (LPCTSTR)str_reqno.c_str()); 157 | 158 | // Busy for data arrival 159 | // Semaphore같은거 쓰면 좋을수도 있는데 귀찮다 160 | // C++11 지원되게 컴파일하면 일단 reading은 tread-safe하니 OK...? 161 | reqMap[rqname] = false; 162 | while (!reqMap[rqname]); 163 | 164 | reqMap.erase(reqMap.find(rqname)); 165 | std::string resp = boost::locale::conv::utf_to_utf(resultMap[rqname]); 166 | resultMap.erase(resultMap.find(rqname)); 167 | return crow::response{ resp }; 168 | }); 169 | 170 | CROW_ROUTE((*crowApp), "/order") 171 | .methods("POST"_method) 172 | ([this](const crow::request& req) 173 | { 174 | auto x = crow::json::load(req.body); 175 | if (!x) { 176 | return crow::response(400); 177 | } 178 | 179 | this->reqno++; 180 | std::wstring str_reqno = std::to_wstring(this->reqno); 181 | 182 | std::wstring rqname, screenno, accno, code, hogagb, orgorderno; 183 | rqname = std::wstring(L"sendorder") + str_reqno; 184 | screenno = str_reqno; 185 | // str to wstr, thanks to https://wendys.tistory.com/40 186 | accno.assign(x["accno"].s().begin(), x["accno"].s().end()); 187 | code.assign(x["code"].s().begin(), x["code"].s().end()); 188 | int order_type = x["ordertype"].i(); 189 | int qty = x["qty"].i(); 190 | int price = x["price"].i(); 191 | hogagb.assign(x["hogagb"].s().begin(), x["hogagb"].s().end()); 192 | 193 | // Delayed connectedness check for faster debug of the crow part 194 | if (!this->kiwoomConnected) { 195 | return crow::response{ "{\"error\": \"Kiwoom not connected yet\"}" }; 196 | } 197 | 198 | int errCode = this->kiwoom.SendOrder( 199 | (LPCTSTR) rqname.c_str(), 200 | (LPCTSTR) screenno.c_str(), 201 | (LPCTSTR) accno.c_str(), 202 | order_type, 203 | (LPCTSTR) code.c_str(), 204 | qty, 205 | price, 206 | (LPCTSTR) hogagb.c_str(), 207 | (LPCTSTR) orgorderno.c_str() // 원주문번호, 정정 때 쓰임 208 | ); 209 | 210 | // Busy for data arrival 211 | // Semaphore같은거 쓰면 좋을수도 있는데 귀찮다 212 | // C++11 지원되게 컴파일하면 일단 reading은 tread-safe하니 OK...? 213 | reqMap[rqname] = false; 214 | while (!reqMap[rqname]); 215 | 216 | reqMap.erase(reqMap.find(rqname)); 217 | std::string resp = boost::locale::conv::utf_to_utf(resultMap[rqname]); 218 | resultMap.erase(resultMap.find(rqname)); 219 | return crow::response{ "{\"message\": \"" + resp + "\"}" }; 220 | }); 221 | } 222 | 223 | BOOL CKiwoomRestfulCppDlg::OnInitDialog() 224 | { 225 | CDialogEx::OnInitDialog(); 226 | 227 | // 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는 228 | // 프레임워크가 이 작업을 자동으로 수행합니다. 229 | SetIcon(m_hIcon, TRUE); // 큰 아이콘을 설정합니다. 230 | SetIcon(m_hIcon, FALSE); // 작은 아이콘을 설정합니다. 231 | 232 | // 로그인 ㄱㄱ 233 | this->kiwoomConnected = false; // Will become true when KiwoomOnEventConnect() received. 234 | this->reqno = 0; 235 | this->kiwoom.CommConnect(); 236 | 237 | // https://int-i.github.io/cpp/2020-07-22/vcpkg-boost/ 238 | // Install boost with vcpkg! 239 | // .\vcpkg install boost boost:x64-windows 240 | // .\vcpkg integrate install 241 | auto crowApp = new crow::SimpleApp(); 242 | this->crowApp = (void*)crowApp; 243 | this->initCrowHandlers(crowApp); 244 | crowApp->bindaddr(theApp.bindAddr); 245 | crowApp->port(theApp.portNumber); 246 | AfxBeginThread(CrowThreadProc, (LPVOID) crowApp); 247 | 248 | return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다. 249 | } 250 | 251 | // 대화 상자에 최소화 단추를 추가할 경우 아이콘을 그리려면 252 | // 아래 코드가 필요합니다. 문서/뷰 모델을 사용하는 MFC 애플리케이션의 경우에는 253 | // 프레임워크에서 이 작업을 자동으로 수행합니다. 254 | 255 | void CKiwoomRestfulCppDlg::OnPaint() 256 | { 257 | if (IsIconic()) 258 | { 259 | CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다. 260 | 261 | SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); 262 | 263 | // 클라이언트 사각형에서 아이콘을 가운데에 맞춥니다. 264 | int cxIcon = GetSystemMetrics(SM_CXICON); 265 | int cyIcon = GetSystemMetrics(SM_CYICON); 266 | CRect rect; 267 | GetClientRect(&rect); 268 | int x = (rect.Width() - cxIcon + 1) / 2; 269 | int y = (rect.Height() - cyIcon + 1) / 2; 270 | 271 | // 아이콘을 그립니다. 272 | dc.DrawIcon(x, y, m_hIcon); 273 | } 274 | else 275 | { 276 | CDialogEx::OnPaint(); 277 | } 278 | } 279 | 280 | // 사용자가 최소화된 창을 끄는 동안에 커서가 표시되도록 시스템에서 281 | // 이 함수를 호출합니다. 282 | HCURSOR CKiwoomRestfulCppDlg::OnQueryDragIcon() 283 | { 284 | return static_cast(m_hIcon); 285 | } 286 | 287 | 288 | void CKiwoomRestfulCppDlg::OnDestroy() 289 | { 290 | CDialogEx::OnDestroy(); 291 | 292 | // Delete crow 293 | crow::SimpleApp* app = (crow::SimpleApp*) this->crowApp; 294 | app->stop(); 295 | delete app; 296 | } 297 | 298 | 299 | BEGIN_EVENTSINK_MAP(CKiwoomRestfulCppDlg, CDialogEx) 300 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 5, CKiwoomRestfulCppDlg::KiwoomOnEventConnect, VTS_I4) 301 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 2, CKiwoomRestfulCppDlg::KiwoomOnReceiveRealData, VTS_BSTR VTS_BSTR VTS_BSTR) 302 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 3, CKiwoomRestfulCppDlg::KiwoomOnReceiveMsg, VTS_BSTR VTS_BSTR VTS_BSTR VTS_BSTR) 303 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 4, CKiwoomRestfulCppDlg::KiwoomOnReceiveChejanData, VTS_BSTR VTS_I4 VTS_BSTR) 304 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 6, CKiwoomRestfulCppDlg::KiwoomOnReceiveInvestRealData, VTS_BSTR) 305 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 7, CKiwoomRestfulCppDlg::KiwoomOnReceiveRealCondition, VTS_BSTR VTS_BSTR VTS_BSTR VTS_BSTR) 306 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 8, CKiwoomRestfulCppDlg::KiwoomOnReceiveTrCondition, VTS_BSTR VTS_BSTR VTS_BSTR VTS_I4 VTS_I4) 307 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 9, CKiwoomRestfulCppDlg::KiwoomOnReceiveConditionVer, VTS_I4 VTS_BSTR) 308 | ON_EVENT(CKiwoomRestfulCppDlg, IDC_KHOPENAPICTRL1, 1, CKiwoomRestfulCppDlg::KiwoomOnReceiveTrData, VTS_BSTR VTS_BSTR VTS_BSTR VTS_BSTR VTS_BSTR VTS_I4 VTS_BSTR VTS_BSTR VTS_BSTR) 309 | END_EVENTSINK_MAP() 310 | 311 | 312 | void CKiwoomRestfulCppDlg::KiwoomOnEventConnect(long nErrCode) 313 | { 314 | this->kiwoomConnected = true; 315 | if (nErrCode != 0) 316 | { 317 | std::string msg = std::string("Kiwoom connection failed with code=") + std::to_string(nErrCode); 318 | AfxMessageBox((LPCTSTR) msg.c_str(), MB_OK | MB_ICONSTOP); 319 | } 320 | else 321 | { 322 | CWnd* label = GetDlgItem(IDC_STATIC); 323 | label->SetWindowText(L"Kiwoom Connected"); 324 | } 325 | } 326 | 327 | 328 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveRealData(LPCTSTR sRealKey, LPCTSTR sRealType, LPCTSTR sRealData) 329 | { 330 | // TODO: 여기에 메시지 처리기 코드를 추가합니다. 331 | return; 332 | } 333 | 334 | 335 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveMsg(LPCTSTR sScrNo, LPCTSTR sRQName, LPCTSTR sTrCode, LPCTSTR sMsg) 336 | { 337 | // For some reason sRQName isn't requested by me. 338 | if (reqMap.find(sRQName) == reqMap.end()) 339 | return; 340 | 341 | std::wstring result; 342 | if (startswith(sRQName, L"sendorder")) 343 | { 344 | resultMap[sRQName] = sMsg; 345 | reqMap[sRQName] = true; // Mark ready. Order matters! 346 | } 347 | 348 | return; 349 | } 350 | 351 | 352 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveChejanData(LPCTSTR sGubun, long nItemCnt, LPCTSTR sFIdList) 353 | { 354 | // TODO: 여기에 메시지 처리기 코드를 추가합니다. 355 | return; 356 | } 357 | 358 | 359 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveInvestRealData(LPCTSTR sRealKey) 360 | { 361 | // TODO: 여기에 메시지 처리기 코드를 추가합니다. 362 | return; 363 | } 364 | 365 | 366 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveRealCondition(LPCTSTR sTrCode, LPCTSTR strType, LPCTSTR strConditionName, LPCTSTR strConditionIndex) 367 | { 368 | // TODO: 여기에 메시지 처리기 코드를 추가합니다. 369 | return; 370 | } 371 | 372 | 373 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveTrCondition(LPCTSTR sScrNo, LPCTSTR strCodeList, LPCTSTR strConditionName, long nIndex, long nNext) 374 | { 375 | // TODO: 여기에 메시지 처리기 코드를 추가합니다. 376 | return; 377 | } 378 | 379 | 380 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveConditionVer(long lRet, LPCTSTR sMsg) 381 | { 382 | // TODO: 여기에 메시지 처리기 코드를 추가합니다. 383 | return; 384 | } 385 | 386 | 387 | void CKiwoomRestfulCppDlg::KiwoomOnReceiveTrData(LPCTSTR sScrNo, LPCTSTR sRQName, LPCTSTR sTrCode, LPCTSTR sRecordName, LPCTSTR sPrevNext, long nDataLength, LPCTSTR sErrorCode, LPCTSTR sMessage, LPCTSTR sSplmMsg) 388 | { 389 | // For some reason sRQName isn't requested by me. 390 | if (reqMap.find(sRQName) == reqMap.end()) 391 | return; 392 | 393 | CString trcode(sTrCode); 394 | std::wstring result; 395 | 396 | if (trcode == "OPW00004") // cash... 어차피 정확하게 알기 힘듦... my자산 들어가서 나오는 양은 대체 어케 본담? 397 | { 398 | std::wstring cash = cstring2int2str(kiwoom.GetCommData(sTrCode, sRQName, 0, L"d+2추정예수금")); 399 | result = L"{"; 400 | result += L"\"cash\":" + cash; 401 | int cnt = kiwoom.GetRepeatCnt(sTrCode, sRQName); 402 | for (int i = 0; i < cnt; ++i) 403 | { 404 | std::wstring code = kiwoom.GetCommData(sTrCode, sRQName, i, L"종목코드"); 405 | trim(code); 406 | std::wstring qty = cstring2int2str(kiwoom.GetCommData(sTrCode, sRQName, i, L"보유수량")); 407 | result += L",\"" + code + L"\":" + qty; 408 | } 409 | result += L"}"; 410 | } 411 | 412 | resultMap[sRQName] = result; 413 | reqMap[sRQName] = true; // Mark ready. Order matters! 414 | } 415 | -------------------------------------------------------------------------------- /KiwoomRestfulCppDlg.h: -------------------------------------------------------------------------------- 1 |  2 | // KiwoomRestfulCppDlg.h: 헤더 파일 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | 11 | // CKiwoomRestfulCppDlg 대화 상자 12 | class CKiwoomRestfulCppDlg : public CDialogEx 13 | { 14 | // 생성입니다. 15 | public: 16 | CKiwoomRestfulCppDlg(CWnd* pParent = nullptr); // 표준 생성자입니다. 17 | 18 | // KIwoom stuff 19 | private: 20 | void initCrowHandlers(void *voidCrowApp); 21 | 22 | // crow::SimpleApp *app; 23 | void *crowApp; // Sorry man! It's not easy to include crow_all.h here! 24 | CKHOpenAPI kiwoom; 25 | bool kiwoomConnected; 26 | int reqno; 27 | std::map reqMap; 28 | std::map resultMap; 29 | 30 | public: 31 | // 대화 상자 데이터입니다. 32 | #ifdef AFX_DESIGN_TIME 33 | enum { IDD = IDD_KIWOOMRESTFULCPP_DIALOG }; 34 | #endif 35 | 36 | protected: 37 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 지원입니다. 38 | 39 | 40 | // 구현입니다. 41 | protected: 42 | HICON m_hIcon; 43 | 44 | // 생성된 메시지 맵 함수 45 | virtual BOOL OnInitDialog(); 46 | afx_msg void OnPaint(); 47 | afx_msg HCURSOR OnQueryDragIcon(); 48 | afx_msg void OnDestroy(); 49 | DECLARE_MESSAGE_MAP() 50 | public: 51 | DECLARE_EVENTSINK_MAP() 52 | void KiwoomOnEventConnect(long nErrCode); 53 | void KiwoomOnReceiveRealData(LPCTSTR sRealKey, LPCTSTR sRealType, LPCTSTR sRealData); 54 | void KiwoomOnReceiveMsg(LPCTSTR sScrNo, LPCTSTR sRQName, LPCTSTR sTrCode, LPCTSTR sMsg); 55 | void KiwoomOnReceiveChejanData(LPCTSTR sGubun, long nItemCnt, LPCTSTR sFIdList); 56 | void KiwoomOnReceiveInvestRealData(LPCTSTR sRealKey); 57 | void KiwoomOnReceiveRealCondition(LPCTSTR sTrCode, LPCTSTR strType, LPCTSTR strConditionName, LPCTSTR strConditionIndex); 58 | void KiwoomOnReceiveTrCondition(LPCTSTR sScrNo, LPCTSTR strCodeList, LPCTSTR strConditionName, long nIndex, long nNext); 59 | void KiwoomOnReceiveConditionVer(long lRet, LPCTSTR sMsg); 60 | void KiwoomOnReceiveTrData(LPCTSTR sScrNo, LPCTSTR sRQName, LPCTSTR sTrCode, LPCTSTR sRecordName, LPCTSTR sPrevNext, long nDataLength, LPCTSTR sErrorCode, LPCTSTR sMessage, LPCTSTR sSplmMsg); 61 | }; 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KiwoomRestfulCpp 2 | 3 | Under construction. 4 | See https://github.com/forcecore/KiwoomRestfulCpp/blob/master/docker/README.md instead 5 | 6 | ## License 7 | 8 | Preparing MIT License? Whichever most unrestrictive license I may use. 9 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM archlinux:latest 2 | 3 | # Add multilib for wine 4 | # Locale disable by default. Let's enable it. 5 | # Also choose faster mirrors, we have a lot to download... 6 | RUN printf "\n\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n" >> /etc/pacman.conf && \ 7 | sed -i 's/NoExtract/# NoExtract/g' /etc/pacman.conf && \ 8 | pacman -Syy && \ 9 | pacman -S --noconfirm reflector && \ 10 | reflector --country "South Korea" --sort rate --latest 10 --number 5 --save /etc/pacman.d/mirrorlist 11 | 12 | # Install wine. Wine is so huge so we want to split the run command. 13 | # winetricks must up-to-date so not using winetricks package. 14 | RUN pacman --noconfirm -S wine && \ 15 | curl https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks > /bin/winetricks && \ 16 | chmod +x /bin/winetricks 17 | 18 | # Install VNC stuff 19 | RUN pacman --noconfirm -S glibc \ 20 | xorg-xinit tigervnc xterm openbox ttf-dejavu ttf-liberation \ 21 | xorg-server-xvfb \ 22 | lib32-gnutls gnutls cabextract unzip && \ 23 | echo "ko_KR.UTF-8 UTF-8" >> /etc/locale.gen && locale-gen 24 | 25 | # Setup wine env 26 | ENV WINEARCH=win32 WINEPREFIX=/kiwoom 27 | WORKDIR /kiwoom/drive_c 28 | 29 | # Need fonts to see hangul 30 | RUN winetricks corefonts && winetricks cjkfonts 31 | # vcrun2019, as we are running an MFC application. 32 | # -q for silent install. 33 | # But even with -q, the installation will fail without the X server. 34 | # We use xvfb-run! 35 | RUN xvfb-run winetricks -q vcrun2019 36 | RUN xvfb-run winetricks -q vcrun6 37 | 38 | # Install OpenAPISetup.exe, with the answer preset ans.iss. 39 | # ans.iss can be created by running 40 | # wine OpenAPISetup.exe /r /f1"c:\\ans.iss" 41 | ENV LANG=ko_KR.utf8 LC_ALL=ko_KR.utf8 42 | COPY ./OpenAPISetup.exe ./ans.iss /kiwoom/drive_c/ 43 | RUN xvfb-run wine OpenAPISetup.exe /s /f1"c:\\ans.iss" 44 | 45 | # If you only run OpenAPISetup.exe, you get 46 | # 0024:err:ole:com_get_class_object class {a1574a0d-6bfa-4bd7-9020-ded88711818d} not registered 47 | # 0024:err:ole:com_get_class_object class {a1574a0d-6bfa-4bd7-9020-ded88711818d} not registered 48 | # 0024:err:ole:com_get_class_object no class object {a1574a0d-6bfa-4bd7-9020-ded88711818d} could be created for context 0x3 49 | # when you run the KiwoomRestfulCpp.exe. 50 | # The solution is to run regsvr32 to register the active x component manually. 51 | # Running the command once doesn't work for some reason, 52 | # (will not be commiteed into wine's system.reg) 53 | # so we run regsvr32 TWICE :) 54 | # Also, if you merge these commands with OpenAPISetup.exe, it will 55 | # also make regsvr32 not commit into system.reg. 56 | # Even if you see this message without xvfb-run, 57 | # regsvr32: Successfully registered DLL 'C:/OpenAPI/khopenapi.ocx' 58 | # It didn't work and not commited into system.reg :) 59 | # As you can see, the setup is very brittle. 60 | WORKDIR /kiwoom/drive_c/OpenAPI 61 | RUN LANG=C LC_ALL=C xvfb-run regsvr32 C:/OpenAPI/khopenapi.ocx && \ 62 | sleep 1 && \ 63 | LANG=C LC_ALL=C xvfb-run regsvr32 C:/OpenAPI/khopenapi.ocx && \ 64 | sleep 1 65 | 66 | WORKDIR /kiwoom/drive_c 67 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker for Kiwoom OpenAPI 2 | 3 | 재현 가능성을 위해 docker로 wrapping함. 4 | 5 | ## 빌드 6 | 7 | 이 폴더 안에 OpenAPISetup.exe 를 배치한다. 8 | 그리고 빌드한 (혹은 github repo의 releases 페이지에서 에서 받은) KiwoomRestfulCpp.exe 도 같이 둔다. 9 | 10 | ``` 11 | bash ./build.sh 12 | ``` 13 | 14 | 와인, MFC 등이 설치되어야 해서 한참 걸릴 것이다. 15 | 16 | ``` 17 | Wine version 18 | wine-6.22 19 | 20 | Winetricks version 21 | 20210206-next - sha256sum: 10d6518a92035512f9d5a20fcd8eab0b15dedb8a3c42eb7b6ad703ce909482a2 22 | ``` 23 | 24 | 성공한 상태에는 버전들이 이랬다. 25 | 26 | ## 인증서와 비번 입력 27 | 28 | 자동화를 위해서는 공동인증서를 복사하고 비번을 저장해야한다. 29 | 30 | * 공동인증서 복사마저 와인으로 하긴 힘드니까, 31 | * 그리고 공동인증서 갱신은 1년에 한번만 이루어지니까 32 | 꼼수를 쓰기로 하자. 33 | 34 | ### 공동인증서를 가져오는 꼼수 35 | 36 | * 윈도우 머신에서 공동인증서를 갱신/발급 받는다. 37 | * `C:\users\USER\AppData\LocalLow\NPKI` 여기 공인인증서가 있을 것이다. 38 | * NPKI 폴더를 PC쪽에서 복사하여 `$HOME/.ssh/NPKI` 가 될 수 있도록하자. 39 | * `chmod 700 ~/.ssh/NPKI` 로 권한을 소유자만 볼 수 있도록 해서 보안에 나름 신경을 써주자. 40 | 41 | ### VNC로 KiwoomRestfulCpp.exe 실행 42 | 43 | `bash run_vnc.sh` 로 VNC 실행. 44 | 자동으로 VNC가 실행될 것이다. 45 | 참고로 openbox 세션이고 비밀번호는 123456이다. 46 | 비밀번호가 마음에 들지 않으면 `entry_point.sh`를 열어서 수정하면 된다. 47 | 48 | 그리고 접속은 `localhost:5901` 로 하면 될 것이다. 49 | 아닐 수 있는데, 50 | ` vncext: Listening for VNC connections on all interface(s), port 5901` 51 | 이런 메시지에 주목해서 알아내면 된다. 52 | 53 | 접속을 하면 아직 GUI가 "부팅중" 일 것이다. 54 | 기다렸다 접속해도 상관 없다. 55 | xinitrc가 openbox를 실행하고, 56 | openbox는 autostart에 서술된 프로그램을 실행하여 57 | 결국에는 `wine KiwoomRestfulCpp.exe`가 실행되는 구조이다. 58 | 59 | 이쯤에서 KiwoomRestfulCpp.exe의 command line argument에 대해 설명하는 것이 좋을 것 같다. 60 | 필요하다면 autostart를 고쳐서 docker 빌드하면 됨. 61 | 62 | ``` 63 | # http://localhost:12233 에 bind, default 64 | wine KiwoomRestfulCpp.exe 65 | 66 | # http://localhost:3333 에 bind. (첫 parameter가 port번호로 쓰임) 67 | wine KiwoomRestfulCpp.exe 3333 68 | 69 | # http://0.0.0.0:3333 에 bind. 70 | # Parameter가 두개면 첫째가 bind address, 둘째가 port 71 | wine KiwoomRestfulCpp.exe 0.0.0.0 3333 72 | ``` 73 | 74 | 언급만하고 이후엔 그냥 default로 실행하겠음. 75 | 76 | ![](./img/vnc01.png) 77 | 78 | 실행에 성공하면 이렇게 반가운 화면이 나올 것이다. 로그인 ㄱㄱ! 79 | 80 | ### 버전처리 81 | 82 | ![](./img/vnc02.png) 83 | 84 | 로그인을 시도하면 위와 같이 버전처리에 대한 말이 나올 것이다. 85 | 윈도우에서도 마찬가지다. 86 | Unnamed Window라고 된 창만 끄고 확인 버튼을 누른다. 87 | 88 | ![](./img/vnc03.png) 89 | 90 | 성공하면 위와 같은 메시지가 나올 것이다. 91 | 92 | ### 로그인과 비번 입력 93 | 94 | 그런데 확인버튼을 누르면 아무것도 없는 검은 화면만 남을 것이다. 95 | 우클릭을 해서 xterm을 하나 켠다. 96 | 97 | ![](./img/vnc04.png) 98 | 99 | KiwoomRestfulCpp.exe 을 다시 실행한다: 100 | 101 | ![](./img/vnc05.png) 102 | 103 | ``` 104 | cd /kiwoom/drive_c 105 | wine KiwoomRestfulCpp.exe 106 | ``` 107 | 108 | 그리고 로그인까지 해본다. 109 | 110 | ![](./img/vnc06.png) 111 | 112 | 성공하면 스크린샷의 왼쪽 위처럼 트레이에 보여야 할 아이콘도 보임을 알 수 있다. 113 | 114 | ![](./img/vnc07.png) 115 | 116 | 그리고 Unnamed Window 에 클릭해보면 connected로 상태가 바뀌어있다. 117 | 118 | ![](./img/vnc08.png) 119 | 120 | 계속 켜놓으면 보안프로그램에 대한 불평을 하지만 무시해도 됨. 121 | 122 | ![](./img/vnc09.png) 123 | 124 | 무시하고 트레이에 우클릭, 윈도우에서와 마찬가지로 계좌 비밀번호를 입력하고, 125 | 저장도 해서 자동로그인에 문제 없게 만들자. 126 | 127 | 이제 거의 다 됐다. 128 | 바탕화면에 우클릭해서 로그아웃을 하자. 129 | 130 | ## 재시작 131 | 132 | 로그아웃을 하면 GUI가 닫히고, VNC도 접속 종료되고, 133 | Docker host에서 실행했던 `run_vnc.sh`도 끝날 것이다. 134 | 135 | `run_vnc_saved.sh`에도 적혀있지만, 136 | `docker start -i kiwoom_run` 137 | 로 명령어로 다시 켤 수 있다. 138 | 자동로그인을 위에서 성공적으로 해두었다면 139 | 바로 OpenAPI 창이 뜨면서 Kiwoom connected라고 뜰 것이다. 140 | 141 | ## 리눅스 서버에 서비스로 등록 142 | 143 | 제대로 되었으면 서버에서 `./run_vnc_saved.sh` 가 실행되어있기만 하면 (Ctrl+C로 종료) 144 | 145 | ``` 146 | data = { 147 | "accno": "키움계좌번호" 148 | } 149 | resp = requests.post("http://localhost:12233/balance", json=data) 150 | result = resp.json() 151 | ``` 152 | 153 | 이런 request를 보내면 국내주식 잔고가 잘 반환 될 것이다. 154 | 이를 주기적으로 켜고 끈다든지하려면 가장 좋은 것은 155 | crontab으로 docker start, stop을 내려주는 것일 듯? 156 | 157 | ``` 158 | # 0630 kiwoom start 159 | 32 21 * * 0-4 /usr/bin/docker start kiwoom_run 160 | # 1830 kiwoom stop 161 | 32 9 * * 1-5 /usr/bin/docker stop kiwoom_run 162 | ``` 163 | 164 | 이런 식이다. 165 | 166 | 도커 자체가 서비스처럼 run될 수 있고 실행 방법에 따라 auto start도 가능하니 167 | 굳이 systemctl로 만드는 것은 비추. 168 | 해봤는데 systemctl은 cron에서 제어가 힘들다. 169 | 굳이 한다면 kiwoom.service 파일 참고. 170 | (cron systemctl dbus 로 검색하면 오류해결법도 나올 것임) 171 | 172 | ## Customization 173 | 174 | entry_point.sh를 적절히 수정해서 쓴다. 175 | vnc 비밀번호도 entry_point.sh에서 수정할 수 있음. 176 | Plain text로 스크립트에 적혀있는 것이 싫으면 해당 라인을 삭제하고 177 | GUI에서 xterm띄워서 vncpasswd명령어로 손으로 실행하면 될 것이다. 178 | 179 | ## 매수/매도/잔고 예제 180 | 181 | Docker로 서버를 가동했다고 치고, `server_url = http://localhost:12233` 가 base endpoint라고 해보자. 182 | 183 | 매수/매도: 184 | 185 | ``` 186 | import requests 187 | 188 | data = { 189 | "accno": accno, # 계좌번호 190 | "code": code, # 종목코드 191 | "qty": qty, # 수량 192 | "price": price, # 0 for market order 193 | "ordertype": orderty, # 1=신규매수, 2=신규매도 194 | "hogagb": "00", # "03": 시장가, "61": 장전시간외종가 195 | } 196 | resp = requests.post(server_url + "/order", json=data) 197 | result = resp.json() 198 | ``` 199 | 200 | 잔고: 201 | 202 | ``` 203 | import requests 204 | 205 | data = { 206 | "accno": accno # 계좌번호 207 | } 208 | resp = requests.post(server_url + "/balance", json=data) 209 | print(resp) 210 | result = resp.json() 211 | ``` 212 | -------------------------------------------------------------------------------- /docker/ans.iss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/ans.iss -------------------------------------------------------------------------------- /docker/autostart: -------------------------------------------------------------------------------- 1 | exec xterm -e "cd /kiwoom/drive_c ; wine KiwoomRestfulCpp.exe" 2 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -e OpenAPISetup.exe ] ; then 4 | echo "OpenAPISetup.exe 를 이 폴더에 복사하시오" 5 | exit 1 6 | fi 7 | 8 | if [ ! -e KiwoomRestfulCpp.exe ] ; then 9 | echo "KiwoomRestfulCpp.exe 를 이 폴더에 복사하시오" 10 | exit 1 11 | fi 12 | 13 | docker build -t kiwoom . 14 | -------------------------------------------------------------------------------- /docker/entry_point.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo Wine version 4 | wine --version 5 | printf "\n\n" 6 | 7 | echo Winetricks version 8 | winetricks --version 9 | 10 | # Copy files from the host (as these may be upgraded) 11 | cp /docker/xinitrc /root/.xinitrc 12 | mkdir -p /root/.config/openbox 13 | cp /docker/autostart /root/.config/openbox/autostart 14 | cp /docker/KiwoomRestfulCpp.exe /kiwoom/drive_c 15 | 16 | printf "123456\n123456\n\n" | vncpasswd 17 | rm -f /tmp/.X1-lock 18 | xinit openbox-session -- /usr/sbin/Xvnc :1 -PasswordFile /root/.vnc/passwd 19 | -------------------------------------------------------------------------------- /docker/img/vnc01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc01.png -------------------------------------------------------------------------------- /docker/img/vnc02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc02.png -------------------------------------------------------------------------------- /docker/img/vnc03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc03.png -------------------------------------------------------------------------------- /docker/img/vnc04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc04.png -------------------------------------------------------------------------------- /docker/img/vnc05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc05.png -------------------------------------------------------------------------------- /docker/img/vnc06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc06.png -------------------------------------------------------------------------------- /docker/img/vnc07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc07.png -------------------------------------------------------------------------------- /docker/img/vnc08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc08.png -------------------------------------------------------------------------------- /docker/img/vnc09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/docker/img/vnc09.png -------------------------------------------------------------------------------- /docker/kiwoom.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Kiwoom Restful API Server 3 | After=network-online.target 4 | Wants=network-online.target 5 | 6 | # https://dev.to/suntong/autostart-docker-container-with-systemd-5aod 7 | # The -a option in the Docker command for ExecStart makes sure it is running in attached mode, i.e., attaching STDOUT/STDERR and forwarding signals. 8 | # The -t option in the Docker command for ExecStop specifies seconds to wait for it to stop before killing the container. 9 | [Service] 10 | ExecStart=/usr/bin/docker start -a kiwoom_run 11 | ExecStop=/usr/bin/docker stop kiwoom_run 12 | Restart=always 13 | RestartSec=30 14 | #RemainAfterExit=yes 15 | 16 | #[Service] 17 | #ExecStart=/usr/bin/docker start -i kiwoom_run 18 | #Restart=always 19 | #RestartSec=10 20 | 21 | [Install] 22 | WantedBy=default.target 23 | -------------------------------------------------------------------------------- /docker/run_vnc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -e $HOME/.ssh/NPKI ] ; then 4 | echo "공동인증서를 ~/.ssh/NPKI 로 복사하시오" 5 | exit 1 6 | fi 7 | 8 | docker run \ 9 | -i \ 10 | -v $HOME/.ssh/NPKI:/kiwoom/drive_c/users/root/AppData/LocalLow/NPKI \ 11 | -v `pwd`:/docker \ 12 | --net=host \ 13 | --env WINEARCH=win32 \ 14 | --env WINEPREFIX=/kiwoom \ 15 | --env LANG=ko_KR.utf8 \ 16 | --env LC_ALL=ko_KR.utf8 \ 17 | --name kiwoom_run \ 18 | -t kiwoom \ 19 | /bin/bash /docker/entry_point.sh 20 | -------------------------------------------------------------------------------- /docker/run_vnc_saved.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 이것만 하면 vnc가 실행됨 4 | docker start -i kiwoom_run 5 | -------------------------------------------------------------------------------- /docker/xinitrc: -------------------------------------------------------------------------------- 1 | exec openbox-session 2 | -------------------------------------------------------------------------------- /framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef VC_EXTRALEAN 4 | #define VC_EXTRALEAN // 거의 사용되지 않는 내용은 Windows 헤더에서 제외합니다. 5 | #endif 6 | 7 | #include "targetver.h" 8 | 9 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 일부 CString 생성자는 명시적으로 선언됩니다. 10 | 11 | // MFC의 공통 부분과 무시 가능한 경고 메시지에 대한 숨기기를 해제합니다. 12 | #define _AFX_ALL_WARNINGS 13 | 14 | #include // MFC 핵심 및 표준 구성 요소입니다. 15 | #include // MFC 확장입니다. 16 | 17 | 18 | #include // MFC 자동화 클래스입니다. 19 | 20 | 21 | 22 | #ifndef _AFX_NO_OLE_SUPPORT 23 | #include // Internet Explorer 4 공용 컨트롤에 대한 MFC 지원입니다. 24 | #endif 25 | #ifndef _AFX_NO_AFXCMN_SUPPORT 26 | #include // Windows 공용 컨트롤에 대한 MFC 지원입니다. 27 | #endif // _AFX_NO_AFXCMN_SUPPORT 28 | 29 | #include // MFC의 리본 및 컨트롤 막대 지원 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | #ifdef _UNICODE 40 | #if defined _M_IX86 41 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 42 | #elif defined _M_X64 43 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 44 | #else 45 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 46 | #endif 47 | #endif 48 | 49 | 50 | -------------------------------------------------------------------------------- /pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: 미리 컴파일된 헤더에 해당하는 소스 파일 2 | 3 | #include "pch.h" 4 | 5 | // 미리 컴파일된 헤더를 사용하는 경우 컴파일이 성공하려면 이 소스 파일이 필요합니다. 6 | -------------------------------------------------------------------------------- /pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: 미리 컴파일된 헤더 파일입니다. 2 | // 아래 나열된 파일은 한 번만 컴파일되었으며, 향후 빌드에 대한 빌드 성능을 향상합니다. 3 | // 코드 컴파일 및 여러 코드 검색 기능을 포함하여 IntelliSense 성능에도 영향을 미칩니다. 4 | // 그러나 여기에 나열된 파일은 빌드 간 업데이트되는 경우 모두 다시 컴파일됩니다. 5 | // 여기에 자주 업데이트할 파일을 추가하지 마세요. 그러면 성능이 저하됩니다. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // 여기에 미리 컴파일하려는 헤더 추가 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /res/KiwoomRestfulCpp.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/res/KiwoomRestfulCpp.ico -------------------------------------------------------------------------------- /res/KiwoomRestfulCpp.rc2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcecore/KiwoomRestfulCpp/439110494f0622cfa08ab8361a13de7ab01e656f/res/KiwoomRestfulCpp.rc2 -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++에서 생성한 포함 파일입니다. 3 | // KiwoomRestfulCpp.rc에서 사용되고 있습니다. 4 | // 5 | #define IDD_KIWOOMRESTFULCPP_DIALOG 102 6 | #define IDR_MAINFRAME 128 7 | #define IDC_KHOPENAPICTRL1 1000 8 | 9 | // Next default values for new objects 10 | // 11 | #ifdef APSTUDIO_INVOKED 12 | #ifndef APSTUDIO_READONLY_SYMBOLS 13 | #define _APS_NEXT_RESOURCE_VALUE 131 14 | #define _APS_NEXT_COMMAND_VALUE 32771 15 | #define _APS_NEXT_CONTROL_VALUE 1001 16 | #define _APS_NEXT_SYMED_VALUE 101 17 | #endif 18 | #endif 19 | -------------------------------------------------------------------------------- /targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDKDDKVer.h를 포함하면 최고 수준의 가용성을 가진 Windows 플랫폼이 정의됩니다. 4 | 5 | // 이전 Windows 플랫폼에 대해 애플리케이션을 빌드하려는 경우에는 SDKDDKVer.h를 포함하기 전에 6 | // WinSDKVer.h를 포함하고 _WIN32_WINNT 매크로를 지원하려는 플랫폼으로 설정하십시오. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crow-examples", 3 | "version": "master", 4 | "dependencies": [ 5 | { 6 | "name": "boost-array", 7 | "version>=": "1.70.0" 8 | }, 9 | { 10 | "name": "boost-algorithm", 11 | "version>=": "1.70.0" 12 | }, 13 | { 14 | "name": "boost-asio", 15 | "version>=": "1.70.0" 16 | }, 17 | { 18 | "name": "boost-date-time", 19 | "version>=": "1.70.0" 20 | }, 21 | { 22 | "name": "boost-functional", 23 | "version>=": "1.70.0" 24 | }, 25 | { 26 | "name": "boost-lexical-cast", 27 | "version>=": "1.70.0" 28 | }, 29 | { 30 | "name": "boost-optional", 31 | "version>=": "1.70.0" 32 | }, 33 | { 34 | "name": "openssl-windows" 35 | }, 36 | { 37 | "name": "zlib" 38 | } 39 | ], 40 | "overrides": [ 41 | { 42 | "name": "boost-array", 43 | "version": "1.70.0" 44 | }, 45 | { 46 | "name": "boost-algorithm", 47 | "version": "1.70.0" 48 | }, 49 | { 50 | "name": "boost-asio", 51 | "version-semver": "1.70.0-2" 52 | }, 53 | { 54 | "name": "boost-date-time", 55 | "version": "1.70.0" 56 | }, 57 | { 58 | "name": "boost-functional", 59 | "version": "1.70.0" 60 | }, 61 | { 62 | "name": "boost-lexical-cast", 63 | "version": "1.70.0" 64 | }, 65 | { 66 | "name": "boost-optional", 67 | "version": "1.70.0" 68 | } 69 | ], 70 | "builtin-baseline": "44d94c2edbd44f0c01d66c2ad95eb6982a9a61bc" 71 | } 72 | --------------------------------------------------------------------------------