[데이터분석]SpartaCodingClub

넷째주_백테스팅

dowon 2023. 1. 31. 11:01

설날으로 인해 한 주 수업을 못들었다

이번주에 와다다 들었더니 빡세긴 하다ㅎㅎ

 

넷째주에는 장기/단기 이동평균선을 활용하여 언제 주식을 사고 팔아야 하는지 백테스팅을 하는 법을 배웠다

!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

우선 필요한 라이브러리들이다

yfinance, pandas-datareader, finance-datareader 총 3가지가 필요하다

df_1 =  fdr.DataReader('005930','2018')
df_2 = fdr.DataReader('066570','2018')

df = pd.DataFrame()
df['Samsung'] = df_1['Change']
df['Lg'] = df_2['Change']

df.plot(figsize = [15, 8])

df_1은 삼성전자, df_2는 LG의 2018년 주식정보이다

데이터프레임을 만들고 각 회사의 '변화'추이를 plot으로 그려 확인한다

figsize는 그래프의 사이즈를 조절하는 함수이다

그래프를 그리면,

이렇게 나온다

df.tail(100).plot(figsize = [15, 8])

마지막 100개 항목에 대해서만 추려 그래프를 그려보면,

이렇게 나온다

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

이번에는 함수를 만들어보았다

회사의 code와 원하는 연도 갯수 n을 대입하면 수익률이 나오는 함수이다

df.rolling(n).mean()은 n개 연도 이동평균선을 뜻하고

그 뒤의 shift(1)은 데이터 프레임에서 한 줄 내려서 비교하겠다는 의미이다

이동평균선을 그리기 위해 데이터를 조작하기 위해서는 사고, 파는 때의 열이 일치해야 하기 때문에 한 줄 민 것이다

cond1은 buy에서 sell로 바뀔 때를, cond2는 sell에서 buy로 바뀌는 순간을 정의한 것이고

df_result에서 axis = 1은 데이터 프레임을 가로방향으로 합칠 때 사용하는 방법이다

get_return('005930', 3)

삼성전자의 3일 이동평균선 누적 수익률은 약 0.45정도이고

get_return('066570', 3)

LG의 경우에는 -0.58 정도이다

ㅏ....LG.....

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

이번에는 사고 팔았을 때 수익률을 계산하는 함수이다

맨 마지막 날은 무조건 팔아야 하므로 df.iloc[-1,-1] = 'sell'로 지정하였다

get_return_sl('005930', 6, 30)

삼성전자의 6일과 30일 이동평균선을 구분하여 사고 팔 때의 수익률을 구하면

6일과 30일 이동평균선이 31번째 교차할 때 팔았을 때 수익률이 4.63 나온다

dfs = []
for short in range(3,11):
  for long in range(30,61):
    df = get_return_sl('005930',short,long)
    dfs.append(df)

df_result = pd.concat(dfs)
df_result.sort_values(by='수익률', ascending=False)

그럼 최적의 단기/장기 이동평균선은 어떻게 구할 수 있을까?

연산을 하는데 굉장히 오래 걸리지만 위의 코드를 실행해보면

6일 / 59일 이동평균선을 활용하여 사고 팔 때를 결정하는 것이 수익률을 극대화할 수 있다는 것을 알 수 있다