본문 바로가기
Hobby/Hobby_4 - Coding

[코딩실전] 주식봇 도전 - 2. 변동성돌파 전략 예제 구현

by 와우멍 2021. 1. 25.

안녕하세요 와우멍입니다

오늘은 지난 포스팅에서 구현한 환경을 기반으로, 예제 전략 하나를 직접 돌리는 예제를 하나 마무리해보겠습니다.

2021/01/02 - [Hobby/Hobby_4 - Coding] - [코딩 실전] 주식봇 도전 - 1.파이썬 및 기타 환경 구축


 

 이번 글 중반까지는 유튜브 조코딩 님의 채널(www.youtube.com/channel/UCQNE2JmbasNYbjGAcuBiRRg)에 있는 영상을 보면서 수행한 과정과 겪은 시행착오를 함께 정리한 것입니다. 또, 조코딩님은 '파이썬 증권 데이터 분석' 책을 레퍼런스로 사용했다고 하셨습니다.


지난 글을 통해

0. 대신증권 계좌개설

1. 크레온 HTS 설치

2. 파이썬 환경 세팅

3. Slack으로 주가 조회하여 전송하는 봇 만들기

까지 완료를 했었지요.

 사실 이 다음에 실제 전력을 넣고 돌리는데 발생한 에러가.... 장중에 확인이 필요한 녀석이라 건드리질 못해서 오래 걸렸습니다 ㅠㅠㅠ 일단 다시 시작해보겠습니다!!!

출처: 조코딩 유튜브 채널

 오늘 사용할 전략은 기술적 투자 분야의 대가인 래리 윌리엄스(Larry R. Williams)가 사용했던 전략인 '변동성 돌파 전략' 입니다.

 간단하게 요약해보면 주가의 움직임이 일정수준 이상을 뛰어넘는 강한 상승세를 돌파 신호로 파악하고 상승하는 추세를 따라가며 짧은 수익을 실현하는 단기트레이딩 전략입니다. 

 간단히 요약하면 다음과 같습니다.

a. 변수 정의: 전일의 (고가 - 저가)를 변동폭이라 정의

  변동폭 = 전일 고가 - 전일 저가

b. 돌파신호 정의: 당일 장중 가격이 시가 +  변동폭 * K를 넘을 경우 매수.

  If 현재 가격 > 당일 시가 + 변동폭 * K, then 매수 (K 작을수록 변동성 작아도 매수가능하고, 이 K를 세팅하는 것이 핵심이겠지요..!!)

c. 매도 청산: 특정 시간 기준으로 매도(여기서는 종가)

 더 자세한 것은 이것을 구현한 자동매매 서비스를 구현하는 아래 사이트들에 자세히 설명이 되어 있습니다. 저도 나중에 이 페이지를 통해서 공부를 더 해보려 합니다 :-)

ldgeao99.tistory.com/443?category=875616

 

단기 변동성 돌파 전략(헤이비트 Advanced 1.0 버전)

