본문 바로가기

파이썬으로 퀀트 프로그램 만들기 project/백테스팅

[벡테스팅]볼린저 밴드를 이용한 백테스팅

728x90

이번 백테스팅에 사용할 지표는 볼린저 밴드입니다.

저의 알고리즘은 다음과 같습니다.

매수 조건: 고점대비  20% 이상 하락을 경험하고, 볼린저밴드의 +1 시그마 상향 돌파시 매수

매도조건1: -1 시그마 터치시 전액 매도

매도조건2: 매도조건 1 충족 안됐을 때, 직전 고점 대비 +10% 달성시 50% 익절

매도조건3: 매도조건 2 충족됐을 때, 1시그마 터치시 전액 청산 

 

본격적으로 저의 코드를 소개하겠습니다.

import talib
import yfinance as yf

stock_data = yf.download('^GSPC')#S&P500 데이터에 해당
count = 5000
stock_data = stock_data.tail(count)

stock_data['upper'], middle, stock_data['lower'] = talib.BBANDS(stock_data["Close"], 
                                                                nbdevup=1, # 1 표준편차
                                                                nbdevdn=1,
                                                                timeperiod=120)

s&p500의 5000일 데이터를 다운해줍니다.

talib 패키지를 통해 볼린저 밴드를 만들어줍니다.

 

 

필요한 열들을 추가해줍니다.

stock_data = stock_data[["Open","High","Low","Close","upper","lower"]]

stock_data['highest'] = stock_data['High'].cummax()
stock_data['buy_condition'] = 0 # 0 = 안 삼, 1 = 매수조건1 충족, 2 = 매도조건2 충족
stock_data['hpr'] = 1 # 누적 수익률
stock_data['ror'] = 1 
stock_data['buy_price'] = 0 # 평단가
stock_data['dr'] = 1 # 금일 수익률
stock_data["weight"] = 0 # 비중
stock_data["trading_number"] = 0
stock_data["win"] = 0
stock_data["lose"] = 0

 

 

for문을 통해 backtesting을 진행시켜줍니다.

#전고점
befor_high = 0

#전고점 대비 20퍼 하락시 True
ready = False

for i in range(1,count):
    if stock_data.iloc[i-1,7] == 0:
        
        # 고점대비 20퍼 하락 
        if stock_data.iloc[i-1,3] <= stock_data.iloc[i-1,6]*0.8:
            ready = True

        # 1시그마 상향 돌파
        if ready == True and (stock_data.iloc[i-2,3] < stock_data.iloc[i-2,4] and stock_data.iloc[i-1,3] > stock_data.iloc[i-1,4]):   
            stock_data.iloc[i,7] = 1 # buy condition
            stock_data.iloc[i,11] = stock_data.iloc[i,3]/stock_data.iloc[i,0] # dr
            stock_data.iloc[i,9] = stock_data.iloc[i,11]# ror
            stock_data.iloc[i,10] = stock_data.iloc[i,0] # buy price
            stock_data.iloc[i,12] = 1 # weight
            stock_data.iloc[i,13] = stock_data.iloc[i-1,13] + 1 # trading_number + 1
            befor_high = stock_data.iloc[i,6]
            ready = False
            
        else:
            stock_data.iloc[i,13] = stock_data.iloc[i-1,13]
      

    elif stock_data.iloc[i-1,7] == 1:
        # -1시그마 터치시 청산
        if stock_data.iloc[i-1,3] > stock_data.iloc[i-1,5] and stock_data.iloc[i,2] <= stock_data.iloc[i,5]:
            stock_data.iloc[i,7] = 0 # buy condition
            stock_data.iloc[i,10] = 0 # buy price
            stock_data.iloc[i,9] = stock_data.iloc[i,5]/stock_data.iloc[i-1,10] # ror
            stock_data.iloc[i,11] = stock_data.iloc[i,5]/stock_data.iloc[i-1,3] # dr
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12] # weight
            
        # 고점대비 +10퍼 -> 50퍼 익절
        elif stock_data.iloc[i-1,2] <= befor_high*1.1 and stock_data.iloc[i,1] >= befor_high*1.1: 
            stock_data.iloc[i,7] = 2 # buy condition
            stock_data.iloc[i,10] = stock_data.iloc[i-1,10] # buy price
            stock_data.iloc[i,9] = 0.5*(befor_high*1.1 + stock_data.iloc[i,3])/stock_data.iloc[i,10] # ror
            stock_data.iloc[i,11] = 0.5*(befor_high*1.1 + stock_data.iloc[i,3])/stock_data.iloc[i-1,3] # dr
            n = stock_data.iloc[i,3]/(befor_high*1.1)
            stock_data.iloc[i,12] = (0.5*n)/(0.5*n + 0.5) # weight

        else:
            stock_data.iloc[i,7] = stock_data.iloc[i-1,7] # buy condition
            stock_data.iloc[i,10] = stock_data.iloc[i-1,10] # buy price
            stock_data.iloc[i,9] = stock_data.iloc[i,3]/stock_data.iloc[i,10] # ror
            stock_data.iloc[i,11] = stock_data.iloc[i,3]/stock_data.iloc[i-1,3] # dr
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12] # weight

        stock_data.iloc[i,13] = stock_data.iloc[i-1,13]
        
        


    else: #stock_data.iloc[i-1,7] == 2
        # 1시그마 터치시 청산
        if stock_data.iloc[i-1,3] > stock_data.iloc[i-1,4] and stock_data.iloc[i,2] <= stock_data.iloc[i,4]:
            stock_data.iloc[i,7] = 0 # buy condition
            stock_data.iloc[i,10] = 0 # buy price
            n = stock_data.iloc[i,4]/stock_data.iloc[i-1,3]
            stock_data.iloc[i,11] = stock_data.iloc[i-1,12] * n + 1-stock_data.iloc[i-1,12]# dr
            stock_data.iloc[i,9] = stock_data.iloc[i-1,9]*stock_data.iloc[i,11] # ror
            stock_data.iloc[i,12] = 0
            

        else:
            n = stock_data.iloc[i,3]/stock_data.iloc[i-1,3]
            stock_data.iloc[i,7] = stock_data.iloc[i-1,7]
            stock_data.iloc[i,11] = stock_data.iloc[i-1,12] * n + 1-stock_data.iloc[i-1,12] # dr
            stock_data.iloc[i,9] = stock_data.iloc[i-1,9]*stock_data.iloc[i,11]# ror
            stock_data.iloc[i,12] = (stock_data.iloc[i-1,12]*n)/(stock_data.iloc[i-1,12]*n + (1-stock_data.iloc[i-1,12])) # weight

        stock_data.iloc[i,13] = stock_data.iloc[i-1,13]


