Skip to content

Backtesting.py - 파이썬으로 전략 백테스팅 하기

Published: at 오전 09:49

Table of contents

Open Table of contents

backtesting.py 란?

backtesting.py는 과거 전략에 대한 실행가능성 추론을 위한 백테스팅 라이브러리입니다.

backtrader의 비전을 따르며, 다음과 같은 특징을 가지고 있습니다.

시작하기

공식문서에 나와있는 예제를 보고 기본적인 사용법을 익힐 수 있습니다.

아래 코드는 단순하고 최적화되지 않은 이동평균선 교차 전략을 보여줍니다.

현금 10,000 달러와 수수료 및 슬리피지를 0.2% 고려하여 9년치의 Alphabet Inc 의 주식을 거래합니다.

매매 방식은 10일간의 종가의 이동 평균선이 20일간의 종가 이동평균선을 상향 돌파하면 매수하고, 20일간의 종가 이동평균선이 10일간의 종가 이동평균선을 상향 돌파하면 매도하는 전략입니다.

from backtesting import Backtest, Strategy
from backtesting.lib import crossover

from backtesting.test import SMA, GOOG


class SmaCross(Strategy):
    n1 = 10
    n2 = 20

    def init(self):
        close = self.data.Close
        self.sma1 = self.I(SMA, close, self.n1)
        self.sma2 = self.I(SMA, close, self.n2)

    def next(self):
        if crossover(self.sma1, self.sma2):
            self.buy()
        elif crossover(self.sma2, self.sma1):
            self.sell()


bt = Backtest(GOOG, SmaCross,
              cash=10000, commission=.002,
              exclusive_orders=True)

output = bt.run()
bt.plot()

코드 분석

Data

아래 Backtest 객체를 생성하여 백테스팅을 진행할 수 있습니다.

bt = Backtest(GOOG, SmaCross,
              cash=10000, commission=.002,
              exclusive_orders=True)

첫번째 인자로 들어가는 GOOG은 데이터 값이며 백테스트 내부에서 동작이 가능하도록 필드명을 일치시켜주는 과정이 필요합니다.

GOOG의 필드는 Date, Open, High, Low, Close, Volume로 구성되어 있습니다.

입력받은 데이터는 self.data로 접근이 가능하며, self.data.Close와 같이 필드명을 사용하여 데이터에 접근할 수 있습니다.

next 메서드 내에서 배열의 가장 마지막 값은 가장 최근 값입니다.

예를들어 아래와 같이 가장 최근 종가를 출력할 수 있습니다.

def next(self):
    close = self.data.Close
    print(close[-1]) # 가장 최근 종가

만약 데이터의 인덱싱이 필요하면 .s.df를 통해 접근할 수 있습니다.

# ex)
data.Close.s # Pandas series
data.df # DataFrame

Strategy

class SmaCross(Strategy):
    n1 = 10
    n2 = 20

    def init(self):
        close = self.data.Close
        self.sma1 = self.I(SMA, close, self.n1)
        self.sma2 = self.I(SMA, close, self.n2)

Strategy 클래스를 상속받아 전략 로직의 구현을 시작합니다.

initnext 메서드를 구현하여 초기화 및 매매 로직을 정의합니다.

self.I는 인디케이터를 생성하는 메서드입니다.

인디케이터는 주로 가격 움직임을 분석하고 거래 결정을 내리는 데 사용되는 계산된 데이터 시리즈입니다.

주요 인디케이터의 예

  1. 이동 평균 (Moving Average): 일정 기간 동안의 평균 가격을 계산하여 주가의 추세를 보여줍니다.
  2. 상대 강도 지수 (RSI, Relative Strength Index): 주가의 상승 및 하락 속도를 측정하여 과매수 또는 과매도 상태를 파악합니다.
  3. 볼린저 밴드 (Bollinger Bands): 주가의 변동성을 측정하며, 주가가 이동 평균을 중심으로 일정 범위 내에서 움직이는지를 보여줍니다.
  4. MACD (Moving Average Convergence Divergence): 두 개의 이동 평균 간의 차이를 이용하여 주가의 추세 변화를 파악합니다.

self.I로 생성된 인디케이터는 overlay=True 옵션이면 캔들차트에 겹쳐 표시되고, overlay=False 옵션이면 별도의 차트로 표시됩니다.

SMA는 테스트로 제공된 이동평균선 계산 함수입니다.

내부적으로 아래와 같이 구현되어 있습니다.

def SMA(arr: pd.Series, n: int) -> pd.Series:
    """
    Returns `n`-period simple moving average of array `arr`.
    """
    return pd.Series(arr).rolling(n).mean()

스트림 백테스팅

next() 메서드는 데이터 스트림을 처리하는 메서드입니다.

시계열 데이터에 대해 한 번에 하나의 데이터 포인트를 처리하며, 데이터 스트림을 통해 순차적으로 호출됩니다.

def next(self):
    if crossover(self.sma1, self.sma2):
        self.buy()
    elif crossover(self.sma2, self.sma1):
        self.sell()

crossover 함수는 두 인디케이터가 교차하는지 여부를 판단하는 함수입니다.

만약 종가가 이동평균선을 돌파하는지를 판단하고 싶다면 아래와 같이 사용할 수 있습니다.

>>> crossover(self.data.Close, self.sma)
True

매수 및 매도

buy()sell() 메서드는 매수와 매도를 수행하는 메서드입니다.

def buy(self, *, size=.9999, limit=None, stop=None, sl=None, tp=None)

size

limit

stop

sl

tp

def sell(self, *, size=.9999, limit=None, stop=None, sl=None, tp=None)

short 주문을 냅니다. 파라미터는 buy와 동일합니다.

결과값 확인

OUTPUT

Start                     2004-08-19 00:00:00
End                       2013-03-01 00:00:00
Duration                   3116 days 00:00:00
Exposure Time [%]                       94.27
Equity Final [$]                     68935.12
Equity Peak [$]                      68991.22
Return [%]                             589.35
Buy & Hold Return [%]                  703.46
Return (Ann.) [%]                       25.42
Volatility (Ann.) [%]                   38.43
Sharpe Ratio                             0.66
Sortino Ratio                            1.30
Calmar Ratio                             0.77
Max. Drawdown [%]                      -33.08
Avg. Drawdown [%]                       -5.58
Max. Drawdown Duration      688 days 00:00:00
Avg. Drawdown Duration       41 days 00:00:00
# Trades                                   93
Win Rate [%]                            53.76
Best Trade [%]                          57.12
Worst Trade [%]                        -16.63
Avg. Trade [%]                           1.96
Max. Trade Duration         121 days 00:00:00
Avg. Trade Duration          32 days 00:00:00
Profit Factor                            2.13
Expectancy [%]                           6.91
SQN                                      1.78
_strategy              SmaCross(n1=10, n2=20)

run() 메서드를 호출하면 백테스팅 결과를 확인할 수 있습니다.

백테스팅 결과에는 각종 지표들이 포함되어 있습니다.

시각화

plot() 메서드를 호출하면 백테스팅 결과를 시각화할 수 있습니다.

img.png