데이터분석 4주차 강의 노트정리 [국비지원_스파르타 코딩클럽]

    728x90

    스파르타 코딩클럽 데이터분석 4주차 강의 '핵심' 노트정리

    스파르타 코딩클럽 데이터분석 4주차 강의 '핵심' 노트정리

     

    1. 백테스팅 개념

     

    ▶ 백테스팅이란?

    : 이전 주가의 추이를 보고 전략을 대입해 보는 것

     

    ex. 만약 어떤 조건에 따라 사고 파는 걸 반복하면 돈을 벌 수 있을까?

    → 만약 날씨가 흐리면 사고, 좋으면 팔고를 반복하면 수익이 날까?

     


    2. 백테스팅 전략 세우기

    2-1) 이동평균선

     

    ▶이동평균선이란?

    : 이전 며칠(3일, 5일, 20일, 50일 등) 간의 가격의 평균값이 움직이는 선

    : 일 수가 커질수록 곡선이 완만해지는 게 특징

     

     

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

     

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

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

     

    → 그럼 골든크로스 때 사고, 데드크로스 때 팔면 싸게 사서 비싸게 파는 것인가?

     


    3. 주가 가져오기

     

    ▶ yfinance, pandas-datareader, finance-datareader 라이브러리 설치

    !pip install yfinance pandas-datareader finance-datareader

     

    ▶주가 가져오기 코드스니펫

    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','2020')
    
    df.head()

    : '005930' 은 삼성전자의 종목코드 

    : '2020' 년도 데이터부터 불러와라

     

    *조건 추가: Change 값 > 0.05 인 데이터

     


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

     

    ① 한 종목 그래프만 그려보기

    df.plot(y = ['Close'], figsize = (15,8), grid = True)

    : df.plot(y = ['Close']) Close 값으로 그래프를 그려라

    : figsize  = 그래프의 크기 조정

    : grid = True 그래프에 격자무늬를 넣어라

    : df.plot(y = ['Open', 'Close']) 하면 Open과 Close 열의 그래프가 같이 그려짐

     

     

     

    ② 여러 종목 그래프 그려보기

     

    df_1 = fdr.DataReader('005930','2020')
    df_2 = fdr.DataReader('066570','2020')
    
    df = pd.DataFrame()
    df['Samsung'] = df_1['Change']
    df['LG'] = df_2['Change']
    
    df.tail(100).plot(figsize = (15,8))

    : '005930'은 삼성전자 , '066570'은 LG전자의 종목코드

    : 새로운 DataFrame을 하나 만들고 삼성전자의 변동율과 LG전자의 변동율 나타내기

    : df.plot 으로 그래프 그리는데

    : figsize = 로 그래프 크기 조정해주고

    : .tail(100) 으로 최근 100일 정도의 데이터만 불러와서 그래프 그려보기

     

     

     


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

     

    df = fdr.DataReader('005930','2020')
    
    df = df[['Close']]
    
    df['ma'] = df.rolling(3).mean()
    
    df

    : df.rolling(3) 3일씩 묶어서

    : .mean() 3일간의 이동평균 값을 구해줌

     

    01

     


    6. buy 와 sell 표기

     

    ① 한 칸 내려주기

     

    : 3일의 평균이 파란색 박스

    : 각 날짜의 종가가 빨간색 박스

     

    → 빨간색 박스와 파란색 박스의 값을 비교해야 함.

    : 그 날의 종가가 3일간의 이동평균선 값을 뚫고 올라가면 사겠다는 것.

    → 보기 좋게 만들려면 파란색 박스를 한 칸 내려줘야 함.

    df['ma'] = df.rolling(3).mean().shift(1)

    : .shift(1) 만 뒤에 붙여서 적어주면 한 칸 내려주기 끝!

     

     

     

    ② buy인지 sell인지 액션을 새로운 열에 표시하기

     

    df['action'] = np.where(df['Close'] > df['ma'], 'buy', 'sell')

    : np.where(조건, 조건에 맞을 때 표시할 값, 조건에 맞지 않을 때 표시할 값)

     

     

     


    7. 수익률 구하기(1)

     

    ◈ 실제로 사는 시점 생각하기

    : buy - buy - buy 인 경우에는 사는 게 아니라 산 주식을 들고 있는 상태임.

    → buy가 sell로, sell이 buy로 바뀌는 순간이 중요!

     

     

    ① action값과 action.shift(1) 값 비교

    df['action_temp'] = df['action'].shift(1)

    : df['action_temp'] 는 action을 .shift(1)한 값

    : action값과 action_temp값이 다를 때 (buy와 sell이 바뀌는 시점) buy 해야 함.

     

     

    : action오늘의 행동

    : action.shift(1)은 오늘의 행동을 한 칸 내린 값.

    즉, 내일의 action값 입장에서는 어제의 행동이 됨.

     

     

    ② real_buy값 도출하기

    cond = (df['action'] == 'buy') & (df['action'].shift(1) == 'sell')
    
    df['real_buy'] = np.where(cond, 'buy', '')

    : df['real_buy']  어제 sell이었는데 오늘 buy로 바뀌었으면 buy 라고 표시해줌.

     

     

     

    ③ buy → sell ,  sell → buy 바뀔 때를 cond 이용해서 잡아내기

    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_buy는 buy 타이밍만 모은 데이터

    : df_sell은 sell 타이밍만 모은 데이터

     

     

    ④ 데이터의 마지막은 무조건 sell

    → 그래야 수익률을 구할 수 있음.

    df.iloc[-1,-1] = 'sell'

     


    8. 수익률 구하기(2)

     

    ① df_buy와 df_sell 옆으로 이어붙이기

    df_result = pd.concat([df_buy, df_sell], axis = 1)

    : .axis = 1  df_buy와 df_sell을 옆으로 이어붙이기

     

    : 빨간 박스일 때 사고, 파란 박스일 때 팔고~

    : 파란 박스가 또 한 줄 밑에 있기 때문에 이걸 합쳐줘야 함.

     

     

    ② .reset_index()

    df_buy = df[cond1].reset_index()
    df_sell = df[cond2].reset_index()

     

    : 1 / 7일에 사고, 1 / 15일에 팔고~

    : 1 / 16일에 사고, 1 / 21일에 팔고~

     

     

    ③ 열 이름 바꾸기, 수익률 구하기

    df_buy.columns = ['날짜','종가(buy)','이평값','액션']
    df_sell.columns = ['날짜','종가(sell)','이평값','액션']
    
    df_result['수익률'] = df_result['종가(sell)'] / df_result['종가(buy)']

     

     

     


    9. 수익률 구하기(3)

     

    ① 수익률 계산하기

    df = df[['Close']].copy()  # .copy를 안 하면 오류 남.
    df_result[['수익률']].cumprod().iloc[-1,-1] -1

    : .cumprod() 각각의 수익률 값을 누적으로 계속 곱해줌.

    .iloc[-1,-1] 열의 마지막 값 출력

    : -1을 해줘야 수익률이 얼마인지 제대로 나옴.

     

     

     

    ② def 함수 사용하기

     

    def get_return(code, n):
      df = fdr.DataReader(code,'2020')
    
      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', 6)

     

     

     


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

     

    ① 단기/장기 이평선 구하기

    →  rolling( )의 숫자만 바꾸면 됨.

    df['action'] 조건 바꿔주기 

    : ma1이 단기 이평선

    : ma2가 장기 이평선

    : ma1 > ma2 일 때 buy 해야 함.

     

    df = fdr.DataReader('005930','2020')
    
    df = df[['Close']].copy()
    
    df['ma1'] = df['Close'].rolling(3).mean().shift(1)
    df['ma2'] = df['Close'].rolling(30).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['단기'] = 3
    df_final['장기'] = 30
    
    df_final

    : df_final 값에서 .tail(1) 은  수익률의 마지막 값을 보여줌.

     

     

     

    ② def 함수 사용하기

    → df_result[['수익률']].cumprod().iloc[-1,-1] -1 로 나온 값을 표로 만드는 게 목적

    (단기는 며칠이고, 장기는 며칠이고, 수익률은 어떻게 되는지)

     

    def get_return_sl(code,short,long):
      df = fdr.DataReader(code,'2020')
    
      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)

     

     


    ♥ 한 종목에 대한 최적 단기/장기 이동평균선 구해보기

    (단기 며칠 / 장기 며칠이 이 종목에 가장 적절한 전략일지)

     

    >> 데이터분석 4주차 숙제 (데이터분석 4주차 후기에서 확인 가능!)

     

    ♣ 데이터분석 4주차 강의 후기 ♣   https://nasena.tistory.com/20
    728x90

    댓글