본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 01. 15:34

백테스트를 78% 절감했다 — LightGBM 대리 모델(Surrogate Model)로 가상화폐 Bot 후보를 사전 필터링하는 구현

요약

백테스트(BT)의 높은 계산 비용을 줄이기 위해 LightGBM 기반의 대리 모델(Surrogate Model)을 활용하여 가상화폐 매매 전략 후보를 사전 필터링하는 방법을 소개합니다. 전략 타입, 통화 쌍 등 범주형 변수만을 사용하여 합격 가능성이 낮은 후보의 78.5%를 사전에 기각함으로써 탐색 효율을 약 5배 높였습니다.

핵심 포인트

  • LightGBM 대리 모델을 통해 백테스트 부하 78.5% 절감
  • 정밀도보다 재현율(Recall)을 우선하여 합격 후보 누락 방지
  • 전략 타입, 통화 쌍 등 단순 범주형 변수만으로 학습 구현
  • 계산 비용 절감을 통해 동일 자원으로 더 많은 전략 탐색 가능

백테스트(Backtest, BT)는 무거운 처리입니다. 시그널을 15개 생성하여 각각을 IS(In-Sample) 기간 및 OOS(Out-of-Sample) 기간으로 테스트하면, 1회의 야간 배치(Batch) 작업에 수 시간이 걸립니다.

이 기사에서는 과거의 백테스트 결과로부터 "합격할 것 같은 후보"를 LightGBM으로 예측하여, BT 실행 전에 후보의 78%를 기각하는 메커니즘의 구현을 소개합니다.

문제: 백테스트가 병목 현상(Bottleneck)

자동 매매 Bot 개발에서는 알파(Alpha, 이익 기회)를 탐색할 때 대량의 후보를 백테스트해야 합니다.

필자가 운용하고 있는 GMO coin용 시스템에서는 매일 밤 15개의 후보를 생성하여 IS/OOS 평가를 수행하고 있습니다. 합격률은 약 2.75%(2,036개 후보 중 56개 합격)입니다.

즉, 97.25%의 후보는 BT를 돌려도 불합격입니다. 이 낭비를 줄일 수 있다면, 동일한 계산 비용으로 더 많은 후보를 탐색할 수 있습니다.

합격률: 56 / 2036 = 2.75%
→ 100개 후보 BT를 해도 2~3개만 합격
→ 합격하지 않는 97개의 BT 시간이 통째로 낭비됨

접근 방식: 메타 모델(Meta-model)로 사전 필터링

아이디어는 간단합니다.

과거의 BT 결과를 트레이닝 데이터(Training Data)로 사용하여, "이 전략 타입 × 통화 쌍 × 시간 프레임의 조합은 합격하기 쉬운가?"를 학습시킨다.

이것이 "대리 모델(Surrogate Model)"이라고 불리는 접근 방식입니다. 실제 BT를 실행하는 대신, 저렴한 예측을 통해 사전에 후보를 좁힙니다.

특징량(Feature): 단 3개의 범주형 변수

놀라울 정도로 심플하며, 특징량은 다음 3가지만 사용합니다.

특징량
strategy_typemean_reversion, donchian_breakout 등
...

"BT 결과의 수치(PF, Calmar ratio 등)는 사용하지 않나?"라고 생각하실 수도 있습니다.

사용하지 않습니다. 이유는 두 가지입니다.

  • BT 전 시점에서는 당연히 BT 결과가 존재하지 않음 (추론 시에 사용할 수 없음)
  • "어떤 조합이 역사적으로 합격하기 쉬운가"라는 구조적 패턴을 배우고 싶음

구현 코드

학습 (train_categorical_surrogate.py)

import csv, json, pickle
from pathlib import Path
import pandas as pd
...

추론 (auto_bt_runner.py 내)

import pickle
from pathlib import Path
# 모델 로드
...

결과: 78.5%의 후보를 기각

평가 결과(evaluation_report.json):

데이터: 2,036건 (합격=56, 합격률=2.75%)
5-fold CV AUC: 0.753 ± 0.048
임계값(Threshold) 0.30에서의 성능:
...

