[데이터분석] week4. 파이썬 백테스팅

Jake·2023년 1월 12일

01. 오늘 배울 것

  • 1) 백테스팅을 직접 구현해봅니다!
    📖 백테스팅이란? - 이전 주가의 추이에 전략을 대입해보는 것
    • 만약 어떤 조건에 따라 사고 파는 걸 반복한다면 돈을 벌 수 있을까?

      ⇒ 예) 날씨가 흐리면 사고, 좋으면 팔아볼까? → 모든 전략이 백테스팅 대상입니다.

      ⇒ 이 전략을 몇 년치 과거 주가에 대입해보고, 수익률이 괜찮으면 해보는 것이죠!

  • 2) 간단한 그래프도 그려볼 거예요!
    • 전체 주가를 가져와서, 시계열 그래프를 그려보겠습니다.

02. 백테스팅 전략 세우기& 주가 가져오기

[백테스팅 전략 세우기]

📖 **금융지식을 한스푼만 살펴봅니다!**
  • 1) 이동평균선이란?

    ⇒ 이전 며칠(3일, 5일, 20일, 50일 등) 간의 가격을 평균하여 움직이는 선!

    ⇒ 일수가 커질수록 곡선이 완만해질 수밖에 없겠죠!

  • 2) 우리가 쓸 전략 : 골든크로스, 데드크로스 전략

    ⇒ 골든크로스 : 주가가 높아질 가능성이 있다는 신호

    ⇒ 데드크로스 : 주가가 낮아질 가능성이 있다는 신호

    ⇒ 그러면, “골든크로스 때는 사고, 데드크로스 때는 팔면 → 싸게 사서 비싸게 파는 것 아닐까?”

[주가 가져오기]

  • 1) 라이브러리 설치 ⇒ pandas-datareaderfinance-datareader 라이브러리를 활용해서 주가를 바로! 가져올 수 있답니다. 우선 설치를 함께 해볼게요!
    !pip install yfinance pandas-datareader finance-datareader
  • 2) 주가 가져오기 ⇒ 필요한 라이브러리들을 import 하고, 단 한줄로 가져올 수 있답니다. ⇒ 다른 코드를 넣으면, 종목 별로 모두 가져올 수 있죠!
    • [코드스니펫] 라이브러리 가져오기
      from pandas_datareader import data as pdr
      
      import yfinance as yf
      yf.pdr_override()
      
      import numpy as np
      import pandas as pd
      
      import FinanceDataReader as fdr
      
      df = fdr.DataReader('005930','2018')
      
      df.head()
  • 3) 살펴보기 ⇒ Open: 시초가 High: 고가 Low: 저가 Close: 종가 Volume: 거래량 Change: 변동 ⇒ 아래와 같이 하면, 종가만 가져올 수 있겠죠!
    df[['Close']]
    ⇒ 변동이 20% 이상인 날들만 가져와보기
    df[abs(df['Change']) > 0.05]

03. 간단한 그래프 그려보기

📖 이제부턴 ‘네이버금융’을 켜두고 할게요! [https://finance.naver.com/](https://finance.naver.com/)
  • 1) 주가 그래프 그려보기 ⇒ 엄청 쉽답니다! .plot(y=['컬럼명'])
    df = fdr.DataReader('005930','2018')
    df.plot(y=['Close'])
    ⇒ 좀 더 크게 그려볼까요?
    df.plot(y=['Close'],figsize=(15,8))
    ⇒ 격자 추가하기
    df.plot(y=['Close'],figsize=(15,8),grid=True)
  • 2) 종목 두 개의 주가 변동 그래프를 그려보기 ⇒ 삼성전자와 LG전자를 가지고 해볼까요? ⇒ 최근 100일만 해볼게요!
    df_1 = fdr.DataReader('005930','2018')
    df_2 = fdr.DataReader('066570','2018')
    
    df_tot = pd.DataFrame()
    df_tot['Samsung'] = df_1[['Change']]
    df_tot['LG'] = df_2[['Change']]
    
    df_tot.tail(100).plot(figsize=(15,8))

