Skip to content

coreanq/kw_condition

Repository files navigation

사용상 부주의로 인한 책임은 사용자에게 있습니다

kw_condition

개발 환경

개발 문서

참고 소스

개발 환경 설정

# poetry 설치 (using pipx)
> python -m pip install pipx
> python -m pipx ensurepath
> pipx install poetry

# poetry virtualenv 환경, 프로젝트 내부 경로로 설정 
> poetry config virtualenvs.in-project true
> poetry config virtualenvs.path "./.venv"

# poetry 32bit python 사용토록 설정
> poetry env use /path/to/32bit_python/python.exe

# 종속 패키지 설치 
> poetry install

사용 방법

0. python 실행 경로 확인

- python 가상 환경을 사용 중이라면 가상 환경의 path 가 맞는지 확인한다. 
%gui qt5
%matplotlib inline

import matplotlib.pyplot as plt
import pandas as pd

import sys
print(sys.executable)
d:\1git\kw_condition\.venv\Scripts\python.exe

1. 객체 생성

from kw_condition import KiwoomOpenApiPlus
from kw_condition.utils import common_util

from PySide2.QtWidgets import QApplication

myApp = None
if isinstance(QApplication.instance(), type(None)):
    print('make instance')
    myApp = QApplication([])
else:
    print("use already")
    myApp = QApplication.instance()

kw_obj = KiwoomOpenApiPlus()
make instance

2. 서버접속

  • 자동로그인이 설정되어 있는 경우 로그인이 자동으로 처리 되며, 그렇지 않은 경우 팝업 창에서 수동으로 아이디, 비밀번호 정보를 입력해야함
  • TODO
    • 자동 로그인 설정되지 않은 상태서 로그인 정보를 외부에서 입력한 경우 매크로로 자동처리 할수 있도록 해야함 pywinauto
kw_obj.tryConnect()
common_util.process_qt_events(kw_obj.isConnected, 60)
* 14:23:13.209393 tryConnect 
* 14:23:14.109850 order_init_entered 
* 14:23:14.130275 disconnected_entered 
* 14:23:22.932260 _OnEventConnect  0
* 14:23:22.946283 connected_entered 
account count: 1, keyboard_boan: 1, firewall: 2
* 14:23:22.988526 order_standby_entered 

3. 서버 접속 상태 확인

kw_obj.isConnected()
True

4. 코드 번호를 통해 종목 이름 확인

name = "삼성전자"
code = kw_obj.code_by_names[name]
code 
'005930'

5. 계좌 정보확인

account_list = kw_obj.get_account_list()

account_num = kw_obj.get_first_account()

6. TR (주식기본정보요청) - Single Data

아래 처럼 직접 TR 요청에 필요한 입력값을 설정해 요청하고, 이후 들어오는 이벤트 또한 직접 처리해주는 방식으로 사용할 수 있다.

rqname = '주식기본정보요청'
trcode = 'opt10001'
screen_no = '0001'  # 화면번호, 0000 과 9000 이상을 제외한 4자리 숫자 임의로 지정, screen_no 생략한 경우 임의로 화면 번호 지정 

inputs = {'종목코드': '005930'}

kw_obj.add_transaction(rqname, trcode, inputs, screen_no)

common_util.process_qt_events(kw_obj.has_transaction_result(rqname), 5)

# TR result 를 get 해야 다시 동일 rqname 으로 재요청 가능함 
kw_obj.get_transaction_result(rqname) 
* 14:23:23.251290 request_transaction  {'rqname': '주식기본정보요청', 'trcode': 'opt10001', 'screen_no': '9199', 'prev_next': '0001', 'inputs': {'종목코드': '005930'}}
* 14:23:23.315368 _OnReceiveTrData  sScrNo: 9199, rQName: 주식기본정보요청, trCode: opt10001, recordName: , prevNext 0





['005930', '+76400', '-41200', '58800']

7. TR(주식일봉차트조회요청) - Multi Data

import datetime

rqname = '주식일봉차트조회요청'
trcode = 'opt10081'

current_time_str = datetime.datetime.now().strftime('%Y%m%d')

inputs = {'종목코드': '005930', '기준일자' : current_time_str, "수정주가구분": '1'}

kw_obj.add_transaction(rqname, trcode, inputs)

common_util.process_qt_events(kw_obj.has_transaction_result(rqname), 5)

# result 를 get 해야 다시 동일 rqname 으로 재요청 가능함 

