백테스팅 기본 예제
backtesting.backtesting API documentation
Module backtesting.backtesting Core framework data structures. Objects from this module can also be imported from the top-level module directly, e.g. from backtesting import Backtest, Strategy class Backtest (data, strategy, *, cash=10000, commission=0.0,
kernc.github.io
backtesting 라이브러리 설치
pip install backtesting
backtesting 라이브러리 임포트
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA, GOOG
- backtesting : 핵심 모듈
- import class : Backtest, Strategy, Order, Position, Trade
- Backtest(data, strategy, cash=10000, commission=0.0, margin=1.0, trade_on_close=False, hedging=False, exclusive_orders=False)
- (데이터프레임, Starategy class, 초기현금, 수수료, 증거금(레버리지), 종가거래, 헷징, 단일거래)
- .run() : 백테스팅 실행 메서드. 결과(통계)를 시리즈로 반환.
- .plot() : 백테스트 실행 플로팅
- Strategy : 투자 전략 기본 클래스. Strategy.init() , Strategy.next()에 자신의 전략을 정의하면 된다.
- def init(): 함수 안에는 indicator(지표)를 선언한다. indicator들을 self.I 로 감싼다.
- def next(): 함수 안에는 지표를 이용한 전략과 포지션 등을 설정한다.
- I : self.I(지표, 종가, 파라미터(기간))
- 이외에도 buy, sell 등
- backtesting.lib : 전략 클래스, 보조 기능 모음집
- backtesting.test : 테스트용 데이터 및 유틸리티
- 전역변수
- EURUSD : 2017년 4월부터 2018년 2월까지의 시간별 EUR/USD 외환 데이터 DataFrame.
- GOOG : 2004년부터 2013년까지 일간 NASDAQ:GOOG(Google) 주가 데이터의 DataFrame.
- fuction
- def SMA(arr, n) : n배열의 기간 단순 이동 평균을 반환 arr한다.
- 전역변수
backtesting 전략 클래스 생성
# 골든 크로스 매수 - 데드 크로스 매도
class SmaCross(Strategy): # 내 전략 (Strategy 상속)
def init(self):
price = self.data.Close # 데이터의 종가
self.ma1 = self.I(SMA, price, 10) # 지표 : 단기이평선
self.ma2 = self.I(SMA, price, 20) # 지표 : 장기이평선
def next(self): # 포지션 잡을 시점
if crossover(self.ma1, self.ma2): # crossover 조건 : 골든
self.buy() # 매수
elif crossover(self.ma2, self.ma1): # crossover 조건 : 데드
self.sell() # 매도
- 이동평균선 골든크로스 매수, 데드크로스 매도 전략
- init 함수에 10일 이평선, 20일 이평선 정의
- next 함수에는 crossover시에 포지션 잡기
- .sell() 메서드가 실행되면, 매수했던 주식을 청산(매도) 하면서 동시에 숏(공매도) 포지션을 잡게된다.
backtesting 실행
bt = Backtest(GOOG,
SmaCross,
commission=.002,
exclusive_orders=True)
stats = bt.run()
print(stats)
bt.plot()
- (구글데이터프레임, SmaCross 전략 Class, 수수료0.2%, 단일거래)
- .run()으로 백테스팅 실행 후 결과 반환
- .plot()으로 그래프 나타내기
fdr에서 크롤링한 데이터로 백테스팅하기
import FinanceDataReader as fdr
# 골든 크로스 매수 - 데드 크로스 매도
class SmaCross(Strategy):
def init(self):
price = self.data.Close
self.ma1 = self.I(SMA, price, 10)
self.ma2 = self.I(SMA, price, 20)
def next(self):
if crossover(self.ma1, self.ma2):
self.buy()
elif crossover(self.ma2, self.ma1):
self.sell()
# 005930 : 삼성전자
data = fdr.DataReader('005930')
bt = Backtest(data,
SmaCross,
cash=1000000,
commission=.002,
exclusive_orders=True)
stats = bt.run()
print(stats)
bt.plot()
- cash = 최초 자산
⇒ 단기 이평선과 장기 이평선의 날짜를 변경해보면서 테스트하기
숏 포지션 없이 매도만 사용하기
# 골든 크로스 매수 - 데드 크로스 매도
class SmaCross(Strategy):
def init(self):
price = self.data.Close
self.ma1 = self.I(SMA, price, 10)
self.ma2 = self.I(SMA, price, 20)
# buy 포지션으로 어떤 시점에 매수할지 시그널.
self.signal = self.I(lambda: np.repeat(np.nan, len(self.data.Close)), scatter=True)
def next(self):
if crossover(self.ma1, self.ma2):
self.position.close() # 포지션 청산
self.buy()
elif crossover(self.ma2, self.ma1):
self.position.close() # buy포지션 청산
# buy시점을 그려주는데 필요한 함수
def buy(self, *args, **kwargs):
self.signal[-1] = 1 # On buy, input 1 instead of existing NaN
super().buy(*args, **kwargs)
# 005930 : 삼성전자
data = fdr.DataReader('005930')
bt = Backtest(data,
SmaCross,
cash=1000000,
commission=.002,
exclusive_orders=True)
stats = bt.run()
print(stats)
bt.plot()
- lambda(매개변수, 표현식) : 함수를 한줄로 만들 수 있는 명령어. 매개변수 생략 가능
- np.repeat(복사할 값, 반복 횟수, 축방향) : 배열 반복 복사
- .position.close() : Position클래스의 close메소드. 활성된 거래 포지션 청산
- def buy : 매수 함수 실행
- super(). : 부모 클래스의 속성을 사용하고싶을 경우. 자식 클래스가 기존 메서드를 확장하되, 원래 구현을 재정의에 포함하고 싶을 때.
=> 골든크로스 신호 조건일때 buy함수 호출하고 data.Close길이만큼의 NaN배열에서 현재 마지막 NaN을 1로 바꿈으로 시그널을 표시한다.
비중 조절
# 골든 크로스 매수 - 데드 크로스 매도
class SmaCross(Strategy):
def init(self):
price = self.data.Close
self.ma1 = self.I(SMA, price, 10)
self.ma2 = self.I(SMA, price, 20)
self.signal = self.I(lambda: np.repeat(np.nan, len(self.data.Close)), scatter=True)
self.portion = 0.5 # 디폴트 : 0.9999
def next(self):
if crossover(self.ma1, self.ma2):
self.position.close()
self.buy(size=self.portion) # buy 호출 시에 portion적용
elif crossover(self.ma2, self.ma1):
self.position.close()
def buy(self, *args, **kwargs):
self.signal[-1] = 1 # On buy, input 1 instead of existing NaN
super().buy(*args, **kwargs)
# 005930 : 삼성전자
data = fdr.DataReader('005930')
bt = Backtest(data,
SmaCross,
cash=1000000,
commission=.002,
exclusive_orders=True)
stats = bt.run()
print(stats)
bt.plot()
- portion : 투자 비중. buy 호출 시에 인자로 들어간다.
- 수익률은 줄지만 MDD도 하락한다.
'퀀트' 카테고리의 다른 글
23. 래리 윌리엄스의 변동성 돌파 전략 (0) | 2023.01.13 |
---|---|
22. 켈리 베팅 (0) | 2023.01.12 |
20. 투자 전략을 과거 시장에 실험해보기 : 백테스트 (0) | 2023.01.09 |
19. 암호화폐 데이터 수집 및 상관관계 분석 (0) | 2023.01.09 |
18. FinanceDataReader를 이용한 섹터 분석 (0) | 2023.01.07 |