이 실험의 산출은 최종 모델이 아니다. 여기서 고른 것은 지금까지 중
가장 나은 후보 하나다. 대회 시즌 전에도 중에도 더 나은 후보가 나오면
갈아 끼운다. 실제 제출에 무엇을 쓰는지는 이 페이지가 아니라
experiments/CONTEST_MODELS.yaml 이 정하고 그 결정은 사람이 한다.
대회 라이브(2026-09-28~)가 유일한 최종 OOS 이므로 그 전에는 어떤 모델도
“최종”이라고 부를 근거가 없다.
지금까지의 후보 중 가장 나은 것은 선물 1분봉 + 10년선물 + 일별 컨텍스트 70피처에
ExtraTrees, 레짐 필터 없음이다. 워크포워드 승률 60.36%(n=1,491, 누적 +669틱)로
사전등록한 기준선 셋을 모두 넘었고 적응 상수 대비 McNemar p=0.0009.
그런데 풀링 승률보다 중요한 것은 연도별 표다 — 모델이 상수를 이긴다기보다,
모델은 어느 해에도 지지 않는다. 여덟 해 전부 56.7% 이상인 반면 적응 상수는
47.7%~61.3%로 요동친다.
가설과 결론
- 가설
- 가진 데이터를 전부 쓰고 기저율을 모델 안에 넣으면, 그리고 관측
드리프트
d를 뺄셈이 아니라 피처로 주면(가설 대장 H3), 연도 확장창 워크포워드에서 적응 상수와 20분 모멘텀 규칙을 모두 넘는다. 덧붙여 미시 신호가 언제 사는지를 레짐 필터가 가려 준다. - 결론
- 채택 — 단, 가설의 절반만. 선정된 조합
T0_core / ExtraTrees / 게이트 없음이 기준선 셋을 모두 넘었다 (60.36% 대 적응 상수 55.06% · 규칙 58.75% · 컨셉1 53.99%, McNemarb=315 : c=236, p=0.0009). 그러나 레짐 필터는 값을 못 했고 (학습창이 고른 게이트가 필터 없음보다 −0.68%p), H3도 지지되지 않았다 (체결틱과d_obs를 넣은 티어가 넣지 않은 티어와 같은 60.30% — 선정된 T0는 60.36%). 이긴 것은 “데이터를 더 넣어서”가 아니라 모델 계열을 바꿔서다 — 강하게 무작위화한 배깅 트리가 부스팅과 선형 계열을 모두 이겼다.
용어
이 리포트 안에서만 읽고 끝낼 수 있게 쓴 기호를 여기서 정의한다.
- P34
- 15:34 봉의 현재가. 봉 라벨이 종료시각이라 15:34:00에 확정된다. 제출 창(15:30:00~15:34:59) 안에서 볼 수 있는 마지막 봉 가격이다.
- P_ref
- 대회 기준가. 15:35 직전 마지막 체결가다. 채점의 출발점이지만 제출 시점에는 관측되지 않는다 — 이 사실이 아래 여러 판단을 가른다.
- P45
- 대회 정답 가격. 15:45 동시호가로 체결되는 종가다.
- ref_tick
- 정답을 틱으로 센 값,
(P45 − P_ref) / 0.01. 이 부호를 맞히는 것이 대회 과제다. - d_obs
- 컷오프에서 관측 가능한 드리프트,
P_obs_3458 − P34.P_obs_3458은 15:34:58까지의 마지막 체결가로,P_ref와 같은 날이 75.03%다. H3이 쓰는 피처가 이것이다. - 0틱
ref_tick이 0인 날. 대회에서 무효로 처리되며 학습과 채점 양쪽에서 뺀다. 실측 2,387일 중 532일(22.3%)이다.- 승률
- 0틱 무효일을 뺀 평가일 중 방향을 맞힌 날의 비율. 대회 순위의 1차 기준이고 동률이면 누적틱으로 가른다.
- 워크포워드
- 연도 확장창 방식. 시험연도 Y에 대해 학습은 티어 시작연도부터 Y−1까지, 시험은 Y만. 해가 갈수록 학습창이 길어진다. 시험연도를 본 뒤 설정을 고르지 않는 것이 이 방식의 요점이다.
- 적응 상수
- 기준선. 그날까지의 후행 120일 다수 클래스를 매일 그대로 제출한다. 0002가 “어떤 모델도 못 넘었다”고 보고한 고정 상수의 갱신판이다.
- 레짐 게이트
- 열리면 모델 방향, 닫히면 적응 상수를 내는 이진 문. 버리는 날이 없어 커버리지 100%가 유지된다.
- 티어
- 피처 그룹 묶음. 그룹마다 원천 시작 연도가 달라 티어마다 학습 시작과 시험연도가 다르다. 티어끼리 승률을 직접 비교하지 않는다(표본이 다르다).
데이터와 컷오프
일곱 그룹 150피처. 정본은 src/project_at/signals/contest_features.py다
— 실험 폴더가 아니라 src/에 둔 이유는 라이브 예측기가 같은 코드로
피처를 계산해야 학습과 서빙이 갈리지 않기 때문이다.
| 그룹 · 원천 | 기간 | 해상도 | 열 | 쓰임 | 컷오프 |
|---|---|---|---|---|---|
3년선물 1분봉 price_data_f3y_1m_raw | 2016-12 ~ 2026-09 | 1분 | 24 | 모멘텀·범위 내 위치·거래량·미결제 · 후보 모델에 포함 | 15:34:00 |
10년선물·커브 price_data_f10y_1m_raw | 2016-12 ~ 2026-09 | 1분 | 6 | 10년 수익률·3/10 커브 · 후보 모델에 포함 | 15:34:00 |
| 일별·캘린더 (수급·입찰·금통위·지표·휴장·롤·기저율) | 2012 ~ 2026-09 | 일 | 37 | 후행 상승비율·전일 정답·이벤트 플래그 · 후보 모델에 포함 | T-1 까지만 |
장중 투자자수급 intraday_ktb_positions | 2019-01 ~ 2026-08 | 30초 | 28 | 투자자별 순매수 수준·변화 (T1 이상) | 15:30:00 |
해외 장중 ust_intra (미국채 2/5/10년) | 2021-01 ~ 2026-09 | 1분 | 10 | 미국채 금리 변화·커브 (T2 이상) | 15:34:00 |
| 국내 교차자산 (IRS · 국고채 현물 · 달러원 · KOSPI200) | 2019-01 ~ 2026-09 | 1분 | 33 | 금리 bp 변화·가격 수익률 (T2 이상) | 15:29 / 15:34 |
체결틱 미시구조 선물3년_체결틱 | 2023-01 ~ 2026-08 | 틱 | 12 | 5분창 체결·틱룰·대량비율 + d_obs (T3) | 15:34:58 |
일별 표는 발표 시각이 전부 미확인이라 T-1까지만 쓴다 — 모르는 것을 추측해 당일 값을 쓰지 않는다. 장중 수급은 벤더 집계 지연이 미실측이라 4분 마진을 두고 15:30에서 끊었다. IRS·현물이 15:29인 이유는 9절에 있다. 표본은 대상일 2,387일 중 0틱 무효 532일과 가드(봉 누락·수능일 비정상 세션) 14일을 뺀 유효일 1,841일이다.
모델과 학습 설정
하이퍼파라미터는 탐색하지 않고 config.yaml에 사전등록한 규제
설정을 그대로 쓴다. 표본이 하루 1행 1,841일뿐이라 폴드 안 탐색까지 하면 탐색 자체가
과적합 통로가 되기 때문이다. 시드 다섯 개는 확률을 평균하고, 시드별 승률의 표준편차를
따로 기록해 “차이가 시드 흔들림보다 큰가”를 판정에 쓴다.
| 이름 | 계열 | 설정 |
|---|---|---|
lgb | LightGBM 분류 | n_estimators 400 · max_depth 4 · num_leaves 15 · min_data_in_leaf 40 · learning_rate 0.03 · subsample 0.8 · colsample_bytree 0.7 · reg_lambda 1.0 |
xgb | XGBoost 분류 | n_estimators 400 · max_depth 4 · learning_rate 0.03 · subsample 0.8 · colsample_bytree 0.7 · min_child_weight 10 · reg_lambda 1.0 |
et | ExtraTrees (선정 후보) | n_estimators 400 · max_depth 6 · min_samples_leaf 20 · max_features sqrt · 중앙값 대치 |
rf | RandomForest | 같은 설정 (n_estimators 400 · max_depth 6 · min_samples_leaf 20) |
lin | 선형회귀 | ref_tick을 회귀하고 부호를 읽는다 · 중앙값 대치 + 표준화 |
ridge | 릿지 회귀 | alpha 10.0 · 나머지 동일 |
log | 로지스틱 회귀 | C 0.1 (L2) |
svm_rbf | SVM · RBF 커널 | C 1.0 · gamma scale · 확률 보정 |
svm_lin | SVM · 선형 커널 | C 0.05 · 3겹 Platt 보정 |
ens | 확률 평균 | 멤버 lgb · log · svm_rbf · ridge |
LightGBM의 min_data_in_leaf는 scikit-learn 래퍼에서
min_child_samples라는 이름으로 들어간다 — 같은 값이다.
ExtraTrees·RandomForest는 결측을 못 받으므로 학습창에서만 적합한 중앙값
대치를 파이프라인 안에 넣었다(대치 통계가 시험연도를 보지 않는다).
| 티어 | 그룹 | 피처 | 학습 시작 | 시험연도 |
|---|---|---|---|---|
T0_core | 선물봉 + 10년 + 일별 | 70 | 2017 | 2019~2026 (8해) |
T1_flow | + 장중 수급 | 98 | 2019 | 2021~2026 (6해) |
T2_cross | + 해외 · 국내 교차자산 | 137 | 2021 | 2023~2026 (4해) |
T3_full | + 체결틱 · d_obs | 149 | 2023 | 2024~2026 (3해) |
Tall_nan | 전부 · 결측 허용(트리만) | 149 | 2017 | 2019~2026 (8해) |
T0_core의 폴드 여덟 개. 시험연도 Y의 학습은
2017년부터 Y−1년 말까지(짙은 막대), 채점은 Y년만(주황 막대)
한다. 막대 안 숫자는 그 폴드가 실제로 쓴 날 수다 — 런 로그에서 그대로
읽었다. 해가 갈수록 짙은 막대만 길어지는 것이 확장창이고, 점선
계단이 그 경계가 한 해씩 미래로 가는 모습이다. 주황 막대를 모두 더한
1,491일이 이 리포트에 나오는 모든 승률의 분모다. 아래: 티어마다
원천 시작 연도가 달라 학습 시작과 첫 시험연도가 다르다 — 티어끼리
승률을 직접 비교하지 않는 이유가 이 그림이다.g1_pos_20m ≤ 0.8508 로 갈린다. 각 상자의 n 은 학습 1,693행 중 그 노드에 떨어진 수다.| 분기 위치 \ 피처 | g6_is_auction_day | g1_pos_day | g1_pos_20m | g1_pos_60m | g6_dow | g6_dom | g6_prev_up | g6_rate_CD3개월 | g6_days_to_expiry | g6_is_roll_week | g6_rate_CALL1일한은 | g6_rate_통안1년 | g6_event_any | g6_vol_60d | g1p_curve_20m | g1_ma_dev_20m | g1_ma_dev_60m | g6_dflow_증권선물순매수수량 | g6_prev5_up | g1p_f10_ret_20m | g6_dflow_개인순매수수량 | g6_baserate_20d | g1_ret_20m | g6_dflow_은행순매수수량 | g1p_f10_ret_open | g6_abs_px_chg_20d | g1_ret_10m | g1p_f10_ret_60m | g6_abs_baserate_dev | g6_baserate_60d | g6_baserate_120d | g6_baserate_250d | g6_dflow_외국인합순매수수량 | g1_rng_20m | g1_vol_last30_share | g1_ret_60m | g1_ret_120m | g1_ret_1m | g1p_f10_ret_5m | g6_px_chg_60d | g1_ret_30m | g1_ret_open | g1p_curve_60m | g6_vol_20d | g1_ret_5m | g1_vwap_dev | g6_px_chg_20d | g6_is_auction_3y | g6_dflow_투신순매수수량 | g1_vol_day | g6_prev_ref_tick | g1_rng_day | g1_vol_last5_share | g6_dflow_보험순매수수량 | g1_rng_60m | g1_oi_chg_day | g6_px_chg_5d | g6_rate_통안1년_chg5d | g6_prev_d_tick | g1_oi_chg_60m | g6_prev_abs_ref_tick | g6_is_month_end | g6_rate_CALL1일한은_chg5d | g1_gap_open | g6_is_mpc_day | g6_rate_CD3개월_chg5d | g6_is_quarter_end | g1_n_bars | g6_us_event_today | g6_us_event_prev | 합 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 깊이 0 (뿌리) | 26 | 31 | 35 | 39 | 5 | 12 | 5 | 4 | 7 | 19 | 2 | 1 | 11 | 6 | 6 | 9 | 7 | 4 | 7 | 13 | 0 | 6 | 10 | 2 | 7 | 3 | 7 | 13 | 0 | 1 | 0 | 3 | 2 | 3 | 1 | 6 | 5 | 9 | 10 | 1 | 6 | 1 | 7 | 4 | 3 | 4 | 6 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 10 | 0 | 0 | 0 | 7 | 0 | 0 | 0 | 1 | 4 | 1 | 2 | 0 | 0 | 0 | 400 |
| 깊이 1 | 58 | 46 | 44 | 47 | 15 | 23 | 7 | 18 | 16 | 51 | 14 | 8 | 14 | 12 | 21 | 17 | 9 | 7 | 10 | 13 | 8 | 8 | 14 | 14 | 9 | 7 | 15 | 10 | 6 | 9 | 2 | 3 | 9 | 8 | 5 | 13 | 8 | 8 | 8 | 12 | 11 | 10 | 10 | 10 | 5 | 3 | 8 | 5 | 4 | 3 | 4 | 4 | 3 | 5 | 5 | 9 | 4 | 5 | 4 | 4 | 4 | 5 | 2 | 0 | 6 | 3 | 3 | 0 | 0 | 0 | 765 |
| 깊이 2 | 75 | 62 | 64 | 60 | 39 | 26 | 24 | 24 | 28 | 42 | 25 | 27 | 16 | 26 | 31 | 29 | 28 | 19 | 16 | 15 | 19 | 17 | 24 | 15 | 18 | 21 | 19 | 18 | 6 | 14 | 19 | 14 | 15 | 19 | 20 | 26 | 20 | 16 | 16 | 17 | 17 | 20 | 16 | 11 | 18 | 17 | 11 | 10 | 11 | 17 | 7 | 14 | 11 | 12 | 15 | 17 | 4 | 10 | 5 | 12 | 7 | 11 | 6 | 5 | 6 | 3 | 4 | 0 | 0 | 0 | 1,326 |
| 깊이 3 | 95 | 64 | 60 | 63 | 63 | 60 | 47 | 47 | 42 | 51 | 42 | 41 | 49 | 40 | 30 | 33 | 40 | 38 | 30 | 37 | 30 | 26 | 30 | 32 | 28 | 28 | 27 | 30 | 31 | 17 | 25 | 30 | 23 | 31 | 20 | 20 | 25 | 24 | 21 | 26 | 18 | 14 | 28 | 28 | 32 | 16 | 21 | 22 | 18 | 17 | 18 | 18 | 21 | 22 | 23 | 12 | 14 | 18 | 18 | 11 | 14 | 14 | 7 | 4 | 8 | 5 | 2 | 1 | 0 | 0 | 1,940 |
| 깊이 4 | 106 | 104 | 73 | 73 | 77 | 66 | 79 | 75 | 65 | 34 | 53 | 61 | 44 | 41 | 40 | 36 | 39 | 44 | 46 | 42 | 39 | 42 | 35 | 35 | 39 | 44 | 49 | 36 | 43 | 44 | 43 | 37 | 38 | 39 | 40 | 37 | 34 | 35 | 36 | 29 | 36 | 29 | 24 | 30 | 26 | 36 | 33 | 32 | 32 | 29 | 30 | 24 | 26 | 25 | 19 | 10 | 23 | 19 | 14 | 13 | 16 | 7 | 15 | 15 | 2 | 4 | 0 | 0 | 0 | 0 | 2,541 |
| 깊이 5 | 114 | 101 | 88 | 64 | 93 | 84 | 97 | 70 | 64 | 22 | 78 | 60 | 45 | 53 | 47 | 46 | 45 | 56 | 58 | 41 | 60 | 53 | 38 | 53 | 49 | 47 | 32 | 42 | 58 | 54 | 50 | 52 | 47 | 31 | 45 | 28 | 35 | 34 | 33 | 36 | 32 | 44 | 29 | 31 | 28 | 33 | 28 | 35 | 37 | 35 | 40 | 33 | 30 | 27 | 25 | 21 | 33 | 24 | 31 | 20 | 25 | 8 | 13 | 11 | 3 | 9 | 3 | 0 | 0 | 0 | 2,891 |
| 분기 합 | 474 | 408 | 364 | 346 | 292 | 271 | 259 | 238 | 222 | 219 | 214 | 198 | 179 | 178 | 175 | 170 | 168 | 168 | 167 | 161 | 156 | 152 | 151 | 151 | 150 | 150 | 149 | 149 | 144 | 139 | 139 | 139 | 134 | 131 | 131 | 130 | 127 | 126 | 124 | 121 | 120 | 118 | 114 | 114 | 112 | 109 | 107 | 104 | 103 | 102 | 100 | 94 | 92 | 91 | 88 | 79 | 78 | 76 | 72 | 67 | 66 | 45 | 43 | 36 | 29 | 25 | 14 | 1 | 0 | 0 | 9,863 |
뿌리(깊이 0) 행이 각 트리가 가장 먼저 보는 피처다. 그룹별 설명력은 SHAP 절의 그림이 따로 답한다.
결과 — 연도별
| 연도 | n | 모델 | 적응 상수 | 규칙 | 모델−상수 | 그해 기저율 |
|---|---|---|---|---|---|---|
| 2019 | 178 | 62.92% | 51.69% | 60.11% | +11.2%p | 46.6% |
| 2020 | 183 | 57.92% | 56.28% | 54.10% | +1.6%p | 40.4% |
| 2021 | 194 | 56.70% | 55.67% | 57.22% | +1.0%p | 44.3% |
| 2022 | 210 | 60.48% | 50.00% | 59.52% | +10.5%p | 51.4% |
| 2023 | 197 | 61.42% | 47.72% | 61.42% | +13.7%p | 48.7% |
| 2024 | 196 | 59.69% | 60.71% | 59.69% | −1.0%p | 39.3% |
| 2025 | 191 | 62.83% | 61.26% | 56.54% | +1.6%p | 38.7% |
| 2026 | 142 | 61.27% | 58.45% | 61.97% | +2.8%p | 41.6% |
| 풀링 | 1,491 | 60.36% | 55.06% | 58.75% | +5.3%p | 43.9% |
2024~2026만 따로 보면 모델 61.25% · 적응 상수 60.30%로 차이가 0.95%p뿐이다. 채택 판정은 사실상 2019·2022·2023이 만든 것이다 — 숨기지 않는다. 적응 상수 대비 짝지은 검정(McNemar)은 b=315 : c=236, p=0.0009(양측)이다.
레짐 필터는 값을 했나
게이트가 열리면 모델 방향, 닫히면 적응 상수를 낸다. 게이트 종류와 문턱은 학습창의 폴드 밖 예측에서만 골랐다 — 시험연도를 보고 고르면 그건 필터가 아니라 사후 선택이다.
| 게이트 | 무엇을 보나 | 승률 | 필터 없음 대비 |
|---|---|---|---|
| none | 필터 없음 — 대조군 | 60.36% | — |
| vol | 후행 20일 실현변동성 | 60.56% | +0.20%p |
| trend | 60일 추세 강도 | 60.43% | +0.07%p |
| conf | 모델 확신도 | 59.76% | −0.54%p |
| event | 입찰일·금통위일·롤 주간 | 59.49% | −0.81%p |
| baserate | 기저율 쏠림 | 58.82% | −1.48%p |
| selected | 학습창이 스스로 고른 것 | 59.62% | −0.74%p |
vol·trend도
+0.2%p로 시드 흔들림(±2.78%p) 안이고, 정작 학습창이 스스로 고른 게이트가
필터 없음보다 낮다(−0.68%p). 학습창에서 좋아 보이던 문턱이 시험연도에서
유지되지 않았다는 뜻 — 게이트 선택 자체가 과적합 통로였다.이것은 “필터가 쓸모없다”는 일반 결론이 아니라 이 표본에서 이 게이트들이 값을
못 했다는 측정이다. 레짐 축을 재 보자는 요구가 없었으면 vol 게이트를
근거 없이 넣었을 수도 있었다 — 재서 빼는 것이 이 절의 성과다.
모델 열 종과 티어
세 가지가 읽힌다.
- 강하게 무작위화한 배깅 트리가 부스팅을 이긴다. ExtraTrees 0.597~0.614, RandomForest 0.586~0.612 대 LightGBM 0.547~0.594, XGBoost 0.551~0.603. 표본이 작아 부스팅은 잔차를 좇다 과적합한다. ExtraTrees는 분할 지점까지 무작위로 뽑아 분산을 한 번 더 줄인다 — 신호 대 잡음이 극도로 낮은 이 문제의 모양과 맞는다.
- 선형 계열이 가장 약하다. 선형회귀 0.497~0.557, 릿지 0.518~0.555. SVM은 RBF 커널(0.576~0.598)이 선형 커널(0.542~0.573)보다 일관되게 높다 — 비선형이 필요하다는 같은 이야기다.
- 앙상블이 최고 단일 모델보다 낮다(0.531~0.580). 멤버 넷 중 셋이 약해 평균이 끌어내려졌다. 0002의 H6(이질 모델 확률 평균)이 여기서도 재현 실패다.
데이터를 더 넣어도 거의 나아지지 않는다. ExtraTrees 기준 T0_core(70피처)
0.6036과 T2_cross(137피처) 0.6143의 차이는 1.1%p로 시드 흔들림 안이다.
T3_full(체결틱·d_obs 포함)은 0.6030으로 T0(0.6036)와 사실상 같다 — H3은 지지되지
않는다. d에 정보가 있다는 0001의 관찰은 맞지만, 봉·일별 피처가 이미
그 정보를 담고 있어 따로 줄 값이 없다.
0001의 최고 갈래는 비교 대상이 아니다. 그 실험의
sign(ŷ−d) 60.05%는 d = P_ref − P34를 쓰는데
P_ref는 제출 시점에 관측되지 않는다(0001 리포트가 “제출 시점엔
모르지만 사후에 정확히 관측된다”고 스스로 적고 있다). 대회에서 낼 수 없는
숫자라 채택 후보에서 뺐다. 관측 가능한 P_obs_3458은
P_ref와 24.97%의 날에 다르다.
교집합 529일(2024~2026)에서 상위 조합의 1·2위 차이는 1승뿐이다(1위 62.76%).
그래서 후보 선정을 여기서 하지 않는다 — 2026-09-14 개정(protocol.md).
같은 데이터에서 1위가 lgb/selected→log/selected로 1승 차이에
뒤집혔고, 그 1위는 제 티어 1,491일에서 56.00%로 무너졌다. 529일 위의 argmax는
모델이 아니라 잡음을 고른다. 선정은 풀링 1,491일에서 적응 상수 대비 초과폭으로
하고(사전등록 문장이 원래 “풀링 승률 1위”였다), 교집합은 티어를 같은 날로 맞대 보는
자리로만 쓴다. 그 규칙이 T0_core · ExtraTrees · 게이트 없음을 뽑았다. 운영상 좋은 소식이다 — 라이브
수집기가 하나 늘 때마다 고장 지점이 하나 는다. 선물 1분봉과 일별 컨텍스트만으로 같은
성적이 나면, 대회 기간에 미국채·IRS·현물·환율 피드가 죽어도 제출이 멈추지 않는다.
해석 — SHAP
폴드마다 시험연도 행에서만 뽑아 이어 붙인 폴드 밖 SHAP이다(1,491일 × 70피처,
TreeExplainer 정확 계산). 학습 표본 SHAP은 모델이 무엇을 외웠는지를
설명할 뿐 새 날에 무엇을 보고 찍는지를 설명하지 않는다.
g6_is_auction_day(국고채 입찰일
플래그)이고, 2~4위가 g1_pos_20m·g1_pos_day·g1_pos_60m
— 15:34 가격이 최근 고저 범위의 어디에 있는가다. 5위는 롤 주간,
6~8위는 10년선물과 3/10 커브. 눈여겨볼 것은 기저율 피처가 상위 8에 없다는
점이다 — 모델이 적응 상수를 흉내 내서 이기는 것이 아니다.
g1_pos_*가 좌우로 길게 벌어진다 —
범위 상단에 있으면 한쪽, 하단이면 반대쪽으로 민다.
g1_pos_20m의 기울기가
단조에 가깝다 — 마감 직전 20분 범위에서의 위치가 그대로 방향 기여로 이어진다.
g1_ret_1m·g6_px_chg_20d·g6_dow를
위로 올린다. 두 방법이 다른 질문에 답하기 때문이다(SHAP은 “예측을 얼마나 밀었나”,
순열은 “섞으면 정확도가 얼마나 떨어지나”). 둘 다에서 살아남는 것은
선물 자체의 단기 모멘텀과 달력이다.
터진 것 — 승률 70.4%의 누설
첫 런의 결과는 60%가 아니라 70.4%였다. 이전 최고가 61%였으니 믿을 값이 아니었고, 그대로 제출 모델로 갔으면 대회에서 그대로 졌을 것이다.
단변량 진단(피처 부호만으로 찍었을 때의 승률)에서 범인이 드러났다 —
g4_irs_2YMID_chg_since15가 부호만으로 69.7%. IRS 표는
09:00~15:30만 있어 “컷오프 이후 행”이 아예 없는데도 그랬다. 행의
타임스탬프가 그 값이 관측된 시각이 아니었던 것이다.
| 창 끝 | 정답과의 상관 | 부호만으로 찍은 승률 |
|---|---|---|
| → 15:20 | 0.048 | 0.501 |
| → 15:29 | −0.058 | 0.539 |
| → 15:30 | −0.263 | 0.685 |
15:29까지는 잡음인데 15:30 행이 들어오는 순간 튄다. 벤더가 그날의 마지막 슬롯에 장 마감 뒤 확정되는 종가 호가를 써 넣기 때문이다. 그 값은 15:45 동시호가와 같은 ‘마감’을 담고 있으니 정답을 미리 아는 것과 같다.
고친 방식은 “숫자가 좋아질 때까지 컷오프를 옮긴다”가 아니라, 하루가
15:30에 끝나는 벤더 표의 마지막 슬롯은 관측값이 아니다라는 규칙을 세우고 그 계열
둘(irs_intra·otr_bond_intra)에 똑같이 적용한 것이다(15:29 컷).
나머지 표는 전 구간 깨끗했다 — 환율·주가는 15:45, 미국채는 23:59, 수급은 16:00까지
간다. 스캔 결과는 런 폴더의 leak_scan.json에 남겼다.
직접 원인은 검사의 구멍이었다. 처음 쓴 사보타주 검사가 선물 봉만 보고 있었다. 그룹이 일곱인데 가드가 하나였다. 지금은 세 겹이다 — 표별 컷오프 고정, 전역보다 늦출 수 없음, 그리고 누설 카나리아(어떤 피처도 부호만으로 62%를 넘을 수 없다. 가장 센 정직한 신호가 56.3%이고 실제 누설은 69.7%였으니 걸린다).
한계
- 장중 수급 집계 지연 미실측. 15:30 컷오프로 4분 마진을 뒀지만 지연이 그보다 길면 T1 이상 티어는 여전히 누설이다. 후보 모델은 수급을 쓰지 않으므로 이 위험 밖에 있다 — 우연한 이득이다.
- 진짜 블라인드 구간이 없다. 2026-08-26 이전 데이터를 전부 봤다. 2026 시험(61.27%, n=142)도 사후 표본이다. 대회 라이브가 유일한 최종 OOS다.
- 다중비교. 게이트 문턱 탐색까지 합쳐 조합 9,306개를 재고 308개를 표로 보고했다. 그중 하나는 우연히 좋다. 그래서 게이트를 학습창에서 고르고, 후보 선정을 풀링 1,491일에서 적응 상수 대비 초과폭으로 하고(529일 위의 argmax가 일반화하지 않는 것을 실측하고 2026-09-14에 고쳤다), 짝지은 검정을 썼다. 그래도 선택 편의가 0이라고 주장하지 않는다 — 표본을 넓혀 줄였을 뿐이고, 대회 라이브가 유일한 최종 OOS다.
- 하이퍼파라미터 미탐색. 규제 설정을 사전등록하고 탐색하지 않았다. 더 나은 설정이 있을 수 있다.
- 대회 표본이 작다. 유효일 약 69일에서 ±1σ가 6%p다 — 대회 성적으로는 이 모델과 적응 상수를 구분할 수 없다. 판정은 워크포워드가 했고 대회는 운영 검증이다.
- 2019 시험은 학습창이 두 해(344행)뿐이라 얇다. 빼고 풀링해도 결론은 같다(2020~2026 n=1,313, 모델 59.9% 대 적응 상수 55.5%).
산출물과 인수인계
정본 런은 results/20260914_132405_selection/다.
| 파일 | 내용 |
|---|---|
metrics.json | 지표 계약 + contest_score(승률·McNemar·연도별·교집합·미등록 원천 원장) |
predictions.csv | 일자 × 티어 × 모델 × 게이트 × 확률 × 방향 × 정답 |
gate_table.csv · year_table.csv · intersection_table.csv | 게이트·연도·교집합 표 |
shap_values.parquet · shap_summary.json | 폴드 밖 SHAP 원본과 요약 |
leak_scan.json | 표별 “몇 시까지 믿을 수 있는가” 실측 |
model_candidate.joblib | 배포 모델 — 전 구간 1,841행으로 다시 적합한 ExtraTrees + 피처 70개 계약 |
feature_spec.json | 열 이름·그룹·자료형 — 라이브가 읽는 계약 |
내일 자동 제출로 넘기는 것은 셋이다 — model_candidate.joblib,
feature_spec.json, 그리고 피처 정본
src/project_at/signals/contest_features.py. 폴백 순서는
모델 → 적응 상수 → 전일 방향이고, 미제출만은 막는다.
수능일(2026-11-13)은 거래소가 장을 한 시간 늦게 열고 16:45에 마감하므로 라이브
컷오프를 한 시간 밀어야 한다 — 연구 표본에서는 그 열흘을 통째로 뺐다.
이 페이지의 수치는 analysis.md와
results/20260914_132405_selection/metrics.json에서 옮겨 왔다. 어긋나면
저장소 쪽이 맞다. 데스크로 돌아가려면 데스크.