Table of contents
backtesting.py 란?
backtesting.py는 과거 전략에 대한 실행가능성 추론을 위한 백테스팅 라이브러리입니다.
backtrader의 비전을 따르며, 다음과 같은 특징을 가지고 있습니다.
- 외환, 주식, 암호화폐 등 다양한 시장에 대한 백테스팅을 지원합니다.
- TA-Lib 또는 Tulip 과 같은 기술 분석 라이브러리와 호환됩니다.
- 인터렉티브한 시각화 도구를 제공합니다.
- Pandas, NumPy, Bokeh와 같은 라이브러리 기반으로 구성돼 매우 빠르고 편리합니다.
- 시그널 기반 백테스팅, 스트리밍 기반 백테스팅 두 가지 방식을 지원합니다.
- 마켓 타이밍, 스윙 트레이딩, 자금관리, 손절 및 익절 등 고급 API를 지원합니다.
시작하기
공식문서에 나와있는 예제를 보고 기본적인 사용법을 익힐 수 있습니다.
아래 코드는 단순하고 최적화되지 않은 이동평균선 교차 전략을 보여줍니다.
현금 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
클래스를 상속받아 전략 로직의 구현을 시작합니다.
init
과 next
메서드를 구현하여 초기화 및 매매 로직을 정의합니다.
self.I
는 인디케이터를 생성하는 메서드입니다.
인디케이터는 주로 가격 움직임을 분석하고 거래 결정을 내리는 데 사용되는 계산된 데이터 시리즈입니다.
주요 인디케이터의 예
- 이동 평균 (Moving Average): 일정 기간 동안의 평균 가격을 계산하여 주가의 추세를 보여줍니다.
- 상대 강도 지수 (RSI, Relative Strength Index): 주가의 상승 및 하락 속도를 측정하여 과매수 또는 과매도 상태를 파악합니다.
- 볼린저 밴드 (Bollinger Bands): 주가의 변동성을 측정하며, 주가가 이동 평균을 중심으로 일정 범위 내에서 움직이는지를 보여줍니다.
- 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
- 매수할 주식의 비율을 나타내며, 기본값은 0.9999입니다.
- 음수인 경우 short position을 의미합니다.
- 0과 1사이인 경우 현재 사용가능한 현금의 비율을 나타냅니다.
- 1보다 크거나 같은경우 절대적인 주식 수를 나타냅니다.
limit
- 지정가 주문의 가격을 나타냅니다.
None
인 경우 시장가 주문을 의미합니다.
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()
메서드를 호출하면 백테스팅 결과를 확인할 수 있습니다.
백테스팅 결과에는 각종 지표들이 포함되어 있습니다.
Start
: 백테스팅 시작일End
: 백테스팅 종료일Duration
: 백테스팅 기간Exposure Time [%]
: 전체 기간 중 노출된 시간의 비율Equity Final [$]
: 최종 자본금Equity Peak [$]
: 최대 자본금Return [%]
: 수익률Buy & Hold Return [%]
: Buy & Hold 전략의 수익률Return (Ann.) [%]
: 연간 수익률Volatility (Ann.) [%]
: 연간 변동성Sharpe Ratio
: 샤프 지수Sortino Ratio
: 소르티노 지수Calmar Ratio
: 칼마 지수Max. Drawdown [%]
: 최대 손실률Avg. Drawdown [%]
: 평균 손실률Max. Drawdown Duration
: 최대 손실 기간Avg. Drawdown Duration
: 평균 손실 기간# Trades
: 거래 횟수Win Rate [%]
: 승률Best Trade [%]
: 최대 수익률Worst Trade [%]
: 최대 손실률Avg. Trade [%]
: 평균 거래 수익률Max. Trade Duration
: 최대 거래 기간Avg. Trade Duration
: 평균 거래 기간Profit Factor
: 수익 요인Expectancy [%]
: 기대 수익률SQN
: SQN 지수_strategy
: 전략 클래스
시각화
plot()
메서드를 호출하면 백테스팅 결과를 시각화할 수 있습니다.
-
가장 위의 차트는 날짜에 따른 자본금의 변화를 보여줍니다.
-
두 번째 차트는 매매 포지션 수와 승/패 여부 그리고 그에 따른 수익률(profit/loss)을 보여줍니다.
-
세 번째 차트는 종가와 인디케이터 정보(이동평균선)을 보여줍니다.
-
또한 초록선의 이동과 빨간선의 이동은 포지션 보유 기간동안 가격의 이동을 보여줍니다.