[파이썬] 코인 자동매매 프로그램 만들기 #3 - 전략 보완
개발/Python

[파이썬] 코인 자동매매 프로그램 만들기 #3 - 전략 보완

반응형


코인 자동매매 프로그램 만들기 #2의 백테스팅 코드에서 이어집니다

 

상승장 투자 전략

변동성이 심한 암호화폐 시장에서 조금 더 안정적인 수익을 위해 상승장일 때만 매매를 진행하는 전략을 추가해 보겠습니다

5일 이동평균선을 기준으로 당일 시가가 이동평균선 위일 경우에만, 즉 상승장일 경우에만 매매하도록 코드를 수정해 줍니다

먼저 거래일 기준 이전 5일간의 종가평균으로 5일 이동평균값을 구해줍니다

 # 5일 이동평균선
self.daily_data['ma5'] = self.daily_data['close'].rolling(window=5, min_periods=1).mean().shift(1)

 

상승장과 하락장 여부를 판단하는 컬럼을 추가하고 거래일 시가가 5일 이동평균보다 크면 True, 작으면 False를 저장합니다

# 상승장 여부
self.daily_data['bull'] = self.daily_data['open'] > self.daily_data['ma5']

 

이전 백테스팅 코드에서 매수 신호를 확인하던 코드에 이 상승장 여부 조건을 추가해줍니다

# 매수 신호 확인
self.buy_signal = np.where((row['targetPrice'] <= row['high']) & row['bull'], True, False) # 목표가에 달성한 경우 목표가에 매수해 다음날 시가에 매도한 것으로 판단 + 상승장 조건 추가

 

상승장 투자 전략을 추가하여 1년 정도의 데이터를 테스트해보겠습니다

  변동성 돌파 전략 변동성 돌파 + 상승장 투자 전략
수익률 1.13배 1.34배
최대낙폭 -23.40% -12.60%

변동성 돌파 전략만 사용하였을 때보다 수익률과 최대낙폭이 증가한 것을 확인할 수 있습니다

 

노이즈 분석

지금까지의 백테스팅 코드에서는 변동성 돌파 전략에서 돌파계수에 해당하는 K값을 0.5로 고정하여 계산하였습니다

이번에는 시장의 상황에 따라서 K값을 유동적으로 조절하여 백테스팅을 진행해 보겠습니다

K값을 유동적으로 조절하는데에는 noise ratio를 이용하도록 하겠습니다

noise ratio는 아래와 같이 구할 수 있습니다

noise = 1- 절대값(시가 - 종가) / (고가 - 저가)

 

노이즈는 차트 데이터 캔들에서 캔들 전체길이 대비 위아래 꼬리의 비율을 나타냅니다

위아래 꼬리비율이 짧다는 것은 차트가 한 방향으로 움직이고 있음을 의미합니다

따라서 목표매수가를 구할 때 사용했던 K값 대신 이 노이즈값을 사용해 보도록 하겠습니다

# 노이즈 계산 ( 1- 절대값(시가 - 종가) / (고가 - 저가) )
self.daily_data['noise'] = 1 - abs(self.daily_data['open'] - self.daily_data['close']) / (self.daily_data['high'] - self.daily_data['low'])
# 노이즈 20일 평균
self.daily_data['noise_ma20'] = self.daily_data['noise'].rolling(window=20, min_periods=1).mean()

# 변동폭 ( 고가 - 저가 )
self.daily_data['range'] = self.daily_data['high'] - self.daily_data['low']
# 목표매수가 ( 시가 + 변동폭 * K )
self.daily_data['targetPrice'] = self.daily_data['open'] + self.daily_data['range'].shift(1) * self.daily_data['noise_ma20']

 

마찬가지로 1년 간의 데이터로 테스트를 진행해 보겠습니다

  변동성 돌파 전략 변동성 돌파 + 노이즈 분석
수익률 1.13배 1.38배
최대 낙폭 -23.40% -21.03%

변동성 돌파 전략과 큰 차이를 보이지는 않지만 수익률이 약간 증가하고 최대 낙폭이 약간 하락하였습니다

 

정리

마지막으로 모든 전략을 조합하여 지난 1년간의 수익률과 최대 낙폭을 비교해 보도록 하겠습니다

  변동성 돌파 전략 변동성 + 상승장 변동성 + 노이즈 변동성 + 상승장 + 노이즈