임계값 0.30이라는 낮은 설정이 중요한 포인트입니다.

"합격하기 쉽다"라는 확신이 없더라도 합격 가능성이 있는 후보는 모두 BT를 실행합니다. 정밀도(Precision)보다 재현율(Recall)을 최우선으로 한 설계입니다.

합격 후보를 놓칠 리스크보다, 불필요한 BT를 줄이는 것이 목적이므로 이 설계는 합리적입니다.

실제 BT 절감 효과:

  • 15개 후보 → 78.5% 기각 → 실제로 BT를 하는 것은 약 3.2개 후보 (15 × (1 - 0.785))
  • 탐색 효율: 이론상 약 5배 (15 ÷ 3.225)

구현 시 주의할 점 (ハマりポイント)

pickle + LightGBM의 환경 의존성 문제

categorical_surrogate.pkl은 LightGBM 모델을 pickle로 직렬화한 것입니다. 이 파일을 로드하려면 실행하는 Python 환경에 lightgbm이 설치되어 있어야 합니다.

필자의 경우, 학습 환경(.venv_ml)과 BT 실행 환경(.venv_quant)에서 venv를 나누어 사용하고 있었습니다.

학습: .venv_ml/bin/python3 → lightgbm ✅
실행: .venv_quant/bin/python3 → lightgbm ❌ (설치 누락!)

이 상태에서 cron이 야간에 실행되면, 다음과 같은 에러가 발생하며 조용히 실패합니다:

ModuleNotFoundError: No module named 'lightgbm'

수동 실행은 별도의 쉘에서 venv를 활성화(activate)하여 작동하고 있었기 때문에, "수동은 성공하지만 cron은 실패하는" 수수께진 증상이 나타났습니다.

교훈: pickle로 직렬화된 ML 모델을 별도의 venv에서 사용할 경우, 의존 라이브러리(dependency library)가 설치되어 있는지 반드시 확인해야 한다.

# cron에서 사용하는 python에 lightgbm이 있는지 확인
/path/to/.venv_quant/bin/python3 -c "import lightgbm; print(lightgbm.__version__)"

학습 데이터의 품질이 핵심

2,036개의 샘플로 학습했다고는 하지만, 합격률 2.75%(56/2036)라는 극단적인 불균형 데이터(imbalanced data)입니다.

scale_pos_weight = 불합격 수 / 합격 수 = 1980 / 56 ≈ 35

라는 가중치 부여가 없다면, 모델은 모든 데이터를 "불합격"으로 예측할 뿐입니다.

LightGBM의 LGBMClassifier에는 scale_pos_weight 파라미터가 있으며, 이를 설정함으로써 불균형 데이터를 다룰 수 있습니다.

미지의 카테고리 (새로운 전략 추가 시)

새로운 전략 타입(strategy type)을 추가할 경우, LabelEncoder가 미지의 카테고리(unknown category)를 만나 에러가 발생합니다.

# 대책: 미지의 카테고리를 핸들링
try:
    enc_val = encoder.transform([category])[0]
...

구현에서는 미지의 카테고리가 들어올 경우 대리 필터(surrogate filter)를 통과하도록 설계했습니다.

요약

단 3개의 카테고리 변수만으로 78%의 BT(백테스트)를 스킵할 수 있는, 심플하고 효과적인 대리 모델(surrogate model) 구현을 소개했습니다.

포인트를 정리하면:

  • 특징량(feature)은 "전략 타입 × 통화쌍 × 시간봉" 3가지만 사용
  • LGBMClassifier + scale_pos_weight (불균형 대응)
  • 임계값(threshold)은 낮게(0.30) 설정하여 high-recall 설계
  • pickle 로드 시 lightgbm의 venv 확인 필수

대리 모델은 탐색 효율화의 기초 단계이지만, 실제 운용 시 축적된 BT 데이터가 늘어날수록 정밀도가 향상됩니다. 2,036건에서 AUC=0.753이었으나, 데이터가 두 배가 된다면 추가적인 개선을 기대할 수 있습니다.

관련 기사

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn ML의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0