12 minute read

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_timestart_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_dictacc_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초마다 재귀호출을 통해서 해당 메서드가 반복되도록 설정하고 조건식에 해당하는 종목들 데이터를 포트폴리오에 저장 및 실시간으로 등록한다.

또한 메서드 마지막에 매수 중지 및 매수 재시작 조건을 설정하여 사용자가 설정한 시간에 자동매매 되게 한다.