PYTHON/Back test

1.2 백테스팅하기 - 필요한 자료 구축하기

  • -

 

들어가기 전에

백테스팅을 진행하기 위해 필요한 자료에는 어떤 것들이 있을지 생각해보자. 개개인이 만들고자 하는 알고리즘이 어떤 알고리즘인가에 따라 모두 다르겠지만, 기본적으로 종목별로 상이한 데이터(상장주식수, 거래량, 거래대금, 주가 차트 데이터 등)와 종목에 상관없이 공통적인 데이터(지수 데이터, 시황 데이터 등) 두 개는 반드시 필요할 것이다.

여기서 백테스팅을 진행하는 데에 있어서 필요한 여러 가지 데이터들을 백테스팅을 시작한 시점에서 데이터들을 조회하도록 하고 특정 변수에 저장하도록 한 후에 알고리즘 파일을 진행하는 것이 효과적이다. 아래의 그림을 참고해보자.

1번 작업 절차의 경우에는 boss 파일이 실행되는 순간 지수와 관련된 데이터들을 불러온 후, 알고리즘이 실행되면 데이터들이 저장되어 있는 모든 종목들의 데이터들을 불러오게 된다. 다만 2번 작업 절차의 경우에는 boss 파일이 실행되어도 아무런 작업을 수행하지 않고, 알고리즘이 실행되면 모든 데이터들을 조회하기 시작한다.

만약 백테스팅을 진행하는 과정에서 1번 알고리즘을 기반으로 한 백테스팅이 끝난 후에 2번 알고리즘을 실행한다고 가정하보도록 하자. 이 경우 1번 프로세스에서는 2번 알고리즘의 종목별 데이터만 조회하면 되겠지만, 2번 프로세스의 경우에는 지수 데이터와 종목별 데이터를 또 새롭게 조회해야 하기 때문에 작업 속도가 더 느려지게 된다.

물론 지수 데이터에 해당하는 코스피 지수 데이터와 코스닥 지수 데이터는 데이터 양이 방대하지 않기 때문에 금방 불러오게 되고, 1번 프로세스와 2번 프로세스 중 어떤 방법을 선택하더라도 큰 차이는 없다. 하지만 작업 절차를 어떻게 가져가냐에 따라 작업 수행헤 소요되는 시간이 달라진다는 점을 고려하여 boss 파일 내에서 구축할 데이터와 알고리즘 파일에서 구축할 데이터를 사전에 구분한 후에 코드를 구축하는 게 좋다. 물론 본인은 1번 프로세스로 제작할 것이다.

 

 

[파일명 : db_function] 데이터 조회 파일 생성하기

사실 백테스팅을 하는 과정에 있어서 데이터를 조회하는 함수를 상당히 자주 사용한다. 예를 들면 pd.read_sql이 가장 대표적인데, 그때마다 관련 변수를 입력하는 등의 번거로운 작업들을 거쳐주어야 한다. 따라서 데이터베이스에 저장되어 있는 값들을 불러올 수 있는 파일을 제작한 후, 데이터베이스와 관련된 작업들은 모두 해당 파일을 거쳐서 진행하도록 하기 위한 코드를 제작해보자.
※ 데이터베이스를 관리하기 위한 파일 이름은 db_function으로 설정했습니다.
※ boss 파일 내에서는 import db_function as dbf로 설정했습니다.

데이터베이스 내에 있는 DB와 연결하는 코드 역시 이전 게시글에서 작성했었는데, 그 내용을 참고하도록 하고 여기서는 데이터를 조회하는 반복적인 작업을 어떻게 함수로 구축해서 사용할 것인지만 다루어보고자 한다. 혹시 모를 경우를 위해 아래에 링크를 걸어둘 예정이고, 또 혹시 모를 경우를 대비하여 dbf 파일 내의 코드를 첨부할 예정이다.
※ 파이썬 + Mysql, 설치와 설정 방법
※ 파이썬 + Mysql, 파이썬으로 연동하기
※ 파이썬 + Mysql, 데이터베이스 자동 처리하기

이제 파이썬과 Mysql을 연결했다면, 데이터를 불러오는 함수를 제작해보도록 하자. 파이썬과 Mysql을 연결하는 과정에서 느꼈겠지만, 우리가 사용하고자 하는 데이터베이스에 따라 서로 다른 engine을 read_sql의 인자로 사용해야 한다. 따라서 함수 내에서 사용할 데이터베이스의 이름을 조건문을 통해 판별한 후에, 특정 engine을 사용하여 데이터를 불러오도록 설정해야 한다.

from sqlalchemy import create_engine
import pandas as pd

engine_all = create_engine('mysql+mysqldb://root:(비밀번호)@127.0.0.1:3306/all_data', encoding='utf-8')
con_all = engine_all.connect()

engine_3min = create_engine('mysql+mysqldb://root:(비밀번호)@127.0.0.1:3306/3min_data', encoding='utf-8')
con_3min = engine_3min.connect()

engine_15min = create_engine('mysql+mysqldb://root:(비밀번호)@127.0.0.1:3306/15min_data', encoding='utf-8')
con_15min = engine_15min.connect()

