Skip to content

유튜브에서 발견한 backtest 전략 추가하기 #2

사이드 프로젝트 링크 : https://www.github.com/fromitive/backtest

코드 작성

코드는 아래와 이전 글에서 언급한 아래의 요구사항 대로 구현하였다.

롱 포지션 함수 작업
준비작업 : EMA 200 곡선, Stocastic RSI, RSI

- 매수 
  1. ema 200 값보다 캔들이 위에 있어야 함
  2. stocastic rsi가 25 미만이어야 함
  3. k(파란색 선)가 d(주황색 선)보다 위에 있어야 한다(이전 캔들과 비교하여 돌파 하는 걸 캐치하면 될 것 같다.)

- 매도 :
  1. 매수를 진행하고 나서 3퍼센트 이상 넘으면 매도하라고 하는데 여기서는 2 퍼센트 이상일 때 매도 한다.
  2. 손절을 1퍼센트 밑으로 떨어질 때 매도

함수명은 emastocastic-rsi를 조합한 전략으로서 stocastic_rsi_ema_mix_function.py로 작성했다. 함수 네이밍 하는게 제일 시간 오래걸리는 것 같다.

stocastic_rsi_ema_mix_function.py
def stocastic_rsi_ema_mix_funnction(data: StockData, weight: int, name: str, timeperiod: int = 200, rsi_period: int = 14, fastk_period=3, fastd_period=3, fastd_matype=0,
                                buy_rate: float = 25.0, sell_profit: float = 2.0, sell_lose: float = -1.5):
    df = data.data # pandas DataFrame이며 주식 데이터 OHLCV(시가-open, 고가-high, 저가-low, 종가-close, 거래량-volume)가 들어 있다.
    df['ema'] = talib.EMA(df['close'], timeperiod=timeperiod)
    df['fastk'], df['fastd'] = talib.STOCHRSI(df['close'], timeperiod=rsi_period, fastk_period=fastk_period, fastd_period=fastd_period, fastd_matype=fastd_matype)

    # 현 Stocastic RSI를 이전 값과 비교하기 위해 추가하였다.
    df['before_fastk'] = df['fastk'].shift(1)
    df['before_fastd'] = df['fastd'].shift(1)

    def _buy_signal(r: pd.Series):
        # 매수조건 1 : 종가가, ema 200일 선보다 위로 올라가 있어야 한다.
        if r.close > r.ema:
            # 매수조건 2 : buy_rate(Stocastic RSI 값)이 25 미만일 경우 진입 준비 한다.
            if r.fastk < buy_rate and r.fastd < buy_rate:
                # 매수조건 3: r.fastk가 r.fastd 보다 위로 올라가져 있으면 매수를 한다. 또한, 이전 StocasticRSI와 비교하여 상향할경우도 추가하였다.
                if r.before_fastk < r.fastk and r.before_fastd < r.fastd:
                    if r.fastk > r.fastd:
                        return (StrategyResultColumnType.BUY, weight)
        return (StrategyResultColumnType.KEEP, 0)

해당 전략을 만든 후 아래와 같이 ETH(이더리움) 2023-06-01 부터 현재까지 30분 봉 데이터를 불러와서 백테스팅을 진행해주자.

backtest.py
%load_ext autoreload
%autoreload 2
import pandas as pd
from backtest.use_cases.backtest_execute import backtest_execute
from backtest.use_cases.strategy_execute import stocastic_rsi_ema_mix_funnction
from backtest.request.stockdata_from_repo import build_stock_data_from_repo_request
from backtest.use_cases.stockdata_from_repo import stockdata_from_repo
from backtest.repository.webrepo.crypto.upbit_repo import UpbitRepo
from backtest.domains.backtest_plot_package import BacktestPlotPackage
from backtest.domains.backtest import Backtest
from backtest.domains.strategy import Strategy
from backtest.domains.stockdata import StockData


## 데이터를 UpbitAPI에서 가져오는 부분
request = build_stock_data_from_repo_request(
    filters={'order__eq': 'ETH', 'from__eq': '2023-06-01','chart_interval__eq':'30m'})