04. 이동평균값 만들기(3일)

  • 1) 우선 종가만 모아보기 ⇒ Close 만 가져와보겠습니다
    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df
  • 2) 3일마다 평균 값을 구하기 ⇒ .rolling 을 사용할 수 있어요!
    df.rolling(3).mean()
    ⇒ 붙여서 함께 보기
    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean()
    
    df

05. buy & sell 표기

  • 1) shift 를 가지고 열을 맞춰보기

    ⇒ 사실은 다음 날 값과, ma 값을 비교해야 하는 것!

    ⇒ 같은 행에 두면 넘었는지 / 안 넘었는지 쉽게 알 수 있겠죠

    ⇒ 이렇게!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df
  • 2) buy & sell 표기하기

    ⇒ 그러면, 넘었으면 buy, 아니면 sell 로 표기할 수 있겠죠!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    df

06. 수익률 구하기

  • 1) 생각하기1 - 실제로 사는 시점

    ⇒ buy → buy → buy 이면, 사는 게 아니겠죠!

    ⇒ 즉, buy와 sell이 바뀌는 순간이 중요!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    cond = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    
    df['real_buy'] = np.where(cond,'buy','')
    
    df

    ⇒ 사는 순간을 모아볼까요!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    cond = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    
    df[cond]
  • 2) 생각하기2 - 실제로 파는 시점

    ⇒ 완전히 같겠죠!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
    # df_buy = df[cond1]
    df_sell = df[cond2]
    
    df_sell
  • 3) 생각하기3 - 마지막은?

    ⇒ 무조건 팔아야 함! 그래야 수익율을 알 수 있겠죠!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    df.iloc[-1,-1] = 'sell'
    
    cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
    df_buy = df[cond1]
    df_sell = df[cond2]
  • 4) 사고, 파는 시점을 붙이기

    ⇒ 마찬가지로 concat 을 이용하면 된답니다

    ,axis=1 를 붙이면 되지요!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    df.iloc[-1,-1] = 'sell'
    
    cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
    df_buy = df[cond1]
    df_sell = df[cond2]
    
    df_result = pd.concat([df_buy,df_sell],axis=1)
    
    df_result
  • 5) 전략 세워보기

    ⇒ 생각을 해보면! buy 때 사서, sell에 파는 것

    ⇒ buy 때의 Close와 sell 때의 Close 에 주목하면 되는 것

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    df.iloc[-1,-1] = 'sell'
    
    cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
    df_buy = df[cond1].reset_index()
    df_sell = df[cond2].reset_index()
    
    df_result = pd.concat([df_buy,df_sell],axis=1)
    
    df_result
  • 6) 수익률 구하기

    ⇒ 먼저 Close라는 컬럼명을 다르게 수정해줘야 값을 고를 수 있겠죠!

    ⇒ 그리고 수익률 = 판 값 / 산 값 을 구합니다.

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean().shift(1)
    
    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
    df.iloc[-1,-1] = 'sell'
    
    cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
    df_buy = df[cond1].reset_index()
    df_buy.columns = ['날짜','종가(buy)','이평값','액션']
    
    df_sell = df[cond2].reset_index()
    df_sell.columns = ['날짜','종가(sell)','이평값','액션']
    
    df_result = pd.concat([df_buy,df_sell],axis=1)
    
    df_result['수익률'] = df_result['종가(sell)'] / df_result['종가(buy)']
    
    df_result
  • 7) 오류해결하기

    ⇒ dataframe을 복사해서 그렇습니다.

    ⇒ 아래처럼 바꿔주세요!

    df = df[['Close']].copy()
  • 8) 수익률 계산하기

    ⇒ 아래처럼 입력하면 누적 곱을 볼 수 있어요

    df_result[['수익률']].cumprod()

    ⇒ 마지막 값을 가져옵니다.

    df_result[['수익률']].cumprod().iloc[-1,-1]

    ⇒ 1(원금)을 빼주고 100을 곱해줘야 수익률이겠죠!

    (df_result[['수익률']].cumprod().iloc[-1,-1] - 1)*100
  • 9) 함수로 만들기

    ⇒ 코드만 입력하면 될 수 있도록!

    def get_return(code,n):
      df = fdr.DataReader(code,'2018')
    
      df = df[['Close']].copy()
    
      df['ma'] = df.rolling(n).mean().shift(1)
    
      df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')
    
      df.iloc[-1,-1] = 'sell'
    
      cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
      cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
      df_buy = df[cond1].reset_index()
      df_buy.columns = ['날짜','종가(buy)','이평값','액션']
    
      df_sell = df[cond2].reset_index()
      df_sell.columns = ['날짜','종가(sell)','이평값','액션']
    
      df_result = pd.concat([df_buy,df_sell],axis=1)
    
      df_result['수익률'] = df_result['종가(sell)'] / df_result['종가(buy)']
    
      return df_result[['수익률']].cumprod().iloc[-1,-1] - 1
    get_return('005930',3)
    
    get_return('066570',6)