daily_list = kw_obj.get_transaction_result(rqname)
print( len(daily_list) )
daily_list[-5: ] 
* 14:23:23.546144 request_transaction  {'rqname': '주식일봉차트조회요청', 'trcode': 'opt10081', 'screen_no': '9198', 'prev_next': 0, 'inputs': {'종목코드': '005930', '기준일자': '20220831', '수정주가구분': '1'}}
* 14:23:23.607395 _OnReceiveTrData  sScrNo: 9198, rQName: 주식일봉차트조회요청, trCode: opt10081, recordName: , prevNext 2
600





[['', '20200403', '47400', '47600', '46550', '47000', '22784682'],
 ['', '20200402', '46200', '46850', '45350', '46800', '21621076'],
 ['', '20200401', '47450', '47900', '45800', '45800', '27259532'],
 ['', '20200331', '48000', '48500', '47150', '47750', '30654261'],
 ['', '20200330', '47050', '48350', '46550', '47850', '26797395']]

7. TR(주식일봉차트조회요청) - Multi Data - 연속 조회

import datetime

rqname = '주식일봉차트조회요청'
trcode = 'opt10081'

current_time_str = datetime.datetime.now().strftime('%Y%m%d')

inputs = {'종목코드': '005930', '기준일자' : current_time_str, "수정주가구분": '1'}

# 연속 조회시 prev_next 값을 2로 입력한다.  
kw_obj.add_transaction(rqname, trcode, inputs, prev_next=2 )

common_util.process_qt_events(kw_obj.has_transaction_result(rqname), 5)

# result 를 get 해야 다시 동일 rqname 으로 재요청 가능함 
daily_list.extend( kw_obj.get_transaction_result(rqname) ) 
print( len(daily_list) )
daily_list[ -5:]


* 14:23:23.849497 request_transaction  {'rqname': '주식일봉차트조회요청', 'trcode': 'opt10081', 'screen_no': '9197', 'prev_next': 2, 'inputs': {'종목코드': '005930', '기준일자': '20220831', '수정주가구분': '1'}}
* 14:23:23.923274 _OnReceiveTrData  sScrNo: 9197, rQName: 주식일봉차트조회요청, trCode: opt10081, recordName: , prevNext 2
1200





[['', '20171025', '54040', '54420', '53700', '53900', '5882850'],
 ['', '20171024', '54700', '54780', '54040', '54040', '5806050'],
 ['', '20171023', '54600', '54640', '54000', '54300', '8311050'],
 ['', '20171020', '52800', '54100', '52800', '53840', '8027050'],
 ['', '20171019', '54700', '54700', '52980', '52980', '12108700']]

8. 일봉 차트 출력 샘플

import mplfinance as mpf

daily_df = pd.DataFrame( daily_list, columns=["StockCode", "Date", "Open", "High", "Low", "Close", "Volume"] ) 

# 일봉 조회의 경우 종목 코드가 2번째 row 부터 공백이므로 삭제 
daily_df.drop(columns='StockCode', axis =1, inplace = True)

# string date -> datetime 
daily_df['Date'] = pd.to_datetime( daily_df['Date'], format = '%Y%m%d') 

# str to int
selected_cols = ["Open", "High", "Low", "Close", "Volume"]
daily_df[ selected_cols ] = daily_df[selected_cols].astype('int')

daily_df = daily_df.set_index('Date')

daily_df = daily_df.sort_values(by= 'Date')

print(daily_df.head(5))

# 5, 10 , 20 , 60 일 평균 선 추가 
mpf.plot(daily_df, type='candle', mav=(5, 10, 20, 60), volume= True)
             Open   High    Low  Close    Volume
Date                                            
2017-10-19  54700  54700  52980  52980  12108700
2017-10-20  52800  54100  52800  53840   8027050
2017-10-23  54600  54640  54000  54300   8311050
2017-10-24  54700  54780  54040  54040   5806050
2017-10-25  54040  54420  53700  53900   5882850


