46강에서는 실시간 장운영구분 데이터를 받아왔습니다. 이번 강의는 정말 중요한 부분을 다룰 것입니다. 바로 실사간 자동매매를 위한(매수/매도) 주식체결 정보를 가져오겠습니다.
[유튜브 강의, 링크]와 같이 보시면 많은 도움이 되실 겁니다.
1. 복습 : DB에 저장한 후 불어왔던 정보 확인
43강에서 배웠다싶이 아래와 같은 정보를 DB 데이터에서 가져온거 기억 하시시죠?
- portfolio_stock_dict에는 종목코드/종목명/현재가/신용비율/매수가/매수수량/익절가/손절가/주문용스크린번호를 입력하였습니다.
앞으로 실시간 주식체결 정보를 가지고 오면 현재가와 매수가/손절가/익절가와 비교하여 매수/매도가 진행되게 할 것입니다.
KOA Studio를 한번 더 확인해 보겠습니다. 주식을 체결하게 되면 총종목에 따라 총 20개의 데이터를 받아올 수 있습니다. 우리는 20개 중 12개의 데이터만 받아오는 코딩을 할 터인데 우선적으로 주식체결에 대한 개념을 한번 더 잡고 가겠습니다.
아래 그림을 보시면 68100, 68200, 68300이 하나의 틱이며, 하나의 틱이 변화할 때 주식체결 데이터가 우리 컴퓨터로 넘어오게 됩니다. 만약 틱이 변하지 않고(계속 68200원에서 거래가 발생, 68200원의 잔량 1594개가 1개씩 소모) 거래가 발생하면, 우리는 주식체결에 대한 데이터를 받을 수 없습니다.
다시 말씀드리지만 주식체결은 반드시 1개의 틱 정보가 변화할 때입니다.
2. 주식체결 데이터 받아오기 코딩
만약 sRealType이 "주식체결"이고 protfolio_stock_dict에 sCode가 들어 있다면 아래 코드를 수행하게 됩니다. 그리고 관련 데이터를 받아올 때는 반드시 GetCommRealData 함수를 사용합니다. 46강에서 말씀드렸지만 self.realdata_slot에는 sCode와 sRealType이 넘어오게 됩니다.
우선적으로 원하는 fid 번호를 가져온 후 GetCommRealData로 요청만 하시면 주식체결 때마다 원하는 값을 받을 수 있습니다.
- fid1 = self.realType.REALTYPE[sRealType]["체결시간] : 체결시간에 대한 FID 번호를 가져옵니다. 잘 모르시겠다면 45강을 확인해 보시기 바랍니다.
- a = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid1) : GetCommRealData함수를 이용해 체결 시간에 대한 정보를 받아와 a라는 객체에 저장합니다. 이때, sCode(주식코드번호)와 fid1(체결시간 fid 번호) 반드시 입력해야 합니다.
위와 같은 형식으로 총 12개의 데이터를 받아옵니다. 받아오는 데이터는 아래와 같습니다.
- 체결시간 : 차 후에 데이터 베스화 할 때 필요하겠죠?
- 현재가 : 현재가를 이용하여 매수/매도를 수행할 것입니다.
- 전일대비 : 전일대비 얼마큼 상승/하락했는지에 대한 가격이 출력되 매수/매도 시 또 하나의 지표가 될 수 있습니다.
- 등락률 : 전일대비 얼만큼 상승/하락했는지에 대한 퍼센티지
- 최우선 매도호가 : 최우선 매도호가에 매수를 넣게 되면 시장가와 동일하게 바로 매수됩니다.
- 최우선 매수호가 : 최우선 매수호가에 매도를 넣게 되면 시장가와 동일하게 바로 매도됩니다.
- 거래량 : 틱에 대한 거래량이므로 그렇게 중요? 한가 싶지만 일단 가져오겠습니다.
- 누적거래량 : 누적거래량이 10만 이상일 때 또는 100만 이상일 대 매수해라 등의 기술들이 존재합니다.
- 고가 : 오늘자 가장 높은 가격을 나타내고 있습니다. 저항선 등으로 사용할 수 있습니다.
- 시가 : 오늘자 시작한 가격을 나타내고 있습니다.
- 저가 : 오늘자 가장 낮은 가격을 나타내고 있습니다. 지지선 등으로 사용할 수 있습니다.
- 거래회전율 : 누적거래 회전율로 이정보를 이용하면, 안정적인 급/등락 종목 매수/매도가 가능해집니다.
위의 데이터들을 다 받아 왔으면, 어디서든 사용할 수 있게 저장을 해야겠죠? 그전에 우리가 입력하지 않은 정보들이 아주 가~끔(사용자든 서버의 문제든) 넘어올 때를 대비하여 에러가 날 수 있는 부분을 아래와 같이 해결합니다.
- if sCode not in self.k.portfolio_stock_dict : 만약 portfolio_stock_dict에 넘어온 코드가 없다면, 에러가 발생할 수 있으니 아래와 같이 portfolio_stock_dict에 관련 코드를 이용해 하나의 방을 만들어 놓습니다.
- self.k.portfolio_stock_dict.update({sCode : {}})
딕셔너리 형태의 포트폴리오에 원하는 값을 넣는 많이 반복하였기에 숙달되었다고 생각합니다. 그래도 하나의 샘플에 대하여 설명드리겠습니다.
- self.k.portfolio_stock_dict[sCode].update({"체결시간" : a}) : self.k를 이용해 메타 클래스 기반의 싱글턴에 접속 후 싱글턴에 존재하는 portfolio_stock_dict에 저장된 sCode에 "체결시간" : a라는 값을 저장합니다. 나머지도 다 동일합니다.
위의 행위를 마치시면, 틱이 변할 때마다 주식체결 정보가 넘어오게 되고, 12가지의 데이터가 portfolio_stock_dict 에 저장되게 됩니다.
복습에서 배웠듯이 기존의 portfolio_stock_dict에는 종목코드/종목명/현재가/신용비율/매수가/매수수량/익절가/손절가/주문용스크린번호가 입력되어 있었죠? 틱이 변할 때마다 현재가가 업데이트되고 우리는 현재가와 익절가/손절가 등을 이용해 원하는 매수/매도를 다음 강의에서 진행할 것입니다.
오늘 배웠던 것은 실시간 매수/매도에 매우 중요한 부분이므로 반드시 이해하시고 넘어가시면 되겠습니다.
3. 코드 공개
import os # 현재 디렉토리 확인 기능
from PyQt5.QtCore import * # 쓰레드 함수를 불러온다.
from kiwoom import Kiwoom # 로그인을 위한 클래스
from kiwoomType import *
class Thread3(QThread):
def __init__(self, parent): # 부모의 윈도우 창을 가져올 수 있다.
super().__init__(parent) # 부모의 윈도우 창을 초기화 한다.
self.parent = parent # 부모의 윈도우를 사용하기 위한 조건
################## 키움서버 함수를 사용하기 위해서 kiwoom의 능력을 상속 받는다.
self.k = Kiwoom()
##################
################## 사용되는 변수
account = self.parent.accComboBox.currentText() # 콤보박스 안에서 가져오는 부분
self.account_num = account
# 계좌번호 가져오는 부분은 Qthread_3 분리 시 로그인 후 계좌번호를 가져오는 함수로 교체된다. Lecture_0529.py
################# 매수관련 변수
self.Load_code() # 매수 종목/금액/수량 가져오기
####### 주문 전송 시 필요한 FID 번호
self.realType = RealType() # 실시간 FID 번호를 모아두는 곳
######################################################################
###### 등록된 계좌 전체 해제하기(작동 정지 되었을 때 등록 정보를 다 끊어야 한다.)
self.k.kiwoom.dynamicCall("SetRealRemove(QString, QString)", ["ALL", "ALL"])
######################################################################
######################################################################
###### 선정된 종목 등록하기 : 키움서버에 리얼 데이터 등록하기
self.screen_num = 5000
for code in self.k.portfolio_stock_dict.keys(): # 포트폴리오에 저장된 코드들을 실시간 등록
fids = self.realType.REALTYPE['주식체결']['체결시간'] # 주식체결에 대한 모든 데이터를 로드할 수 있다.
self.k.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", self.screen_num, code, fids, "1") # 실시간 데이터를 받아오기 위해 각 코드들을 서버에 등록(틱 변화가 있으면 데이터 송신)
self.screen_num += 1
# print("실시간 등록 : %s, 스크린번호 : %s, FID 번호 : %s" % (code, screen_num, fids))
print("종목등록 완료")
print(self.k.portfolio_stock_dict.keys())
######################################################################
###### 현재 장 상태 알아보기 (장 시작 / 장 마감 등)
self.screen_start_stop_real = "300" # 장시 시작 전/후 상태 확인용 스크린 번호
self.k.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", self.screen_start_stop_real, '', self.realType.REALTYPE['장시작시간']['장운영구분'], "0") # 장의 시작인지, 장 외인지등에 대한 정보 수신
###### 실시간 슬롯 (데이터를 받아오는 슬롯을 설정한다)
self.k.kiwoom.OnReceiveRealData.connect(self.realdata_slot) # 실시간 데이터를 받아오는 곳
self.k.kiwoom.OnReceiveChejanData.connect(self.chejan_slot) # (주문접수, 체결통보)=0, (잔고변경) = 1 데이터 전송
def Load_code(self):
if os.path.exists("dist/Selected_code.txt"):
f = open("dist/Selected_code.txt", "r", encoding="utf8")
lines = f.readlines() # 여러 종목이 저장되어 있다면 모든 항목을 가져온다.
screen = 4000
for line in lines:
if line != "": # 만약에 line이 비어 있지 않다면
ls = line.split("\t") # \t(tap)로 구분을 지어 놓는다.
t_code = ls[0]
t_name = ls[1]
curren_price = ls[2]
dept = ls[3]
mesu = ls[4]
n_o_stock = ls[5]
profit = ls[6]
loss = ls[7].split("\n")[0]
self.k.portfolio_stock_dict.update({t_code: {"종목명": t_name}})
self.k.portfolio_stock_dict[t_code].update({"현재가": int(curren_price)})
self.k.portfolio_stock_dict[t_code].update({"신용비율": dept})
self.k.portfolio_stock_dict[t_code].update({"매수가": int(mesu)})
self.k.portfolio_stock_dict[t_code].update({"매수수량": int(n_o_stock)})
self.k.portfolio_stock_dict[t_code].update({"익절가": int(profit)})
self.k.portfolio_stock_dict[t_code].update({"손절가": int(loss)})
self.k.portfolio_stock_dict[t_code].update({"주문용스크린번호": screen}) # 아래 내용을 업데이트
screen += 1
f.close()
def realdata_slot(self, sCode, sRealType, sRealData): # 실시간으로 서버에서 데이터들이 날라온다.
if sRealType == "장시작시간":
fid = self.realType.REALTYPE[sRealType]['장운영구분']
# 실시간시세 데이터 수신 이벤트인 OnReceiveRealData() 가 발생될때 실시간데이터를 얻어오는 함수
value = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid)
if value == '0':
print("장 시작 전")
elif value == '3':
print("장 시작")
elif value == '2':
print("장 종료, 동시호가로 넘어감감")
elif value == '4':
print("장 마감했습니다.")
elif sRealType == "주식체결" and sCode in self.k.portfolio_stock_dict:
fid1 = self.realType.REALTYPE[sRealType]['체결시간'] # 체결시간은 string으로 나온다. HHMMSS
a = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid1)
fid2 = self.realType.REALTYPE[sRealType]['현재가'] # 현재가는 +/-로 나온다.
b = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid2)
b = abs(int(b))
fid3 = self.realType.REALTYPE[sRealType]['전일대비'] # 전달 대비 오르거나/내린 가격
c = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid3)
c = abs(int(c))
fid4 = self.realType.REALTYPE[sRealType]['등락율'] # 전달 대비 오르거나/내린 퍼센테이지
d = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid4)
d = float(d)
fid5 = self.realType.REALTYPE[sRealType]['(최우선)매도호가'] # 매도쪽에 첫번재 부분(시장가)
e = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid5)
e = abs(int(e))
fid6 = self.realType.REALTYPE[sRealType]['(최우선)매수호가'] # 매수쪽에 첫번재 부분(시장가)
f = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid6)
f = abs(int(f))
fid7 = self.realType.REALTYPE[sRealType]['거래량'] # 틱봉의 현재 거래량 (아주 작으 단위)
g = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid7)
g = abs(int(g))
fid8 = self.realType.REALTYPE[sRealType]['누적거래량']
h = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid8)
h = abs(int(h))
fid9 = self.realType.REALTYPE[sRealType]['고가'] # 오늘자 재일 높은 가격
i = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid9)
i = abs(int(i))
fid10 = self.realType.REALTYPE[sRealType]['시가'] # 시가
j = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid10)
j = abs(int(j))
fid11 = self.realType.REALTYPE[sRealType]['저가'] # 전체 재일 낮은 가격
k = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid11)
k = abs(int(k))
fid12 = self.realType.REALTYPE[sRealType]['거래회전율'] # 누적 거래회전율
l = self.k.kiwoom.dynamicCall("GetCommRealData(QString, int)", sCode, fid12)
l = abs(float(l))
if sCode not in self.k.portfolio_stock_dict: # 만약 서버에 등록된 코드가 포트폴리오에 없다면 코드를 등록
self.k.portfolio_stock_dict.update({sCode: {}})
# 포트폴리오 종목코드마다 아래 실시간 데이터를 입력
self.k.portfolio_stock_dict[sCode].update({"채결시간": a}) # 아래 내용을 업데이트
self.k.portfolio_stock_dict[sCode].update({"현재가": b})
self.k.portfolio_stock_dict[sCode].update({"전일대비": c})
self.k.portfolio_stock_dict[sCode].update({"등락율": d})
self.k.portfolio_stock_dict[sCode].update({"(최우선)매도호가": e})
self.k.portfolio_stock_dict[sCode].update({"(최우선)매수호가": f})
self.k.portfolio_stock_dict[sCode].update({"거래량": g})
self.k.portfolio_stock_dict[sCode].update({"누적거래량": h})
self.k.portfolio_stock_dict[sCode].update({"고가": i})
self.k.portfolio_stock_dict[sCode].update({"시가": j})
self.k.portfolio_stock_dict[sCode].update({"저가": k})
self.k.portfolio_stock_dict[sCode].update({"거래회전율": l})
'주식 자동매매 강의 > 기초반(모든 코딩의 뿌리)' 카테고리의 다른 글
주식자동매매 49강. 실시간 자동매매 구현(9), 관심 종목 실시간 매도(익절/손절) 주문 요청 하기 (8) | 2022.06.16 |
---|---|
주식자동매매 48강. 실시간 자동매매 구현(8), 실시간 종목 원하는 가격에 매수하기 (13) | 2022.06.15 |
주식자동매매 46강. 실시간 자동매매 구현(7), 장시작시간/장운영구분 데이터 받아오기 (6) | 2022.06.14 |
주식자동매매 45강. 실시간 자동매매 구현(6), 실시간 장 운영 상태 확인 요청 및 관련 데이터 수신 (12) | 2022.06.13 |
주식자동매매 44강. 실시간 자동매매 구현(5), 원하는 종목 실시간 현재가 키움 서버에 요청하기 (4) | 2022.06.10 |