"금융 파이썬 쿡북 Ch2. 파이썬에서의 기술적 분석 "의 내용을 기반으로 작성하였습니다. (실습 깃헙)
1. 볼린저 밴드 (Bollinger Band)란?
볼린저 밴드란 시간에 따른 특정 자산 가격과 변동성의 정보를 도출하는데 사용하는 통계적 방법이다.
볼린저 밴드를 구하려면 지정된 윈도우(일반적으로 20일)를 사용해 시계열 가격의 이동 평균과 표준편차를 계산해야 한다.
이후 상, 하한 밴드를 이동 평균 상, 하의 표준 편차의 K배(일반적으로 2배)로 설정한다.
이 때 밴드는 변동성이 증가하면 넓어지고 변동성이 감소하면 축소한다.
더욱 자세한 설명은 이 곳에서 확인 가능하다.
2. 기본 전략
이 글에서 구현할 단순 거래 전략은 다음과 같다.
- 가격이 아래 볼린저 밴드를 상향으로 교차할 때 매수하라
- 가격이 위 볼린저 밴들를 하향 교차할 때 매도하라
- 올인 전략 - 매수 전략을 수립할 때 최대한 많은 주식을 매수하라
- 공매도는 허용되지 않는다.
가장 기본적인 전략으로, 개발자 존 볼린저 또한 밴드 자체의 폭이 축소되고 밀집되는 구간을 거차고 난 후 상단 밴드를 돌파할 때 주식을 매입하고 하단 밴드를 벗어날 때 주식을 공매도 하는 것을 추천하였다.
볼린저 밴드에서 폭이 좁아지는 것 = 주가 안정기 이고 그 후 추세를 결정하는데 상단 밴드를 건드리면 상단 돌파, 하단 밴드를 건드리면 하향 추세로 간다는 가정이 위 방식의 배경이다.
3. 구현
기본적인 내용은 이전 backtrader 백테스트에서 설명하였고 이번 글에서는 추가, 변경된 부분만 확인한다. (환경 세팅도 동일)
https://needmorecaffeine.tistory.com/21
class BBand_Strategy(bt.Strategy):
params = (('period', 20),
('devfactor', 2.0),)
def __init__(self):
# keep track of close price in the series
self.data_close = self.datas[0].close
self.data_open = self.datas[0].open
# keep track of pending orders/buy price/buy commission
self.order = None
self.price = None
self.comm = None
# add Bollinger Bands indicator and track the buy/sell signals
self.b_band = bt.ind.BollingerBands(self.datas[0],
period=self.p.period,
devfactor=self.p.devfactor)
# CrossOver
## 1 : 첫 데이터가 두번째 데이터를 상향으로 가로지르면
## -1 : 하향으로 가르지르면
self.buy_signal = bt.ind.CrossOver(self.datas[0],
self.b_band.lines.bot)
self.sell_signal = bt.ind.CrossOver(self.datas[0],
self.b_band.lines.top)
def log(self, txt):
'''Logging function'''
dt = self.datas[0].datetime.date(0).isoformat()
print(f'{dt}, {txt}')
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# order already submitted/accepted - no action required
return
# report executed order
if order.status in [order.Completed]:
if order.isbuy():
self.log(
f'BUY EXECUTED --- Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Commission: {order.executed.comm:.2f}'
)
self.price = order.executed.price
self.comm = order.executed.comm
else:
self.log(
f'SELL EXECUTED --- Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Commission: {order.executed.comm:.2f}'
)
# report failed order
elif order.status in [order.Canceled, order.Margin,
order.Rejected]:
self.log('Order Failed')
# set no pending order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log(f'OPERATION RESULT --- Gross: {trade.pnl:.2f}, Net: {trade.pnlcomm:.2f}')
def next_open(self):
if not self.position:
if self.buy_signal > 0:
# calculate the max number of buyable shares ('all-in')
size = int(self.broker.getcash() / self.datas[0].open)
# buy order
self.log(f'BUY CREATED --- Size: {size}, Cash: {self.broker.getcash():.2f}, Open: {self.data_open[0]}, Close: {self.data_close[0]}')
self.buy(size=size)
else:
if self.sell_signal < 0:
# sell order
self.log(f'SELL CREATED --- Size: {self.position.size}')
self.sell(size=self.position.size)
data = bt.feeds.PandasData(dataname=yf.download('MSFT', '2018-01-01', '2018-12-31'))
cerebro = bt.Cerebro(stdstats = False, cheat_on_open=True)
# cheat_on_open = t일의 종가에 신호를 계산했지만 t+1일의 시가를 기준으로 사고 싶은 주식을 매수했음을 의미
cerebro.addstrategy(BBand_Strategy)
cerebro.adddata(data)
cerebro.broker.setcash(10000.0)
# 수수료 추가
cerebro.broker.setcommission(commission=0.001)
cerebro.addobserver(bt.observers.BuySell)
cerebro.addobserver(bt.observers.Value)
# Return = 전체 기간에 걸친 서로 다른 로그 수익률 : 전체 복리 수익률, 전 기간 동안의 평균 수익률과 연간 수익률
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
# TimeReturn = 시간에 따른 수익률의 모음 (여기서는 일일)
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='time_return')
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
backtest_result = cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
%matplotlib inline
configure_plotly_browser_state()
init_notebook_mode(connected=True)
# 진짜 해결하기 드럽게 힘드네,,, java 코드 지원 안되는 오류 아래 두 라인으로 해결
plt.rcParams['figure.figsize'] = [15, 12]
plt.rcParams.update({'font.size': 12})
# 결과 도면 표시
cerebro.plot(iplot = False, volume = False)
전략에서 설명하였듯 밴드와 이동평균선의 교차 지점(corss over)과 그 값 (-1 or 1)을 아래 두 plot에서 확인할 수 있다.
다른 return 측도도 확인해보자.
# 다른 return 측도
print(backtest_result[0].analyzers.returns.get_analysis())
일별 포트폴리오 수익도 확인해보자.
# 일별 포트폴리오 수익률 도식화
returns_dict = backtest_result[0].analyzers.time_return.get_analysis()
returns_df = pd.DataFrame(list(returns_dict.items()),
columns = ['report_date', 'return']).set_index('report_date')
returns_df.plot(title='Portfolio returns')
위 수익률 plot에서 편평한 부분은 오픈 포지션이 없는 구간(매수, 매도를 하지 않음)이다.
(해당 게시물 학습을 위한 임의적 설정이므로, 수익을 대변하지 않습니다.)
Ref
- 금융 파이썬 쿡북, 에릭 르윈슨.
- 위키북스 AI 자동 투자 봇 만들기 (https://wikidocs.net/87171)
'Advance Deep Learning > [Quant] 금융 파이썬 쿡북' 카테고리의 다른 글
시계열 모델링 - ARIMA (0) | 2023.02.07 |
---|---|
시계열 모델링 - 지수 평활법 (0) | 2023.02.03 |
시계열 정상성 검정 & 교정 (0) | 2023.02.02 |
시계열 분해 (0) | 2023.02.02 |
단순이동평균(SMA)를 기반으로 전략 백테스팅 (0) | 2023.01.30 |