d:\1git\kw_condition\.venv\lib\site-packages\mplfinance\_arg_validators.py:36: UserWarning: 

 ================================================================= 

   WARNING: YOU ARE PLOTTING SO MUCH DATA THAT IT MAY NOT BE
            POSSIBLE TO SEE DETAILS (Candles, Ohlc-Bars, Etc.)
   For more information see:
   - https://github.com/matplotlib/mplfinance/wiki/Plotting-Too-Much-Data
   
   TO SILENCE THIS WARNING, set `type='line'` in `mpf.plot()`
   OR set kwarg `warn_too_much_data=N` where N is an integer 
   LARGER than the number of data points you want to plot.

 ================================================================ 
  warnings.warn('\n\n ================================================================= '+

png

9. 전종목 일봉 Excel 출력

전체 종목의 일봉 데이터를 Excel 로 만든다

주의사항: 과도한 조회는 오류 팝업 발생 후 재접속 해야 하므로 주의!

import datetime
import pandas as pd
import time

current_time_str = datetime.datetime.now().strftime('%Y%m%d')

for code in kw_obj.code_by_names.values():
    trcode = 'opt10081'
    stock_name = kw_obj.getMasterCodeName( code )
    rqname = '{}: 주식일봉차트조회요청'.format( stock_name ) 

    inputs = {'종목코드': '{}'.format( code ), '기준일자' : current_time_str, "수정주가구분": '1'}

    daily_list = []
    prev_next = 0

    while False:
        kw_obj.add_transaction(rqname, trcode, inputs, prev_next = prev_next)
        common_util.process_qt_events(kw_obj.has_transaction_result(rqname), 5)
        
        has_additional_data = kw_obj.has_transaction_additional_data(rqname)

        # result 를 get 해야 다시 동일 rqname 으로 재요청 가능함 
        daily_list.extend( kw_obj.get_transaction_result(rqname) )

        if( has_additional_data == True ):
            prev_next = 2
        else:

            daily_df = pd.DataFrame( daily_list, columns=["StockCode", "Date", "Open", "High", "Low", "Close", "Volume"] )     

            # 일봉 조회의 경우 종목 코드가 2번째 row 부터 공백이므로 삭제 
            daily_df.drop(columns='StockCode', axis =1, inplace = True)

            # string date -> datetime 
            daily_df['Date'] = pd.to_datetime( daily_df['Date'], format = '%Y%m%d') 

            # str to int
            selected_cols = ["Open", "High", "Low", "Close", "Volume"]
            daily_df[ selected_cols ] = daily_df[selected_cols].astype('int')

            daily_df = daily_df.set_index('Date')

            daily_df = daily_df.sort_values(by= 'Date')

            print(daily_df.head(2))

            # Excel 생성 
            daily_df.to_excel('{}({}).xlsx'.format( stock_name, code ) )
            time.sleep(10)

            break

10. TR(계좌평가잔고내역조회요청) - Multi Data

    rqname = '계좌평가잔고내역요청'
    trcode = 'opw00018'

    inputs = {'계좌번호': kw_obj.get_first_account(), '비밀번호' : '', '비밀번호입력매체구분': '00', '조회구분': '1' }

    kw_obj.showAccountWindow()
    kw_obj.add_transaction(rqname, trcode, inputs)

    common_util.process_qt_events(kw_obj.has_transaction_result(rqname), 5)

    # result 를 get 해야 다시 동일 rqname 으로 재요청 가능함 

    jango = kw_obj.get_transaction_result(rqname)
    print( len(jango) )
    jango[-5: ] 
* 14:23:36.946492 request_transaction  {'rqname': '계좌평가잔고내역요청', 'trcode': 'opw00018', 'screen_no': '9196', 'prev_next': 0, 'inputs': {'계좌번호': '4175351811', '비밀번호': '', '비밀번호입력매체구분': '00', '조회구분': '1'}}
* 14:23:37.059007 _OnReceiveTrData  sScrNo: 9196, rQName: 계좌평가잔고내역요청, trCode: opw00018, recordName: , prevNext 
2





[['서울식품',
  'A004410',
  '000000000000253',
  '000000000252',
  '000000000000002',
  '000000000000002',
  '000000000253'],
 ['KD',
  'A044180',
  '000000000000966',
  '000000000949',
  '000000000000001',
  '000000000000001',
  '000000000938']]

11. 조건 검색 (사용자 설정 조건 리스트 읽기 from HTS)

예시의 정상동작을 위해서는 아래에서 사용되는 조건들과 같은 이름을 가지는 조건들이 미리 저장되어 있어야 한다.

참고로 조건들을 편집하고 저장하는건 영웅문 HTS 내부에서만 가능하기 때문에 따로 HTS 를 열어 편집해주어야 한다.

kw_obj.load_condition_names()
common_util.process_qt_events(kw_obj.has_condition_names, 5)
print( kw_obj.get_condition_names() )
* 14:23:37.346085 _OnReceiveConditionVer  ret: 1, msg: [OK] 사용자 조건검색식 읽기
{'장초반': 1, '휴식': 2, '장후반': 0, '이탈3': 4, '이탈15': 6, '새조건명': 3, '새조건명2': 5}

12. 조건검색 (사용자 조건과 일치하는 종목 리턴)

위에서 서버로부터 조건명을 읽어오면 조건명을 입력하여, 조건명에 해당하는 종목리스트를 얻어 온다

condition_name = '장초반'
kw_obj.request_condition(condition_name)
common_util.process_qt_events(kw_obj.has_transaction_result('condition'), 5)
codes = kw_obj.get_transaction_result('condition')
print(codes)
* 14:23:37.470569 _OnReceiveTrCondition  scrNo: 9195, codeList: 000660;005930;009830;012450;064350;389260;, conditionName: 장초반 index: 1, next: 0
condition list add: 000660 SK하이닉스
condition list add: 005930 삼성전자
condition list add: 009830 한화솔루션
condition list add: 012450 한화에어로스페이스
condition list add: 064350 현대로템
condition list add: 389260 대명에너지
['000660', '005930', '009830', '012450', '064350', '389260']

13. 실시간 조건 검색

14. 주문처리(시장가 매수)

    request_name = "1주 시장가 신규 매수"  # 사용자 구분명, 구분가능한 임의의 문자열
    account_no = kw_obj.get_first_account()   # 계좌번호 10자리, 여기서는 계좌번호 목록에서 첫번째로 발견한 계좌번호로 매수처리
    order_type = 1  # 주문유형, 1:신규매수
    code = "004410"  # 종목코드, 서울식품 종목코드 (싼거)
    quantity = 1  # 주문수량, 1주 매수
    price = 0  # 주문가격, 시장가 매수는 가격 설정 의미 없으므로 기본값 0 으로 설정
    quote_type = "03"  # 거래구분, 03:시장가
    original_order_no = ""  # 원주문번호, 주문 정정/취소 등에서 사용

    kw_obj.add_order( request_name, account_no, order_type, code, quantity, price, quote_type, original_order_no)
    common_util.process_qt_events(False, 3)
* 14:23:37.573540 order_waiting_entered 
* 14:23:37.590820 request_order  {'rqname': '1주 시장가 신규 매수', 'screen_no': '9194', 'account_no': '4175351811', 'order_type': 1, 'code': '004410', 'quantity': 1, 'price': 0, 'quote_type': '03', 'original_order_no': ''}
* 14:23:37.684528 _OnReceiveTrData  sScrNo: 9194, rQName: 1주 시장가 신규 매수, trCode: KOA_NORMAL_BUY_KP_ORD, recordName: , prevNext 
TR Receive not implemented! 
접수 004410 서울식품 142337 1 2 1 0 number: 0047254
체결 004410 서울식품 142337 0 2 1 0 number: 0047254
잔고정보 004410 3 3 253 서울식품 254 3 253 254 2
* 14:23:37.897191 order_standby_entered 
time out!

14. 주문처리(시장가 매도)

    request_name = "1주 시장가 신규 매도"  # 사용자 구분명, 구분가능한 임의의 문자열
    account_no = kw_obj.get_first_account()   # 계좌번호 10자리, 여기서는 계좌번호 목록에서 첫번째로 발견한 계좌번호로 매수처리
    order_type = 2  # 주문유형, 2:신규매도 
    code = "004410"  # 종목코드, 서울식품 종목코드 (싼거)
    quantity = 1  # 주문수량, 1주 매수
    price = 0  # 주문가격, 시장가 매수는 가격 설정 의미 없으므로 기본값 0 으로 설정
    quote_type = "03"  # 거래구분, 03:시장가
    original_order_no = ""  # 원주문번호, 주문 정정/취소 등에서 사용

    kw_obj.add_order( request_name, account_no, order_type, code, quantity, price, quote_type, original_order_no)
    common_util.process_qt_events(False, 3)
* 14:23:40.734433 order_waiting_entered 
* 14:23:40.753305 request_order  {'rqname': '1주 시장가 신규 매도', 'screen_no': '9193', 'account_no': '4175351811', 'order_type': 2, 'code': '004410', 'quantity': 1, 'price': 0, 'quote_type': '03', 'original_order_no': ''}
* 14:23:40.855911 _OnReceiveTrData  sScrNo: 9193, rQName: 1주 시장가 신규 매도, trCode: KOA_NORMAL_SELL_KP_ORD, recordName: , prevNext 
TR Receive not implemented! 
접수 004410 서울식품 142340 1 1 1 0 number: 0047257
잔고정보 004410 3 2 253 서울식품 253 3 253 254 1
체결 004410 서울식품 142340 0 1 1 0 number: 0047257
잔고정보 004410 2 2 253 서울식품 253 2 253 254 1
* 14:23:41.053941 order_standby_entered 
time out!

Releases

No releases published

Packages

No packages published

Languages