PYTHON/Back test

1.5 백테스팅하기 - 종목별 변수 자동 생성하기 (2)

  • -

이제 algorithm_1 이라는 파일 내에 있는   class algo_1()  에서는   kospi_data  와   kosdaq_data  , 그리고   item_data  라는 세 개의 데이터를 인자로 받아 각각   self.kospi_data   self.kosdaq_data   self.item_data  라는 변수에 저장해주었다. 이제 여기서 self.item_data라는 변수를 대상으로 for문을 돌면서 종목 코드를 찾아내고, 그 종목 코드를 하나의 변수로 입력해줄 예정이다.

 

 

[파일명 : algorithm_1] 데이터프레임 내에서 for문 사용하기

데이터프레임 형태의 데이터를 대상으로 반복문에 해당하는 for문을 사용하기 위해서는   .itertuples()  라는 메서드만 추가해주면 된다. 일단 종목별 종목 코드를 바탕으로 변수로 설정하는 함수를 별도로 만들어주도록 하자. 여기서 다소 어렵게 느껴질 수 있는 부분들이 있긴 한데, 일단 하나하나 차근차근 살펴보도록 하자.
※ 새롭게 구축한 코드 : 1~4번째 줄

def _load_day_data(self):
    for i in self.item_data.itertuples():

 

자세한 설명을 위해, 먼저 self.item_data 변수에는 아래의 사진과 같은 형태로 데이터들이 입력되어 있으며 000020부터 시작해서 모든 종목의 종목 코드와 차트 데이터 저장 여부(1은 저장, NULL은 미저장)를 포함하고 있다.

그렇다면 앞서 제작한 for문은 위의 데이터를 대상으로 반복을 진행하라는 것인데, 가장 먼저 첫 번째로 가져올 데이터는 어떤 형태의 데이터일까?

  • 첫째, 000020만 가져온다
  • 둘째, 000020, 20211228, 1, 20211228, 1, NULL, NULL, 20211228, 1, 16891000, 2.58, 20211203을 가져온다.

정답은 둘째이다. 즉, 한 줄에 있는 모든 데이터를 가져온다는 것이다. 여기서 우리가 맨 앞에 있는 000020이라는 값만을 사용하고 싶다면, self.item_data 변수 내에서 맨 위에 각 데이터의 분류 기준을 설명하는 칼럼(code, day_date, day_SV 등)을 사용해주면 된다. 

 

def _load_day_data(self):
    for i in self.item_data.itertuples():
        item_code = i.code
        index_num = int(i.Index) + 1

즉, 위의 코드에서 i는 곧 000020, 20211228, 1, 20211228, 1, NULL, NULL, 20211228, 1, 16891000, 2.58, 20211203이라는 데이터가 담겨 있으며, i.code는 000020, i.day_date는 20211228, i.day_SV는 1, i.3min_date는 2021128, i.3min_SV는 1, i.5min_date는 'NULL', i.5min_SV 역시 'NULL' 형태로 데이터가 반환된다는 것이다. 그렇다면 코드 내에 있는 i.code는 000020일테고, int(i.Index)는 0일 것이다. 그 뒤에 +1을 한 것은 0부터 시작하는 인덱스 값을 첫 번째 종목은 1, 두 번째 종목은 2와 같은 형태로 입력하기 위함이다. 

 

 


728x90

 

 

 

[파일명 : algorithm_1] 종목별 차트 데이터 불러오기 

이제 종목별 데이터(  self.item_data  )를 얻어왔고 그 안에서 종목 코드를 추출(  i.code  )해내는 방법까지 살펴보았다. 그렇다면 이제 그 종목 코드를 하나의 변수로 설정해야 하는데, 그 절차는 전역 변수로 설정하는 globals()를 통해 실현 가능하다.

하지만 그 전에 앞서 일단 특정 종목코드에 해당하는 차트 데이터를 불러오는 것이 선행되어야 한다. 그래야 변수를 제작한 후에 그 변수 안에 차트 데이터를 입력할 수 있기 때문이다. 아래의 코드를 살펴보도록 하자.
※ 추가된 코드 : 8번째 줄

from etc import db_function as dbf

def _load_day_data(self):
    for i in self.item_data.itertuples():
        item_code = i.code
        index_num = int(i.Index) + 1
        
        day_data = dbf.load('all_data', 's'+i.code, row=None)

가장 먼저 etc 폴더 내에 있는 db_function 파일을 dbf라는 이름으로 import한 후에 db_function 내에 있는 def load() 함수를 통해 해당 종목의 차트 데이터를 가져와야 한다. 저장되어 있는 차트 데이터의 테이블 이름 역시 종목코드만으로 구성할 수 없기 때문에 그 앞에 알파벳 's'를 추가함으로써 저장했었기 때문에 불러오는 과정에 있어서도 'all_data'라는 데이터베이스 이름과 's'+i.code처럼 's'가 추가된 종목코드 형태를 테이블이름으로 전달해주어야 데이터를 불러올 수 있다. (아래의 사진을 보면 일봉과 3분봉, 15분봉 차트 모두 sXXXXXX과 같은 형태로 저장돼있음을 알 수 있다.)

 

 

 

