MainApp.py 에서 사용했던 logger이다. logging 모듈을 사용했고 텔레그램, 디버깅파일, 로그파일, 콘솔출력 총 4가지를 사용한다. 

#logger print info message
@singleton
class Telog():
    def __init__(self):
        self.logger = logging.getLogger("AppInfoText")
        self.logger.setLevel(logging.DEBUG)

Telog라는 클래스를 만들어준다. 여기서 텔레그램, 디버깅파일, 로그파일, 콘솔출력 4가지를 동시에 진행할것이다.

Logger의 이름은 AppInfoText라고 지어줬고 디버그파일을 출력하기위해 Level을 logging.DEBUG로 설정해주었다.

또한 동시에 여러파일에서 사용되기 때문에 중복생성을 방지하기위해 싱글톤형식으로 만들어주었다.

	#set debugging file handler
        debugformat = '[%(asctime)s|{}|%(levelname)s|%(filename)s:%(funcName)s:%(lineno)s] %(message)s'. \
            format(self.logger.name)
        debugformatter = logging.Formatter(debugformat)
        debug_log_path = datetime.now().strftime("debug/Debugger %Y-%m-%d %H-%M-%S.log")
        debug_handler = logging.FileHandler(filename = debug_log_path, encoding="utf-8")
        debug_handler.setFormatter(debugformatter)
        debug_handler.setLevel(logging.DEBUG)

디버그파일을 만드는 코드. 대충 format을 설정해주었고 파일명은 다음과 같이 작성해주었다.

실제 작성된 디버그파일 이름

그렇게 handler를 만들어서 적용해주었고, 디버그내용을 표시하기 위해 Level을 Debug로 설정했다.

실제 Debugger 파일에 출력되는 형식

 

	#set file handler
        fileformat = '[%(asctime)s|{}|%(levelname)s|%(filename)s:%(funcName)s:%(lineno)s] %(message)s'. \
            format(self.logger.name)
        fileformatter = logging.Formatter(fileformat)
        file_log_path = datetime.now().strftime("log/AppLog %Y-%m-%d.log")
        file_handler = logging.FileHandler(filename = file_log_path, encoding="utf-8")
        file_handler.setFormatter(fileformatter)
        file_handler.setLevel(logging.INFO)

두번째로 로그를 출력해줄 파일 핸들러를 만들어주었다. 아까와 같은 형식이지만 파일명을 로그 폴더안에 날짜만 출력되도록 하였다.

실제 작성된 로그파일 이름

개발은 해당 날짜에 한번만 실행되도록 만들었기때문에 어차피 로그가 하루에 하나 뿐이지만, 디버깅을 해본 결과 같은날짜에 두번 이상 프로그램을 실행했다면, 해당로그파일 밑에 이어서 작성이 된다.

 

로그를 찍어주는 파일이기 때문에 Level을 INFO로 설정해주었다.

실제 AppLog 파일에 출력된 로그(INFO 이상 모든 레벨을 출력한다)

(위의 종목은 테스트용으로 임의의 종목을 선택한 것이므로 실제 매매와 전혀 관련없는 종목이다)

 

        #set console handler
        streamformat='%(asctime)s [%(levelname)s] %(message)s - %(filename)s:%(lineno)d'
        streamformatter = logging.Formatter(streamformat)
        stream_handler = logging.StreamHandler()
        stream_handler.setFormatter(streamformatter)
        stream_handler.setLevel(logging.DEBUG) #for test

콘솔의 출력을 관리해주는 핸들러다. Formatter는 조금 더 보기 쉽게 파일과 다른 형식을 사용하였다.

실제 콘솔에서 출력되는 방식

테스트를 위해 Level을 Debug로 설정해주었다.

 

마지막으로 텔레그램 출력을 위한 핸들러를 만들어주어야한다.

import telegram

#telegram handler
class LogTelHandler(logging.Handler):
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.telbot = telegram.Bot(token=keyManager.getJsonData("telegram_token"))
        self.chatid = keyManager.getJsonData("telegram_chatid")
    
    def emit(self, record):
        if self.formatter is None: 
            text = record.msg
        else:
            text = self.formatter.format(record)
        
        self.telbot.sendMessage(chat_id=self.chatid, text=text)

텔레그램 모듈은 python-telegram-bot 모듈을 사용하였다.

