발단: 단순한 설정 누락
-
docker-compose.yml의environment항목 — 어떤 환경변수를 컨테이너에 넘길지 명시적으로 나열하는 방식 - 누락된 항목 —
HYBRID_ENABLE_ML - 결과 —
.env파일에 값을 뭐라고 적어두든 컨테이너까지 미전달 - 그래서
enable_ml— 항상 기본값false로 결정 - 실질 영향 — 앙상블 전략에서 ML에 배정된 가중치 0.15가 항상 0을 출력
- 발견 시점 — 2026년 7월 28일 개장 점검 과정
고치는 건 간단했다, 그런데
- 조치 —
HYBRID_ENABLE_ML=${HYBRID_ENABLE_ML:-false}를 목록에 추가해 전달 경로 복구 - 하지 않은 것 — “이제 .env 값대로 켜지겠지”로 종료
-
추가 결정 — 기본값을
false로 확정. 즉 지금 당장 동작을 바꾸지 않음 - 이유: “그럼 ML을 켜는 게 실제로 더 나은가”라는 질문이 별도로 잔존
- 원칙: 버그 수정과, 수정 후 그 기능을 켤지 말지는 별개의 판단
세 방향에서 검증하고, 셋 다 기각했다
36개 종목 대상, 세 가지 방식으로 확인:
1) ML을 켰을 때와 껐을 때의 A/B 백테스트
| 기간 | 설정 | Sharpe | 평균수익률 | 승률 |
|---|---|---|---|---|
| OOS | OFF | -1.69 | -0.19% | 31.2% |
| OOS | ON | -1.25 | -0.31% | 27.9% |
| 2년 | OFF | -0.84 | +3.07% | 45.8% |
| 2년 | ON | -1.31 | +1.61% | 39.4% |
- 두 기간 공통 — ML을 켰을 때 승률과 수익률 모두 악화
- 증가한 것 — 거래량만 약 40%
2) 분류 타깃을 바꿔봐도 기저율을 못 넘었다
| 방식 | cv_acc | 기저율 |
|---|---|---|
| 3분류(현행, ±1%) | 0.442 | 0.485 |
| 클래스 가중 3분류 | 0.431 | 0.485 |
| 2분류(flat 제거) | 0.516 | 0.555 |
- 결과: 세 가지 분류 방식 어느 쪽도 “아무것도 안 하고 다수 클래스만 찍는 것” 대비 성능 우위 확보 실패
3) 회귀 타깃으로 바꿔서 더 엄격하게 재검증
- 방식 — 회귀로 전환 후 정보계수(IC) 측정
- walk-forward 엄격 측정 결과 — IC +0.0051
- 95% 신뢰구간 — [-0.033, +0.044]. 0을 포함
- 해석 — 통계적으로 예측력이 있다고 말할 수 없는 수준
- 백테스트 비교 — baseline이 수익(+2.91%), 승률(45.4%) 모두에서 회귀 ML(+2.33%, 43.1%)을 상회
스스로 잡아낸 함정
여기서 중요한 대목 하나:
- 진단 초기
TimeSeriesSplit교차검증(CV) 결과 — IC +0.050 - 언뜻 보면 — 쓸만한 수치
- 취한 행동 — 그대로 믿지 않고 더 엄격한 walk-forward로 재측정
- 재측정 결과 — +0.050이 0과 통계적으로 구분되지 않는 수준으로 붕괴
진단 단계의 TimeSeriesSplit CV에서는 IC +0.050이 나왔으나, 엄격 walk-forward로 재측정하니 0과 구분되지 않았다. CV 방식의 낙관 편향이었다.
- 성격: “처음 방법이 틀렸다”를 스스로 인정하고 더 보수적인 검증으로 이행한 사례
- 배경 지식: CV가 실제보다 낙관적 결과를 주는 경향 인지
- 그에 따른 처리: 그 결과를 최종 판단 근거로 미사용, 재검증 1단계 추가
근본 원인: 라벨이 아니라 피처
세 검증 모두 ML 활성화를 기각. 단, 그 이유를 타깃(라벨) 정의의 문제로 보지 않았다. 실제 원인은 피처 쪽.
- 사용 중인 피처 — 15개
-
IC 0.03 이상인 피처 — 단 2개 ( feat_atr-0.055,feat_volatility-0.047) -
함의 — 타깃을 3분류로 하든 2분류로 하든 회귀로 하든, 애초에 학습할 만한 정보가 피처에 미포함
- 피처를 살리는 데 필요한 것 — 재무 데이터, 수급 데이터, 뉴스 같은 완전히 새로운 정보원
- 그때까지의 결정 —
HYBRID_ENABLE_ML을true로 변경하지 않음
검증
docker compose config | grep HYBRID_ENABLE_ML
→ HYBRID_ENABLE_ML: "false" # .env 값이 정상 전달됨(주석 혼입 없음)
- 확인한 것 — 전달 경로 자체의 복구 여부
- 적용 시점 — 다음 컨테이너 재시작(장 마감 후)으로 연기
- 연기 이유 — 실거래 중인 시스템이라 장중에 컨테이너 미조작
남는 교훈
설정 버그 하나(환경변수 누락)를 고치는 일이, “이 기능을 켜는 게 맞는가”라는 훨씬 큰 질문으로 이어졌다.
- 답을 찾는 과정의 부산물: 처음 쓴 검증 방법(TimeSeriesSplit CV)의 낙관 편향 발견
-
후속 조치: 더 엄격한 방법(walk-forward)으로 재확인
- 머신러닝 실무에서 이런 낙관 편향은 빈발
- 이 작업 전체를 관통한 태도: 좋아 보이는 첫 결과를 그대로 믿지 않고 “이 검증 방법 자체가 나를 속이고 있는 건 아닌가”를 한 번 더 의심