Data/python

[stochastic] python을 활용한 백테스팅

노는토요일 2022. 7. 3. 16:32

스토캐스틱 Trading 

스토캐스틱 트레이딩에서 참고해야할 지표는 크게 3개이다. 

 

Fast %K 

# 함수 정의 
# Fast %K ((현재가 - n기간 중 최저가)/(n기간 중 최고가 - n 기간 중 최저가)) * 100

def get_stochastic_fast_k(close_price, low, high, n =5):
    fast_k = (close_price - low.rolling(window=n).min())/(high.rolling(window=n).max()
                                                         - low.rolling(window=n).min())*100
    return fast_k
    

Slow %K(Fast %D)

# Slow %K  = Fast %K의 m기간 이동평균(SMA) = # Fast % D
def get_stochastic_slow_k(fast_k, n=3):
    slow_k = fast_k.rolling(window=n).mean()
    return slow_k

Slow %D

# Slow %D  = Slow %K의 t기간 이동평균(SMA)
def get_stochastic_slow_d(slow_k, n=3):
    slow_d = slow_k.rolling(window=n).mean()
    return slow_d

위의 3가지를 참고해 트레이딩을 진행하는데, Fast %k 가 가장 급변하는 변화를 잘 잡아내고, Slow %D가 2번의 이동평균화를 거치다 보니 조금더 완만하게 변화를 잡아낸다. 

어떤 전략을 쓸지는 투자자의 판다에 딸라 달라집니다. 필자의 경우  주가의 급등락을 빠르게 잡아내고 싶어서 Fast %k를 활용해 트레이딩을 적용해 보았습니다.

 

Trade BackTesting

Fast % k 80이상 매도 20이하 매수

###################################
## Fast %K 80이상 매도 20이하 매수 ##
#####################################

###############
## 삼성 전자 ##
###############

#데이터 로드 
raw_df=fdr.DataReader('005930','2022-01-01').reset_index()

# seriese 정의 
high_ = raw_df.High
low_ = raw_df.Low
close_ = raw_df.Close

raw_df['fast_k'] = get_stochastic_fast_k(close_,low_,high_)
fast_k = raw_df['fast_k']
raw_df['slow_k'] = get_stochastic_slow_k(fast_k)
slow_k = raw_df['slow_k']
raw_df['slow_d'] = get_stochastic_slow_d(slow_k)
slow_d = raw_df['slow_d']

buy_idx = np.where(raw_df['fast_k']<=20)[0] 
sell_idx =np.where(raw_df['fast_k']>=80)[0]

# 이전 action이 buy였으면 공란 
for k in range(len(raw_df)):
    if k in buy_idx:
        raw_df.loc[k,'action'] = 'buy'
    elif k in sell_idx:
        raw_df.loc[k,'action'] = 'sell'
    else:
        raw_df.loc[k,'action'] = ''

rst_df = raw_df.copy()

for k in range(len(rst_df)):
    if k ==0:
        continue      
    if rst_df.loc[k-1,'action'] =='buy': # 이전 시점에 buy면 holding   ## action 
        rst_df.loc[k,'action'] = 'holding'
    elif rst_df.loc[k-1,'action'] =='sell': # 이전 시점에 sell이면 sell_holding  ## action 
        rst_df.loc[k,'action'] = 'sell_holding'
    elif (rst_df.loc[k-1,'action'] =='sell_holding') and(rst_df.loc[k,'action'] =='buy') : # 이전시점이 sell_hlidng면서 현재 buy러 바뀌면 넘기기 
        continue # 포지션 바뀌는 경우 
    elif (rst_df.loc[k-1,'action'] =='holding') and(rst_df.loc[k,'action'] =='sell') : # 포지션 바뀌는 경우 
        continue         
    elif rst_df.loc[k-1,'action'] =='sell_holding': # 이전이 sell_holding 이이고 다음도 같으면 포지션 유지  # position stay
        rst_df.loc[k,'action'] = 'sell_holding'        
    elif rst_df.loc[k-1,'action'] =='holding':  # 이전이 holding 이고 다음도 같으면 포지션 유지 # position stay
        rst_df.loc[k,'action'] = 'holding'
    else:
        continue        
               
rst_df

Date/Open/High/Low/Close/Volume/Change/fast_k/slow_k/slow_d/action/

2022-01-03 79400 79800 78200 78600 13502112 0.003831 NaN NaN NaN  
2022-01-04 78800 79200 78300 78700 12427416 0.001272 NaN NaN NaN  
2022-01-05 78800 79000 76400 77400 25470640 -0.016518 NaN NaN NaN  
2022-01-06 76700 77600 76600 76900 12931954 -0.006460 NaN NaN NaN  
2022-01-07 78100 78400 77400 78300 15163757 0.018205 55.882353 NaN NaN  
... ... ... ... ... ... ... ... ... ... ...
2022-06-15 61300 61500 60200 60700 26811224 -0.019386 10.000000 9.130435 7.753623 holding
2022-06-16 61300 61800 60500 60900 23394895 0.003295 16.666667 14.685990 9.871176 holding
2022-06-17 59400 59900 59400 59800 29053450 -0.018062 11.764706 12.810458 12.208961 holding
2022-06-20 59800 59900 58100 58700 34111306 -0.018395 14.634146 14.355173 13.950540 holding
2022-06-21 58700 59200 58200 58500 24902181 -0.003407 10.810811 12.403221 13.189617 holding

114 rows × 11 columns

 

Trade history 조회 

df_sample = rst_df.copy()
df_sample['range'] = df_sample['High']-df_sample['Low']
df_sample[df_sample['action'].isin(['buy','sell'])]

수익률  계산

def get_profit(df):
    final_df = df[df['action'].isin(['buy','sell'])]
    final_df = final_df.reset_index(drop=True)
    trading_cnt = final_df.shape[0]
    
    profit = []
    buy_lst = []
    for idx in range(len(final_df)):
        # 매수 action이 들어올 경우 매수 
        if final_df.action[idx] =='buy':
            buy_price = final_df.Close[idx]
            buy_lst.append(buy_price)
        # 첫 action이 매도인 경우
        elif idx == 0 and final_df.action[idx] =='sell':
            continue
        #그 외의 경우 정상 매도 
        else:
            sell_price = final_df.Close[idx]
            profit.append(-(buy_price) + sell_price) # 음수가 되어야함. 
            # 1 buy_pirce > sell_preice 인경우 비싸게 싸서 싸게 판경우 즉 손실 
            # 2 buy_pirce < sell_preice 인 경우 싸게사서 비싸게 판경우 그래서 이득 
       
    profit_sum = np.sum(profit)
    average_buying = np.mean(buy_lst) # 매수 평단가 
    profit_percentage = round(profit_sum/average_buying,2)
    profit_num = len(profit)
    
    return trading_cnt,profit_sum,profit_num,profit_percentage
    

삼성전자 6개월간 Fast%k 기반 수익률 

get_profit(rst_df) # Fast% K 

트레이딩 14회 ,  한 주당 수익 : -1700원, 이익횟수 : 6번, 수익률 -2%