참고사항 시스템 트레이딩 관련 블로그들은 개인 경험을 바탕으로 작성되었습니다. 각자 자신만의 투자방식과 매매전략을 만들어 가시기 바랍니다.

개요

시스템 트레이딩에서는 백테스트를 통해 매매 로직을 검증합니다. 백테스트 환경은 실전거래와 최대한 유사하게 설정되어야 합니다. 백테스트 시 고려해야 할 사항과 그에 대한 처리 방법에 대해 알아보겠습니다.

백테스트 고려사항

백테스트를 진행하기 전에는 매매 로직을 처리하는 데 필요한 데이터가 있어야 합니다. 기본적으로 가격 정보에 대한 데이터가 반드시 필요합니다. 특히 캔들 데이터를 활용할 경우, 매매 로직에 필요한 대부분의 정보를 포함하고 있다고 생각합니다.

아래는 백테스트를 위해 필요한 데이터입니다

  • 백테스트를 위해 필요한 데이터
    • 종목 리스트(각 시점별 데이터) 및 캔들 데이터 (오늘부터라도 데이터를 수집합니다)
      • 종목이 상장되면 종목 리스트에 추가하고 데이터 수집 대상으로 포함합니다.
      • 종목이 상폐되면 종목 리스트에서 삭제되고 데이터 수집 대상에서 제외됩니다. (상폐 전에는 보통 투자 유의 대상으로 포함되므로 투자 유의 전까지는 캔들 데이터가 필요합니다.)
    • 종목에 대한 투자 유의 여부 데이터 (가상 자산 거래소의 공지사항에서 확인할 수 있습니다.)
    • 기타 매매 로직에 맞게 데이터를 추가로 수집합니다.

데이터가 준비되면 백테스트 환경에서 매매 로직을 검증해야 할 것입니다. 이미 구체적인 매매 로직이 있으면 해당 로직에 맞는 데이터를 활용하여 구현하기 쉬울 것입니다. 아직 로직을 설명하기 어렵다고 생각된다면, 일단은 이평선을 기준으로 한 매수/매도 로직을 만들어보는 것이 좋습니다. 일단은 매매로직을 만들고, 백테스트 해서한 사이클을 경험하면 개념이 잡힐 것입니다.

또한, 백테스트 환경을 탄탄하게 만들어야 합니다. 이를 위해서는 매매 수행 결과를 분석할 수 있도록 결과를 출력할 수 있는 기능이 함께 있어야 합니다. 매매 결과에는 투입된 금액, 매매 수행 결과, MDD, 손익 비율 등 거래 후에 확인되는 주요 지표들을 모두 제공해야 합니다.

백테스트를 위한 데이터 정재 작업

백테스트를 진행하면 로직 처리 과정에서 여러 번의 반복 작업이 필요합니다. 데이터를 읽어오고 종목별 또는 특정 시간별로 계속 반복 작업해야 하는 구조입니다. 이로 인해 성능이 떨어질 수 있습니다. 로직 검증에 많은 시간이 소요된다면 백테스트를 진행하는데 힘들어질 수 있습니다. 미리 데이터를 백테스트 환경에 맞게 정제해 놓으면 테스트를 빠르게 진행할 수 있습니다.

  • 데이터 수집 방식
    • 데이터를 수집할 때 기본 조건은 종목 코드이기 때문에 종목별로 수집해야 합니다.
      • 데이터 저장 구조: 종목 코드/날짜.json (종목별로 분리된 데이터)
    • 백테스트 환경에서는 날짜와 종목 코드 조건을 반복적으로 읽어와 매매를 수행합니다.
  • 데이터 정제 작업
    • 백테스트 환경에서는 대량의 데이터를 한 번에 읽어와 처리할 수 있는 구조로 저장되어 있다면 테스트 시간을 개선할 수 있습니다.
    • 수집된 데이터를 한 번 더 정제합니다. 기존 종목 코드로 분리된 캔들 데이터를 날짜를 기준으로 병합하는 작업을 수행합니다.
      • 데이터 저장 구조: 날짜.json (모든 종목의 데이터)

같은 데이터가 다른 형식으로 두 개 존재하므로 저장 용량이 커질 수 있습니다. 그러나 테스트 시간을 줄이면 더 많은 테스트와 더 많은 데이터로 테스트를 할 수 있습니다. 테스트를 진행하다보면 1분, 1초도 매우 답답하게 느껴질 것입니다. 최소한으로 내가 만든 로직의 결과는 1분 이내로 나와야 한다고 생각합니다. (저는 캔들 데이터(6년 데이터) 213MB를 읽어와 로직 처리하는 데 약 35초 정도 소요되었습니다.)

물론 데이터 변경 외에도 병렬 처리와 같은 프로그램 기술을 사용하면 더 빠르게 처리할 수 있습니다. 추후에 백테스트가 어느 정도 원활하게 진행되었을 때 고민해보시는 것도 좋습니다.

백테스트를 위한 컴포넌트

백테스트를 위해서는 몇 가지 컴포넌트가 필요합니다. 이후에는 동일한 매매 로직으로 실전 거래를 수행할 것이기 때문에, 공통된 컴포넌트들은 백테스트나 실전 거래와 무관한 데이터로 설계해야 합니다.

  • 백테스트를 위한 컴포넌트
    • 공통 컴포넌트 (백테스트와 실전 거래에 공통으로 사용됨)
      • 개별 종목 데이터 관리 객체 (종목 객체) - 캔들 데이터가 입력되면 매매 변수를 생성하고 매매 로직을 이용하여 매매 결과를 반환합니다.
      • 여러 종목을 관리하는 객체 (종목 그룹 객체) - 종목들의 매매 수행 결과를 통합하여 필요한 데이터를 제공합니다.
    • 자금을 관리하는 객체 (백테스트 환경에서만 사용) (자금 관리자 객체)
      • 백테스트 환경에서는 거래소와 유사한 역할을 수행합니다. 매매 결과를 이용하여 관리되고 있는 자금 내에서 최종 매매 여부를 확인하며, 매매 결과에 대한 리포트를 제공합니다.

