Skip to content
This repository was archived by the owner on Sep 24, 2026. It is now read-only.

[검증] 백테스트를 표본 외로 돌린다 — v3 는 살아남았고, 재현율은 떨어졌다 - #96

Merged
RosieOh merged 3 commits into
releasefrom
fix/backtest-out-of-sample
Sep 22, 2026
Merged

RosieOh merged 3 commits into
releasefrom
fix/backtest-out-of-sample

Conversation

@RosieOh

@RosieOh RosieOh commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Closes #95

발견

const LIST_EDATE = '20260714';   // 하드코딩

두 달 뒤에 다시 돌려도 같은 68주를 다시 읽었다. 배포된 v3 는 바로 그 68주에서 후보 10개를 비교해 고른 것이라, 같은 데이터에서 잘 나오는 것은 당연하다. 고밀도 주가 26건뿐이니 과적합 위험이 실질적이었고, 확인할 유일한 방법인 새 주간이 영영 들어오지 않고 있었다.

고치니 표본이 136 → 156 단위(고밀도 26 → 34)로 늘었다.

결과 — v3 는 살아남았다

선택 당시 (136) 표본 외 포함 (156) 2026 홀드아웃 (40)
v3 AUC 0.886 0.871 0.842
베이스라인 AUC 0.823 0.812 0.809
v3 danger+ 재현율 73.1% 64.7% 63.6%
v3 오경보율 2.8% 2.6% 18.2%
v3 정밀도 70.4% 71.0% —

① 순위 능력은 유지됐다. 베이스라인과의 AUC 격차가 +0.063 → +0.059 로 거의 그대로이고, 2026 홀드아웃에서도 앞선다. 과적합으로 무너지지 않았다.

② 재현율은 떨어졌다. 73.1% → 64.7%. 같은 기간 베이스라인은 69.2% → 73.5% 로 올랐다. 지금은 "지난주 NIFS 보고서를 그대로 베끼는 것" 이 엔진보다 더 많이 잡는다. 안전 서비스에서 미경보는 가장 피해야 할 결과다.

⚠️ 다만 34건 중 22 vs 25, 3주 차이다. 짝지은 부트스트랩에서 v3 와 구별되는 후보가 하나도 없었다((b3)·(d)·(e)·(g)·(h) 전부 ΔAUC CI 가 0 을 포함).

그래서 이번에는 점수표를 고치지 않는다. 지금 데이터로 다시 고르면 그건 또 한 번의 선택-위-검증이고, 이 PR 이 막으려는 것이 정확히 그것이다.

함께 고친 보고 문제 둘

① 헤드라인이 배포본이 아니었다. 과제 A·B 의 "룰" 은 DEFAULT_RULE_SCORES(v1 폴백)다 — 코드 주석에도 "운영 점수표가 아니다" 라고 적혀 있다. 헤드라인만 읽으면 AUC 0.742 를 현행 성적으로 오해한다(v3 는 같은 실행에서 0.871). 두 절에 무엇으로 낸 숫자인지 적었다.

② 홀드아웃 경고가 하드코딩이었다. '표본 20개(고밀도 3개)' 가 박혀 있어, 실제로 40개(고밀도 11개)가 된 뒤에도 "순위를 논할 수준이 아니다" 라고 말했다. 주의 문구가 실제보다 세면 맞는 결과까지 버리게 된다. 실제 수를 세고, 고밀도 15건 미만일 때만 강한 경고를 낸다.

다음 재실행에서 볼 것

재현율 격차가 유지되는가. 유지되면 그때는 (b3) 고40/저10(재현율 CI 최대 +14.8%, 오경보율 최대 +8.8%)을 놓고 미경보와 과경보 중 무엇을 감수할지 정해야 한다. 그건 측정이 아니라 정책 결정이다.

검증

lint 0 · unit 1377 · typecheck OK
git rebase --exec 'npx tsc --noEmit'  → 3 커밋 전부 단독 통과
백테스트 재실행: 156단위 (NIFS 주간보고 78주 파싱, KMA 6,636조합)

