[Python] 키움API 조건검색 텔레그램 알림 만들기 (2)

앞의 게시물에 이어, 조건 검색 관련 함수들을 Qt Designer로 만든 UI에 연결시키도록 하겠습니다.

 

1편 : kminito.tistory.com/36

2편에서 다룰 내용

 

 - UI와 함수 연결

 - 로그창 작동

 - 텔레그램 알림 구현

 

참고

 : 조건 검색 실행은 각 조건식별로 1분에 5회의 검색 제한이 있습니다. 테스트를 하다 보면 같은 조건식을 1분에 5회 이상 실행하게 될 경우가 있으므로, 조건 검색이 실패할 경우 잠시 기다렸다가 1분이 지나서 다시 실행하시면 됩니다.

 

4. UI와 함수 연결하기

1) 콤보박스에 조건 검색식 불러오기 

 

조건검색식 리스트가 표시된 화면

위의 PyTrader 화면의 조건 검색식을 고르는 ComboBox에 사용자의 조건식을 불러오려고 합니다. 해당 콤보박스의 Object Name은 "comboBox_cond" 입니다.

 

따라서 kiwoom에서 getConditinoLoad() 실행하여 조건식 목록을 kiwoom.condition에 불러오면, 조건식들을 "인덱스;이름"형태의 문자열을 원소로 가지는 cond_list를 만듭니다. 콤보박스에 그 리스트가 나오도록 합니다.

 

pytrader.py

class MyWindow(QMainWindow, form_class):
    # 중략
    def load_condition_list(self):
        print("pytrader.py [load_condition_list]")
        """ condiComboBox에 condition List를 설정한다. """

        cond_list = []
        try:
            # 조건식 실행
            self.kiwoom.getConditionLoad()
            # getConditionLoad 가 정상 실행되면 kiwoom.condition에 조건식 목록이 들어간다.
            dic = self.kiwoom.condition
            
            for key in dic.keys():
                cond_list.append("{};{}".format(key, dic[key]))
            
            # 콤보박스에 조건식 목록 추가
            self.comboBox_cond.addItems(cond_list)

        except Exception as e:
            print(e)

 

프로그램 시작시 조건검색식 목록이 로드되어야 하므로, 초기화 함수에 집어 넣어 자동 실행이 되도록 합니다.

class MyWindow(QMainWindow, form_class):
    def __init__(self):
		# 중략
        self.load_condition_list()

이제 pytrader.py를 실행하면 정상적으로 로드가 되어야 합니다.

 

 

2) 조건 검색 시작 버튼 설정

조건검색식을 선택하여 '적용'버튼을 누르면 조건 검색이 시작되고, 해제 버튼을 누르면 중단되는 함수를 구현 및 푸시버튼에 연결토록 하겠습니다.

 

 

적용 버튼 누를 시 

  - 선택된 조건검색식 시작

  - '적용' 버튼을 '해제' 버튼으로 텍스트 변경

  - 콤보박스 및 체크박스 선택 비활성화

 

해제 버튼 누를 시

  - 선택된 조건검색식 중단

  - '해제' 버튼을 '적용' 버튼으로 텍스트 변경

  - 콤보박스 및 체크박스 선택 활성화

 

 

pytrader.py 추가 코드

설명은 아래에 있습니다.

class MyWindow(QMainWindow, form_class):
    def __init__(self):
		# 중략        
        ## 조건검색식 관련 추가
        self.load_condition_list()
        self.checkBox_cond.setChecked(True) # 체크박스 체크를 기본 설정으로
        self.pushButton_cond.clicked.connect(self.start_cond)
        
	# 중략
    def start_cond(self):
        conditionIndex = self.comboBox_cond.currentText().split(';')[0]
        conditionName = self.comboBox_cond.currentText().split(';')[1]

        if self.pushButton_cond.text() == "적용":
            try: 
                self.kiwoom.sendCondition("0",conditionName,int(conditionIndex),1)
                self.pushButton_cond.setText("해제")
                self.comboBox_cond.setEnabled(False)
                self.checkBox_cond.setEnabled(False)
                print("{} activated".format(conditionName))

            except Exception as e:
                print(e)

        else:
            self.kiwoom.sendConditionStop("0",conditionName,conditionIndex)
            self.pushButton_cond.setText("적용")
            self.comboBox_cond.setEnabled(True)
            self.checkBox_cond.setEnabled(True)

 

 

(1) 조건 검색식 시작

조건 검색을 시작하기 위해서는 사용자 조건검색식의 인덱스와 이름이 필요합니다. 위에서 콤보박스에 조건검색식의 "인덱스;이름"의 형태로 나오도록 코딩을 했으므로, 다시 세미콜론으로 나누어 조건검색식을 시작하면 됩니다. (kiwoom.sendCondition 함수)

    conditionIndex = self.comboBox_cond.currentText().split(';')[0]
    conditionName = self.comboBox_cond.currentText().split(';')[1]
    
            # 중략
            self.kiwoom.sendCondition("0",conditionName,int(conditionIndex),1)

 