[파일명 : algorithm_1] globals() 사용하기 

파이썬 내에는 지역 변수와 전역 변수라는 두 가지 개념이 존재한다. 이름에서도 한 번에 확인할 수 있듯이 지역 변수는 특정 함수 내에서만 사용할 수 있는 변수이고 전역 변수의 경우에는 어디서나 사용할 수 있는 변수이다. 종목 코드에 해당 종목의 주가 데이터가 담겨 있는 경우라면, 해당 변수는 전역 변수로 설정해주어야 백테스팅이 보다 효율적으로 이루어질 것이다. 따라서 종목코드를 변수 이름으로 하고 주가 데이터를 그 값으로 하는 변수는 당연히 전역 변수로 제작되어야 하며, 그 전역 변수로 설정하는 것은 globals()를 통해 만들 수 있다. 아래의 코드를 살펴보자.
※ 추가된 코드 : 6, 7번째 줄

def _load_day_data(self):

    for i in self.item_data.itertuples():
        item_code = i.code
        index_num = int(i.Index) + 1
        day_data = dbf.load('all_data', 's'+i.code, row=None)
        globals()['s{}'.format(item_code)] = day_data

맨 밑 줄의 코드를 보면 알 수 있듯이, 해당 변수가 전역 변수임을 나타내기 위해서는 앞에 globals()를 입력해주면 되고 그 변수의 이름은 [], 대괄호로 감싸면 된다. 다시 말해 맨 밑 줄의   globals()['s{}'.format(item_code)] = day_data  란 s000020이라는 이름으로 변수를 생성한 후, 그 변수 안에 000020 종목의 주가 데이터를 입력하라는 것이다. 위의 절차를 거친 후부터는 우리는 s000020이라는 변수를 print()하면 주가 데이터를 얻어올 수 있게 된다.

또한 우리는 이 절차를 for문 내에서 구축했기 때문에 모든 종목의 종목 코드를 변수로 설정한 후 그 변수 안에는 주가 데이터를 입력하게 되는 것이다. 하지만 모든 종목의 주가 데이터가 포함되어 있지 않은 경우에는 비어 있는 변수가 생성될 수 있기 때문에, 우리는 주가 데이터가 저장되어 있는 종목만을 대상으로 변수를 생성하도록 해보자. 

 

 

 

[파일명 : algorithm_1] 주가 데이터가 있는 종목 코드만 변수로 생성하기 

제목이 길어서 뭔가 거창한 코드를 제작해야 할 것 같지만, 사실 결론부터 이야기하자면 한 줄만 추가하면 된다. 왜냐하면 우리는 self.item_data 내에서 주가 데이터가 저장되지 않은 경우에는 NULL이 입력되어 있기 때문이다. 

 

하지만 문제는 파이썬 내에서는 이 NULL이라는 값을 제대로 읽어들이지 못한다는 것이다. 물론 print()문을 사용할 경우에는 NULL이 출력되긴 하지만 이게 어떤 문자열에 해당한다거나 하는 값이 아니기 때문에 if a == "NULL" 등과 같은 코드를 통해 비어있는 칸인지 아닌지를 구별할 수 없다. 이 때 우리가 사용할 수 있는 메서드가 바로   .fillna()  이다. 이는 비어있는 칸에 사전에 설정한 값들을 입력해주는 메서드인데, 예를 들어 NULL 자리에 0을 입력해서 가져오고 싶다면   .fillna(0)  을 사용해주면 모든 NULL 자리에 0이라는 값이 입력된다. 그렇다면 이 메서드를 item_data를 불러오는 boss 파일 내에 입력해주도록 하자.
※ 수정된 코드 : 14번째 줄

class tradesystem(QMainWindow, form_class):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        ## QT GUI 이벤트 연결 메서드
        self.pushButton_4.clicked.connect(self.pushB_4)
        self.lineEdit.returnPressed.connect(self.pushB_4)

        ## LOAD INDEX DATA
        self._load_index_data()
        
        ## LOAD ITEM DATA
        self.item_data = dbf.load('item_data', 'sv_load_chart', row=None).fillna(0)

 

그렇다면 이제 NULL 자리, 즉 주가 데이터가 없는 자리에는 0이라는 값들이 입력된 채로 self.item_data라는 변수에 저장된다. 그렇다면 우리는 여기서 이 0이라는 값을 바탕으로 주가 데이터가 없는 종목의 경우에는 변수를 생성하지 않도록 설정할 수 있다. 이렇게 코드를 구축하는 이유는 불필요한 또는 구축되어 있지 않은 데이터에 대한 작업을 제외함으로써 백테스팅 시간을 단축할 수 있기 때문이다.
※ 추가된 코드 : 7번째 줄

def _load_day_data(self):

    for i in self.item_data.itertuples():
        item_code = i.code
        index_num = int(i.Index) + 1

        if i.day_date != 0:
            day_data = dbf.load('all_data', 's'+i.code, row=None)
            globals()['s{}'.format(item_code)] = day_data

 

 


728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.