response = stockdata_from_repo(UpbitRepo(), request=request, cache=True)
stockdata = response.value

## 전략을 세팅하는 부분, options에는 아무것도 넣지 않고 기본값으로 세팅하였다.
strategy = Strategy(name='stocastic_rsi_ema_mix_funnction', function=stocastic_rsi_ema_mix_funnction, weight=1,  options={})

## 백테스팅을 만드는 부분
backtest = Backtest(strategy_list=[strategy],
                    stockdata_list=[stockdata])
def custom_weight_score_function(first,second,third):
    return ((first)*2 / (second + third+1))
plot_package= BacktestPlotPackage()

## 백테스팅을 실행하는 부분
backtest_result = backtest_execute(
    backtest, verbose=False, save_strategy_result=True,save_raw_csv_file='example.csv',weight_score_function=custom_weight_score_function,plot_package=plot_package).value

그래프를 따로 출력한 결과, Stocastic RSI값이 실제 값과 이상하게 나오는 것을 확인하였다.

아래는 이더리움에 대한 그래프이며, bithumb사이트에 나온 Stocastic RSI 부분을 비교했을때, 실제 값과 상당히(매우) 차이가 나는 것을 확인할 수 있었다.

또한 아래의 그림에서 붉은색 원으로 표시한 부분들을 보면 100과 ,0이 이렇게 많이 나오는 것을 의미하는데, 의도한 것과 다르게 동작하는 것을 알 수 있었다.

Image title

그림 1 - bithumb 그래프와 비교

오류 Searching 후 적용

구글링을 하여, talib.STOCHRSI의 값이 이상하다는 글이 많았고 이를 좀더 거래소 웹 사이트에서 제공하는 데이터와 비슷하게 만들기 위해 여러 시행착오를 거친 내용들이 많았다.

그 중 이 링크에서 깊게 연구하신 분 덕분에 STOCHRSI 값을 어느정도 유사하게 할 수 있었다.

결론부터 설명하자면, RSI 값을 따로 구한 뒤, STOCH 모듈을 사용하면 어느정도 패턴이 일치하는 그래프가 그려진다는 것이다.

위의 결과에 따라, 아래처럼 코드를 수정하였다. 수정한 부분은 하이라이트 된 부분이다.

stocastic_rsi_ema_mix_function2.py
def stocastic_rsi_ema_mix_funnction(data: StockData, weight: int, name: str, timeperiod: int = 200, rsi_period: int = 14, fastk_period=3, fastd_period=3, fastd_matype=0,
                                buy_rate: float = 25.0, sell_profit: float = 2.0, sell_lose: float = -1.5):
    df = data.data # pandas DataFrame이며 주식 데이터 OHLCV(시가-open, 고가-high, 저가-low, 종가-close, 거래량-volume)가 들어 있다.
    df['ema'] = talib.EMA(df['close'], timeperiod=timeperiod)
    df['RSI'] = talib.RSI(df['close'], timeperiod=rsi_period)
    df['fastk'], df['fastd'] = talib.STOCH(df['RSI'], df['RSI'], df['RSI'], fastk_period=14,slowk_period=3,slowk_matype=0,slowd_period=3, slowd_matype=0)


    # 현 Stocastic RSI를 이전 값과 비교하기 위해 추가하였다.
    df['before_fastk'] = df['fastk'].shift(1)
    df['before_fastd'] = df['fastd'].shift(1)

    ...

적용 결과는 아래와 같고, 붉은색 박스를 보면 이제 빗썸의 Stocastic RSI와 어느정도 비슷해 진것을 확인할 수 있었다.

Image title

그림 2 - bithumb 그래프와 비교2

다음은?

아쉽게도 이번 글에서는 BUY 시그널 생성 후 SELL 시그널을 찾는 구체적인 방법은 생각해보지 않았다.

다음 글에서 본격적으로 SELL 시그널을 만드는 방법을 포스팅 해보겠다.


Comments