※ sendCondition 함수의 첫번째 Argument는 화면번호로, 여러가지 요청에 대한 응답을 받을 경우 어떤 요청에 의한 것인지를 구분하기 위하여 숫자를 지정하나, 여기서는 조건 검색은 한번에 하나만 요청할 예정이므로 문자열 "0"으로 고정하였습니다. 

 

sendCondition 함수의 네 번째 Argument는 실시간 검색 여부로, 0은 1회성, 1은 실시간 조회를 뜻합니다. 여기서는 항상 실시간 요청으로 받을 것이므로 그냥 1로 설정하였습니다.

 

 

(2) 조건 검색식 중단

조건검색 시작과 마찬가지입니다. 

 self.kiwoom.sendConditionStop("0",conditionName,conditionIndex)

 

 

(3) UI 변경 (활성/비활성, 버튼 이름 변경)

pushButton은 setText 메소드로 텍스트를 변경합니다.

comboBox와 checkBox는 setEnabled 메소드로 활성화/비활성화를 설정합니다

    def start_cond(self):
        # 중략
        if self.pushButton_cond.text() == "적용":
		# 중략
            try: 
                self.kiwoom.sendCondition("0",conditionName,int(conditionIndex),1)
                self.pushButton_cond.setText("해제")
                self.comboBox_cond.setEnabled(False)
                self.checkBox_cond.setEnabled(False)
		# 중략
        else:
            self.kiwoom.sendConditionStop("0",conditionName,conditionIndex)
            self.pushButton_cond.setText("적용")
            self.comboBox_cond.setEnabled(True)
            self.checkBox_cond.setEnabled(True)

 

(4) 적용 버튼과 start_cond 함수 연결

        self.load_condition_list()
        self.checkBox_cond.setChecked(True) # 텔레그램 알림받기 체크를 기본으로
        self.pushButton_cond.clicked.connect(self.start_cond) #적용버튼과 start_cond 함수 연결

checkBox_cond.setChecked(True)는 텔레그램 메시지 받기를 기본으로 설정하기 위한 코드입니다.

 

 

5. 로그창 작동

기존에 print 함수를 통하여 콘솔창에 출력하던 것을, 로그창에 출력이 되도록 합니다. 아래와 같은 방식으로 구현합니다.

 

1) Kiwoom.py 에서 함수를 실행하면 출력값을 클래스 변수 msg에 저장한다 (self.msg)

2) pytrader.py 의 Qtimer에서 kiwoom.self.msg이 있으면 로그창에 출력하고 해당 변수 내용 초기화

 

 

1) self.msg에 로그 내용 보내기

먼저 Kiwoom.py 파일에서 로그가 Kiwoom 클래스의 self.msg 변수에 저장이 되도록 합니다. 

 

(1) Kiwoom 클래스에 변수 msg 추가

Kiwoom.py

class Kiwoom(QAxWidget):
    def __init__(self):
	# 중략
    self.msg = ""

 

(2) 각 함수의 실행 결과를 self.msg에 추가

kiwoom.py

 

class Kiwoom(QAxWidget):
     def receiveTrCondition(self, screenNo, codes, conditionName, conditionIndex, inquiry):
        try:
            # 중략
            msg = ""            
            for code in codeList:
                msg += "{} {}\n".format(code, self.get_master_code_name(code))      
            self.msg += msg

    def receiveRealCondition(self, code, event, conditionName, conditionIndex):
        # 중략
        msg = "{} {} {}\n".format("종목편입" if event == "I" else "종목이탈", code, self.get_master_code_name(code))
        self.msg += msg
        
    def sendCondition(self, screenNo, conditionName, conditionIndex, isRealTime):
        # 중략
        msg = "{} 실행\n".format(conditionName)
        self.msg += msg

    def sendConditionStop(self, screenNo, conditionName, conditionIndex):
        # 중략
        msg = "{} 중지\n".format(conditionName)        
        self.msg += msg
 

 

2) 창에서 로그 내용 표시하기

(1) Qtimer 설정

pytrader.py

class MyWindow(QMainWindow, form_class):
    def __init__(self):
		#중략
        self.timer = QTimer(self)
        self.timer.start(500)
        self.timer.timeout.connect(self.timeout)

기존 코드에서 QTimer의 timeout 함수 작동 시간 간격이 1,000밀리초(=1초)였으나, 여기서 500밀리초로 변경했습니다. 1초 안에 신호가 여러번 와서, 텔레그램 메시지를 발송이 1초 단위로 지연되는 경우를 방지하기 위함입니다. 물론 종목 변동이 적은 조건식을 사용할 경우에는 기존대로 1초로 해도 상관없고, 또 타이머를 하나 더 추가해도 됩니다.

 