각 컴포넌트의 역할은 논리적으로 분리돼 있어야 합니다. 개별 종목들은 개별적으로 매매 로직을 수행하고 매매 결과도 개별적으로 관리하는 것이 좋습니다. (이렇게 하면 나중에 실전 거래 시에 매매 계획을 생성할 때 종목별로 쉽게 추출할 수 있습니다.)

백테스트 처리 방안

아래는 앞서 언급한 컴포넌트를 기반으로 한 백테스트를 수행하는 Pseudo 코드입니다. (4시간 캔들 데이터를 기준으로 매매를 수행합니다)

** 시작날짜와 마지막날짜는 입력변수입니다.
var 시작날짜 = 2017년01월01일
var 마지막날짜 = 2023년09월21일

var 종목리스트 = 저장소에서 종목리스트가져오기()

**  종목,종목그룹에는 실전거래에서 재사용되므로 자금과 관련된 정보는 저장하면 않습니다. 
종목 = {
  종목명, 종목코드, 유의종목여부
  지표관리
  데이터처리(캔들데이터) - 캔들데이터를 저장하고 자동으로 매매변수들을 생성함.
  매매관리자 = { -- 종목내 캔들데이터가 들어오면 매매로직을 참고하여 매매를 수행함. 
    매매이력목록, 
    매매로직목록 - 매수/매도로직 목록을 관리
    매매수행()  -  매수/매도로직을 이용하여 매매를 수행후 결과를 저장
    매매계획(조회날짜,조회시간) - 해당일/시간의 매매결과를 리턴함.
  }
}

종목그룹 = {
  종목리스트,
  데이터처리(종목코드, 캔들데이터) - 종목별로 캔들데이터 저장함
  매매수행() - 종목별 매매수행() 호출함
  매매계획(조회날짜,조회시간) - 해당일/시간의 종목별 매매결과() 리턴함.
}

** 자금관리자는 백테스트환경에서 투자금액  손익을 계산하고 관리하기 위해서 사용됩니다(실전투자환경에서는 거래소에서 자금관리가 되고 있으로 자금관리자는 사용되지 않습니다)

자금관리자(거래소역할) ={
  총투자금액 = 100만원
  건별투자금액 = 1만원
  가상매매(매매계획) - 종목별 매매결과를 받아서, 투입된금액과 건별매매금액을 계산하여 내부
  중간매매결과출력(종목그룹,조회날짜,조회시간) - 해당일/시간에 매매했던 결과를 리턴함.
  전체매매결과출력() - 기간내 매매결과를 summary하여 손익비,MDD등 여러지표를 계산함.
}

for 조회날짜 from 시작날짜 to 마지막날짜 loop
   for 조회시간 in (01:00, 05:00, 09:00, 13:00, 17:00,21:00) loop
      for 종목 in 종목그룹.종목리스트 loop
       var 캔들데이터 = 저장소에서 캔들데이터가져오기(조회날짜,조회시간,종목.종목코드)
       종목그룹.데이터처리(종목.종콕코드, 캔들데이터)
      end loop
      종목그룹.매매수행()
      var 매매계획 = 종목그룹.매매계획(조회날짜,조회시간)
      자금관리자.가상매매(매매계획)
      자금관리자.중간매매결과출력(조회날짜,조회시간)
   end loop
end loop

종목그룹.전체매매결과출력()

Pseudo 코드이기 때문에 개략적으로 작성되었습니다. 전체적인 코드는 수집된 데이터를 기반으로 종목 객체에 캔들 데이터를 넣고 매매 로직을 수행한 후 결과를 추출하고, 추출한 결과를 가상 매매로 수행하여 최종 매매가 이루어지는 구조로 작성되어 있습니다.

  • 만약 이 코드를 실전 거래를 위한 코드로 변경한다면,
    1. 수집된 데이터를 종목에 넣는 작업 대신 실제 거래소에서 몇일 동안의 데이터를 가져오는 로직으로 변경합니다.
    2. 자금 관리자의 가상 매매를 수행하는 대신에 실제 거래소에서 주문을 수행하는 것으로 변경합니다.

종목과 종목그룹 객체는 백테스트 환경과 실전 거래 환경에서 모두 공통으로 사용됩니다. 데이터를 읽는 부분과 주문을 처리하는 부분만 변경하면 매매 로직을 변경 없이 그대로 사용할 수 있습니다. (개발할 때는 항상 실전 거래를 염두에 두고 개발해야 합니다.)

마무리

백테스트 처리 방법을 전반적으로 살펴보았습니다. 처음으로 백테스트를 시도하고 기능을 구현하는 것은 제 가장 어려운 부분입니다. 데이터 수집부터 백테스트까지 한다면 이제 로직만 잘 만들면 될 것 같습니다.

실제로 가장 어려운 부분은 매매 로직을 만드는 부분입니다. 그렇지만 검증 가능한 기반 시스템인 백테스트가 있다면 다양한 서적과 자료를 참고하여 나만의 매매 전략을 만들 수 있습니다. 내가 확신을 갖고 투자하는 방식과 직감을 활용하는 방식 중 어떤 투자 방식이 더 적합한지는 직접 백테스트를 통해 확인해보시기 바랍니다.

댓글남기기