과제 E-5(시간 분할 홀드아웃)는 이미 구현돼 있었다 — 데이터가 안 늘어서 쓸모가 없었을 뿐이다.

⚠️ `LIST_EDATE` 가 **'20260714' 로 하드코딩**돼 있었다. 그래서 두 달 뒤에 다시 돌려도 같은
68주를 다시 읽었다 — **다시 돌린다고 새로 아는 것이 없었다.**

이게 특히 나쁜 자리인 이유 — 배포된 점수표(v3)는 **바로 그 68주에서 후보 10개를 비교해
고른 것**이다. 고른 데이터로 다시 재면 잘 나오는 게 당연하고, 고밀도 주가 26건뿐이라
과적합 위험이 실질적이었다. 그걸 확인할 유일한 방법이 **선택에 쓰이지 않은 새 주간**인데,
종료일이 고정돼 있으면 그 데이터가 영영 들어오지 않는다.

기본을 실행일로 두고 BACKTEST_FROM / BACKTEST_TO 로 덮을 수 있게 했다. 특정 시점을
재현해야 할 때는 BACKTEST_TO 로 고정한다.

고친 뒤 표본이 136 → 156 단위(고밀도 26 → 34)로 늘었다.
과제 A·B 의 "룰" 숫자는 `DEFAULT_RULE_SCORES` 로 낸 것인데, 그건 **v1 폴백**이다
(risk-factors.ts 주석: "이건 DB 에 룰이 없을 때의 안전망이지 운영 점수표가 아니다").
실제 운영 점수는 RISK_RULE_VERSION 이 고른 DB 값(v3)에서 온다.

그래서 헤드라인만 읽으면 **AUC 0.742 를 현행 성적으로 오해한다.** 배포본(v3)은 같은
실행에서 AUC 0.871 이고, 그 숫자는 과제 E 표 안에만 있었다.

⚠️ 홀드아웃 경고 문구도 '표본 20개(고밀도 3개)' 로 하드코딩돼 있었다. 데이터가 늘어
실제로는 40개(고밀도 11개)가 됐는데도 "순위를 논할 수준이 아니다" 라고 말했다.
**주의 문구가 실제보다 세면 맞는 결과까지 버리게 된다.** 실제 수를 세어 찍고, 고밀도가
15건 미만일 때만 강한 경고를 낸다.
                     선택 당시(136)  표본외 포함(156)  2026 홀드아웃(40)
  v3 AUC                  0.886           0.871            0.842
  베이스라인 AUC           0.823           0.812            0.809
  v3 재현율               73.1%           64.7%            63.6%

**① 순위 능력은 유지됐다.** 베이스라인과의 격차가 +0.063 → +0.059 로 거의 그대로이고,
홀드아웃에서도 앞선다. 과적합으로 무너지지 않았다.

**② 재현율은 떨어졌다.** 73.1% → 64.7%. 같은 기간 베이스라인은 69.2% → 73.5% 로 올랐다.
즉 지금은 **"지난주 NIFS 보고서를 그대로 베끼는 것" 이 엔진보다 더 많이 잡는다.**
안전 서비스에서 미경보는 가장 피해야 할 결과이므로 가볍게 볼 수 없다.

⚠️ 다만 34건 중 22 vs 25, **3주 차이**다. 짝지은 부트스트랩에서 v3 와 구별되는 후보가
하나도 없었다(ΔAUC CI 가 전부 0 을 포함). **지금 데이터로 점수표를 다시 고르면 그건 또
한 번의 선택-위-검증이다.** 이번에는 고치지 않는다 — 그게 이번 실행의 결론이다.

다음 재실행에서 재현율 격차가 유지되면, 그때는 (b3) 고40/저10 을 놓고 **미경보와 과경보
중 무엇을 감수할지** 정해야 한다. 그건 측정이 아니라 정책 결정이다.
@RosieOh
RosieOh merged commit f63d248 into release Sep 22, 2026
2 checks passed
@RosieOh
RosieOh deleted the fix/backtest-out-of-sample branch September 22, 2026 10:00
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant