본문 바로가기

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

[백테스팅]EMA 골든 크로스를 응용한 비중 투자

728x90

이번에 사용할 알고리즘을 간략하게 소개하겠습니다.

매수 1번째 조건: 20일, 120일 EMA 골든 크로스 발생시 50% 비중으로 매수합니다.

매수 2번째 조건: 1번째 조건 발생후, 캔들이 EMA 20일 선을 터치하면 나머지 50% 추가 매수합니다.

단, 조건 1에서의 매수단가보다 높은 가격에서 추가 진입해야 할 경우 매수하지 않습니다.

매도 조건: 데드크로스 발생시 전액 매도

 

S&P500의 3000일의 일봉 데이터를 사용하였습니다.

본격적인 코드를 소개하겠습니다.

 

우선 S&P500데이터를 다운받은 후, talib 패키지를 통해 ema 20일, 120일을 계산해줍니다.

import talib
import yfinance as yf

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

stock_data['EMA_20'] = talib.EMA(stock_data['Close'], 20)
stock_data['EMA_120'] = talib.EMA(stock_data['Close'], 120)

 

 

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

stock_data = stock_data[["Open","High","Low","Close","EMA_20","EMA_120"]]

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문을 통해 백테스팅을 진행해줍니다.

for i in range(1, count):
    if stock_data.iloc[i-1,6] == 0: # 안삼
        # 첫번째 조건 충족
        if stock_data.iloc[i-1,4] <= stock_data.iloc[i-1,5] and stock_data.iloc[i,4] > stock_data.iloc[i,5]: 
            stock_data.iloc[i,6] = 1 # buy condition
            stock_data.iloc[i,9] = stock_data.iloc[i,3] # 평단가
            stock_data.iloc[i,11] = 0.5 # 비중
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12] + 1 # 매매횟수 +1

        else: # 매수 조건 불충족
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12]
        

    elif stock_data.iloc[i-1,6] == 1:
        # 두번째 조건 충족
        if stock_data.iloc[i,2] <= stock_data.iloc[i,4] and stock_data.iloc[i,1] >= stock_data.iloc[i,4] and stock_data.iloc[i,3] < stock_data.iloc[i-1,8]:
            stock_data.iloc[i,6] = 2 # buy condition
            stock_data.iloc[i,9] = (stock_data.iloc[i-1,9] + stock_data.iloc[i,3])/2 # 평단가
            stock_data.iloc[i,11] = 1 # 비중
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12] + 1 # 매매횟수 +1
        
        # 매도 조건 충족
        elif stock_data.iloc[i-1,4] >= stock_data.iloc[i-1,5] and stock_data.iloc[i,4] < stock_data.iloc[i,5]:
            stock_data.iloc[i,6] = 0
            stock_data.iloc[i,9] = 0
            stock_data.iloc[i,11] = 0
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12]

        else:
            stock_data.iloc[i,6] = stock_data.iloc[i-1,6] # buy condition
            stock_data.iloc[i,9] = stock_data.iloc[i-1,8] # 평단가
            stock_data.iloc[i,11] = stock_data.iloc[i,3]/stock_data.iloc[i-1,3] * stock_data.iloc[i-1,10] 
            stock_data.iloc[i,11] = stock_data.iloc[i,10]/(stock_data.iloc[i,10] + 0.5) # 비중
            stock_data.iloc[i,12] = stock_data.iloc[i-1,12]

        #금일 수익률
        stock_data.iloc[i,10] = stock_data.iloc[i,3]/stock_data.iloc[i-1,3]*stock_data.iloc[i-1,11] + (1-stock_data.iloc[i-1,11]) 
        #갖고 있는 포지션에 대한 수익률
        stock_data.iloc[i,8] = stock_data.iloc[i-1,8] * stock_data.iloc[i,10]

    else: # stock_data.iloc[i-1,6] == 2
        # 매도 조건 충족
        if stock_data.iloc[i-1,4] >= stock_data.iloc[i-1,5] and stock_data.iloc[i,4] < stock_data.iloc[i,5]:
            stock_data.iloc[i,6] = 0 # buy condition
            stock_data.iloc[i,9] = 0 # 평단가
            stock_data.iloc[i,11] = 0
        else:
            stock_data.iloc[i,6] =  stock_data.iloc[i-1,6]# buy condition
            stock_data.iloc[i,9] = stock_data.iloc[i-1,8] # 평단가
            stock_data.iloc[i,11] = stock_data.iloc[i-1,10]
        
        stock_data.iloc[i,12] = stock_data.iloc[i-1,12]
        #금일 수익률
        stock_data.iloc[i,10] = stock_data.iloc[i,3]/stock_data.iloc[i-1,3]*stock_data.iloc[i-1,11] + (1-stock_data.iloc[i-1,11])
        #갖고 있는 포지션에 대한 수익률
        stock_data.iloc[i,8] = stock_data.iloc[i-1,8] * stock_data.iloc[i,10]


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

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

 

 

#결과

결과를 plot 해봅시다.

import matplotlib.pyplot as plt

plt.figure(figsize=(14,10))
plt.plot(range(count), stock_data['hpr']*stock_data.iloc[120,0], label = "trading", color='black')
plt.plot(range(count), stock_data['Close'], label="index", color="orange")
plt.xlim(120,count)
plt.legend()

 

 

약 2012년 부터 현재까지 S&P500 차트 데이터와 백테스팅 수익률 비교 그래프입니다.

그저 buy and hold가 훨씬 수익률이 높네요.

 

 

win_ratio=stock_data.iloc[-1,13]/stock_data.iloc[-1,12]
buy_and_hold = stock_data.iloc[-1,3]/stock_data.iloc[0,0]
hpr = stock_data.iloc[-1,7]
print("승률: ",win_ratio)
print("buy and hold: ", buy_and_hold)
print("hpr: ",hpr)

 

승률:  0.5
buy and hold:  3.57659930029685
hpr:  1.4859917181401687

 

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