├── __init__.py ├── eBest ├── __init__.py ├── order.py ├── connection.py ├── stock_info.py └── account_info.py ├── kiwoom └── __init__.py ├── daishin ├── __init__.py ├── connection_D.py ├── order_D.py ├── account_info_D.py └── stock_info_D.py ├── config_example.cfg ├── requirements.txt ├── LICENSE ├── .gitignore ├── main.py ├── handler.py └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eBest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eBest/order.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kiwoom/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /daishin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eBest/connection.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eBest/stock_info.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config_example.cfg: -------------------------------------------------------------------------------- 1 | 2 | ## Broker Info 3 | ## 정보 입력 후 config.cfg로 저장 4 | [BROKER] 5 | BROKER_NAME = 6 | BROKER_ID = 7 | BROKER_PW = 8 | CERT_PW = 9 | 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2020.11.8 2 | chardet==3.0.4 3 | click==7.1.2 4 | comtypes==1.1.7 5 | constants==0.6.0 6 | Flask==1.1.2 7 | idna==2.10 8 | itsdangerous==1.1.0 9 | Jinja2==2.11.2 10 | MarkupSafe==1.1.1 11 | numpy==1.19.4 12 | pandas==1.1.4 13 | PyQt5==5.15.2 14 | PyQt5-sip==12.8.1 15 | python-dateutil==2.8.1 16 | pytz==2020.4 17 | pywin32==300 18 | pywinauto==0.6.8 19 | requests==2.25.0 20 | six==1.15.0 21 | urllib3==1.26.2 22 | Werkzeug==1.0.1 23 | wincertstore==0.2 24 | -------------------------------------------------------------------------------- /eBest/account_info.py: -------------------------------------------------------------------------------- 1 | ## 이베스트 - 계좌정보 2 | import win32com.client 3 | import pythoncom 4 | 5 | class XASessionEventHandler: 6 | login_state = 0 7 | 8 | def OnLogin(self, code, msg): 9 | if code == "0000": 10 | print("로그인 성공") 11 | XASessionEventHandler.login_state = 1 12 | else: 13 | print("로그인 실패") 14 | 15 | 16 | class Account(self): 17 | def __init__(self): 18 | self.instXASession = win32com.client.DispatchWithEvents("XA_Session.XASession", XASessionEventHandler) 19 | 20 | id = "아이디" 21 | passwd = "비밀번호" 22 | cert_passwd = "공인인증서" 23 | 24 | ## 로그인 25 | def login(self): 26 | self.instXASession.ConnectServer("hts.ebestsec.co.kr", 20001) 27 | 28 | self.instXASession.Login(id, passwd, cert_passwd, 0, 0) 29 | 30 | while XASessionEventHandler.login_state == 0: 31 | pythoncom.PumpWaitingMessages() 32 | 33 | ## 계좌정보 34 | def acoount_info(self, acc_index): 35 | 36 | acc = self.instXASession.GetAccountList(acc_index) 37 | 38 | ## 결과값 리턴 39 | res = { 40 | 'acc_num': acc, 41 | 'tot_amount':tot_amount, 42 | 'profit_amount':profit_amount, 43 | 'twoday_amount':twoday_amount 44 | } 45 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Hayman 4 | All rights reserved. 5 | 6 | --- 7 | 복제, 배포, 수정의 권한 허용 : 가능 8 | 배포시 라이선스 사본 첨부 : 가능 9 | 저작권 고지사항 또는 Attribution 고지사항 유지 : 가능 10 | 배포시 소스코드 제공의무(Reciprocity)와 범위 : 명시되어있지않음 11 | 조합저작물(Lager Work) 작성 및 타 라이선스 배포 허용 : 조건부 가능 12 | 수정 시 수정내용 고지 : 명시되어있지않음 13 | 명시적 특허 라이선스의 허용 : 명시되어있지않음 14 | 라이선시가 특허소송 제기 시 라이선스 종료 : 명시되어있지않음 15 | 이름, 상표, 상호에 대한 사용제한 : 가능 16 | 보증의 부인 : 가능 17 | 책임의 제한 : 가능 18 | --- 19 | 20 | Redistribution and use in source and binary forms, with or without 21 | modification, are permitted provided that the following conditions are met: 22 | 23 | 1. Redistributions of source code must retain the above copyright notice, this 24 | list of conditions and the following disclaimer. 25 | 26 | 2. Redistributions in binary form must reproduce the above copyright notice, 27 | this list of conditions and the following disclaimer in the documentation 28 | and/or other materials provided with the distribution. 29 | 30 | 3. Neither the name of the copyright holder nor the names of its 31 | contributors may be used to endorse or promote products derived from 32 | this software without specific prior written permission. 33 | 34 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 35 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 36 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 37 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 38 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 39 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 40 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 41 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 42 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 43 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | -------------------------------------------------------------------------------- /daishin/connection_D.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 대신증권 - 연결 3 | ''' 4 | from pywinauto import application 5 | import os 6 | import win32com.client 7 | import time 8 | 9 | class Connection(): 10 | def __init__(self): 11 | self.obj_CpUtil_CpCybos = win32com.client.Dispatch('CpUtil.CpCybos') 12 | 13 | ## 로그인 14 | def login(self, _id, _pwd, _pwdcert, trycnt=300): 15 | 16 | ## 연결 체크 후 연결되었지 않았을 경우 실행 17 | if not self._connected(): 18 | self.disconnect() 19 | self.kill_client() 20 | app = application.Application() 21 | app.start( 22 | 'C:\\CREON\\STARTER\\coStarter.exe /prj:cp /id:{id} /pwd:{pwd} /pwdcert:{pwdcert} /autostart'.format( 23 | id=_id, pwd=_pwd, pwdcert=_pwdcert 24 | ) 25 | ) 26 | 27 | cnt = 0 28 | 29 | ## 로그인 후 체크 (300초 이내) 30 | while not self._connected(): 31 | if cnt > trycnt: 32 | return False 33 | 34 | time.sleep(1) 35 | cnt += 1 36 | 37 | return True 38 | 39 | ## 연결 40 | def _connected(self): 41 | b_connected = self.obj_CpUtil_CpCybos.IsConnect 42 | 43 | if b_connected == 0: 44 | return False 45 | 46 | return True 47 | 48 | ## 연결해제 49 | def disconnect(self): 50 | if self.connected(): 51 | self.obj_CpUtil_CpCybos.PlusDisconnect() 52 | return True 53 | 54 | return False 55 | 56 | ## 강제종료 57 | def kill_client(self): 58 | os.system('taskkill /IM coStarter* /F /T') 59 | os.system('taskkill /IM CpStart* /F /T') 60 | os.system('taskkill /IM DibServer* /F /T') 61 | os.system('wmic process where "name like \'%coStarter%\'" call terminate') 62 | os.system('wmic process where "name like \'%CpStart%\'" call terminate') 63 | os.system('wmic process where "name like \'%DibServer%\'" call terminate') 64 | 65 | ## 요청제한 감시 66 | def avoid_reqlimitwarning(self): 67 | remainTime = self.obj_CpUtil_CpCybos.LimitRequestRemainTime 68 | remainCount = self.obj_CpUtil_CpCybos.GetLimitRemainCount(1) # 시세 제한 69 | 70 | if remainCount <= 3: 71 | time.sleep(remainTime / 1000) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | config.cfg 131 | -------------------------------------------------------------------------------- /daishin/order_D.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 대신증권 - 매수매도 3 | ''' 4 | import win32com.client 5 | 6 | class Order(): 7 | def __init__(self): 8 | self.obj_CpTrade_CpTdUtil = win32com.client.Dispatch('CpTrade.CpTdUtil') 9 | self.objStockOrder = win32com.client.Dispatch("CpTrade.CpTd0311") 10 | 11 | self.initCheck = self.obj_CpTrade_CpTdUtil.TradeInit(0) 12 | 13 | ## 매수주문 14 | ## 계좌번호, 종목코드, 수량, 주문단가 15 | def buy(self, acc, stock_code, amount, price): 16 | 17 | # 주문 초기화 18 | if (self.initCheck != 0): 19 | print("주문 초기화 실패") 20 | 21 | return False 22 | 23 | # 주식 매도 주문 24 | accFlag = self.obj_CpTrade_CpTdUtil.GoodsList(acc, 1) # 주식상품 구분 25 | 26 | self.objStockOrder.SetInputValue(0, "2") # 2: 매수 27 | self.objStockOrder.SetInputValue(1, acc) # 계좌번호 28 | self.objStockOrder.SetInputValue(2, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째 29 | self.objStockOrder.SetInputValue(3, stock_code) # 종목코드 - A003540 - 대신증권 종목 30 | self.objStockOrder.SetInputValue(4, amount) # 매도수량 31 | self.objStockOrder.SetInputValue(5, price) # 매도주문단가 32 | self.objStockOrder.SetInputValue(7, "0") # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK 33 | self.objStockOrder.SetInputValue(8, "01") # 주문호가 구분코드 - 01: 보통 34 | 35 | # 매수 주문 요청 36 | self.objStockOrder.BlockRequest() 37 | 38 | rqStatus = self.objStockOrder.GetDibStatus() 39 | 40 | if rqStatus == -1: 41 | rqStatus = '주문 실패' 42 | 43 | rqRet = self.objStockOrder.GetDibMsg1() 44 | 45 | return (rqStatus, rqRet) 46 | 47 | ## 매도주문 48 | ## 계좌번호, 종목코드, 수량, 주문단가 49 | def sell(self, acc, stock_code, amount, price): 50 | 51 | # 주문 초기화 52 | if (self.initCheck != 0): 53 | print("주문 초기화 실패") 54 | 55 | return False 56 | 57 | # 주식 매도 주문 58 | accFlag = self.obj_CpTrade_CpTdUtil.GoodsList(acc, 1) # 주식상품 구분 59 | 60 | self.objStockOrder.SetInputValue(0, "1") # 1: 매도 61 | self.objStockOrder.SetInputValue(1, acc) # 계좌번호 62 | self.objStockOrder.SetInputValue(2, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째 63 | self.objStockOrder.SetInputValue(3, stock_code) # 종목코드 - A003540 - 대신증권 종목 64 | self.objStockOrder.SetInputValue(4, amount) # 매도수량 65 | self.objStockOrder.SetInputValue(5, price) # 매도주문단가 66 | self.objStockOrder.SetInputValue(7, "0") # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK 67 | self.objStockOrder.SetInputValue(8, "01") # 주문호가 구분코드 - 01: 보통 68 | 69 | # 매도 주문 요청 70 | self.objStockOrder.BlockRequest() 71 | 72 | rqStatus = self.objStockOrder.GetDibStatus() 73 | 74 | if rqStatus == -1: 75 | rqStatus = '주문 실패' 76 | 77 | rqRet = self.objStockOrder.GetDibMsg1() 78 | 79 | return (rqStatus, rqRet) -------------------------------------------------------------------------------- /daishin/account_info_D.py: -------------------------------------------------------------------------------- 1 | # 대신증권 - 계좌정보 2 | import pandas as pd 3 | import win32com.client 4 | 5 | class Account(): 6 | 7 | ## 초기화 8 | def __init__(self): 9 | self.obj_CpTrade_CpTdUtil = win32com.client.Dispatch('CpTrade.CpTdUtil') 10 | self.obj_CpTrade_CpTd6033 = win32com.client.Dispatch("CpTrade.CpTd6033") 11 | 12 | self.initCheck = self.obj_CpTrade_CpTdUtil.TradeInit(0) 13 | 14 | ## 계좌정보 15 | def account_info(self): 16 | acc = self.obj_CpTrade_CpTdUtil.AccountNumber[0] #계좌번호 17 | accFlag = self.obj_CpTrade_CpTdUtil.GoodsList(acc, 1) # 주식상품 구분 18 | 19 | ### 필요 데이터 입력 20 | self.obj_CpTrade_CpTd6033.SetInputValue(0, acc) 21 | self.obj_CpTrade_CpTd6033.SetInputValue(1, accFlag[0]) 22 | self.obj_CpTrade_CpTd6033.SetInputValue(2, 20) 23 | self.obj_CpTrade_CpTd6033.SetInputValue(3, '2') # 0프로 기준 수익률 출력 24 | 25 | self.obj_CpTrade_CpTd6033.BlockRequest() 26 | 27 | ### 계좌이름, 총평가액, 평가손익, D+2예수금 28 | name = self.obj_CpTrade_CpTd6033.GetHeaderValue(0) 29 | tot_amount = self.obj_CpTrade_CpTd6033.GetHeaderValue(3) 30 | profit_amount = self.obj_CpTrade_CpTd6033.GetHeaderValue(4) 31 | twoday_amount = self.obj_CpTrade_CpTd6033.GetHeaderValue(9) 32 | 33 | ## 결과값 리턴 34 | res = { 35 | 'acc_num': acc, 36 | 'tot_amount':tot_amount, 37 | 'profit_amount':profit_amount, 38 | 'twoday_amount':twoday_amount 39 | } 40 | 41 | return res 42 | 43 | ## 주식잔고 조회(6033) 44 | def account_stock_info(self): 45 | 46 | ## 필요데이터 선언 47 | stock_account_idx = [12, 0, 3, 7, 17, 18, 11, 15] 48 | stock_account_col = [ 49 | '종목코드', '종목명', '(결제)잔고수량', '(체결)잔고수량', 50 | '체결장부단가', '손익단가', '수익률', '매도가능수량' 51 | ] 52 | stock_account_df = pd.DataFrame(columns = stock_account_col) 53 | 54 | 55 | ### 계좌정보 호출 56 | acc = self.obj_CpTrade_CpTdUtil.AccountNumber[0] #계좌번호 57 | accFlag = self.obj_CpTrade_CpTdUtil.GoodsList(acc, 1) # 주식상품 구분 58 | 59 | ### 필요 데이터 입력 60 | self.obj_CpTrade_CpTd6033.SetInputValue(0, acc) 61 | self.obj_CpTrade_CpTd6033.SetInputValue(1, accFlag[0]) 62 | self.obj_CpTrade_CpTd6033.SetInputValue(2, 50) 63 | self.obj_CpTrade_CpTd6033.SetInputValue(3, '2') # 0프로 기준 수익률 출력 64 | 65 | self.obj_CpTrade_CpTd6033.BlockRequest() 66 | 67 | data_len = self.obj_CpTrade_CpTd6033.GetHeaderValue(7) 68 | print(data_len) 69 | ## 연속 조회용 데이터프레임(데이터 이어붙이기) 70 | temp_stock_account_df = pd.DataFrame(columns = stock_account_col) 71 | 72 | ## 데이터 호출 73 | for i in range(len(stock_account_col)): 74 | t = stock_account_idx[i] 75 | temp_res = [self.obj_CpTrade_CpTd6033.GetDataValue(t, i) for i in range(data_len)] 76 | temp_stock_account_df[stock_account_col[i]] = temp_res 77 | 78 | stock_account_df = pd.concat( 79 | [stock_account_df, temp_stock_account_df]).reset_index(drop=True) 80 | 81 | return stock_account_df 82 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | from flask import Flask, request, jsonify 3 | 4 | from handler import Broker 5 | 6 | ## 초기 설정 7 | app = Flask(__name__) 8 | app.config['JSON_AS_ASCII'] = False 9 | 10 | config = configparser.ConfigParser() 11 | config.read('config.cfg', encoding='utf-8') 12 | 13 | broker_name = config.get('BROKER', 'BROKER_NAME') 14 | id = config.get('BROKER', 'BROKER_ID') 15 | pw = config.get('BROKER', 'BROKER_PW') 16 | cert_pw = config.get('BROKER', 'CERT_PW') 17 | 18 | b = Broker(broker_name, id, pw, cert_pw) 19 | 20 | ## 연결 21 | @app.route('/connection', methods=['GET', 'POST', 'DELETE']) 22 | def handle_connect(): 23 | ## 접속 상태 24 | if request.method == 'GET': 25 | # check connection status 26 | return jsonify(b.check_connection()) 27 | 28 | ## 로그인 29 | elif request.method == 'POST': 30 | # make connection 31 | return jsonify(b.login()) 32 | 33 | ## 계좌정보 34 | @app.route('/accountinfo', methods=['GET']) 35 | def get_acc_info(): 36 | res = b.account_info() 37 | 38 | return jsonify(res) 39 | 40 | ## 계좌정보 41 | @app.route('/stockaccountinfo', methods=['GET']) 42 | def get_acc_stock_info(): 43 | res = b.account_stockinfo() 44 | 45 | return res 46 | 47 | ## 차트정보 48 | @app.route('/chart', methods=['GET']) 49 | def chart_data(): 50 | 51 | ## 입력값 확인 52 | stockcode = request.args.get('code') 53 | n = request.args.get('n') 54 | date_from = request.args.get('date_from') 55 | date_to = request.args.get('date_to') 56 | 57 | ## 필요데이터 (갯수, 시작일)가 없을경우 58 | if not (n or date_from): 59 | res = { 60 | 'status' : 400, 61 | 'res' : 'Need to provide "n" or "date_from" argument.' 62 | } 63 | 64 | res = b.get_price(stockcode, n, date_from, date_to) 65 | 66 | return res 67 | 68 | ## 종목분석 69 | @app.route('/stockfeatures', methods=['GET']) 70 | def handle_stockfeatures(): 71 | stockcode = request.args.get('code') 72 | 73 | if not stockcode: 74 | return 'stock code is none!', 400 75 | 76 | res = b.get_stockfeatures(stockcode) 77 | 78 | return res 79 | 80 | ## 공매도현황 81 | @app.route('/short', methods=['GET']) 82 | def handle_short(): 83 | stockcode = request.args.get('code') 84 | 85 | if not stockcode: 86 | return 'stock code is none!', 400 87 | 88 | res = b.get_stockshortselling(stockcode) 89 | 90 | return res 91 | 92 | # 여러종목 감시 93 | @app.route('/marketeye', methods=['GET']) 94 | def marketeye(): 95 | code_list = request.args.getlist('code', type=str) 96 | 97 | if not code_list: 98 | return 'stock code is none!', 400 99 | 100 | code_ind_list = list(dict.fromkeys(code_list)) 101 | res = b.marketeye(code_ind_list) 102 | 103 | return res 104 | 105 | ## 10단호가 106 | @app.route('/hogainfo', methods=['GET']) 107 | def hogainfo(): 108 | stockcode = request.args.get('code') 109 | k = request.args.get('k') 110 | 111 | if not stockcode: 112 | return 'stock code is none!', 400 113 | 114 | res = b.hogainfo(stockcode, k) 115 | 116 | return res 117 | 118 | ## 매매입체분석 119 | @app.route('/tradematrix', methods=['GET']) 120 | def tradematrix_info(): 121 | stockcode = request.args.get('code') 122 | 123 | if not stockcode: 124 | return 'stock code is none!', 400 125 | 126 | res = b.tradematrix(stockcode) 127 | 128 | return res 129 | 130 | ## 매수 131 | @app.route('/buy', methods=['GET', 'DELETE']) 132 | def buy(): 133 | acc = request.args.get('acc') 134 | front_code = request.args.get('front') 135 | code = request.args.get('code') 136 | amount = request.args.get('amount') 137 | price = request.args.get('price') 138 | 139 | stock_code = front_code + str(code) 140 | 141 | ## 파라미터 확인 142 | if request.method == 'GET': 143 | res = b.buy(acc, stock_code, amount, price) 144 | 145 | ## 로그인 146 | elif request.method == 'DELETE': 147 | # make connection 148 | return False 149 | 150 | return res 151 | 152 | ## 매도 153 | @app.route('/sell', methods=['GET', 'DELETE']) 154 | def sell(): 155 | acc = request.args.get('acc') 156 | front_code = request.args.get('front') 157 | code = request.args.get('code') 158 | amount = request.args.get('amount') 159 | price = request.args.get('price') 160 | 161 | stock_code = front_code + str(code) 162 | 163 | ## 파라미터 확인 164 | if request.method == 'GET': 165 | res = b.sell(acc, stock_code, amount, price) 166 | 167 | ## 로그인 168 | elif request.method == 'DELETE': 169 | # make connection 170 | return False 171 | 172 | return res 173 | 174 | ## 실행 175 | if __name__ == "__main__": 176 | app.run(host='0.0.0.0', debug=True) -------------------------------------------------------------------------------- /handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from daishin import account_info_D, connection_D, order_D, stock_info_D 4 | # from eBest import account_info_eB, connection_eB, order_eB, stock_info_eb 5 | 6 | # 중계모듈 7 | # 거래증권사에 따른 매매모듈설정 8 | class Broker(): 9 | def __init__(self, broker, id, pw, certpw): 10 | 11 | self._broker = broker 12 | self._id = id 13 | self._pw = pw 14 | self._certpw = certpw 15 | 16 | ## 대신증권 17 | if self._broker == 'DAISHIN': 18 | self.con_broker = connection_D.Connection() 19 | self.acc_info = account_info_D.Account() 20 | self.stock_info = stock_info_D.StockInfo() 21 | self.order = order_D.Order() 22 | 23 | ## 이후 기타 증권사 추가예정 24 | 25 | else: 26 | print("XX") 27 | 28 | ## 로그인 29 | def login(self): 30 | res_login = self.con_broker.login( 31 | self._id, self._pw, self._certpw 32 | ) 33 | 34 | if res_login is True: 35 | status = 200 36 | message = "로그인 성공!" 37 | broker = "%s" %self._broker 38 | 39 | else: 40 | status = 400 41 | message = "로그인 실패" 42 | broker = "%s" %self._broker 43 | 44 | return (status, message, broker) 45 | 46 | ## 연결 체크 47 | def check_connection(self): 48 | res = self.con_broker._connected() 49 | 50 | if res is True: 51 | status = 200 52 | msg_body = "연결되었습니다." 53 | 54 | else: 55 | status = 400 56 | msg_body = "연결되지 않았습니다." 57 | 58 | res = { 59 | 'status' : status, 60 | 'broker' : self._broker, 61 | 'res' : msg_body 62 | } 63 | 64 | return res 65 | 66 | ## 계좌정보 요청 67 | def account_info(self): 68 | self.con_broker.avoid_reqlimitwarning() 69 | result = self.acc_info.account_info() 70 | 71 | return result 72 | 73 | ## 주식 잔고정보 요청 74 | def account_stockinfo(self): 75 | self.con_broker.avoid_reqlimitwarning() 76 | acc_stockdata = self.acc_info.account_stock_info() 77 | result = json.dumps(json.loads(acc_stockdata.to_json(orient='records')), indent=4, ensure_ascii=False) 78 | 79 | return result 80 | 81 | ## 종목정보 82 | def get_stockfeatures(self, code): 83 | self.con_broker.avoid_reqlimitwarning() 84 | featuredata = self.stock_info.get_stockfeatures(code) 85 | result = json.dumps(featuredata, indent=4, ensure_ascii=False) 86 | 87 | return result 88 | 89 | ## 가격정보 90 | def get_price(self, code, n, date_from, date_to): 91 | self.con_broker.avoid_reqlimitwarning() 92 | pricedata = self.stock_info.get_chart(code, unit='D', n=n, date_from=date_from, date_to=date_to) 93 | result = json.dumps(json.loads(pricedata.to_json(orient='records')), indent=4, ensure_ascii=False) 94 | 95 | return result 96 | 97 | ## 공매도정보 98 | def get_stockshortselling(self, code): 99 | self.con_broker.avoid_reqlimitwarning() 100 | shortdata = self.stock_info.get_shortstockselling(code) 101 | result = json.dumps(json.loads(shortdata.to_json(orient='records')), indent=4, ensure_ascii=False) 102 | 103 | return result 104 | 105 | ## 복수종목 조회 106 | def marketeye(self, code_list): 107 | self.con_broker.avoid_reqlimitwarning() 108 | marketeye_data = self.stock_info.get_MarketEye(code_list) 109 | result = json.dumps(json.loads(marketeye_data.to_json(orient='records')), indent=4, ensure_ascii=False) 110 | 111 | return result 112 | 113 | ## 10단호가 114 | def hogainfo(self, code, k): 115 | self.con_broker.avoid_reqlimitwarning() 116 | hoga_data = self.stock_info.get_hogainfo(code, k) 117 | result = json.dumps(json.loads(hoga_data.to_json(orient='records')), indent=4, ensure_ascii=False) 118 | 119 | return result 120 | 121 | def tradematrix(self, code): 122 | self.con_broker.avoid_reqlimitwarning() 123 | matrix_data = self.stock_info.get_tradematrix(code) 124 | result = json.dumps(json.loads(matrix_data.to_json(orient='records')), indent=4, ensure_ascii=False) 125 | 126 | return result 127 | 128 | ## 매수 129 | def buy(self, acc, code, amount, price): 130 | self.con_broker.avoid_reqlimitwarning() 131 | result = self.order.buy(acc, code, amount, price) 132 | 133 | if result is False: 134 | res = { 135 | 'server_status' : 400, 136 | 'message': '주문 초기화 실패!' 137 | } 138 | 139 | res = { 140 | 'Server_Status' : 200, 141 | 'cybos_status' : result[0], 142 | 'message' : result[1] 143 | } 144 | 145 | return res 146 | 147 | ## 매도 148 | def sell(self, acc, code, amount, price): 149 | self.con_broker.avoid_reqlimitwarning() 150 | result = self.order.sell(acc, code, amount, price) 151 | 152 | if result is False: 153 | res = { 154 | 'server_status' : 400, 155 | 'message': '주문 초기화 실패!' 156 | } 157 | 158 | res = { 159 | 'Server_Status' : 200, 160 | 'cybos_status' : result[0], 161 | 'message' : result[1] 162 | } 163 | 164 | return res -------------------------------------------------------------------------------- /daishin/stock_info_D.py: -------------------------------------------------------------------------------- 1 | # 대신증권 - 종목정보 2 | import win32com.client 3 | from datetime import datetime 4 | import pandas as pd 5 | import time 6 | 7 | class StockInfo(): 8 | 9 | ## 초기화 10 | def __init__(self): 11 | self.obj_CpSysDib_StockChart = win32com.client.Dispatch('CpSysDib.StockChart') 12 | self.obj_CpSysDib_MarketEye = win32com.client.Dispatch('CpSysDib.MarketEye') 13 | self.obj_CpSysDib_CpSvr7238 = win32com.client.Dispatch('CpSysDib.CpSvr7238') 14 | self.obj_CpSysDib_CpSvr7254 = win32com.client.Dispatch('CpSysDib.CpSvr7254') 15 | self.obj_CpSysDib_CpMarketEye = win32com.client.Dispatch('CpSysDib.MarketEye') 16 | self.obj_CbSysDib_CpStatus = win32com.client.Dispatch('CpUtil.CpCybos') 17 | self.obj_CpTrade_CpTdUtil = win32com.client.Dispatch('CpTrade.CpTdUtil') 18 | self.obj_CpUtil_CpCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr') 19 | self.obj_DsCbo1_StockMst = win32com.client.Dispatch("DsCbo1.StockMst") 20 | 21 | self.initCheck = self.obj_CpTrade_CpTdUtil.TradeInit(0) 22 | 23 | ## 종목정보 호출 24 | def get_stockfeatures(self, code): 25 | 26 | ## 1차 데이터 호출 27 | result = { 28 | '이름': self.obj_CpUtil_CpCodeMgr.CodeToName(code), 29 | '증거금률(%)': self.obj_CpUtil_CpCodeMgr.GetStockMarginRate(code), 30 | '시장구분코드 ': self.obj_CpUtil_CpCodeMgr.GetStockMarketKind(code), 31 | '부구분코드': self.obj_CpUtil_CpCodeMgr.GetStockSectionKind(code), 32 | '감리': self.obj_CpUtil_CpCodeMgr.GetStockControlKind(code), 33 | '관리': self.obj_CpUtil_CpCodeMgr.GetStockSupervisionKind(code), 34 | '현재상태': self.obj_CpUtil_CpCodeMgr.GetStockStatusKind(code), 35 | '결산기': self.obj_CpUtil_CpCodeMgr.GetStockFiscalMonth(code), 36 | 'K200여부': self.obj_CpUtil_CpCodeMgr.GetStockKospi200Kind(code), 37 | '업종코드': self.obj_CpUtil_CpCodeMgr.GetStockSectionKind(code), 38 | '상장일': self.obj_CpUtil_CpCodeMgr.GetStockListedDate(code), 39 | '신용가능여부': self.obj_CpUtil_CpCodeMgr.IsStockCreditEnable(code), 40 | } 41 | 42 | ## 2차 데이터 입력값 설정 43 | _fields = [67, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 86, 87, 88, 44 | 89, 90, 91, 92, 93, 94, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 45 | 108, 109, 110, 111 46 | ] 47 | 48 | _keys = ['PER', 'EPS', '자본금(백만)', '액면가', '배당률', '배당수익률', '부채비율', '유보율', 49 | '자기자본이익률(ROE)', '매출액증가율', '경상이익증가율', '순이익증가율', '투자심리', '매출액', 50 | '경상이익', '당기순이익', 'BPS', '영업이익증가율', '영업이익', '매출액영업이익률', '매출액경상이익률', 51 | '이자보상비율', '분기BPS', '분기매출액증가율', '분기영업이익증가율', '분기경상이익증가율', '분기순이익증가율', 52 | '분기매출액', '분기영업이익', '분기경상이익', '분기당기순이익', '분기매출액영업이익률', '분기매출액경상이익률', 53 | '분기ROE', '분기이자보상비율', '분기유보율', '분기부채비율', '최근분기년월' 54 | ] 55 | 56 | self.obj_CpSysDib_MarketEye.SetInputValue(0, _fields) 57 | self.obj_CpSysDib_MarketEye.SetInputValue(1, code) 58 | self.obj_CpSysDib_MarketEye.BlockRequest() 59 | 60 | field_length = self.obj_CpSysDib_MarketEye.GetHeaderValue(0) 61 | 62 | if field_length > 0: 63 | for i in range(field_length): 64 | value = self.obj_CpSysDib_MarketEye.GetDataValue(i, 0) 65 | if type(value) == float: 66 | value = round(value, 4) 67 | 68 | result[_keys[i]] = value 69 | 70 | return result 71 | 72 | ## 차트데이터 호출 73 | def get_chart(self, code, unit='D', n=None, date_from=None, date_to=None): 74 | 75 | _fields = [0, 2, 3, 4, 5, 8, 9] 76 | _keys = ['date', 'open', 'high', 'low', 'close', 'volume', 'vol_amount'] 77 | 78 | if date_to is None: 79 | date_to = datetime.today().strftime('%Y%m%d') 80 | 81 | ## 필요 데이터 입력 82 | self.obj_CpSysDib_StockChart.SetInputValue(0, code) # 주식코드: A, 업종코드: U 83 | 84 | if n is not None: 85 | self.obj_CpSysDib_StockChart.SetInputValue(1, ord('2')) # 0: ?, 1: 기간, 2: 개수 86 | self.obj_CpSysDib_StockChart.SetInputValue(4, n) # 요청 개수 87 | 88 | if date_from is not None or date_to is not None: 89 | if date_from is not None and date_to is not None: 90 | self.obj_CpSysDib_StockChart.SetInputValue(1, ord('1')) # 0: ?, 1: 기간, 2: 개수 91 | if date_from is not None: 92 | self.obj_CpSysDib_StockChart.SetInputValue(3, date_from) # 시작일 93 | if date_to is not None: 94 | self.obj_CpSysDib_StockChart.SetInputValue(2, date_to) # 종료일 95 | 96 | self.obj_CpSysDib_StockChart.SetInputValue(5, _fields) # 필드 97 | self.obj_CpSysDib_StockChart.SetInputValue(6, ord(unit)) 98 | self.obj_CpSysDib_StockChart.SetInputValue(9, ord('1')) # 0: 무수정주가, 1: 수정주가 99 | 100 | ## 호출 101 | self.obj_CpSysDib_StockChart.BlockRequest() 102 | 103 | length = self.obj_CpSysDib_StockChart.GetHeaderValue(3) 104 | list_item = [] 105 | 106 | for i in range(length): 107 | temp_idx = [] 108 | for j in range(len(_keys)): 109 | temp_idx.append(self.obj_CpSysDib_StockChart.GetDataValue(j, i)) 110 | list_item.append(temp_idx) 111 | 112 | result = pd.DataFrame(data=list_item, columns=_keys).reset_index() 113 | 114 | return result 115 | 116 | 117 | ## 종목별 공매도 추이 118 | def get_shortstockselling(self, code): 119 | 120 | ## 필요 데이터 설정 121 | _fields = [0, 1, 2, 4, 5, 6, 7] 122 | _keys = [ 123 | '거래일', '종가', '전일대비', '거래량', '공매도량', '공매도비중', '공매도거래대금' 124 | ] 125 | 126 | ## 데이터 입력 127 | self.obj_CpSysDib_CpSvr7238.SetInputValue(0, code) 128 | 129 | ## 호출 130 | self.obj_CpSysDib_CpSvr7238.BlockRequest() 131 | 132 | ## 데이터 수신 133 | data_len = self.obj_CpSysDib_CpSvr7238.GetHeaderValue(0) 134 | list_item = [] 135 | 136 | for i in range(data_len): 137 | temp_idx = [] 138 | for j in _fields: 139 | temp_idx.append(self.obj_CpSysDib_CpSvr7238.GetDataValue(j, i)) 140 | list_item.append(temp_idx) 141 | 142 | result = pd.DataFrame(data=list_item, columns=_keys).reset_index() 143 | 144 | return result 145 | 146 | # 여러종목 데이터 호출 147 | ## input : code(list) 148 | def get_MarketEye(self, code_list): 149 | 150 | ## 필요 데이터 설정 151 | ## 코드, 종목명, 시간, 152 | ## 현재가, 시가, 고가, 저가, 153 | ## 매도호가, 매수호가, 거래량, 거래대금, 154 | ## 총매도호가잔량, 총매수호가잔량, 최우선매도호가잔량, 최우선매수호가잔량 155 | _fields = (0, 17, 1, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16) 156 | _keys = ('종목코드', '시간', '현재가', '시가', 157 | '고가', '저가', '매도호가', '매수호가', '거래량', 158 | '거래대금', '총매도호가잔량', '총매수호가잔량', 159 | '최우선매도호가잔량', '최우선매수호가잔량', '종목명' 160 | ) 161 | _sorted_keys = ['종목코드', '종목명', '시간', '현재가', 162 | '시가', '고가', '저가', '매도호가', '매수호가', 163 | '거래량', '거래대금', '총매도호가잔량', '총매수호가잔량', 164 | '최우선매도호가잔량', '최우선매수호가잔량' 165 | ] 166 | 167 | ## 입력 변수 설정 168 | self.obj_CpSysDib_CpMarketEye.SetInputValue(0, _fields) # 요청 필드 169 | self.obj_CpSysDib_CpMarketEye.SetInputValue(1, code_list) # 종목코드 or 종목코드 리스트 170 | 171 | ## 요청 172 | self.obj_CpSysDib_CpMarketEye.BlockRequest() 173 | 174 | ## 호출 후 데이터 길이 확인 175 | list_idx = self.obj_CpSysDib_CpMarketEye.GetHeaderValue(2) 176 | 177 | ## 결과값 저장 178 | res_value = [] 179 | 180 | for i in range(list_idx): 181 | temp = [] 182 | 183 | for j in range(len(_fields)): 184 | temp.append(self.obj_CpSysDib_CpMarketEye.GetDataValue(j, i)) 185 | 186 | res_value.append(temp) 187 | 188 | res = pd.DataFrame(data=res_value, columns=_keys).reset_index() 189 | result = res[_sorted_keys] 190 | 191 | return result 192 | 193 | ## 10단호가 호출 194 | ## input : code(str) 195 | def get_hogainfo(self, code, k): 196 | 197 | ## 필요 데이터 설정 198 | buy_dataframe = pd.DataFrame(columns = ['매수매도여부', '호가', '잔량']) 199 | sell_dataframe = pd.DataFrame(columns = ['매수매도여부', '호가', '잔량']) 200 | k = int(k) 201 | 202 | ## 데이터 입력(종목코드) 203 | self.obj_DsCbo1_StockMst.SetInputValue(0, code) 204 | 205 | ## 데이터 호출 206 | self.obj_DsCbo1_StockMst.BlockRequest() 207 | 208 | buy_dataframe['매수매도여부'] = ['매수'] * k 209 | buy_dataframe['호가'] = [self.obj_DsCbo1_StockMst.GetDataValue(1, x) for x in range(k)] # 매수호가 210 | buy_dataframe['잔량'] = [self.obj_DsCbo1_StockMst.GetDataValue(3, x) for x in range(k)] # 매수호가 잔량 211 | 212 | sell_dataframe['매수매도여부'] = ['매도'] * k 213 | sell_dataframe['호가'] = [self.obj_DsCbo1_StockMst.GetDataValue(0, x) for x in range(k)] 214 | sell_dataframe['잔량'] = [self.obj_DsCbo1_StockMst.GetDataValue(2, x) for x in range(k)] 215 | 216 | result = pd.concat([buy_dataframe, sell_dataframe]).reset_index(drop=True) 217 | 218 | return result 219 | 220 | def get_tradematrix(self, code): 221 | 222 | ## 필요데이터 선언 223 | matrix_idx = [0, 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 224 | matrix_col = [ 225 | '날짜', '종가' ,'개인', '외국인', '기관계', '금융투자', '보험', '투신', 226 | '은행', '기타금융', '연기금등', '기타법인', '기타외인', 227 | '사모펀드', '국가지자체' 228 | ] 229 | matrix_df = pd.DataFrame(columns = matrix_col) 230 | 231 | ## 데이터 입력(종목코드) 232 | self.obj_CpSysDib_CpSvr7254.SetInputValue(0, code) 233 | self.obj_CpSysDib_CpSvr7254.SetInputValue(1, 4) 234 | self.obj_CpSysDib_CpSvr7254.SetInputValue(4, '1') 235 | self.obj_CpSysDib_CpSvr7254.SetInputValue(5, 0) 236 | 237 | ## 데이터 호출(최초 1회) 238 | self.obj_CpSysDib_CpSvr7254.BlockRequest() 239 | 240 | ## 연속조회 241 | while self.obj_CpSysDib_CpSvr7254.Continue: 242 | self.obj_CpSysDib_CpSvr7254.BlockRequest() 243 | data_len = self.obj_CpSysDib_CpSvr7254.GetHeaderValue(1) 244 | 245 | ## 연속 조회용 데이터프레임(데이터 이어붙이기) 246 | temp_matrix_df = pd.DataFrame(columns = matrix_col) 247 | 248 | ## 데이터 호출 249 | for i in range(len(matrix_idx)): 250 | t = matrix_idx[i] 251 | temp_res = [self.obj_CpSysDib_CpSvr7254.GetDataValue(t, i) for i in range(data_len)] 252 | temp_matrix_df[matrix_col[i]] = temp_res 253 | 254 | matrix_df = pd.concat([matrix_df, temp_matrix_df]).reset_index(drop=True) 255 | 256 | res = matrix_df[::-1].reset_index(drop=True) 257 | 258 | return res -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Flask-KoreaStockBroker** 2 | 3 | 증권사 API를 Flask 기반으로 제작하여 4 | 5 | 범용 환경에서 트레이딩 할 수 있도록 한다. 6 | 7 | (현재는 대신증권만 지원합니다.) 8 | 9 | [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Flyy9257%2F&count_bg=%23ED6767&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) 10 | 11 | * * * 12 | 13 | ## **사용방법** 14 | 15 | 1. 32비트 가상환경을 생성합니다. 16 | 17 | 2. 의존성을 설치합니다. 18 | > pip install -r requirements.txt 19 | 20 | 3. CreonPlus를 실행합니다. 21 | 22 | 4. config.cfg 파일에 정보를 입력합니다. 23 | 24 | [BROKER] 25 | 26 | BROKER_NAME = DAISHIN 27 | 28 | BROKER_ID = (대신증권 ID) 29 | 30 | BROKER_PW = (대신증권 비밀번호) 31 | 32 | CERT_PW = (공인인증 비밀번호) 33 | 34 | 6. main.py 파일을 실행합니다. 35 | > python main.py 36 | 37 | 38 | * * * 39 | 40 | 41 | ## **API 명세** 42 | 43 | 1. ### 로그인 조회 44 | 45 | - endpoint : /connection 46 | 47 | - method : GET 48 | 49 | - argument 50 | - None. 51 | 52 | - result (Example) 53 | > http://127.0.0.1:5000/connection 54 | 55 | "broker": "DAISHIN", 56 | "res": "연결되었습니다.", 57 | "status": 200 58 | 59 | 2. ### 계좌금액 조회 60 | 61 | - endpoint : /accountinfo 62 | 63 | - method : GET 64 | 65 | - argument 66 | - None. 67 | 68 | - result (Example) 69 | > http://127.0.0.1:5000/accountinfo 70 | 71 | "name": (계좌 사용자 이름) , 72 | "profit_amount": 0, 73 | "tot_amount": 0, 74 | "twoday_amount": (D+2 예수금) 75 | 76 | 3. ### 주식잔고 조회 77 | 78 | - endpoint : /stockaccountinfo 79 | 80 | - method : GET 81 | 82 | - argument 83 | - None. 84 | 85 | - result (Example) 86 | > http://127.0.0.1:5000/stockaccountinfo 87 | 88 | { 89 | "종목코드": "A233740", 90 | "종목명": "KODEX 코스닥150 레버리지", 91 | "(체결)잔고수량": 1, 92 | "매수평단가": 13875, 93 | "손익단가": 13878, 94 | "수익률": 12.8760654346, 95 | "매도가능수량": 1 96 | } 97 | 98 | 4. ### 차트 데이터 조회 99 | 100 | - endpoint : /chart 101 | 102 | - method : GET 103 | 104 | - argument 105 | - code : 종목코드(Axxxxxx, Qxxxxxx) 106 | - n : 조회갯수 107 | - date_from : 조회시작날짜 108 | - date_to : 조회종료날짜(default : 당일) 109 | 110 | - result (Example) 111 | > http://127.0.0.1:5000/chart?code=A233740&n=20 112 | 113 | > http://127.0.0.1:5000/chart?code=A233740&date_from=20200501 114 | 115 | > http://127.0.0.1:5000/chart?code=A233740&date_from=20200501&date_to=20200701 116 | 117 | "index": 0, 118 | "date": 20201127, 119 | "open": 12900, 120 | "high": 13490, 121 | "low": 12900, 122 | "close": 13395, 123 | "volume": 32898683, 124 | "vol_amount": 437723000000 125 | 126 | 127 | 5. ### 종목정보 조회 128 | 129 | - endpoint : /stockfeatures 130 | 131 | - method : GET 132 | 133 | - argument 134 | - code : 종목코드(Axxxxxx, Qxxxxxx) 135 | 136 | - result (Example) 137 | > http://127.0.0.1:5000/stockfeatures?code=A207940 138 | 139 | "이름": "삼성바이오로직스", 140 | "증거금률(%)": 40, 141 | "시장구분코드 ": 1, 142 | "부구분코드": 1, 143 | "감리": 0, 144 | "관리": 0, 145 | "현재상태": 0, 146 | "결산기": 12, 147 | "K200여부": 10, 148 | "업종코드": 1, 149 | "상장일": 20161110, 150 | "신용가능여부": 1, 151 | "PER": 148.76, 152 | "EPS": 5371, 153 | "자본금(백만)": 165412, 154 | "액면가": 2500, 155 | "배당률": 0.0, 156 | "배당수익률": 0.0, 157 | "부채비율": 35.76, 158 | "유보율": 2532.48, 159 | "자기자본이익률(ROE)": 0.0, 160 | "매출액증가율": 30.94, 161 | "경상이익증가율": -48.7, 162 | "순이익증가율": -9.46, 163 | "투자심리": 0.0, 164 | "매출액": 701591, 165 | "경상이익": 155435000000, 166 | "당기순이익": 202904000000, 167 | "BPS": 67994, 168 | "영업이익증가율": 64.77, 169 | "영업이익": 91742000000, 170 | "매출액영업이익률": 13.08, 171 | "매출액경상이익률": 22.15, 172 | "이자보상비율": 3.58, 173 | "분기BPS": 67994, 174 | "분기매출액증가율": 103.33, 175 | "분기영업이익증가율": 0.0, 176 | "분기경상이익증가율": 0.0, 177 | "분기순이익증가율": 0.0, 178 | "분기매출액": 789470, 179 | "분기영업이익": 200210000000, 180 | "분기경상이익": 189265000000, 181 | "분기당기순이익": 144753000000, 182 | "분개매출액영업이익률": 25.36, 183 | "분기매출액경상이익률": 23.97, 184 | "분기ROE": 4.36, 185 | "분기이자보상비율": 16.07, 186 | "분기유보율": 2619.77, 187 | "분기부채비율": 36.42, 188 | "최근분기년월": 202009 189 | 190 | 6. ### 공매도정보 조회 191 | 192 | - endpoint : /short 193 | 194 | - method : GET 195 | 196 | - argument 197 | - code : 종목코드(Axxxxxx, Qxxxxxx) 198 | 199 | - result (Example) 200 | > http://127.0.0.1:5000/short?code=A233740 201 | 202 | "index": 0, 203 | "거래일": 20201127, 204 | "종가": 799000, 205 | "전일대비": 0, 206 | "거래량": 88639, 207 | "공매도량": 25, 208 | "공매도비중": 0.0282, 209 | "공매도거래대금": 2007 210 | 211 | 7. ### 다수종목 조회 (CYBOS MarketEye) 212 | 213 | - endpoint : /marketeye 214 | 215 | - method : GET 216 | 217 | - argument 218 | - code : 종목코드 219 | - &code=(종목코드)&code=(종목코드) 형식으로 입력 220 | 221 | - result(Example) 222 | > http://127.0.0.1:5000/marketeye?code=A233740&code=A251340 223 | 224 | [ 225 | "종목코드": "A233740", 226 | "종목명": "KODEX 코스닥150", 227 | "시간": 1559, 228 | "현재가": 13315, 229 | "시가": 13510, 230 | "고가": 13695, 231 | "저가": 13315, 232 | "매도호가": 13315, 233 | "매수호가": 13310, 234 | "거래량": 31637099, 235 | "거래대금": 427861610000, 236 | "총매도호가잔량": 90912, 237 | "총매수호가잔량": 180863, 238 | "최우선매도호가잔량": 8689, 239 | "최우선매수호가잔량": 15692 240 | ], 241 | [ 242 | "종목코드": "A251340", 243 | "종목명": "KODEX 코스닥150", 244 | "시간": 1559, 245 | "현재가": 5025, 246 | "시가": 4995, 247 | "고가": 5025, 248 | "저가": 4960, 249 | "매도호가": 5030, 250 | "매수호가": 5025, 251 | "거래량": 58000829, 252 | "거래대금": 289614330000, 253 | "총매도호가잔량": 891209, 254 | "총매수호가잔량": 612776, 255 | "최우선매도호가잔량": 138613, 256 | "최우선매수호가잔량": 129132 257 | ] 258 | 259 | 8. ### 매수 260 | 261 | - endpoint : /buy 262 | 263 | - method : GET 264 | 265 | - argument 266 | - acc : 계좌번호 267 | - front : 종목 앞 구분코드(A, Q) 268 | - code : 종목코드 숫자 269 | - amount : 수량 270 | - price : 주문가격 271 | 272 | - result (Example, 모의투자로 테스트) 273 | > http://127.0.0.1:5000/buy?acc=(계좌번호)&front=A&code=233740&amount=1&price=13900 274 | 275 | { 276 | "Server_Status": 200, 277 | "cybos_status": 0, 278 | "message": "0040 모의투자 매수 주문이 완료되었습니다.(ordss.cststkord)" 279 | } 280 | 281 | 282 | 9. ### 매도 283 | 284 | - endpoint : /sell 285 | 286 | - method : GET 287 | 288 | - argument 289 | - acc : 계좌번호 290 | - front : 종목 앞 구분코드(A, Q) 291 | - code : 종목코드 숫자 292 | - amount : 수량 293 | - price : 주문가격 294 | 295 | - result (Example, 모의투자로 테스트) 296 | > http://127.0.0.1:5000/sell?acc=(계좌번호)&front=A&code=233740&amount=1&price=13900 297 | 298 | { 299 | "Server_Status": 200, 300 | "cybos_status": 0, 301 | "message": "0039 모의투자 매도 주문이 완료되었습니다.(ordss.cststkord)" 302 | } 303 | 304 | 305 | 10. ### 매매입체분석 (대신증권 매매입체분석) 306 | - [대신증권 투자주체별 현황(7254) 참고링크](https://m.blog.naver.com/songtong/220941944898) 307 | - endpoint : /tradematrix 308 | 309 | - method : GET 310 | 311 | - argument 312 | - code : 종목코드 313 | 314 | - result (Example, 모의투자로 테스트) 315 | > http://127.0.0.1:5000/tradematrix?code=A005930 316 | 317 | { 318 | "날짜": 20200615, 319 | "종가": 49900, 320 | "개인": 5371193, 321 | "외국인": -1557410, 322 | "기관계": -3866544, 323 | "금융투자": -2058778, 324 | "보험": -100283, 325 | "투신": -298496, 326 | "은행": -8769, 327 | "기타금융": 44549, 328 | "연기금등": -1101531, 329 | "기타법인": 35781, 330 | "기타외인": 16980, 331 | "사모펀드": -343236, 332 | "국가지자체": 0 333 | }, 334 | { 335 | "날짜": 20200616, 336 | "종가": 52100, 337 | "개인": 4326244, 338 | "외국인": -3684032, 339 | "기관계": -756223, 340 | "금융투자": 459079, 341 | "보험": -157813, 342 | "투신": -298776, 343 | "은행": -10871, 344 | "기타금융": 64111, 345 | "연기금등": -407092, 346 | "기타법인": 101648, 347 | "기타외인": 12363, 348 | "사모펀드": -404861, 349 | "국가지자체": 0 350 | }, 351 | 352 | (후략, 6개월치 데이터가 불러와집니다.) 353 | 354 | <<<<<<< HEAD 355 | 10. ### 호가잔량 호출 356 | ======= 357 | 11. ### 호가잔량 호출 358 | >>>>>>> 63073909714fba5bae8155d988104d4cd1b73c7a 359 | - endpoint : /hogainfo 360 | 361 | - method : GET 362 | 363 | - argument 364 | - code : 종목코드 365 | - k : 호가 단수 366 | 367 | - result (Example) 368 | > http://127.0.0.1:5000/hogainfo?code=A233740&k=5 369 | 370 | > KODEX 코스닥150 레버리지의 5단 매수, 매도호가 및 잔량현황 호출 371 | 372 | [ 373 | { 374 | "매수매도여부": "매수", 375 | "호가": 15555, 376 | "잔량": 2660 377 | }, 378 | { 379 | "매수매도여부": "매수", 380 | "호가": 15550, 381 | "잔량": 1205 382 | }, 383 | { 384 | "매수매도여부": "매수", 385 | "호가": 15545, 386 | "잔량": 589 387 | }, 388 | { 389 | "매수매도여부": "매수", 390 | "호가": 15540, 391 | "잔량": 1057 392 | }, 393 | { 394 | "매수매도여부": "매수", 395 | "호가": 15535, 396 | "잔량": 1956 397 | }, 398 | { 399 | "매수매도여부": "매도", 400 | "호가": 15560, 401 | "잔량": 72926 402 | }, 403 | { 404 | "매수매도여부": "매도", 405 | "호가": 15565, 406 | "잔량": 1997 407 | }, 408 | { 409 | "매수매도여부": "매도", 410 | "호가": 15570, 411 | "잔량": 6828 412 | }, 413 | { 414 | "매수매도여부": "매도", 415 | "호가": 15575, 416 | "잔량": 3759 417 | }, 418 | { 419 | "매수매도여부": "매도", 420 | "호가": 15580, 421 | "잔량": 14508 422 | } 423 | ] 424 | 425 | 426 | * * * 427 | 428 | ## **주요 사항** 429 | 1. 이 프로그램을 사용하여 주식 종목을 매수, 매도함으로서 발생하는 수익과 손실에 대한 책임은 운용자에게 있습니다. 430 | 2. BSD 3-Clause License를 준수합니다. 431 | 3. Endpoint /hogainfo?code=(종목코드) 를 이용하여 10단 호가현황을 불러올 수 있으나, 432 | 433 | 실매매에서 테스트되지 않아 사용을 추전하지 않습니다. 434 | * * * 435 | 436 | ## **참고 글** 437 | [퀀티랩 - 대신증권 크레온(Creon) HTS 브리지 서버 만들기 (Flask 편)](http://blog.quantylab.com/creon_hts_bridge.html) 438 | 439 | * * * 440 | --------------------------------------------------------------------------------