engine_item = create_engine('mysql+mysqldb://root:(비밀번호)@127.0.0.1:3306/item_data', encoding='utf-8')
con_item = engine_item.connect()

engine_index = create_engine('mysql+mysqldb://root:(비밀번호)@127.0.0.1:3306/kos_data', encoding='utf-8')
con_index = engine_index.connect()


def load(dbname, tablename, row):

    if dbname == 'all_data':
        data = pd.read_sql("SELECT * FROM " + tablename + " ORDER by date desc", engine_all)
        return data

    elif dbname == '3min_data':
        data = pd.read_sql("SELECT * FROM " + tablename + " ORDER by date desc", engine_3min)
        return data

    elif dbname == '15min_data':
        data = pd.read_sql("SELECT * FROM " + tablename + " ORDER by date desc", engine_15min)
        return data

    elif dbname == 'kos_data':
        data = pd.read_sql("SELECT * FROM " + tablename + " ORDER by date desc", engine_index)
        return data

    elif dbname == 'item_data':
        data = pd.read_sql("SELECT * FROM " + tablename, engine_item)
        return data

 

위처럼   def load   내에서 dbname과 tablename, row라는 세 개의 변수를 인자로 설정한 후에 dbname이라는 변수를 대상으로 조건문을 설정하고, 그 조건문들에 따라서 read_sql 맨 뒤에 있는   engine   변수를 서로 다르게 설정했다. 즉,   dbname  이 "all_data"인 경우에는 engine_all을, "3min_data"인 경우에는 engine_3min을 사용하게 함으로써 특정 데이터베이스에 접근할 수 있도록 한 것이다. 본인이 사용하는 데이터베이스의 이름과 그 내용은 잠시 후, 아래에서 확인할 수 있다.

 

 


728x90

 

 

[파일명 : boss] 지수 데이터 불러오기 

이 지수 데이터는 지수를 고려한 백테스팅을 진행할 것이라면 좋은 내용이겠지만, 그렇지 않은 경우에는 굳이 살펴볼 필요가 없다. 또한 데이터베이스의 경우에는 개개인마다 서로 다른 이름을 갖고 있기 때문에 일률적인 설명은 불가능하다. 다만 혼선을 방지하기 위해 본인의 데이터베이스에는 어떤 DB와 Table들이 있는지를 먼저 고지하고 넘어가도록 하겠다. 

  • 15min_data : 15분봉 차트 데이터
  • 3min_data : 3분봉 차트 데이터
  • 5min_data : 5분봉 차트 데이터
  • all_data : 일봉 차트 데이터
  • condition_list : 조건검색식 관련 DB
  • day_data(미사용)
  • item_data : 종목별 유통주식수와 상장주식수 정보 + 차트 데이터 저장 여부 표시
  • kos_data : 지수 데이터로, kospi_data와 kosdaq_data를 테이블로 갖고 있음
  • kosdaq_data(미사용)
  • kospi_data(미사용)

(좌) WorkBench 내에 있는 데이터베이스 현황    (우) 파이참 내 파일 구조

이제   db_function  파일 내에서 데이터를 불러오는   def load()  함수를 제작했으니 boss 파일 내에서 지수 데이터를 불러와서 확인해보도록 하자.
※ [우측 사진] db_function 파일은 etc 폴더 내에 있습니다.
※ [우측 사진] etc 폴더 내에는 cal_function 파일과 set_logging 파일, talib_calculator 파일이 있습니다.
※ 차근차근 하나씩 설명드릴 예정입니다.
※ set_logging 파일은 로그를 생성하도록 하는 파일로, 다음의 게시물을 참고하시면 됩니다. (바로가기)

 

def _load_index_data(self):
    kospi_index = dbf.load('kos_data', 'kospi_index', row=None).sort_values('date').reset_index(drop=True)
    kosdaq_index = dbf.load('kos_data', 'kosdaq_index', row=None).sort_values('date').reset_index(drop=True)

위와 같이 boss 파일 내에서   _load_index_data  라는 함수를 만들었고 2번째 줄에서는 dbf.load() 메서드를 통해 dbname에는 'kos_data'를, tablename에는 'kospi_index'를, row=None 값을 입력했으며 그 결과값을 대상으로 sort_values() 메서드 내에 'date'를 인자로 전달해줌으로써 일자 순으로 데이터를 정렬한 후, 그 정렬된 데이터를 바탕으로 인덱스 값을 재설정하도록 reset_index() 메서드를 사용했다. reset_index() 내부의 drop=True는 인덱스 값을 재설정한 후에 남아 있는 인덱스 값을 제거할 것인지 남겨둘 것인지를 결정해주는 것이다.

  • dbf.load() 내부의 인자 : dbname, tablename, row
  • sort_values()의 역할 : 괄호 안에 입력된 값을 바탕으로 데이터를 정렬
  • reset_index()의 역할 : 정렬된 데이터프레임 내에서 인덱스 값을 재설정
  • reset_index(drop=True)의 역할 : 인덱스 값을 재설정한 후에 기존에 존재하던 인덱스 값을 삭제(권장 사항)

 

 


728x90
반응형
Contents

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

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