백테스팅 구축 (5) - 매수 조건 제작
지난 게시글에서는 특정 일자의 차트 데이터를 조회하는 코드를 모두 구축했고, 잘 조회되는 것을 확인했다. 이번 게시글에서는 이전부터 계속 설명했던 이동평균선 간의 골든크로스와 데드크로스를 기준으로 매수가 가능한 종목인지 아닌지를 판단하는 코드를 구축하고자 한다. 여느 때처럼, 함수부터 제작하자.
거래의 가불가 판단하기
거래의 가불가를 판단하기 위해서는 일단 데이터가 필요하고, 우리는 그 데이터를 check_list() 함수의 self.chart_data라는 변수 안에 저장했다. 따라서 self.chart_data라는 변수를 또 다른 함수(justify_ma 함수)로 전달해준 후에, 그 함수 내에서 거래의 가능 여부를 판단한 후에 거래가 가능하다면 True를, 불가능하다면 False를 반환함으로써 해당 종목을 거래할 수 있는지 없는지를 확인할 수 있다.
def check_list(self):
self.code_list = self.load_code()
for code in self.code_list['code']:
self.chart_data = self.load_chart_indate(self.yesterday, self.today, code)
print("################")
print("self.yesterday와 self.today:", self.yesterday, self.today)
self.justify_ma()
def justify_ma(self):
print(self.chart_data)
사실 justify_ma()함수로 self.chart_data라는 변수의 값을 전달해주지 않아도 되는 이유가 있다. 왜냐하면 self.가 붙어 있는 변수의 경우에는 해당 클래스 내에서는 전역 변수의 역할을 수행할 수 있기 때문이다. 즉, 특정 클래스 내에서 변수를 선언할 때 self를 붙여서 생성해주면, 해당 클래스 내에 존재하는 어떤 함수(메서드)에서든지 해당 변수를 그대로 이용할 수 있다는 것이다. 따라서 우리는 코드가 흘러가는 과정의 순서만 잘 지켜주면 된다.
일단 justify_ma에서 self.chart_data를 보다 더 정확하게, 잘 사용하기 위해 코드를 아래와 같이 수정해주었다. .iloc이라는 것을 처음 보다 보니 또 골치아플 수 있는데, 어려운 내용은 결코 아니니 겁부터 먹지는 말자. .iloc은 단순하게 0번째 줄에 있는 데이터, 1번째 줄에 있는 데이터를 지칭한다고 보면 된다. 아래의 결과물을 보면 좀 더 쉽게 이해할 수 있다.
def justify_ma(self):
print(self.chart_data.iloc[0])
print(self.chart_data.iloc[1])
▶ 출력결과
위처럼 수많은 데이터들 중 우리가 보고 싶은 것은 단순하게 MA5와 MA20. 딱 두 개의 데이터이다. 두 개의 데이터만을 바탕으로 5일 이동평균선이 20일 이동평균선을 상향 돌파했는지 하향 돌파했는지를 분석하고 상향 돌파했다면 True를, 하향 돌파했다면 False를 전달해주고 반환된 값을 바탕으로 매수 대상 종목인지 아닌지를 판단하면 된다. 아래와 같이 코드를 제작한 후 실행해서 결과를 확인해보도록 하자.
def justify_ma(self):
today_ma5 = self.chart_data['MA5'].iloc[1]
today_ma20 = self.chart_data['MA20'].iloc[1]
yester_ma5 = self.chart_data['MA5'].iloc[0]
yester_ma20 = self.chart_data['MA20'].iloc[0]
print("######")
print("today:", today_ma5, today_ma20)
print("yesterday:", yester_ma5, yester_ma20)
▶ 출력결과
################
self.yesterday와 self.today: 20200102 20200103
today: 8242.0 8006.5
yesterday: 8200.0 7990.5
바로 위의 이미지에서 빨간색 표시를 해두었던 값과 동일한 값이 출력되었다는 사실을 확인할 수 있다. 그렇다면 우리는 이제 위 데이터들 간의 대소 관계를 비교함으로써 True와 False를 반환해주면 거래의 가불가를 판단할 수 있는 코드의 구축이 완료된다. check_list 내에서는 self.justify_ma()함수의 결과값을 result라는 변수에 대입한 후, result를 출력하도록 하고 justify_ma에서는 조건문(if 문)을 구축함으로써 True 또는 False를 반환하도록 하자.
def check_list(self):
self.code_list = self.load_code()
for code in self.code_list['code']:
self.chart_data = self.load_chart_indate(self.yesterday, self.today, code)
print("################")
print("self.yesterday와 self.today:", self.yesterday, self.today)
result = self.justify_ma()
print(result)
def justify_ma(self):
today_ma5 = self.chart_data['MA5'].iloc[1]
today_ma20 = self.chart_data['MA20'].iloc[1]
yester_ma5 = self.chart_data['MA5'].iloc[0]
yester_ma20 = self.chart_data['MA20'].iloc[0]
print("today:", today_ma5, today_ma20)
print("yesterday:", yester_ma5, yester_ma20)
if yester_ma5 < yester_ma20 and today_ma5 > today_ma20:
return True
else:
return False
▶ 출력결과
################
self.yesterday와 self.today: 20200102 20200103
today: 15760.0 15932.5
yesterday: 15780.0 15950.0
False
################
self.yesterday와 self.today: 20200102 20200103
today: 14990.0 14877.5
yesterday: 14920.0 14867.5
False
################
self.yesterday와 self.today: 20200102 20200103
today: 147500.0 147125.0
yesterday: 146000.0 147200.0
True
################
self.yesterday와 self.today: 20200102 20200103
today: 2387.0 2813.0
yesterday: 2341.0 2838.5
False
거래 가능 종목의 데이터를 바탕으로 데이터프레임화 시키기
check_list() 함수 내에서 self.justify_ma()의 결과값인 result를 통해 해당 종목이 매수 조건이 충족되었다는 사실을 확인할 수 있었다. 따라서 for문을 계속해서 돌면서 조회 일자를 기준으로 매수할 수 있는 종목에는 어떤 종목들이 있는지를 확인할 필요가 있다. 왜냐하면 어떤 날에는 하나의 종목만 매수할 수 있었을 수도 있겠지만, 다섯 개의 종목이 조건을 충족시켰다면 그 종목들을 대상으로 몇 가지 조건을 추가해서 어떤 종목을 매수할 것인지를 확인해야 하기 때문이다. 그리고 무엇보다도 중요한 이유 중 하나는 바로 우리의 자금은 무한하지 않다는 것이다.
따라서 check_list 아래에 buy_list = {}와 같은 형태로 우리가 결과를 조회할 때 다시 볼 필요성이 있는 데이터들을 담을 튜플 형태의 변수를 생성한 후, 결과값인 result가 True인 경우에 해당 값들은 buy_list에 입력하도록 함으로써 결과값들을 확인할 수 있다. 데이터프레임화 시키는 것은 Open API를 통해 차트데이터를 조회하고 그를 데이터프레임화 시켜서 to_sql 메서드를 통해 MySQL에 입력할 때에도 한 번 살펴봤던 것이기 때문에, 여기서는 그냥 코드만 제작하고 넘어가도록 하겠다. 데이터프레임화를 시키는 코드는 맨 아래의 두 줄이며, 중간 중간에 값의 확인을 위해 if result == True 문 아래에 print()문을 넣어 값들을 출력하도록 했다.
def check_list(self):
self.code_list = self.load_code()
buy_list = {'code':[], 'date':[], 'close':[], 'ma5':[], 'ma10':[], 'ma20':[], 'ma60':[], 'ma120':[]}
for code in self.code_list['code']:
self.chart_data = self.load_chart_indate(self.yesterday, self.today, code)
result = self.justify_ma()
if result == True:
print("종목코드:" + code + " 조건 충족, " + self.today)
buy_list['code'].append(code)
buy_list['date'].append(self.today)
buy_list['close'].append(self.chart_data['close'].iloc[1])
buy_list['ma5'].append(self.chart_data['MA5'].iloc[1])
buy_list['ma10'].append(self.chart_data['MA10'].iloc[1])
buy_list['ma20'].append(self.chart_data['MA20'].iloc[1])
buy_list['ma60'].append(self.chart_data['MA60'].iloc[1])
buy_list['ma120'].append(self.chart_data['MA120'].iloc[1])
elif result == False:
pass
self.buy_list = pd.DataFrame(buy_list, columns=['code', 'date', 'close', 'ma5', 'ma10', 'ma20', 'ma60', 'ma120'])
print(self.buy_list)
▶ 출력결과
시작일자: 20200102
종료일자: 20210101
전체범위: 20150101
조회일자: 20200102
종목코드:001130 조건 충족, 20200103
종목코드:001550 조건 충족, 20200103
종목코드:002030 조건 충족, 20200103
종목코드:002140 조건 충족, 20200103
종목코드:002240 조건 충족, 20200103
[데이터 없음] 종목 코드:%s 002630
Traceback (most recent call last):
today_ma5 = self.chart_data['MA5'].iloc[1]
TypeError: 'NoneType' object is not subscriptable
주가 데이터가 아직 모두 저장되지 않은 경우에는 TypeError 오류가 발생하며, 이 오류는 justify_ma()함수에서 발생했다. 따라서 역시 try:문을 통해 오류를 처리하고 데이터가 저장되어 있지 않음을 출력하도록 하자.(주가 데이터를 모두 저장했다면 오류는 발생하지 않는다.)
def justify_ma(self):
try:
today_ma5 = self.chart_data['MA5'].iloc[1]
today_ma20 = self.chart_data['MA20'].iloc[1]
yester_ma5 = self.chart_data['MA5'].iloc[0]
yester_ma20 = self.chart_data['MA20'].iloc[0]
if yester_ma5 < yester_ma20 and today_ma5 > today_ma20:
return True
else:
return False
except TypeError:
pass
▶ 출력결과
code date close ... ma20 ma60 ma120
0 001130 20200103 149500 ... 147125.00 153250.000000 166541.666667
1 001550 20200103 15450 ... 15222.50 16610.000000 18031.666667
2 002030 20200103 108000 ... 105050.00 109358.333333 110891.666667
3 002140 20200103 3530 ... 3374.25 3493.500000 3500.750000
4 002240 20200103 18193 ... 17786.45 18738.200000 19729.500000
5 001130 20200103 149500 ... 147125.00 153250.000000 166541.666667
6 001550 20200103 15450 ... 15222.50 16610.000000 18031.666667
7 002030 20200103 108000 ... 105050.00 109358.333333 110891.666667
8 002140 20200103 3530 ... 3374.25 3493.500000 3500.750000
9 002240 20200103 18193 ... 17786.45 18738.200000 19729.500000
다음 게시글에서는 위의 데이터를 바탕으로 매수를 진행하고, 매수한 종목 및 현재 보유하고 있는 종목과 그 금액, 매수 시점 등을 나타낼 수 있는 데이터를 또 따로 제작함으로써 하나의 계좌 현황을 구축할 예정이다. 물론 아직도 갈 길이 멀다. 앞으로도 정말 많은 오류가 발생할 것이지만 그래도 하나하나 잘 해결해 나가다 보면 끝은 보일 것이다.
'AUTO TRADE > Back test' 카테고리의 다른 글
백테스팅 구축 (7) - 매수 조건 설정 및 계좌 현황 제작 (0) | 2021.07.05 |
---|---|
백테스팅 구축 (6) - 오류 수정 (0) | 2021.07.05 |
백테스팅 구축 (4) - 일자별 차트 데이터 불러오기 (0) | 2021.07.04 |
백테스팅 구축 (3) - 일자 계산하기 (0) | 2021.07.04 |
키움증권 Open API - 차트 데이터 함수 수정 (0) | 2021.07.04 |
소중한 공감 감사합니다