[키움 자동매매 프로그램] - 조건식 자동매매 알고리즘(Qthread_9.py)
Qthread_9.py 구현
이제 자동매매 시작 버튼을 누르면 거래를 시작하는 코드를 구현한다.
Qthread_9 - init() 생성
from PyQt5.QtCore import *
from PyQt5.QtTest import *
from kiwoom import Kiwoom
from kiwoomType import *
from datetime import datetime, timedelta
import threading
import os
import re
class Thread9(QThread) :
def __init__(self, parent) :
super().__init__(parent)
self.parent = parent
self.k = Kiwoom()
self.account_num = self.k.acc_number # 계좌번호 인스턴스화
self.realType = RealType() # 실시간 FID 번호를 받아온 뒤 저장
self.order_stock = None # 조건검색식 종목을 받아온 뒤 저장
self.prohibit_duplication = [] # 종목 중복 등록 금지
self.stock_price = int(self.parent.price_stock.value()) # 매수 금액
self.stock_start_port = {} # 매수 시작가 세팅
self.orderitemlist_1 = [] # 중복 매수 금지
self.orderitemlist_2 = [] # 중복 매수 금지
self.orderitemlist_3 = [] # 중복 매수 금지
self.orderitemlist_4 = [] # 중복 매수 금지
self.orderitemlist_5 = [] # 중복 익절 금지
self.orderitemlist_6 = [] # 중복 익절 금지
self.orderitemlist_7 = [] # 중복 손절 금지
self.orderitemlist_8 = [] # 중복 손절 금지
self.orderitemlist_9 = [] # 중복 익절 금지
self.orderitemlist_10 = [] # 중복 익절 금지
self.orderitemlist_11 = [] # 중복 손절 금지
self.orderitemlist_12 = [] # 중복 손절 금지
self.cancel_the_buy = {} # 매수 취소
self.cancel_the_sell = {} # 매도 취소
self.k.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", "300", "", self.realType.REALTYPE['장시작시간']['장운영구분'], "0")
### 실시간 슬롯
self.k.kiwoom.OnReceiveRealData.connect(self.realdata_slot)
self.k.kiwoom.OnReceiveChejanData.connect(self.chejan_slot)
###
### 자동매매 중지 및 재시작
stop_time = self.parent.stop_time.time().toString("HHmmss") # 자동매매 중지
start_time = self.parent.start_time.time().toString("HHmmss") # 자동매매 재시작
self.c_t1 = int(stop_time)
self.c_t2 = int(start_time)
self.stop_stock = 0
self.p1 = 0
self.p2 = 0
###
self.current_time_check()
분할 자동매매 할 때와 유사하나, 추가되는 항목은 다음과 같다.
사용자가 지정한 시간대에서만 거래되도록 설정하는 stop_time 및 start_time, 그에 따른 매수 중지 및 매수 재시작 변수이다.
각각의 orderitemlist는 지정한 파라미터에 익절 or 손절을 하도록 임시 저장하는 리스트이다.
주식 체결
def realdata_slot(self, sCode, sRealType, sRealData) :
if sRealType == "장시작시간" :
fid = self.realType.REALTYPE[sRealType]['장운영구분']
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]['체결시간']
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 = abs(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))
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})
if sCode not in self.stock_start_port.keys() :
self.stock_start_port.update({sCode : {}})
self.stock_start_port[sCode].update({"매수시작가" : b})
이전 분할 자동매매와 다른점은 지정 가격이아닌 현재 거래가 기준의 비율로 거래한다는 것이다.
따라서 코드의 마지막에 매수를 시작한 시간의 가격을 저장하는 코드를 추가해야한다.
주식 체결 - 매수 알고리즘
분할 자동매매와 기본적인 알고리즘은 유사하지만 등락률로 거래하기에 다음과 같이 작성한다.
if self.stop_stock == 0 and sCode in self.k.code_list :
stock_rate = (b - self.stock_start_port[sCode]["매수시작가"]) / self.stock_start_port[sCode]['매수시작가'] * 100 # 등락률
stock_rate = round(stock_rate, 1)
# 1차 매수
if stock_rate <= float(self.k.portfolio_stock_dict[sCode]['1차가격']) :
if sCode not in self.orderitemlist_1 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
QTest.qWait(int(2000))
print("1차 매수 시작 %s" % sCode)
stock_amount = self.stock_price * float(self.k.portfolio_stock_dict[sCode]["1차수량"]) / self.stock_start_port[sCode]["매수시작가"]
self.orderitemlist_1.append(sCode)
order_success1 = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["신규매수", self.k.portfolio_stock_dict[sCode]["주문용스크린번호"], self.account_num, 1, sCode, stock_amount,
self.k.portfolio_stock_dict[sCode]["현재가"], self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/stock_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("1", sCode, b, self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success1 == 0 :
print("현재가로 주문 전달 성공")
else :
print("현재가로 주문 전달 실패")
stock_rate 변수가 등락률을 계산하여 포트폴리오 내 비율과 비교한 다음 매수할지를 결정한다.
UI 상 매수는 최대 4번까지 할 수 있으므로 같은 방식으로 1차, 2차, 3차, 4차 가격에 따라 매수 알고리즘을 완성한다.
주식 체결 - 매도 알고리즘
매도 알고리즘에는 익절과 손절이 존재하고, 각각 1차, 2차가격에 따라 매도를 진행한다.
먼저 매도 조건은 다음과 같다.
if sCode in self.k.jango_dict.keys() and self.k.jango_dict[sCode]["주문가능수량"] > 0 :
jd = self.k.jango_dict[sCode]
stock_rate = (b - jd["매입단가"]) / jd["매입단가"] * 100
stock_rate = round(stock_rate, 1)
여기서 익절 알고리즘은
# 1차 익절
if stock_rate >= float( self.k.portfolio_stock_dict[sCode]["5차가격"]) :
if sCode not in self.orderitemlist_5 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
QTest.qWait(2000)
print("1차 익절 시작 %s" % sCode)
stock_amount = self.stock_price * float(self.k.portfolio_stock_dict[sCode]["5차수량"]) / self.stock_start_port[sCode]["매수시작가"]
self.orderitemlist_5.append(sCode)
order_success1 = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["신규매도", self.k.portfolio_stock_dict[sCode]["주문용스크린번호"], self.account_num, 2, sCode, stock_amount,
self.k.portfolio_stock_dict[sCode]["현재가"], self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/stock_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("5", sCode, b, self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success1 == 0 :
print("현재가로 익절 주문 전달 성공")
else :
print("현재가로 익절 주문 전달 실패")
위처럼 되고, 손절 알고리즘은 아래처럼 된다.
if stock_rate <= float( self.k.portfolio_stock_dict[sCode]["7차가격"]) :
if sCode not in self.orderitemlist_7 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
QTest.qWait(2000)
print("1차 손절 시작 %s" % sCode)
stock_amount = self.stock_price * float(self.k.portfolio_stock_dict[sCode]["7차수량"]) / self.stock_start_port[sCode]["매수시작가"]
self.orderitemlist_7.append(sCode)
order_success1 = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["신규매도", self.k.portfolio_stock_dict[sCode]["주문용스크린번호"], self.account_num, 2, sCode, stock_amount,
self.k.portfolio_stock_dict[sCode]["현재가"], self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/stock_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("7", sCode, b, self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success1 == 0 :
print("현재가로 손절 주문 전달 성공")
else :
print("현재가로 손절 주문 전달 실패")
위의 1차 익절, 손절 알고리즘을 바탕으로 2차 익절, 손절 알고리즘을 작성할 수 있다.
지금까지 작성한 매도 알고리즘은 금일에 거래한 종목들을 대상으로 거래되는데 jango_dict를 기준으로 sCode(종목코드)가 있는지 검사한다.
여기서 jango_dict는 실행마다 초기화되는 반면 계좌 정보는 서버가 항상 가지고 있기 때문에 jango_dict는 금일 거래(정확히는 재실행시 마다 새로 등록된 종목 거래), acc_portfolio는 기존 계좌 거래로 볼 수 있다.
주식 체결 - 기존 계좌 종목 매도 알고리즘
이번에는 계좌에 존재했던 종목에 대해서 분할 익절 및 손절을 시도한다.
먼저 매도 조건은 다음과 같다.
if sCode in self.k.acc_portfolio.keys() and self.k.acc_portfolio[sCode]["매매가능수량"] > 0 :
asd = self.k.acc_portfolio[sCode]
stock_rate = (b - asd["매입가"]) / asd["매입가"] * 100
stock_rate = round(stock_rate, 1)
금일 종목에 대한 매도와 다른점은 위에서 설명했듯이 jango_dict가 acc_portfolio로 바뀌었다.
익절 알고리즘은 위와 유사하다.
if stock_rate >= float(self.k.portfolio_stock_dict[sCode]["5차가격"]) :
if sCode not in self.orderitemlist_9 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
QTest.qWait(2000)
print("1차 익절 시작 %s" % sCode)
stock_amount = round(asd["매매가능수량"]) * float(self.k.portfolio_stock_dict[sCode]["5차수량"])
self.orderitemlist_9.append(sCode)
order_success1 = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["신규매도", self.k.portfolio_stock_dict[sCode]["주문용스크린번호"], self.account_num, 2, sCode, stock_amount,
self.k.portfolio_stock_dict[sCode]["현재가"], self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/stock_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("9", sCode, b, self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success1 == 0 :
print("현재가로 익절 주문 전달 성공")
else :
print("현재가로 익절 주문 전달 실패")
손절 알고리즘은 다음과 같다.
if stock_rate <= float(self.k.portfolio_stock_dict[sCode]["7차가격"]) :
if sCode not in self.orderitemlist_11 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
QTest.qWait(2000)
print("1차 손절 시작 %s" % sCode)
stock_amount = round(asd["매매가능수량"]) * float(self.k.portfolio_stock_dict[sCode]["7차수량"])
self.orderitemlist_11.append(sCode)
order_success1 = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["신규매도", self.k.portfolio_stock_dict[sCode]["주문용스크린번호"], self.account_num, 2, sCode, stock_amount,
self.k.portfolio_stock_dict[sCode]["현재가"], self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/stock_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("11", sCode, b, self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success1 == 0 :
print("현재가로 손절 주문 전달 성공")
else :
print("현재가로 손절 주문 전달 실패")
1차 익절 및 손절 알고리즘을 참고하여 2차 익절 및 손절 알고리즘도 유사하게 구현할 수 있다.
주식 체결 - 미체결 잔고 매수/매도 취소
if len(self.k.not_account_stock_dict) > 0 :
for order_num in self.k.not_account_stock_dict.keys() :
code = self.k.not_account_stock_dict[order_num]["종목코드"]
stock_price = self.k.not_account_stock_dict[order_num]["주문가격"]
not_quantity = self.k.not_account_stock_dict[order_num]["미체결수량"]
order_gubun = self.k.not_account_stock_dict[order_num]['주문구분']
# 매수 취소 주문 : 주문가격이 최우선 매수호가보다 작을 경우
if order_gubun == "매수" and not_quantity > 0 and stock_price < self.k.portfolio_stock_dict[sCode]["현재가"] :
order_success = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["매수취소", self.k.portfolio_stock_dict[sCode]['주문용스크린번호'], self.account_num, 3, code, 0, 0,
self.realType.SENDTYPE['거래구분']['지정가'], order_num])
if order_success == 0 :
print("%s 매수취소 전달 성공" % code)
self.cancel_the_buy.update({code : {"수량" : not_quantity}})
else :
print("%s 매수취소 전달 실패" %code)
wf2 = open("dist/cancel_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("매수취소", self.k.portfolio_stock_dict[sCode]["종목명"], not_quantity, self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
elif not_quantity == 0:
del self.k.not_account_stock_dict[order_num]
# 매도 취소 주문 : 주문가격이 최우선 매도호가보다 클 경우
elif order_gubun =="매도" and not_quantity > 0 and self.k.portfolio_stock_dict[sCode]["현재가"] < stock_price :
order_success = self.k.kiwoom.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["매도취소", self.k.portfolio_stock_dict[sCode]['주문용스크린번호'], self.account_num, 4, code, 0, 0,
self.realType.SENDTYPE['거래구분']['지정가'], order_num])
wf2 = open("dist/cancel_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("매도취소", self.k.portfolio_stock_dict[sCode]['종목명'], not_quantity, self.k.portfolio_stock_dict[sCode]['체결시간']))
wf2.close()
if order_success == 0 :
print("%s 매도취소 전달 성공 % code")
self.cancel_the_sell.update({code : {"수량" : not_quantity}})
else :
print("%s 매도취소전달 실패" % code)
elif not_quantity == 0 :
del self.k.not_account_stock_dict[order_num]
주문 전달했을 때 보다 가격이 올라가거나 내려가서 매수 or 매도가 안된 수량들에 대해 취소 주문을 요청한다.
주식 체결 - 재매수 및 재매도
먼저 재매수 코드는 다음과 같다.
if sCode in self.cancel_the_buy.keys() :
if self.k.portfolio_stock_dict[sCode]["현재가"] <= self.k.portfolio_stock_dict[sCode]["매수가"] :
if sCode not in self.orderitemlist_4 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
print("재매수 시작 %s" % sCode)
self.orderitemlist_4.append(sCode)
order_success3 = self.k.kiwoom.dynamicCall("SendOrdeR(QString, QString, QString, int, QString, int, int, QString, QString)",
["재매수", self.k.portfolio_stock_dict[sCode]['주문용스크린번호'], self.account_num, 1, sCode,
self.cancel_the_buy[sCode]["수량"], self.k.portfolio_stock_dict[sCode]["현재가"],
self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/buy_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("재매수정보", self.k.portfolio_stock_dict[sCode]["종목명"], self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success3 == 0 :
print("재매수 주문 전달 성공")
else :
print("재매수 주문 전달 실패")
이어서 재매도 코드는 다음과 같다.
if sCode in self.cancel_the_sell.keys() :
if self.k.portfolio_stock_dict[sCode]["현재가"] <= self.k.portfolio_stock_dict[sCode]["매수가"] :
if sCode not in self.orderitemlist_5 :
wa = []
wa.append(sCode)
if len(wa) > 1 :
wa.clear()
pass
else :
print("재매도 시작 %s" % sCode)
self.orderitemlist_5.append(sCode)
order_success3 = self.k.kiwoom.dynamicCall("SendOrdeR(QString, QString, QString, int, QString, int, int, QString, QString)",
["재매도", self.k.portfolio_stock_dict[sCode]['주문용스크린번호'], self.account_num, 2, sCode,
self.cancel_the_sell[sCode]["수량"], self.k.portfolio_stock_dict[sCode]["현재가"],
self.realType.SENDTYPE['거래구분']['지정가'], ""])
wf2 = open("dist/sell_database.txt", "a", encoding="utf8")
wf2.write("%s\t%s\t%s\t%s\n" % ("재매도정보", self.k.portfolio_stock_dict[sCode]["종목명"], self.k.portfolio_stock_dict[sCode]["체결시간"]))
wf2.close()
if order_success3 == 0 :
print("재매도 주문 전달 성공")
else :
print("재매도 주문 전달 실패")
잔고 업데이트
마찬가지로 분할 자동매매를 구현할 때와 유사하다.
먼저 미체결 잔고 업데이트를 먼저 보면
def chejan_slot(self, sGubun, nItemCnt, sFIdList) :
if sGubun == "0" :
print("매수/매도 중. 미체결 잔고 업데이트")
else :
print("미체결잔고 해결로 실제 잔고 업데이트")
# 주문 전송 후 미체결 시 아래의 코드 수행
if int(sGubun) == 0 :
account_num = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["계좌번호"])
sCode = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["종목코드"])[1:]
stock_name = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["종목명"])
stock_name = stock_name.strip()
origin_order_number = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["원주문번호"])
order_number = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["주문번호"])
order_status = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["주문상태"])
order_quan = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["주문수량"])
order_quan = int(order_quan)
order_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["주문가격"])
order_price = int(order_price)
not_chegual_quan = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["미체결수량"])
not_chegual_quan = int(not_chegual_quan)
order_gubun = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["주문구분"])
order_gubun = order_gubun.lstrip('+').lstrip('-')
order_gubun = order_gubun.strip()
chegual_time_str = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["주문/체결시간"])
chegual_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["체결가"])
if chegual_price == '' :
chegual_price = 0
else :
chegual_price = int(chegual_price)
chegual_quantity = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['체결량'])
if chegual_quantity == '' :
chegual_quantity = 0
else :
chegual_quantity = int(chegual_quantity)
current_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["현재가"])
current_price = abs(int(current_price))
first_sell_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["(최우선)매도호가"])
first_sell_price = abs(int(first_sell_price))
first_buy_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE["주문체결"]["(최우선)매수호가"])
first_buy_price = abs(int(first_buy_price))
# 새로운 주문의 미체결 주문번호가 미체결잔고에 없으면 아래와 같이 미체결 잔고 업데이트
if order_number not in self.k.not_account_stock_dict.keys() :
self.k.not_account_stock_dict.update({order_number : {}})
self.k.not_account_stock_dict[order_number].update({"종목코드" : sCode})
self.k.not_account_stock_dict[order_number].update({"종목명" : stock_name})
self.k.not_account_stock_dict[order_number].update({"주문번호" : order_number})
self.k.not_account_stock_dict[order_number].update({"주문상태" : order_status})
self.k.not_account_stock_dict[order_number].update({"주문수량" : order_quan})
self.k.not_account_stock_dict[order_number].update({"주문가격" : order_price})
self.k.not_account_stock_dict[order_number].update({"주문구분" : order_gubun})
self.k.not_account_stock_dict[order_number].update({"미체결수량" : not_chegual_quan})
self.k.not_account_stock_dict[order_number].update({"체결량" : chegual_quantity})
self.k.not_account_stock_dict[order_number].update({"원주문번호" : origin_order_number})
self.k.not_account_stock_dict[order_number].update({"주문/체결시간" : chegual_time_str})
self.k.not_account_stock_dict[order_number].update({"체결가" : chegual_price})
self.k.not_account_stock_dict[order_number].update({"현재가" : current_price})
self.k.not_account_stock_dict[order_number].update({"(최우선)매도호가" : first_sell_price})
self.k.not_account_stock_dict[order_number].update({"(최우선)매수호가" : first_buy_price})
매수 or 매도 주문 후 미체결 상태에서 해당 코드가 실행되며 not_account_stock_dict에 위의 데이터들을 추가한다.
간혹 데이터를 얻어올 때 비어있는 상태, 즉 ‘’ 상태로 넘어올 때가 있으므로 이를 0으로 처리해준다.
다음으로 체결 통보가 되어 실제 잔고 업데이트가 이루어지는 코드는 다음과 같다.
elif int(sGubun) == 1 :
account_num = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['계좌번호'])
sCode = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['종목코드'])[1:]
stock_name = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['종목명'])
stock_name = stock_name.strip()
current_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['현재가'])
current_price = abs(int(current_price))
stock_quan = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['보유수량'])
stock_quan = int(stock_quan)
like_quan = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['주문가능수량'])
like_quan = int(like_quan)
buy_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['매입단가'])
buy_price = abs(int(buy_price))
total_buy_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['총매입가'])
total_buy_price = int(total_buy_price)
stock_gubun = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['매도매수구분'])
stock_gubun = self.realType.REALTYPE['매도수구분'][stock_gubun]
first_sell_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['(최우선)매도호가'])
first_sell_price = int(first_sell_price)
first_buy_price = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['(최우선)매수호가'])
first_buy_price = int(first_buy_price)
first_buy_price1 = self.k.kiwoom.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['예수금'])
first_buy_price1 = int(first_buy_price1)
if sCode not in self.k.jango_dict.keys() :
self.k.jango_dict.update({sCode : {}})
self.k.jango_dict[sCode].update({"현재가" : current_price})
self.k.jango_dict[sCode].update({"종목코드" : sCode})
self.k.jango_dict[sCode].update({"종목명" : stock_name})
self.k.jango_dict[sCode].update({"보유수량" : stock_quan})
self.k.jango_dict[sCode].update({"주문가능수량" : like_quan})
self.k.jango_dict[sCode].update({"매입단가" : buy_price})
self.k.jango_dict[sCode].update({"총매입가" : total_buy_price})
self.k.jango_dict[sCode].update({"매도매수구분" : stock_gubun})
self.k.jango_dict[sCode].update({"(최우선)매도호가" : first_sell_price})
self.k.jango_dict[sCode].update({"(최우선)매수호가" : first_buy_price})
if sCode in self.k.acc_portfolio.keys() and stock_quan == 0 :
self.k.acc_portfolio(sCode)['매매가능수량'] = 0
다른 점이 있다면 금일 주문 종목과 기존 계좌에 존재하던 종목 두 가지 경우로 자동매매 알고리즘을 구현하였으므로
금일 주문 종목에 대하여 매도되었을 때는 jango_dict에 그대로 업데이트되지만
기존 계좌에 있던 종목에 대하여 매도되었을 때는 acc_portfolio에 업데이트되고, jango_dict에는 업데이트 되지 않으므로 이를 업데이트 해주기 위해 다음의 코드가 필요하다.
if sCode not in self.k.jango_dict.keys() :
self.k.jango_dict.update({sCode : {}})
이는 기존 계좌에 있던 종목이 매도되었다면 jango_dict, 즉 금일 거래된 종목으로 처리해준다.
또한 기존 계좌에 있던 종목이 매도되고 보유수량이 0이되면 계좌에서 삭제해준다.
자동매매 시간 설정
사용자가 설정한 시간에 자동거래가 진행되도록 하는 코드를 구현한다.
### 자동매매 중지 및 재시작
stop_time = self.parent.stop_time.time().toString("HHmmss") # 자동매매 중지
start_time = self.parent.start_time.time().toString("HHmmss") # 자동매매 재시작
self.c_t1 = int(stop_time)
self.c_t2 = int(start_time)
self.stop_stock = 0
self.p1 = 0
self.p2 = 0
###
self.current_time_check()
매수 알고리즘을 보면 조건이 stop_stock 이 0일때 거래를 진행하게 되어있다.
따라서 해당 값이 0이 아니라면 거래를 중지할 것이다.
def current_time_check(self) :
print("매수 중지/재시작 확인")
new_time = datetime.today().strftime("%HH%MM%SS")
date_today = int(re.sub(r"[A-Z]", "", new_time))
t = threading.Timer(60, self.current_time_check)
t.start()
self.screen_num1 = 5000
self.screen_num2 = 6000
### 종목 실시간 감시
if self.k.portfolio_stock_dict != "" :
for code in self.k.portfolio_stock_dict.keys() :
if code not in self.prohibit_duplication :
self.k.portfolio_stock_dict[code].update({"1차가격" : float(self.parent.text_edit1.value())})
self.k.portfolio_stock_dict[code].update({"1차수량" : float(self.parent.text_edit2.value()) / 100})
self.k.portfolio_stock_dict[code].update({"2차가격" : float(self.parent.text_edit3.value())})
self.k.portfolio_stock_dict[code].update({"2차수량" : float(self.parent.text_edit4.value()) / 100})
self.k.portfolio_stock_dict[code].update({"3차가격" : float(self.parent.text_edit5.value())})
self.k.portfolio_stock_dict[code].update({"3차수량" : float(self.parent.text_edit6.value()) / 100})
self.k.portfolio_stock_dict[code].update({"4차가격" : float(self.parent.text_edit7.value())})
self.k.portfolio_stock_dict[code].update({"4차수량" : float(self.parent.text_edit8.value()) / 100})
self.k.portfolio_stock_dict[code].update({"5차가격" : float(self.parent.text_edit9.value())})
self.k.portfolio_stock_dict[code].update({"5차수량" : float(self.parent.text_edit10.value()) / 100})
self.k.portfolio_stock_dict[code].update({"6차가격" : float(self.parent.text_edit11.value())})
self.k.portfolio_stock_dict[code].update({"6차수량" : float(self.parent.text_edit12.value()) / 100})
self.k.portfolio_stock_dict[code].update({"7차가격" : float(self.parent.text_edit13.value())})
self.k.portfolio_stock_dict[code].update({"7차수량" : float(self.parent.text_edit14.value()) / 100})
self.k.portfolio_stock_dict[code].update({"8차가격" : float(self.parent.text_edit15.value())})
self.k.portfolio_stock_dict[code].update({"8차수량" : float(self.parent.text_edit16.value()) / 100})
self.k.portfolio_stock_dict[code].update({"주문용스크린번호" : self.screen_num2})
fids = self.realType.REALTYPE['주식체결']['체결시간']
self.k.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", self.screen_num1, code, fids, "1")
self.prohibit_duplication.append(code)
self.screen_num1 += 1
self.screen_num2 += 1
print("등록 정보")
print(code)
if date_today >= self.c_t1 and self.p1 == 0 :
print("매수 중지")
self.p1 = 1
self.stop_stock = 1
if date_today >= self.c_t2 and self.p2 == 0 :
print("매수 재시작")
self.p2 = 1
self.stop_stock = 0
사용자가 설정한 시간에만 종목 거래가 될려면 현재 시간을 지속적으로 체크해주면서 비교해야한다.
60초마다 재귀호출을 통해서 해당 메서드가 반복되도록 설정하고 조건식에 해당하는 종목들 데이터를 포트폴리오에 저장 및 실시간으로 등록한다.
또한 메서드 마지막에 매수 중지 및 매수 재시작 조건을 설정하여 사용자가 설정한 시간에 자동매매 되게 한다.