(2) timeout 설정

기존 timeout 함수에 self.kiwoom.msg 변수를 확인하여 안에 내용이 있으면 로그창에 출력합니다. 출력 후에는 해당 변수를 초기화합니다. 이 작업은 0.5초 간격으로 수행됩니다

 

코드에 텔레그램 관련 함수가 끼어있는데, 추후 다시 설명하겠습니다.

pytrader.py

class MyWindow(QMainWindow, form_class):
	# 중략
    def timeout(self):
		# 중략
        if self.kiwoom.msg:
            # 텔레그램
            if self.checkBox_cond.isChecked():
                self.kiwoom.bot.sendMessage(chat_id=self.kiwoom.chat_id, text=self.kiwoom.msg)
            # 로그창에 내용 입력
            self.textEdit_cond.append(self.kiwoom.msg)
            self.kiwoom.msg = ""

 

여기까지 설정을 완료하고 실행하면 아래 화면과 같이 조건 검색 및 로깅이 됩니다.

 

 

6. 텔레그램 알림 설정

로그창에 내용 입력할 때, 텔레그램 메시지 체크박스에 체크가 되어 있으면 텔레그램 메시지도 함께 보냅니다. (0.5초 간격)

 

텔레그램 봇 사용법은 이 곳 참조 kminito.tistory.com/24

1) telegram 라이브러리를 임포트 (python-telegram-bot)

import telegram

2) Kiwoom 클래스에 telegram 관련 변수 생성

class Kiwoom(QAxWidget):
    def __init__(self):
        # 텔레그램
        self.bot = telegram.Bot(token='1281434826:AAG4sO47u63ViIyevbTjf2cpFh2QXNLor-w')
        self.chat_id = 657656740

 

 (1) self.bot = 텔레그램 봇 설정 (토큰 입력)

 (2) sefl.chat_id = 메시지 발송할 채팅방 ID

 

※ 텔레그램을 여기서만 사용하는 거면 해당 변수를 pytrader.py에 집어넣는 게 사실 더 편한데, Kiwoom 클래스의 다른 함수들에서 텔레그램을 사용할 일이 많아 키움에다가 집어 넣었습니다.

 

3) 로그 작동시 텔레그램 메시지 발송

pytrader.py 

    def timeout(self):
        # 중략
        if self.kiwoom.msg:
            # 텔레그램
            if self.checkBox_cond.isChecked():
                self.kiwoom.bot.sendMessage(chat_id=self.kiwoom.chat_id, text=self.kiwoom.msg)
            self.textEdit_cond.append(self.kiwoom.msg)
            self.kiwoom.msg = ""

체크박스에 체크가 되어 있으면 텔레그램 메시지를 보내고, 아니면 그냥 로그창에만 내용을 작성합니다.

 

 

이까지 완성하고 테스트하면 아래와 같이 로깅, 텔레그램 메시지는 잘 작동을 합니다.

 

 

오른쪽 창은 텔레그램 PC버전입니다.

 

 

7. 실시간 테스트

 실시간 테스트를 위하여 회사에서 점심시간에 잠시 작동했습니다. 일부러 작동하는 모습 보기 위하여 계속해서 종목 편입 및 이탈이 일어나는 조건식을 선택했습니다. 여러 종목이 수시로 등장하는 조건식은 알람 설정 의미가 없으므로, 제 생각에는 하루에 몇차례 잘 뜨지 않는 조건 검색을 위해 텔레그램 알림을 사용하는 것이 좋겠습니다.

 

 

실시간 작동 영상

 

후기

 - 일 다니면서 쉬는 날 만들고 글을 쓰려니 쉽지 않네요. 부족한 점에 대해 조언을 주시면 감사히 받겠습니다. 궁금한 점도 남겨주시면 아는 한에서 최대한 답변을 드리겠습니다. 잘못된 점이나 오류가 있으면 지적해주세요.

 

- 조건식의 알림 기능에 집중하여 게시물을 작성하느라 다른 것들이 너무 빈약한 것 같습니다. PyTrader 완성본에다가 조건검색과 텔래그램을 얹을까 하다가, 그러면 너무 복잡해보일 것 같아 일부러 2일차 코드에다가 얹었는데 조금 성의없어 보이는 것 같기도 하네요. 

 

 - 요새 너무 바빠서... 시간이 나면 이전에 올린 SRT 프로그램도 새로 짜고, 주식 관련 프로그램도 새로 멋있게 만들어서 올리겠습니다. 

 

 

이 게시물의 전체 소스코드는 이곳에 있습니다. (2편)

이 게시물 코드 : github.com/kminito/pytrader-condition/tree/main/day2

참고한 Github Repository : github.com/kdseo/PyTrader

 

좋은 책을 무료로 공개해주신 조대표님 정말 감사합니다.

wikidocs.net/book/110

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

댓글

Designed by JB FACTORY