헤이비트의 advance 1.0 전략은 이전의 Basic 버전과 비교하여 MDD와 수익률 면에서 크게 개선된 버전이다. 전략개요 후보 암호화폐 : 5종 K값 : 최근 20일 간의 노이즈 비율의 평균 값 배팅비율 : (이동

ldgeao99.tistory.com

support.heybit.io/ko/articles/2297782-%ED%97%A4%EC%9D%B4%EB%B9%84%ED%8A%B8%EC%9D%98-%EB%B3%80%EB%8F%99%EC%84%B1-%EB%8F%8C%ED%8C%8C-%EC%A0%84%EB%9E%B5-%EC%83%81%EC%84%B8-%EC%95%88%EB%82%B4

 

헤이비트의 '변동성 돌파 전략' 상세 안내

헤이비트 핵심 트레이딩 전략인 '변동성 돌파'에 대해 상세 안내 합니다.

support.heybit.io

 

 이 전략 하나도 시작부터 파생까지 알아보려면 또 엄청난 주제가 되겠지만, 일단은 이 정도로만 이해하고 원래 목적으로 돌아와 예제와 함께 코드를 구현하고 돌려보는 것에 초점을 맞춰보겠습니다. 

**전략을 직접 Zero base부터 시작해서 구현까지 하는 것은 허들이 높습니다(개인적으로). 이렇게 마음을 먹으면 시작에서 넘어야 할 첫산이 너무 높아서 좌절하기 십상이지요.. 하지만 우리는 언제나 그래왔듯이 답을 찾을 것입니다. 왜냐하면 이 세상에는 똑똑한 사람들이 많고, 그 사람들이 이미 많은 것들을 만들어놨습니다. 저희가 해야하는 것은 필요한 것을 찾아 이용/활용하는 것입니다..!!

 이 전략도 아래의 사이트에 구현되어 있는 것을 가져와서 우선 사용하고, 추후 공부해가면서 나만의 전략으로 튜닝해나가면 됩니다. 이 중 08_Volatility_Breakout이 변동성돌파전략입니다.

github.com/INVESTAR/StockAnalysisInPython

 

INVESTAR/StockAnalysisInPython

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

github.com


 그럼 이걸 돌리기 전에 간단히 내용을 파악해보고, 우리가 돌릴 수 있게 파라미터를 수정해보겠습니다.

 이 코드는 11개의 함수Main body로 구성되어 있습니다. 시스템 관련된 것은 이용만 하고, 우리가 수정해야할 부분만 위주로 살펴보지요.

  함수 내용
1 def dbgout(message):  입력받은 message를 파이썬 셀과 슬랙으로 출력
2 def printlog(message, *args): 입력받은 message를 파이썬 셀로 출력
3 def check_creon_system(): 크레온 시스템 연결 상태 점검
4 def get_current_price(code): 입력받은 종목(code)의 현재가, 매수호가, 매도호가를 반환
5 def get_ohlc(code, qty): 입력받은 종목(code)의 OHLC 가격정보를 qty 수만큼 반환
6 def get_stock_balance(code): 입력받은 종목(code)의 종목명과 수량을 반환
7 def get_current_cash(): 주문 가능한 금액(증거금 100%)을 반환
8 def get_target_price(code): 매수 목표가를 반환
9 def get_movingaverage(code, window): 입력받은 종목(code)의 이동평균가격을 반환
10 def buy_etf(code): 입력받은 종목(code)을 최유리 지정가로 매수
11 def sell_all(): 보유한 종목들을 최유리 지정가 조건으로 매도

 이 전략에서는 8번의 변동성돌파가격9번에서 유도된 이동평균선가격 두개를 조합해서 매수조건을 세웁니다. 그러니 전략을 개량하고 싶다면, 이 두개 함수를 수정하거나, 조건을 추가하기 위한 함수를 작성해주면 되겠지요(한가지 전략에만 의존하지 말고, 여러 조건을 섞어서 안전하게)

 현재의 get_target_price에는 오늘 시가 + (전일 고가-저가)*0.5를 목표가로 설정되어 있습니다.

 자, 그럼 이 함수들을 불러다가 코드를 돌아가게 하는 Main body를 한번 살펴보겠습니다.


Main Body 부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
        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)
            t_start = t_now.replace(hour=9, minute=5, second=0, microsecond=0)
            t_sell = t_now.replace(hour=15, minute=15, second=0, microsecond=0)
            t_exit = t_now.replace(hour=15, minute=20, second=0, microsecond=0)
            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:
                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:
                    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) + '`')
cs

 

3 Line: Symbol_list에는 종목 후보들을 채워넣으면 된다. - 이 종목을 대상으로 매매가 이뤄짐

5 Line: bought_list는 코드 돌면서 매수한 종목들

6~7 Line: 종목수와 각 종목별 구매할 비율을 정의 (세금등 기타 비용때문에 마진을 조금 잡아주자!)

9~11 Line: 보유 종목 조회(get_stock_balance)후 증거금 확인하여(get_current_cash) 주문 가능 금액을 total cah에 입력하고, 종목별로 매수량을 결정

18~46 Line: while문은 코드가 돌아가는 시간을 정해주는 loop입니다.

 - 3초마다(46 Line) 현재 시간을 확인하며(19 Line) 

 - 요일이 주말이면 바로 종료시키고(25~27 Line)

 - 9시 5분 전이면 대기 (28~30 Line)

 - 9시 5분과 3시 15분 사이에는(31~38 Line) 사려고 하던 종목의 가격을 체크하여 조건이 맞으면 매수(32~35 Line)

 - 정규장이 종료되는 3시 20분에는 종료(43~45L Line)

 - 잔고는 매시 30분마다 출력(36~38 Line)