https://github.com/python-telegram-bot/python-telegram-bot

 

GitHub - python-telegram-bot/python-telegram-bot: We have made you a wrapper you can't refuse

We have made you a wrapper you can't refuse. Contribute to python-telegram-bot/python-telegram-bot development by creating an account on GitHub.

github.com

init 함수에선 keyManager파일에서 관리하는 json 파일에 저장해둔 telegram 토큰과 chatid를 가져온다.

 

handler내에선 실제 logging을 처리하기 위해 emit이라는 함수를 사용한다. emit함수 내에서 실제 봇이 메시지를 보내도록 설정해준다.

 

        #set telegram handler
        telformat= datetime.now().strftime("[%m/%d %H:%M:%S.%f] ") + '%(message)s'
        telformatter = logging.Formatter(telformat)
        telegram_handler = LogTelHandler()
        telegram_handler.setFormatter(telformatter)
        telegram_handler.setLevel(logging.INFO)

다시 Telog의 init으로 돌아와서, 위와 같이 설정해주었다. 

텔레그램은 모바일로 주로 보기때문에 Formatter을 조금 더 간결하게 만들어 주었다.

실제 telegram 내에서 출력되는 방식 (배경은 신경쓰지말자)

이제 위에서 만든 4개의 handler을 전부 logger에 추가를 해주어야한다.

	# Add Formatters
        self.logger.addHandler(stream_handler)
        self.logger.addHandler(telegram_handler)
        self.logger.addHandler(file_handler)
        self.logger.addHandler(debug_handler)

 


추가로 위와같이 관리를 하니 실제 거래내역만 보기에는 어렵다는 생각이 들었다. 그래서 따로 거래내역만 저장해주는 logger를 추가로 만들어주었다.

#거래 로그
@singleton
class TradeLog():
    def __init__(self):
        self.logger = logging.getLogger("TradeLog")
        self.logger.setLevel(logging.INFO)
        

        #set file handler
        fileformat = '[%(asctime)s] %(message)s'
        fileformatter = logging.Formatter(fileformat)\
        file_log_path = datetime.now().strftime("log/TradeLog %Y-%m-%d.log")
        file_handler = logging.FileHandler(filename = file_log_path, encoding="utf-8")
        file_handler.setFormatter(fileformatter)
        file_handler.setLevel(logging.INFO)

        # Add Formatters
        self.logger.addHandler(file_handler)

위의 로그파일 사진과 같이 TradeLog파일로 출력되며 종목 정보만 출력해주도록 사용을 할 예정이다.

 

아래는 telog.py의 전체코드이다.

import logging
import telegram
from singleton_decorator import singleton
from datetime import datetime

from mydata import keyManager


"""
logging.basicConfig(
    format='%(asctime)s [%(levelname)s] %(message)s - %(filename)s:%(lineno)d',
    level=logging.DEBUG)
"""

#logger print info message
@singleton
class Telog():
    def __init__(self):
        self.logger = logging.getLogger("AppInfoText")
        self.logger.setLevel(logging.DEBUG)
        
        #set debugging file handler
        debugformat = '[%(asctime)s|{}|%(levelname)s|%(filename)s:%(funcName)s:%(lineno)s] %(message)s'. \
            format(self.logger.name)
        debugformatter = logging.Formatter(debugformat)
        debug_log_path = datetime.now().strftime("debug/Debugger %Y-%m-%d %H-%M-%S.log")
        debug_handler = logging.FileHandler(filename = debug_log_path, encoding="utf-8")
        debug_handler.setFormatter(debugformatter)
        debug_handler.setLevel(logging.DEBUG)

        #set file handler
        fileformat = '[%(asctime)s|{}|%(levelname)s|%(filename)s:%(funcName)s:%(lineno)s] %(message)s'. \
            format(self.logger.name)
        fileformatter = logging.Formatter(fileformat)
        file_log_path = datetime.now().strftime("log/AppLog %Y-%m-%d.log")
        file_handler = logging.FileHandler(filename = file_log_path, encoding="utf-8")
        file_handler.setFormatter(fileformatter)
        file_handler.setLevel(logging.INFO)

        #set console handler
        streamformat='%(asctime)s [%(levelname)s] %(message)s - %(filename)s:%(lineno)d'
        streamformatter = logging.Formatter(streamformat)
        stream_handler = logging.StreamHandler()
        stream_handler.setFormatter(streamformatter)
        stream_handler.setLevel(logging.DEBUG) #for test 
        
        #set telegram handler
        telformat= datetime.now().strftime("[%m/%d %H:%M:%S.%f] ") + '%(message)s'
        telformatter = logging.Formatter(telformat)
        telegram_handler = LogTelHandler()
        telegram_handler.setFormatter(telformatter)
        telegram_handler.setLevel(logging.INFO)

        # Add Formatters
        self.logger.addHandler(stream_handler)
        self.logger.addHandler(telegram_handler)
        self.logger.addHandler(file_handler)
        self.logger.addHandler(debug_handler)