수익률 1.13배 1.34배 1.38배 1.37배
최대 낙폭 -23.40% -12.60% -21.03% -9.80%

테스트 하는 기간에 따라서 조금씩 다른 결과값이 나오긴 하지만 대체적으로 안정적인 투자를 할 수 있도록
변동성 돌파 + 상승장 투자 + 노이즈 분석 전략을 이용하여 실제 매매 프로그램을 만들어 보도록 하겠습니다

 

최종코드

import pyupbit
import numpy as np

class backTesting :
    def __init__(self, daily_data, start_cash) :
        self.daily_data = daily_data # 일봉 데이터
        self.fee = 0.0011 # 수수료 ( calculate_fee.xlsx 참고 )
        self.buy_signal = False # 매수 신호
        
        self.start_cash = start_cash # 시작 자산
        self.current_cash = start_cash # 현재 자산
        self.highest_cash = start_cash # 자산 최고점
        self.lowest_cash = start_cash # 자산 최저점

        self.ror = 1 # 수익률
        self.accumulated_ror = 1 # 누적 수익률
        self.mdd = 0 # 최대 낙폭

        self.trade_count = 0 # 거래횟수
        self.win_count = 0 # 승리횟수

    def execute(self) :

        # 노이즈 계산 ( 1- 절대값(시가 - 종가) / (고가 - 저가) )
        self.daily_data['noise'] = 1 - abs(self.daily_data['open'] - self.daily_data['close']) / (self.daily_data['high'] - self.daily_data['low'])
        # 노이즈 20일 평균
        self.daily_data['noise_ma20'] = self.daily_data['noise'].rolling(window=20, min_periods=1).mean()

        # 변동폭 ( 고가 - 저가 )
        self.daily_data['range'] = self.daily_data['high'] - self.daily_data['low']
        # 목표매수가 ( 시가 + 변동폭 * K )
        self.daily_data['targetPrice'] = self.daily_data['open'] + self.daily_data['range'].shift(1) * self.daily_data['noise_ma20']
        # 5일 이동평균선
        self.daily_data['ma5'] = self.daily_data['close'].rolling(window=5, min_periods=1).mean().shift(1)
        # 상승장 여부
        self.daily_data['bull'] = self.daily_data['open'] > self.daily_data['ma5']

        for idx, row in df.iterrows() :
            # 매수 신호 확인
            self.buy_signal = np.where((row['targetPrice'] <= row['high']) & row['bull'], True, False) # 목표가에 달성한 경우 목표가에 매수해 다음날 시가에 매도한 것으로 판단 + 상승장 조건 추가

            # 거래횟수 계산
            self.trade_count += 1 if self.buy_signal else 0

            # 수익률 계산
            self.ror = row['close'] / row['targetPrice'] - self.fee if self.buy_signal else 1 # 다음날 시가와 당일 종가의 시간차이가 거의 없으므로 종가로 계산

            # 승리 횟수 계산
            self.win_count += 1 if self.ror > 1 else 0

            # 누적 수익률 계산
            self.accumulated_ror *= self.ror
            
            # 현재 자산 갱신
            self.current_cash *= self.ror

            # 자산 최고점 갱신
            self.highest_cash = max(self.highest_cash, self.current_cash)

            # 자산 최저점 갱신
            self.lowest_cash = min(self.lowest_cash, self.current_cash)

            # 최대 낙폭 계산
            dd = (self.highest_cash - self.current_cash) / self.highest_cash * 100
            self.mdd = max(self.mdd, dd)

        self.result()

    def result(self) :
        print('='*40)
        print('테스트 결과')
        print('-'*40)
        print('총 거래 횟수 : %s' %self.trade_count)
        print('승리 횟수 : %s' %self.win_count)
        print('승률 : %s' %(self.win_count / self.trade_count * 100))
        print('누적 수익률 : %s' %self.accumulated_ror)
        print('현재 잔액 : %s' % self.current_cash)
        print('최고 잔액 : %s' % self.highest_cash)
        print('최저 잔액 : %s' % self.lowest_cash)
        print('최대 낙폭 (MDD) : %s' % self.mdd)
        print('='*40)

df = pyupbit.get_ohlcv("KRW-BTC", count=365) # 일봉 데이터
backtest = backTesting(df, 1000000)
backtest.execute()

 

* 백테스팅에 대한 소스코드는 Github에서도 확인 가능합니다 - Github