1. 들어가며
지난 시간에는 키움증권 OpenAPI를 통해 현재가를 전역변수에 담아 출력해 보았다.
이번 시간에는 매수한 4종목의 매수가격, 매수량을 받아오는 코드에 대해 알아보자.
매수가격 및 매수량은 익절/손절을 구하기 위해 반드시 구현할 필요가 있다.
* 4종목 : 삼성전자(005930), 경동나비엔(009450), 아프리카TV(067160), 토니모리(214420)
- (익절) 현재가격 > 매수가격 + 2,000원이면, 현재가(시장가)로 매도
- (손절) 현재가격 < 매수가격 - 3,000원이면, 현재가(시장가)로 매도
2. 사전준비
1) 화면번호 찾기
어제 모의계좌를 통해 4종목을 매수하였다.
매수한 4종목의 매수가격 및 매수량을 알아보기 위해 영웅문4에 접속하여
어떤 카테고리에서 제공하는지 열심히 찾아보았다.
믿기지 않겠지만, 주식은 주로 모바일로 매매를 하다보니,
영웅문4에서 경로 찾는데도 시간이 걸린다. -_-;;
화면번호 0345(실시간계좌관리)에서 제공하고 있다.
2) 화면목록 찾기
그렇다면 KOA StudioSA에서는 영웅문4의 정보(현재가격, 매수량 등)를 키움서버로부터 어떻게 받아올까?
KOA StudioSA의 화면목록(①)에서 화면목록(②)을 통해 0345를 입력하니,
실시간잔고(③)가 나온다.
2개의 TR이 있는데, 비교적 익숙한 OPT10085(④, 계좌수익률요청)을 활용할 예정이다.
3) KOA StudioSA에서 출력된 내용
KOA StudioSA의 화면목록(①)의 TR목록(②)에서 "10085"을 입력하면
opt10085(③, 계좌수익률요청)이 나온다.
요청해야할 TR 내용은(④)은 SetInputValue와 CommRqData 2함수이다.
또한, 입력해야될 내용(⑤)은 계좌번호를 입력해주면,
화면 중앙에서 출력(⑥)되는 데이터를 활용할 수 있다.
현재 모의계좌에는 삼섬전자, 경동나비엔, 아프리카TV, 토니모리 4종목이 있다.
받아오는 형태를 보면,
[0][0]
[0][1]
[0][2]
...
[1][0]
[1][1]
[1][2]
특정값에 매칭되어 출력이 된다.
특정값과 특정값이 연계된다는 것은 향후 구현할 때,
딕셔너리 형태로 구현할 필요가 있다고 생각된다.
3. 코드 구현
- 키움증권의 OpenAPI에 접속/로그인하고,
- 계좌에 있는 종목의 내용을 받기 위해 {딕셔너리}형태를 선언하고
- 보유종목인 4개를 입력하여 프로그램을 실행한 후
- 전역변수 형태로 보유종목의 매수가격, 매수량을 받을 예정이다.
※ 로그인과 관련된 내용은 생략한다.
19줄 : 계좌에 있는 종목의 내용을 받기 위해 딕셔너리를 생성한다.
(딕셔너리 안에 아무것도 없는 것은 초기화를 의미한다.)
22줄 : 보유한 4종목의 코드번호를 넣어준다.
26줄 : 모의계좌 번호를 프로그램의 어느곳에서도 사용할 수 있도록 전역변수(global) 선언
27줄 : 모의계좌번호 10자리를 넣어준다.
36줄 : 계좌번호 전달을 통해 키움서버에서 데이터를 얻어올 것이므로,
rq_data_opt10085의 임의의 함수를 만든다.
37줄 : KOA StudioSA에서 확인했듯이, SetInputValue 함수를 통해 키움서버에 계좌번호를 입력한다.
38줄 : KOA StudioSA에서 확인했듯이, CommRqData 함수를 통해 키움서버에 계좌번호를 요청한다.
39줄 : 데이터를 요청하는 동안, 다음코드의 실행을 막을 목적으로 이벤트 루프를 선언한다.
40줄 : 이벤트 루프를 실행한다.( exec_() )
41줄 : 키움서버에서 데이터를 수신하기 위해, trdata_get 임의의 함수를 만든다.
42줄 : 키움에서 받아오는 rqname가 38줄에서 요청한 "opt_10085"이면, 아래 실행
43줄 : GetRepeatCnt는 키움증권 OpenAPI에서 제공하며, 데이터의 개수를 제공한다.
여기서는 계좌에 있는 종목의 숫자를 제공한다.
4종목을 보유하고 있으므로 "4"를 반환한다.
GetRepeatCnt함수를 rows에 넣어서 변수화 시킨다..
(바로 아래의 for문에서 활용하기 위함)
44줄 : for문과 range를 사용하여 4종목의 데이터를 순차적으로 받아온다.
→ 위의 KOA StudioSA에서 확인했듯이, 계좌번호를 넣고 조회하면,
내가 필요한 정보 외에도, 다른 정보들이 많이 수신된다.
45줄 : 수신받은 데이터 중 코드번호(stock_code)를 변수화함 → for문을 통해 데이터 받아오기 위함
46줄 : 수신받은 데이터 중 매수가격(stock_buy_price)를 변수화함
47줄 : 수신받은 데이터 중 매수량(stock_qty)를 변수화함
50줄 : 계좌 안에 코드번호가 있으면, 패쓰(=다음 코드로 넘어가라)
52줄 : 계좌 안에 코드번호가 없으면, 초기화하라
55줄 : 최초 받아온 매수가격(stock_buy_price)은 문자형이므로,
정수(int)화 시키고, 절대값(abs)를 붙여서 양수화 시킨다.
56줄 : 최초 받아온 매수량은 문자형이므로, 정수형(int)으로 태 변환한다.
58줄 : 계좌에 있는 종목에 대해 "매수가격"를 추가(딕셔너리에서 추가는 update 사용)
* 계좌에 있는 종목은 {stock_code, stock_buy_price} 형태인데,
매수량(stock_buy_price)를 추가함
59줄 : 계좌에 있는 종목에 대해 "매수량"을 추가
61줄 : 종목코드가 23줄에서 선언한 리스트의 첫번째[0]과 같다면,
62줄~63줄 : 매수가격과 매수량은 향후 사용할 것이므로 전역변수 선언
64줄 : 첫번째로 받은 종목의 매수가격(stock_buy_price)를 stock_sbuy0에 넣어라.
65줄 : 첫번째로 받은 종목의 매수량(stock_qty)를 stock_qty0에 넣어라
66줄 : 윗에서 받은 첫번째 종목코드, 매수가격, 매수량을 출력하라 (print)
※ 61줄~87줄 : 종목코드가 23줄의 리스트["005930", ... , "214420"]와 비교하여 출력
95줄 : threading.Timer(타이머) 활용을 위해, qty4() 함수를 만들어준다.
96줄 : 36줄의 rq_data_opt10085 함수에 27줄의 계좌(deposit_num)를 넣어서 실행하라.
97줄 : qty4() 함수를 5초마다 실행하라.
99줄 : threading.Timer를 적용하기 위해서는 1번은 실행해 주어야 한다.
99줄의 qty4() 명령어를 통해 95줄의 함수를 실행하고, threading.Timer가 5초마다 실행됨
4. 전체 코드
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import threading
class btl_system():
def __init__(self):
self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
print("로그인 시작!")
self.kiwoom.OnEventConnect.connect(self.login_Connect)
self.kiwoom.OnReceiveTrData.connect(self.trdata_get)
self.kiwoom.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_()
self.account_stock_dict = {} # 딕셔너리 생성
# 보유종목
global jongmok_lists
jongmok_lists = ["005930", "009450", "067160", "214420"] # 삼성전자, 경동나비엔, 아프리카TV, 토니모리
# 계좌번호
global deposit_num
deposit_num = "8032508011"
def login_Connect(self, err_code):
if err_code == 0:
print('로그인 성공했습니다!')
else:
print('로그인 실패했습니다!')
self.login_event_loop.exit()
def rq_data_opt10085(self, deposit_num_name):
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "계좌번호", deposit_num_name)
self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opt_10085", "opt10085", "0", "8877")
self.tr_event_loop = QEventLoop()
self.tr_event_loop.exec_()
def trdata_get(self, sScrNo, rqname, strcode, sRecordName, sPreNext, nDataLength, sErrorCode, sMessage, sSplmMsg):
if rqname == 'opt_10085':
rows = self.kiwoom.dynamicCall("GetRepeatCnt(QString, QString)", strcode, rqname)
for i in range(rows):
stock_code = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", strcode, rqname, i, "종목코드").strip()
stock_buy_price = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", strcode, rqname, i, "매입가")
stock_qty = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", strcode, rqname, i, "보유수량")
if stock_code in self.account_stock_dict:
pass
else:
self.account_stock_dict[stock_code] = {}
stock_buy_price = abs(int(stock_buy_price))
stock_qty = int(stock_qty)
self.account_stock_dict[stock_code].update({"매입가": stock_buy_price})
self.account_stock_dict[stock_code].update({"보유수량": stock_qty})
if stock_code == jongmok_lists[0]:
global stock_sbuy0
global stock_sqty0
stock_sbuy0 = stock_buy_price
stock_sqty0 = stock_qty
print("종목코드 {}의 매입가는 {}원이고, 보유수량은 {}개이다.".format(jongmok_lists[0], stock_sbuy0, stock_sqty0))
elif stock_code == jongmok_lists[1]:
global stock_sbuy1
global stock_sqty1
stock_sbuy1 = stock_buy_price
stock_sqty1 = stock_qty
print("종목코드 {}의 매입가는 {}원이고, 보유수량은 {}개이다.".format(jongmok_lists[1], stock_sbuy1, stock_sqty1))
elif stock_code == jongmok_lists[2]:
global stock_sbuy2
global stock_sqty2
stock_sbuy2 = stock_buy_price
stock_sqty2 = stock_qty
print("종목코드 {}의 매입가는 {}원이고, 보유수량은 {}개이다.".format(jongmok_lists[2], stock_sbuy2, stock_sqty2))
else:
global stock_code_sbuy3
global stock_code_sqty3
stock_sbuy3 = stock_buy_price
stock_sqty3 = stock_qty
print("종목코드 {}의 매입가는 {}원이고, 보유수량은 {}개이다.".format(jongmok_lists[3], stock_sbuy3, stock_sqty3), '\n')
self.tr_event_loop.exit()
if __name__ == "__main__":
app = QApplication(sys.argv)
btl = btl_system()
def qty4():
btl.rq_data_opt10085(deposit_num)
threading.Timer(10, qty4).start()
qty4()
app.exec_()
5. 마치며
지금까지 매수하여 계좌에 보관 중인 4종목의
매수가격과 매수량을 알아보았다.
익절/손절을 위해 매도를 위해 매수가격은 반드시 파악하여야 한다.
향후 위 코드는 사용자가 매수한 수량이 있을 때만,
실행 될 수 있도록 구현할 예정이다.
보유하지도 않았는데, 굳이 현재가를 조회할 필요는 없어 보인다.
또한 키움 OpenAPI의 설명에 따르면,
데이터 조회 횟수 제한은 1초당 5회이다.
자주 현재가를 조회하면, 서버부하가 생겨서 제한하고 있다.
* 키움증권의 조회 제한 : https://www.kiwoom.com/h/common/bbs/VBbsBoardBWOAZView
현재가와 매수가를 각각 구현하였다.
이 다음에는 1분봉 받는 방법을 알아보자.
'1. 국내주식 > 1-2. 키움 OpenAPI (사용)' 카테고리의 다른 글
(주식 자동 매매) 키움증권 OpenAPI - 미체결 주문을 SendOrder 함수로 취소하기 (2) 취소주문 테스트 (5) | 2022.10.07 |
---|---|
(주식 자동 매매) 키움증권 OpenAPI - 미체결 주문을 SendOrder 함수로 취소하기 (1) 취소 방법 (0) | 2022.10.06 |
(주식 자동 매매) 키움증권 OpenAPI - 1분봉 데이터 실시간 받기(opt10080) (2) | 2022.10.05 |
(주식 자동 매매) 키움증권 OpenAPI - SendOrder함수를 이용하여 매수/매도하기 (0) | 2022.10.01 |
(주식 자동 매매) 키움증권 OpenAPI 종목의 현재가를 전역변수에 담기(opt10001) (0) | 2022.09.27 |
(주식 자동 매매) 키움증권 OpenAPI 4종목 현재가 조회(opt10001) (0) | 2022.09.26 |
(주식 자동 매매) 키움증권 OpenAPI 현재가 조회(opt10001) (0) | 2022.09.25 |
(주식 자동 매매) 키움증권 OpenAPI 예수금/주문가능금액 조회 (opw00001) (0) | 2022.09.22 |