stock_data['hpr'] = stock_data['dr'].cumprod()   

for i in range(1,count):
    if stock_data.iloc[i-1,7] != 0 and stock_data.iloc[i,7] == 0: # 매도했을때
        if stock_data.iloc[i,9] > 1: # 이기면
            stock_data.iloc[i,14] = stock_data.iloc[i-1,14] + 1
            stock_data.iloc[i,15] = stock_data.iloc[i-1,15]
        elif stock_data.iloc[i,9] <= 1: # 지면
            stock_data.iloc[i,15] = stock_data.iloc[i-1,15] + 1
            stock_data.iloc[i,14] = stock_data.iloc[i-1,14]
    else:
        stock_data.iloc[i,14] = stock_data.iloc[i-1,14]
        stock_data.iloc[i,15] = stock_data.iloc[i-1,15]

 

 

결과를 plot해봅시다.

mpl_finance 라이브러리를 이용하면 실제 차트처럼 캔들차트를 plot할 수 있습니다.

import matplotlib.pyplot as plt
import mpl_finance
import matplotlib.ticker as ticker

fig, ax = plt.subplots(figsize=(12, 8))
ax.plot(range(count), stock_data['hpr']*stock_data.iloc[120,0], color='black')
ax.plot(range(count), stock_data['upper'], color="yellowgreen")
ax.plot(range(count), stock_data['lower'], color="yellowgreen")
mpl_finance.candlestick2_ohlc(ax, stock_data['Open'], stock_data['High'], stock_data['Low'], stock_data['Close'], width=0.5, colorup='r', colordown='b')
plt.grid(True)

 

 

win_ratio=stock_data.iloc[-1,14]/stock_data.iloc[-1,13]
buy_and_hold = stock_data.iloc[-1,3]/stock_data.iloc[0,0]
hpr = stock_data.iloc[-1,8]
print("매매 횟수: ", stock_data.iloc[-1,13])
print("승률: ",win_ratio)
print("buy and hold: ", buy_and_hold)
print("hpr: ",hpr)

 

매매 횟수: 6

승률: 1.0

buy and hold: 3.970825293074899

hpr: 1.5919956315747747

 

 

5000일 동안 6번 밖에 매매를 못했네요. 그래도 승률은 100퍼입니다.

차라리 비중을 100퍼로 하는게 더 수익률은 좋았겠네요.

다음에는 더 나은 알고리즘으로 돌아오겠습니다.