'작업 스케줄러' 태그의 글 목록 :: Horizontal Grays S2

지난 포스팅에서 무엇을 살것인가에서 ETF를, 그리고 ETF 종목의 조회를 어떻게 하는가 봤다

 

그리고 얼마에 살 것인가는 변동성 돌파 전략에서 K값의 결정에 따라 산다 정도로 설명했다.

 

일단 프로그램 전체를 가져와보자.

 

역시 기본적으로 책인 [파이썬 증권 데이터 분석] 에서 제공하는 github의 소스를 이용한다.

StockAnalysisInPython/ch08_03_EtfAlgoTrader.py at master · INVESTAR/StockAnalysisInPython · GitHub

 

GitHub - INVESTAR/StockAnalysisInPython

Contribute to INVESTAR/StockAnalysisInPython development by creating an account on GitHub.

github.com

다만 slack으로 메시지 보내는 방법은 수정되어야 해서 전에 slack 관련 포스팅과 같은 방식으로 수정하였다.

아래와 같이 post_message 함수 정의해서 사용했다.

def post_message(token, channel, text):
    response = requests.post("https://slack.com/api/chat.postMessage",
        headers={"Authorization": "Bearer "+token},
        data={"channel": channel,"text": text}
    )
    print(response)

내가 직접 짠 소스가 아니므로 여기에 모두 포스팅 하지는 못하겠고 이정도 수정은 필요하신 분이시라면 어렵지 않게 하실수 있을것이라 생각한다.

 

같은 과정으로 vscode 실행하고 새 파이썬 파일 만들어서 소스 복붙하고 위의 post_message 부분으로 수정하면 된다.

 

일단 나의 경우 AutoTrade.py라고 저장하였고. 이 AutoTrade를 실행하기전에 이전포스팅에서 설명했던 AutoConnect.py를 먼저 실행해서 CREON Plus와 연결을 한다. 그리고 이 AutoTrade.py를 실행하는 것이다.

 

우선 main 부분만 훑어 보자.

if __name__ == '__main__':
    try:
        symbol_list = ['A122630', 'A252670', 'A233740', 'A250780', 'A225130',
             'A280940', 'A261220', 'A217770', 'A295000', 'A176950'] #매수 대상 종목 리스트
        bought_list = []     # 매수 완료된 종목 리스트
        target_buy_count = 5 # 매수할 종목 수
        buy_percent = 0.19   #종목별 매수 비율 5 종목 * 19%씩 매수 = 95% 매수하고 5% 금액 남게 된다.
        printlog('check_creon_system() :', check_creon_system())  # 크레온 접속 점검
        stocks = get_stock_balance('ALL')      # 보유한 모든 종목 조회
        total_cash = int(get_current_cash())   # 100% 증거금 주문 가능 금액 조회
        buy_amount = total_cash * buy_percent  # 종목별 주문 금액 계산
        printlog('100% 증거금 주문 가능 금액 :', total_cash)
        printlog('종목별 주문 비율 :', buy_percent)
        printlog('종목별 주문 금액 :', buy_amount)
        printlog('시작 시간 :', datetime.now().strftime('%m/%d %H:%M:%S'))
        soldout = False

        while True:
            t_now = datetime.now() #현재시간
            t_9 = t_now.replace(hour=9, minute=0, second=0, microsecond=0)  # 9:00
            t_start = t_now.replace(hour=9, minute=5, second=0, microsecond=0) # 9:05 시작 시간
                                                                                      참고로 유동성 공급자 활동시간 9:05 ~ 15:20
            t_sell = t_now.replace(hour=15, minute=15, second=0, microsecond=0) # 15:15 매도 시간
            t_exit = t_now.replace(hour=15, minute=20, second=0,microsecond=0) # 15:20 종료 시간
            today = datetime.today().weekday() #금일 요일
            if today == 5 or today == 6:  # 토요일이나 일요일이면 자동 종료
                printlog('Today is', 'Saturday.' if today == 5 else 'Sunday.')
                sys.exit(0)
            if t_9 < t_now < t_start and soldout == False:
                                            #9:00~9:05 사이에 시작하기전 혹시나 남은 주식이 있으면 다 매도
                soldout = True
                sell_all()
            if t_start < t_now < t_sell :  # AM 09:05 ~ PM 03:15 : 매수
                for sym in symbol_list: # 매수대상 종목리스트에서
                    if len(bought_list) < target_buy_count: #매수 완료한 종목이 목표수보다 작으면
                        buy_etf(sym)    #매수
                        time.sleep(1)
                if t_now.minute == 30 and 0 <= t_now.second <= 5:   #30분마다 잔고 등의 정보를 알림
                    get_stock_balance('ALL')
                    time.sleep(5)
            if t_sell < t_now < t_exit:  # PM 03:15 ~ PM 03:20 : 일괄 매도
                if sell_all() == True:
                    dbgout('`sell_all() returned True -> self-destructed!`')
                    sys.exit(0)
            if t_exit < t_now:  # PM 03:20 ~ :프로그램 종료
                dbgout('`self-destructed!`')
                sys.exit(0)
            time.sleep(3)
    except Exception as ex:
        dbgout('`main -> exception! ' + str(ex) + '`')