buy_etf 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def buy_etf(code):
    """인자로 받은 종목을 최유리 지정가 FOK 조건으로 매수한다."""
    try:
        global bought_list  # 함수 내에서 값 변경을 하기 위해 global로 지정
        if code in bought_list:  # 매수 완료 종목이면 더 이상 안 사도록 함수 종료
            # printlog('code:', code, 'in', bought_list)
            return False
        time_now = datetime.now()
        current_price, ask_price, bid_price = get_current_price(code)
        target_price = get_target_price(code)  # 매수 목표가
        ma5_price = get_movingaverage(code, 5)  # 5일 이동평균가
        ma10_price = get_movingaverage(code, 10)  # 10일 이동평균가
        buy_qty = 0  # 매수할 수량 초기화
        if ask_price > 0:  # 매수호가가 존재하면
            buy_qty = buy_amount // ask_price
        stock_name, stock_qty = get_stock_balance(code)  # 종목명과 보유수량 조회
        if current_price > target_price and current_price > ma5_price and current_price > ma10_price:
            printlog(stock_name + '(' + str(code) + ') ' + str(buy_qty) +'EA : ' + str(current_price) + ' meets the buy condition!`')
            cpTradeUtil.TradeInit()
            acc = cpTradeUtil.AccountNumber[0]  # 계좌번호
            accFlag = cpTradeUtil.GoodsList(acc, 1)  # -1:전체,1:주식,2:선물/옵션
            # 최유리 FOK 매수 주문 설정
            cpOrder.SetInputValue(0"2")  # 2: 매수
            cpOrder.SetInputValue(1, acc)  # 계좌번호
            cpOrder.SetInputValue(2, accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
            cpOrder.SetInputValue(3, code)  # 종목코드
            cpOrder.SetInputValue(4, buy_qty)  # 매수할 수량
            cpOrder.SetInputValue(7"2")  # 주문조건 0:기본, 1:IOC, 2:FOK
            cpOrder.SetInputValue(8"12")  # 주문호가 1:보통, 3:시장가
            # 5:조건부, 12:최유리, 13:최우선
            # 매수 주문 요청
            ret = cpOrder.BlockRequest()
            printlog('최유리 FoK 매수 ->', stock_name, code, buy_qty, '->', ret)
            if ret == 4:
                remain_time = cpStatus.LimitRequestRemainTime
                printlog('주의: 연속 주문 제한에 걸림. 대기 시간:', remain_time / 1000)
                time.sleep(remain_time / 1000)
                return False
            time.sleep(2)
            printlog('현금주문 가능금액 :', buy_amount)
            stock_name, bought_qty = get_stock_balance(code)
            printlog('get_stock_balance :', stock_name, stock_qty)
            if bought_qty > 0:
                bought_list.append(code)
                dbgout("`buy_etf(" + str(stock_name) + ' : ' + str(code) +
                       ") -> " + str(bought_qty) + "EA bought!" + "`")
    except Exception as ex:
        dbgout("`buy_etf(" + str(code) + ") -> exception! " + str(ex) + "`")
cs

5~7 Line: 종목별로 입력을 받아서 bought_list에 있으면(이미 있는 종목이면) 패스

8~13 Line: 없으면 get_current_price로 종목의 가격들 추출(매수 평균 가격, 이평선 가격들 등)

17~ Line:  target_price(변동성돌파 가격)와 5일 이평선, 10일 이평선을 함께 고려해서 구매여부를 결정 (5일 이평선, 10일 이평선, 변동성돌파가격보다 높으면 FOK방식으로 매수)


 코드를 이렇게 두고, Creon Plus를 실행한 후 코드를 돌리면 위의 코드가 돌아가며 가격을 감시하고 매수와 매도를 진행할 것 입니다..!! 간단하지요??

중요한 것은 Creon plus도, 파이썬을 실행하기 위한 프로그램(command 창이든 pycharm같은 interpret든)은 꼭 관리자 권한으로 실행시켜야 한다는 것입니다..!!

**관리자 권한으로 실행하지 않았을 경우 - 아래와 같은 에러메세지 반환

**관리자 권한으로 옳게 실행한 경우 - 계좌정보가 제대로 조회됨


 다음 글에서는 아직 손수 해야하는 부분인 '아침에 일어나서 크레온 플러스를 켜고' '코드를 실행시켜준다' 라는 부분까지 자동화시키는 방법에 대해서 포스팅하겠습니다.

댓글