#telegram handler
class LogTelHandler(logging.Handler):
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.telbot = telegram.Bot(token=keyManager.getJsonData("telegram_token"))
        self.chatid = keyManager.getJsonData("telegram_chatid")
    
    def emit(self, record):
        if self.formatter is None: 
            text = record.msg
        else:
            text = self.formatter.format(record)
        
        self.telbot.sendMessage(chat_id=self.chatid, text=text)


#거래 로그
@singleton
class TradeLog():
    def __init__(self):
        self.logger = logging.getLogger("TradeLog")
        self.logger.setLevel(logging.INFO)
        

        #set file handler
        fileformat = '[%(asctime)s] %(message)s'
        fileformatter = logging.Formatter(fileformat)
        file_log_path = datetime.now().strftime("log/TradeLog %Y-%m-%d.log")
        file_handler = logging.FileHandler(filename = file_log_path, encoding="utf-8")
        file_handler.setFormatter(fileformatter)
        file_handler.setLevel(logging.INFO)

        # Add Formatters
        self.logger.addHandler(file_handler)

telog.py

먼저 기본적으로 프로그램을 관리할 MainApp 클래스를 만들어줬다. 

class MainApp:
    def __init__(self):
        self.logger = Telog().logger
        self.logger.debug("Class: MainApp")
        self.kw = Kiwoom()

        #Account
        self.vr_bank = [] #분할매매 변수

        self.SetStart() #vrbank 변수를 초기화해줌
        self.StartApp() #키움 로그인하고 기본설정을 해주는 함수
        self.PriceSetting() #로그인한 정보를 가지고 가상변수를 초기화 / 콜백함수를 설정해주는 함수
        self.Trade() #실제 매매를 시작할 함수

 

MainApp.py

init 함수에서는 로그를 찍는 logger와 실제 키움API를 다루는 kw이라는 변수를 넣어주었다.

 

또한 현재 잔액에서 분할매매를 할 수 있도록 금액을 나누어주는 분할매매 변수또한 만들어주었다.

분할매매 변수는 Dict를 담고있는 List로 [{'사용여부': Boolean, '사용종목':String, '출금가능금액': int}]속성을 가지고있다.

 

 

 

 

 

간단한 조건식을 찾아서 자동으로 단타매매를 해주는 프로그램

알고리즘은 공개 X

깃허브주소: https://github.com/Yuris99/MySTradeBot

 

GitHub - Yuris99/MySTradeBot

Contribute to Yuris99/MySTradeBot development by creating an account on GitHub.

github.com

사용한 API: 키움API

사용한 IDE: VScode

 

블로그 업로드겸 햇갈리는 개념들 정리하고 코드 정리해서 업로드 예정

 

최종적으로는 C++ MFC를 이용하여 GUI화 시키는게 목표 (현재는 CUI환경)

 

* 위험: 패가가 망할수있음 따라하지 마시오 *

 

개발 알고리즘:

영웅문 실시간 조건검색으로 1차적으로 종목 탐색 ->

조건검색에 들어온종목을 가지고 웹 스크래핑을 사용하여 2차적으로 조건 확인 ->

모든 조건이 충족되면 분할계좌의 잔액으로 시장가매수 (잔액이 없으면 매수하지않음) ->

매수 완료되면 해당 종목을 실시간검색 ->

실시간검색중 해당종목이 매수가의 3% 이상 오르면 실시간검색 종료후 바로 시장가매도 ->

시장가 매도가 완료되면 분할계좌를 다시 추가함

 

참고한 코드 : https://github.com/happineer/system_trading 감사합니다

+ Recent posts