일단 설명할 부분은 주석으로 달아놨고

여기서 매수할 종목 대상 리스트는 ETF든 KOSPI든 KOSDAQ이든 미리 알아서 집어넣어두면 된다.

그리고 얼마에 매수할것인지에 대한 결정은 buy_etf()함수에서 불러오는

아래의 get_target_price()에 함수에서 설정되는 K값 0.5로 결정된다.

def get_target_price(code):
    """매수 목표가를 반환한다."""
    try:
        time_now = datetime.now()
        str_today = time_now.strftime('%Y%m%d')
        ohlc = get_ohlc(code, 10)
        if str_today == str(ohlc.iloc[0].name):
            today_open = ohlc.iloc[0].open
            lastday = ohlc.iloc[1]
        else:
            lastday = ohlc.iloc[0]                                      
            today_open = lastday[3]
        lastday_high = lastday[1]
        lastday_low = lastday[2]
        target_price = today_open + (lastday_high - lastday_low) * 0.5
        return target_price
    except Exception as ex:
        dbgout("`get_target_price() -> exception! " + str(ex) + "`")
        return None

현재 K값을 0.5 상수로 두고 있는데 이를 변수로 두던 상수로 두던 값 역시 수정하면 된다.
다만 K값이 낮으면 매수가 쉽고 높으면 어렵다..... 당연하군 ^^;

실제로 이 프로그램으로 매수/매도를 해보질 않았다.
일단 함수단위 테스트를 먼저 해보자.

바로 위 get_target_price()에 KODEX 200(069500) 을 한번 테스트 해보자. 

일단 test2.py 라는 임의로 파이썬 파일을 만들고 AutoTrade.py에서 일부분만 테스트를 위해 좀 잘라서

다음과 같이 테스트프로그램을 작성해서 실행해본다.

import os, sys, ctypes
import win32com.client
import pandas as pd
from datetime import datetime
import requests
import time, calendar

# 크레온 플러스 공통 OBJECT
cpCodeMgr = win32com.client.Dispatch('CpUtil.CpStockCode')
cpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
cpTradeUtil = win32com.client.Dispatch('CpTrade.CpTdUtil')
cpStock = win32com.client.Dispatch('DsCbo1.StockMst')
cpOhlc = win32com.client.Dispatch('CpSysDib.StockChart')
cpBalance = win32com.client.Dispatch('CpTrade.CpTd6033')
cpCash = win32com.client.Dispatch('CpTrade.CpTdNew5331A')
cpOrder = win32com.client.Dispatch('CpTrade.CpTd0311')  

def get_ohlc(code, qty):
    """인자로 받은 종목의 OHLC 가격 정보를 qty 개수만큼 반환한다."""
    cpOhlc.SetInputValue(0, code)           # 종목코드
    cpOhlc.SetInputValue(1, ord('2'))        # 1:기간, 2:개수
    cpOhlc.SetInputValue(4, qty)             # 요청개수
    cpOhlc.SetInputValue(5, [0, 2, 3, 4, 5]) # 0:날짜, 2~5:OHLC
    cpOhlc.SetInputValue(6, ord('D'))        # D:일단위
    cpOhlc.SetInputValue(9, ord('1'))        # 0:무수정주가, 1:수정주가
    cpOhlc.BlockRequest()
    count = cpOhlc.GetHeaderValue(3)   # 3:수신개수
    columns = ['open', 'high', 'low', 'close']
    index = []
    rows = []
    for i in range(count):
        index.append(cpOhlc.GetDataValue(0, i))
        rows.append([cpOhlc.GetDataValue(1, i), cpOhlc.GetDataValue(2, i),
            cpOhlc.GetDataValue(3, i), cpOhlc.GetDataValue(4, i)])
    df = pd.DataFrame(rows, columns=columns, index=index)
    return df

def get_target_price(code):
    """매수 목표가를 반환한다."""
    try:
        time_now = datetime.now()
        str_today = time_now.strftime('%Y%m%d')
        ohlc = get_ohlc(code, 10)
        print(ohlc)
        if str_today == str(ohlc.iloc[0].name):
            today_open = ohlc.iloc[0].open
            lastday = ohlc.iloc[1]
        else:
            lastday = ohlc.iloc[0]                                      
            today_open = lastday[3]
        lastday_high = lastday[1]
        lastday_low = lastday[2]
        target_price = today_open + (lastday_high - lastday_low) * 0.5
        return target_price
    except Exception as ex:
        print("`get_target_price() -> exception! " + str(ex) + "`")
        return None

target_price = get_target_price('A069500')
print("목표가 = " + str(target_price))

2022년 2월 24일 현재 실행결과 아래와 같이 나온다.

즉 KODEX200 의 열흘간 시작가, 최고가, 최저가, 종가 가 출력되고 이를 토대로

