본문 바로가기
프로그래밍 공부/파이썬 구현

(따라해보기) Shooting Star 구현해보기

by 세상만사 네상만사 2022. 1. 21.
반응형

세상에는 코딩을 잘하는 사람이 매우 많고, 알고리즘 트레이딩에 관심이 많은 사람 역시 매우 많다.

이들을 따라해보며 실력을 기르려고 한다.

 

먼저, Code Trading 이란 채널의 "Automated Candlestick Strategy in Python | testing the shooting star" 을 따라해보았다.

 

https://www.youtube.com/watch?v=eN4zh3PEH6c 

 

 

 


 

구현해본 결과는 다음과 같다. 

 

import pandas as pd
from ta.volatility import *
from ta.momentum import *


### 데이터 가져오기
interval = '1h'
df = pd.read_csv(f'C:\\Users\\gudwn\\PycharmProjects\\pythonProject1\\Tradingbot_Maker\\Backtest_file\\Data\\BTC-USDT_Data\\BTC-USDT_Data_{interval}\\BTC_USDT_{interval}_All.csv')

df = df.iloc[:,:5]

### 필요한 지표값 넣기
df['ATR'] = average_true_range(df['High'],df['Low'],df['Close'],window=10,fillna=True)
df['RSI'] = rsi(df['Close'],window=3)


### 캔들패턴 활용한 추세전환 포착 함수
def Reversal_signal_1(df):
    df.dropna() # na 떨구기
    df.reset_index(drop=True,inplace=True) # 없는 값 떨구고 나머지로 다시 세팅

    length = len(df)
    open = list(df['Open'])
    high = list(df['High'])
    low = list(df['Low'])
    close = list(df['Close'])
    signal = [0] * length

    high_diff = [0] * length
    low_diff = [0] * length
    body_diff = [0] * length
    ratio_1 = [0] * length
    ratio_2 = [0] * length

    # 윗꼬리, 아랫꼬리 비율 계산
    for row in range(length):
        high_diff[row] = high[row] - max(open[row],close[row]) # 윗꼬리 절대 크기
        body_diff[row] = abs(open[row]-close[row]) # 몸통 절대 크기
        low_diff[row] = min(open[row], close[row]) - low[row] # 아랫꼬리 절대크기
        if high_diff[row] < 0.002:
            high_diff[row] = 0.002 # ZerodivisionError 방지
        if body_diff[row] < 0.002:
            body_diff[row] = 0.002 # ZerodivisionError 방지
        if low_diff[row] < 0.002:
            low_diff[row] = 0.002 # ZerodivisionError 방지
        ratio_1[row] = high_diff[row] / body_diff [row] # 윗꼬리 비율
        ratio_2[row] = low_diff[row] / body_diff [row] # 아래꼬리 비율

    # 발동 조건 체크
        # 상승 추세 -> 하락 추세 전환으로 short 주문 조건
        if (ratio_1[row] > 2.5 # 윗꼬리 크기가 몸통 크기의 2.5배 이상
            and low_diff[row] < 0.3 * high_diff[row] # 아랫꼬리가 윗꼬리의 30% 이하
            and body_diff[row]/low_diff[row] > 0.7 # 몸통이 아랫꼬리보다 70% 이상일때
            and df.RSI[row] < 70): # RSI < 70일때
            signal[row] = 1

        # 하락 추세 -> 상승 추세 전환으로 long 주문 조건
        elif (ratio_2[row] > 2.5 # 윗꼬리 크기가 몸통 크기의 2.5배 이상
            and high_diff[row] < 0.3 * low_diff[row] # 윗꼬리가 아래꼬리의 30% 이하
            and body_diff[row]/high_diff[row] > 0.7 # 몸통이 아랫꼬리보다 70% 이상일때
            and df.RSI[row] < 50 and df.RSI[row] > 30): # 30 < RSI < 50 일때
            signal[row] = 2

    return signal

### signal 뜬 것을 원래의 dataframe에 넣기
df['signal_1'] = Reversal_signal_1(df)
df['signal_2'] = Reversal_signal_1(df)
print(df[df['signal_1']==1].count())
print(df[df['signal_2']==2].count())


### 백테스트 시작

### Target 도달 여부 (익절가 먼저 touch 하는지, 손절가 먼저 touch하는지 체크)
# 익절가 먼저 도달하면 take profit
# 손절가 먼저 도달하면 stop loss
# barsupfront 이내에 익절, 손절하지 못할 경우 해당 캔들 종가에 청산

def My_target(barsupfront,df):
    length = len(df)
    open = list(df['Open'])
    high = list(df['High'])
    low = list(df['Low'])
    close = list(df['Close'])
    d_atr = list(df['ATR'])
    trend_cat = [None] * length

    for line in range(0,length-barsupfront-1):
        value_Open_Low = 0
        value_Open_High = 0

        high_diff = high[line] - max(open[line],close[line])
        body_diff = abs(open[line] - close[line])

        pip_diff = d_atr[line] * 1

        if pip_diff < 1000:
            pip_diff = 1000

        StopLoss_to_TakeProfit_Ratio = 2 # pip diff * ratio = TP

        for i in range(1,barsupfront+1):
            value1=close[line] - low[line+i]
            value2=close[line] -high[line+i]
            value_Open_Low = max(value1,value_Open_Low)
            value_Open_High = min(value2,value_Open_High)

            if ( (value_Open_Low) >= (StopLoss_to_TakeProfit_Ratio * pip_diff)) \
                    and (abs(value_Open_High) < pip_diff):
                trend_cat[line] = 1 # 하락 추세
                break
            elif ( (value_Open_Low) < (pip_diff)) \
                    and (abs(value_Open_High)) >= (StopLoss_to_TakeProfit_Ratio * pip_diff):
                trend_cat[line] = 2 # 상승 추세
                break
            else:
                trend_cat [line] = 0 # no trend

    return trend_cat

### 트렌드 여부
df['Trend'] = My_target(50, df)

conditions = [(df['Trend'] == 1) & (df['signal_1'] == 1), # 일치
              (df['Trend'] == 2) & (df['signal_1'] == 2)] # 일치
values = [1,2]
df['result'] = np.select(conditions,values)

trend_Id=2
print(df[df['result']==trend_Id].result.count()/df[df['signal_1']==trend_Id].signal_1.count()) # True positive sell signal / All sell signal

# print(df[(df['result'] != trend_Id) & (df['signal_1'] == trend_Id)])
# histrs =df[(df['result'] != trend_Id) & (df['signal_1'] == trend_Id)].RSI

### 그림그리기

# 그림 그리기
dfpl = df[900:1000] # 900~1000번째 캔들 그림그리기
import plotly.graph_objects as go
from datetime import datetime

fig = go.Figure(data=[go.Candlestick(x=dfpl.index,
                open=dfpl['Open'],
                high=dfpl['High'],
                low=dfpl['Low'],
                close=dfpl['Close'])])

fig.show()

 

ta 라이브러리를 활용하여 ATR, RSI를 계산하였고,

나머지는 그대로 따라하였다.

 

이를 통해 배운 것은,

 

1. 익절/손절 라인을 정하고, 익절 손절 라인에 도달하지 못했을때 특정 봉에서 마감하도록 한 로직을 어떻게 구현하는지

2. 캔들스틱 패턴을 활용한 백테스트를 어떻게 구현하는지

 

등이 있다.

 

앞으로도 꾸준히 다른 사람의 영상, 글을 보며 배워야겠다.

 

반응형

댓글