Phython
[4주차] 파이썬 백테스팅 1
김꼬알
2023. 3. 23. 17:04
1. 백테스팅 전략 세우기 & 주가 가져오기
이전 주가의 추이에 전략을 대입해보는 것을 백테스팅이라고 한다.
어떤 조건에 따라 사고 파는 걸 반복하면 돈을 벌 수 있을지 생각해보고 대입해본다.
예를 들면, 날씨가 흐리면 사고 좋으면 파는 전략 세웠다면 몇 년치 과거 주가에 대입해보고 수익률을 확인해본다.
- 백테스팅 전략 세우기
이동평균선이란 이전 며칠(3일, 5일, 20일 등) 간의 가격을 평균으로 내어 움직이는 선이다.
우리는 골든크로스, 데드크로스 전략을 사용한다.
골든크로스는 주가가 높아질 가능성이 있다는 신호, 데드크로스는 주가가 낮아질 가능성이 있다는 신호이다.
그러면 골든크로스 때는 사고, 데드크로스 때 팔면 싸게 사서 비싸게 파는 것!
- 라이브러리 설치
!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','2018')
df.head()
// 종가만 가져오기
df[['Close']]
// 변동이 20% 이상인 날들만 가져와보기
df[abs(df['Change']) > 0.05]
2. 간단한 그래프 그려보기
네이버 금융 홈페이지에서 확인하고 싶은 종목 코드를 가져와서 작업한다.
- 주가 그래프 그려보기
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)
- 종목 두 개의 주가 변동 그래프 그려보기
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))
3. 이동평균값 만들기(3일)
- 종가만 모아보기
df = fdr.DataReader('005930','2018')
df = df[['Close']]
df
- 3일마다 평균 값 구하기
df = fdr.DataReader('005930','2018')
df = df[['Close']]
df['ma'] = df.rolling(3).mean()
df
4. buy & sell 표기
- shift로 열을 맞추기
df = fdr.DataReader('005930','2018')
df = df[['Close']]
df['ma'] = df.rolling(3).mean().shift(1)
df
※ 다음 날의 종가와 ma 값을 비교해야 하기 때문에 같은 행으로 맞춰주기 위해 shift로 한 줄 아래로 내려준다.
- 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
5. 수익률 구하기
- 실제로 사는 시점
// 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]
- 실제로 파는 시점
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
- 마지막으로 파는 시점
마지막은 무조건 팔아야 수익율을 알 수 있다.
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 = 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
- 전략 세워보기
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
- 수익률 구하기
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
- 오류 해결하기
Data frame을 직접 사용하지 않도록 복사본을 만들어준다.
df = df[['Close']].copy()
- 수익률 계산하기
// 누적 곱 구하기
df_result[['수익률']].cumprod()
// 마지막 값을 가져오기
df_result[['수익률']].cumprod()
// 1(원금)을 빼주고 100을 곱해서 수익률 계산하기
(df_result[['수익률']].cumprod().iloc[-1,-1] - 1)*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
get_return('005930',3)
get_return('066570',6)
6. 단기/장기 이평선 적용하기
- 단기/장기 이평선 구하기
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)
- 적절한 값으로 수정하기
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
- 함수로 만들기
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)