07. 단기/장기이평선 적용하기

  • 1) 단기/장기이평선 구하기

    ⇒ rolling의 숫자만 바뀌면 되겠죠!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']].copy()
    
    df['ma_1'] = df['Close'].rolling(3).mean().shift(1)
    df['ma_2'] = df['Close'].rolling(10).mean().shift(1)
    
    df.head(30)
  • 2) 적절한 값으로 수정하기

    ⇒ Close, ma_1, ma_2 를 고려해야합니다!

    df = fdr.DataReader('005930','2018')
    
    df = df[['Close']].copy()
    
    df['ma_1'] = df['Close'].rolling(3).mean().shift(1)
    df['ma_2'] = df['Close'].rolling(10).mean().shift(1)
    
    df['action'] = np.where(df['ma_1'] > df['ma_2'], 'buy', 'sell')
    
    df.iloc[-1,-1] = 'sell'
    
    cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
    df_buy = df[cond1].reset_index()
    df_buy.columns = ['날짜','종가(buy)','이평값1','이평값2','액션']
    
    df_sell = df[cond2].reset_index()
    df_sell.columns = ['날짜','종가(sell)','이평값1','이평값2','액션']
    
    df_result = pd.concat([df_buy,df_sell],axis=1)
    
    df_result['수익률'] = df_result['종가(sell)'] / df_result['종가(buy)']
    
    (df_result[['수익률']].cumprod().iloc[-1,-1] - 1)*100
  • 3) 함수로 만들어두기!

    ⇒ 그러면 short, long 값만 바꿔주면 된답니다!

    def get_return_sl(code, short, long):
      df = fdr.DataReader(code,'2018')
    
      df = df[['Close']].copy()
    
      df['ma1'] = df['Close'].rolling(short).mean().shift(1)
      df['ma2'] = df['Close'].rolling(long).mean().shift(1)
    
      df['action'] = np.where(df['ma1'] > df['ma2'], 'buy', 'sell')
    
      df.iloc[-1,-1] = 'sell'
    
      cond1 = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
      cond2 = (df['action'] == 'sell') & (df['action'].shift(1) == 'buy')
    
      df_buy = df[cond1].reset_index()
      df_buy.columns = ['날짜','종가(buy)','이평값1','이평값2','액션']
    
      df_sell = df[cond2].reset_index()
      df_sell.columns = ['날짜','종가(sell)','이평값1','이평값2','액션']
    
      df_result = pd.concat([df_buy,df_sell],axis=1)
    
      df_result['수익률'] = df_result['종가(sell)'] / df_result['종가(buy)']
    
      df_final = (df_result[['수익률']].cumprod().tail(1) - 1)*100
      df_final['단기'] = short
      df_final['장기'] = long
    
      return df_final
    get_return_sl('005930',3,30)
profile
Walk on the water!

0개의 댓글