K값이 0.5일때 목표가는 \36642.5 원이라는 것이다.

그럼 이제 K값을 변화해서 살펴보자.

K = 0.3 일 때
K = 0.7 일 때

자 각각 K가 0.3, 0.7로 바꿔서 놓고 실행해보면 목표가가 0.3일 때 36561.5원, 0.7일 때 36723.5원 으로 바뀌는 것을 볼 수 있다.

K = 0.3 K = 0.5 K = 0.7
\36561.5 \36642.5 \36723.5

이 매수조건에 현재가가 5일 이동평균선과 10일 이동평균선 위에 있어야 한다는 조건이 추가되어 있는데

이 부분이 buy_etf() 함수에서 get_movingaverage() 함수를 불러 이동평균을 구하고 조건을 추가하는식이다.

...
        ma5_price = get_movingaverage(code, 5)   # 5일 이동평균가
        ma10_price = get_movingaverage(code, 10) # 10일 이동평균가
...
        if current_price > target_price and current_price > ma5_price and current_price > ma10_price:  
         


자 그렇다면 이제

1. 무엇을 살 것인가는 ETF 중에서 또는 관심가는 종목 중 골라서 종목코드를 매수대상 종목리스트 symbol_list에 넣어두면 되고

2. 얼마에 살 것인가는 위에 조건처럼 K값과 이동평균선 조건에 따라서 매수하고 장마감에 매도

이런 아주 기초적인 조건으로 매매는 시작할 수 있게 되겠다.

 

그러고보니 매수대상을 종목리스트에 넣어두는 것은 자동은 아니네 --a 

그리고 추가로 설명할 부분이 주문조건과 주문호가가 있는데 프로그램에서는 ETF를 기본으로 하고 있다보니

거래량이 다른 주식대비 많지 않아 비싼 가격을 지불하지 않도록 [최유리 FOK 매수 주문] 방법으로 매수하고

매도는 [최유리 IOC 매도] 방법으로 매도하도록 되어있다.


비록 아직은 매수대상 종목리스트는 수작업으로 하지만 프로그램의 실행은 윈도우가 알아서 실행하도록 해보자.

 

우선 PC가 켜지는 것은

 1) 켜두거나

 2) BIOS가 지원한다면 BIOS에서 해야하는데 각 회사마다 다르겠지만

   기본적으로 BIOS진입 - Advanced - ACPI Configuration - RTC Alarm Power On 에서 예약 부팅 을 해두면 된다.

 

그리고 PC가 켜지면 윈도우키 + S 를 누른뒤 "작업 스케줄러"로 검색/실행을 하면 아래와 같이 작업스케줄러가 실행이된다.

그리고 좌측 창의 [작업 스케줄러 라이브러리] 클릭하면 가운데 창이 아래 그림처럼 나오는데 그 창에서

우측 클릭 - 새 작업 만들기

그러면 나타나는 창에서

이름과 설명은 원하는 대로 작성하고 형광펜의 '가장 높은 수준의 권한으로 실행' 에 체크

그리고 트리거 탭으로 가서 '새로만들기' 누른뒤 나타나는 창에서 '매일' '오전 8:20' 그리고 아래에 '사용'에 체크

그럼 트리거가 하나 만들어진다.

그리고 동작탭으로 넘어가서 역시 '새로만들기'

설정의 프로그램/스크립트 에서는 찾아보기 버튼을 누른뒤 python.exe가 있는 폴더에 들어가서 선택해주고

인수추가에는 실행할 python 파일 이름 위 그림엔 AutoConnect.py 시작위치는 AutoConnect.py가 있는 폴더의 경로
그리고 확인을 누르면 된다.

 

그리고 같은 방법으로 8:30 정도에 AutoTrade.py도 등록해주면 윈도우즈의 작업스케줄러가 알아서 실행한다.

 


- 지금까지 대부분의 정보는 "파이썬 증권 데이터 분석" 이라는 책의 내용을 보고 정리한 내용이며 일부 현재 동작하지 않는 Slack의 requests 같은 부분은 내가 검색해서 수정하였음을 미리 밝힌다. 그래서 세세한 소스는 직접 책에서 제공하는 github로 링크하였다.

 

- 현재 나도 아직 이 방식으로 주식거래를 시작하지 않았다.  '주식투자자동화' 관련 포스팅은 여기서 끝나는게 아니고

일단 서둘러 해보고 싶은 분들을 위해 1부? 여튼 일단 시작할 수 있는 정도까지 서둘러 포스팅한다.

나는 여기서 종목을 골라서 리스트에 넣는 부분과 K값을 조금씩 조절하고 그에 따른 결과를 자동으로 기록하는 부분까지는 조금 더 프로그램하고 작은 금액이라도 실제 투자해볼 생각이다.

그 부분은 차근차근 여기다 포스팅하고 수익이 나던 손해가 나던 일정 기간 후 역시 결과도 포스팅 해 볼 예정이다.

 

일단은 여기까지!!

+ Recent posts