실험 0003 · 국채선물

국채선물 동시호가 종가 방향예측 — 제출 후보 모델 선정

가진 데이터 일곱 그룹(150피처)에 모델 열 종과 레짐 필터 여섯 종을 얹어, 여덟 개 연도의 연도 확장창 워크포워드로 하나를 골랐다.

채택 최종 아님 · 현재 후보 시험 2019~2026 · 1,491일 유효일 1,841 · 2016-12~2026-09 승률 60.36%

이 실험의 산출은 최종 모델이 아니다. 여기서 고른 것은 지금까지 중 가장 나은 후보 하나다. 대회 시즌 전에도 중에도 더 나은 후보가 나오면 갈아 끼운다. 실제 제출에 무엇을 쓰는지는 이 페이지가 아니라 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%, McNemar b=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_raw2016-12 ~ 2026-091분24모멘텀·범위 내 위치·거래량·미결제 · 후보 모델에 포함15:34:00
10년선물·커브 price_data_f10y_1m_raw2016-12 ~ 2026-091분610년 수익률·3/10 커브 · 후보 모델에 포함15:34:00
일별·캘린더 (수급·입찰·금통위·지표·휴장·롤·기저율)2012 ~ 2026-0937후행 상승비율·전일 정답·이벤트 플래그 · 후보 모델에 포함T-1 까지만
장중 투자자수급 intraday_ktb_positions2019-01 ~ 2026-0830초28투자자별 순매수 수준·변화 (T1 이상)15:30:00
해외 장중 ust_intra (미국채 2/5/10년)2021-01 ~ 2026-091분10미국채 금리 변화·커브 (T2 이상)15:34:00
국내 교차자산 (IRS · 국고채 현물 · 달러원 · KOSPI200)2019-01 ~ 2026-091분33금리 bp 변화·가격 수익률 (T2 이상)15:29 / 15:34
체결틱 미시구조 선물3년_체결틱2023-01 ~ 2026-08125분창 체결·틱룰·대량비율 + d_obs (T3)15:34:58

일별 표는 발표 시각이 전부 미확인이라 T-1까지만 쓴다 — 모르는 것을 추측해 당일 값을 쓰지 않는다. 장중 수급은 벤더 집계 지연이 미실측이라 4분 마진을 두고 15:30에서 끊었다. IRS·현물이 15:29인 이유는 9절에 있다. 표본은 대상일 2,387일 중 0틱 무효 532일과 가드(봉 누락·수능일 비정상 세션) 14일을 뺀 유효일 1,841일이다.

연도별 상승 비율 꺾은선 그래프
앞선 실험들을 죽인 축. 상승 비율이 2022년 51.4%에서 2025년 38.7%까지 흐른다. 기저율이 50%에서 멀수록 “매일 다수 클래스” 상수가 강해지고, 가까울수록 무력해진다 — 이 그림이 아래 연도별 결과를 전부 설명한다.

모델과 학습 설정

하이퍼파라미터는 탐색하지 않고 config.yaml에 사전등록한 규제 설정을 그대로 쓴다. 표본이 하루 1행 1,841일뿐이라 폴드 안 탐색까지 하면 탐색 자체가 과적합 통로가 되기 때문이다. 시드 다섯 개는 확률을 평균하고, 시드별 승률의 표준편차를 따로 기록해 “차이가 시드 흔들림보다 큰가”를 판정에 쓴다.

모델 열 종의 사전등록 설정. 트리는 subsample·colsample_bytree를 1 미만으로 켜서 시드 분산이 0이 되지 않게 했다(0002 교훈).
이름계열설정
lgbLightGBM 분류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
xgbXGBoost 분류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
etExtraTrees (선정 후보)n_estimators 400 · max_depth 6 · min_samples_leaf 20 · max_features sqrt · 중앙값 대치
rfRandomForest같은 설정 (n_estimators 400 · max_depth 6 · min_samples_leaf 20)
lin선형회귀ref_tick을 회귀하고 부호를 읽는다 · 중앙값 대치 + 표준화
ridge릿지 회귀alpha 10.0 · 나머지 동일
log로지스틱 회귀C 0.1 (L2)
svm_rbfSVM · RBF 커널C 1.0 · gamma scale · 확률 보정
svm_linSVM · 선형 커널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년 + 일별7020172019~2026 (8해)
T1_flow+ 장중 수급9820192021~2026 (6해)
T2_cross+ 해외 · 국내 교차자산13720212023~2026 (4해)
T3_full+ 체결틱 · d_obs14920232024~2026 (3해)
Tall_nan전부 · 결측 허용(트리만)14920172019~2026 (8해)
워크포워드 분할 간트 — 위는 선정 티어의 폴드 여덟 개, 아래는 티어별 학습 시작과 시험 구간
학습에 쓴 날과 채점에 쓴 날은 한 번도 겹치지 않는다. 위: 선정 티어 T0_core의 폴드 여덟 개. 시험연도 Y의 학습은 2017년부터 Y−1년 말까지(짙은 막대), 채점은 Y년만(주황 막대) 한다. 막대 안 숫자는 그 폴드가 실제로 쓴 날 수다 — 런 로그에서 그대로 읽었다. 해가 갈수록 짙은 막대만 길어지는 것이 확장창이고, 점선 계단이 그 경계가 한 해씩 미래로 가는 모습이다. 주황 막대를 모두 더한 1,491일이 이 리포트에 나오는 모든 승률의 분모다. 아래: 티어마다 원천 시작 연도가 달라 학습 시작과 첫 시험연도가 다르다 — 티어끼리 승률을 직접 비교하지 않는 이유가 이 그림이다.
그림 A · 모델 구조 — T0_core/et, 시험 2026 (학습 1,693행) 입력 데이터 3년 국채선물 근월 + 6개 원천 2016-12-20 ~ 2026-09-11 유효일 1,841 · 하루 1행 학습 2017~2025 · 1,693행 0틱 무효일 제외 피처 70개 / 3그룹 3년선물 1분봉 10년선물·커브 일별·캘린더 ExtraTrees — T0_core · 시험 2026 모델 f₁ f₂ f₃ f₄ f₅ ··· f₄₀₀ 위는 이 모델 트리 5그루와 마지막 그루의 실제 위상(점 = 잎) — 실물은 그림 B·C log-odds(x) = Σ f(x) 컷오프 봉 15:34:00 · 틱 15:34:58 · 수급 15:30 · IRS·현물 15:29 출력 P(상승) ↓ 0.5 기준 상승 / 하락 레짐 게이트 gate = none 매일 1건 제출 하이퍼파라미터 — 세팅한 상한과 학습이 실제로 닿은 값 n_estimators = 400 이어 붙이는 트리 개수 — 위 f₁ … 가 이만큼 실제 400그루 max_depth = 6 뿌리(깊이 0)에서 잎까지 분기 최대 6번 — 그림 B·C 의 오른쪽 눈금 실제 깊이 전부 6 · 상한에 닿은 트리 400/400 min_samples_leaf = 20 잎 하나에 표본 20개 미만이면 그 분할을 하지 않는다 실제 가장 작은 잎 n=20 max_features = sqrt 분할마다 피처 70개 중 8개만 후보로 뽑는다 트리끼리 서로 다른 곳을 보게 만든다 그루별 잎 수 분포 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 잎 12~40개 레짐 게이트 — 열리면 모델, 닫히면 적응 상수(커버리지 100%) 게이트 열림 P(상승) > 0.5 → 방향 게이트 닫힘 후행 120일 다수 클래스 선택된 게이트: none 게이트·문턱은 학습창 OOF 에서만 고른다
입력 일곱 그룹에서 제출까지 한 장으로. 가운데 작은 트리들은 실제 학습된 f₁~f₅ 와 f₄₀₀ 다(잎 하나가 점 하나).
그림 B · 첫 트리 f₁ 의 실제 분기 깊이 0 · 뿌리 분기 조건 · n 깊이 1 분기 조건 · n 깊이 2 분기 조건 · n 깊이 3 분기 조건 · n 깊이 4 · 접힘 아래를 접었다 max_depth = 6 뿌리→잎 분기 6번까지 이 트리 실제 깊이 6 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 g1_pos_20m ≤ 0.8508 n=1,693 g1_ret_30m ≤ -3.095 n=1,371 g1_vol_last30_share ≤ 0.1855 n=214 g6_prev_up ≤ 0.6069 n=184 하위 3잎 n=106 하위 3잎 n=78 잎값 +0.5000 n=30 g6_dow ≤ 1.787 n=1,157 g6_is_auction_day ≤ 0.7854 n=455 하위 2잎 n=101 하위 4잎 n=354 g6_is_auction_day ≤ 0.9564 n=702 하위 3잎 n=310 하위 4잎 n=392 g1_ret_20m ≤ 3.181 n=322 g6_is_roll_week ≤ 0.7797 n=251 g6_rate_CALL1일한은 ≤ 1.414 n=229 하위 3잎 n=107 하위 3잎 n=122 잎값 +0.7273 n=22 g6_prev_d_tick ≤ 0.6874 n=71 g6_rate_통안1년 ≤ 2.751 n=46 잎값 +0.6818 n=22 잎값 +0.6250 n=24 잎값 +0.7200 n=25 잎값 ≥ 0 (예측을 위로) 잎값 < 0 (예측을 아래로) n = 그 노드에 떨어진 학습 표본 수 · 조건(≤)이 참이면 왼쪽 가지 '예', 거짓(>)이면 오른쪽 '아니오' · 잎이 많아 깊이 4 아래를 접었다(이 트리 전체 깊이 6 · 잎 30개)
뿌리에서 g1_pos_20m ≤ 0.8508 로 갈린다. 각 상자의 n 은 학습 1,693행 중 그 노드에 떨어진 수다.
그림 C · 마지막 트리 f₄₀₀ 의 실제 분기 깊이 0 · 뿌리 분기 조건 · n 깊이 1 분기 조건 · n 깊이 2 분기 조건 · n 깊이 3 분기 조건 · n 깊이 4 분기 조건 · n 깊이 5 분기 조건 · n 깊이 6 · 잎 분기 없음 max_depth = 6 뿌리→잎 분기 6번까지 이 트리 실제 깊이 6 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 아니오 g1_ma_dev_20m ≤ 4 n=1,693 g1_ret_10m ≤ 4.182 n=1,672 g1_ret_5m ≤ 0.2552 n=1,649 g1_pos_20m ≤ 0.296 n=1,146 g6_is_auction_day ≤ 0.6455 n=487 g6_abs_baserate_dev ≤ 0.0892 n=160 잎값 +0.3821 n=123 잎값 +0.5405 n=37 g1_ma_dev_60m ≤ -2.939 n=327 잎값 +0.2581 n=155 잎값 +0.3430 n=172 g6_is_auction_3y ≤ 0.04186 n=659 g1_ret_10m ≤ 1.903 n=571 잎값 +0.4387 n=522 잎값 +0.5714 n=49 g1_vol_day ≤ 1.32e+05 n=88 잎값 +0.5455 n=66 잎값 +0.4545 n=22 g6_dom ≤ 20.63 n=503 g6_baserate_120d ≤ 0.4901 n=319 g1_ma_dev_20m ≤ 1.536 n=217 잎값 +0.5742 n=155 잎값 +0.6774 n=62 g6_vol_60d ≤ 4.074 n=102 잎값 +0.4487 n=78 잎값 +0.6667 n=24 g1_pos_60m ≤ 0.8851 n=184 g1_pos_60m ≤ 0.7277 n=123 잎값 +0.3855 n=83 잎값 +0.4500 n=40 g6_px_chg_60d ≤ 28.83 n=61 잎값 +0.4865 n=37 잎값 +0.6250 n=24 잎값 +0.6957 n=23 잎값 +0.6190 n=21 잎값 ≥ 0 (예측을 위로) 잎값 < 0 (예측을 아래로) n = 그 노드에 떨어진 학습 표본 수 · 조건(≤)이 참이면 왼쪽 가지 '예', 거짓(>)이면 오른쪽 '아니오'
마지막 트리 — 깊이 6 · 잎 18개.
그림 D · 400그루 전체의 깊이×피처 분기 수
분기 위치 \ 피처g6_is_auction_dayg1_pos_dayg1_pos_20mg1_pos_60mg6_dowg6_domg6_prev_upg6_rate_CD3개월g6_days_to_expiryg6_is_roll_weekg6_rate_CALL1일한은g6_rate_통안1년g6_event_anyg6_vol_60dg1p_curve_20mg1_ma_dev_20mg1_ma_dev_60mg6_dflow_증권선물순매수수량g6_prev5_upg1p_f10_ret_20mg6_dflow_개인순매수수량g6_baserate_20dg1_ret_20mg6_dflow_은행순매수수량g1p_f10_ret_openg6_abs_px_chg_20dg1_ret_10mg1p_f10_ret_60mg6_abs_baserate_devg6_baserate_60dg6_baserate_120dg6_baserate_250dg6_dflow_외국인합순매수수량g1_rng_20mg1_vol_last30_shareg1_ret_60mg1_ret_120mg1_ret_1mg1p_f10_ret_5mg6_px_chg_60dg1_ret_30mg1_ret_openg1p_curve_60mg6_vol_20dg1_ret_5mg1_vwap_devg6_px_chg_20dg6_is_auction_3yg6_dflow_투신순매수수량g1_vol_dayg6_prev_ref_tickg1_rng_dayg1_vol_last5_shareg6_dflow_보험순매수수량g1_rng_60mg1_oi_chg_dayg6_px_chg_5dg6_rate_통안1년_chg5dg6_prev_d_tickg1_oi_chg_60mg6_prev_abs_ref_tickg6_is_month_endg6_rate_CALL1일한은_chg5dg1_gap_openg6_is_mpc_dayg6_rate_CD3개월_chg5dg6_is_quarter_endg1_n_barsg6_us_event_todayg6_us_event_prev
깊이 0 (뿌리)2631353951254719211166974713061027371301032316591016174346011111011000070001412000400
깊이 1584644471523718165114814122117971013881414971510692398513888121110101053854344355945444520633000765
깊이 275626460392624242842252716263129281916151917241518211918614191415192026201616171720161118171110111771411121517410512711656340001,326
깊이 39564606363604747425142414940303340383037302630322828273031172530233120202524212618142828321621221817181821222312141818111414748521001,940
깊이 41061047373776679756534536144414036394446423942353539444936434443373839403734353629362924302636333232293024262519102319141316715152400002,541
깊이 51141018864938497706422786045534746455658416053385349473242585450524731452835343336324429312833283537354033302725213324312025813113930002,891
분기 합474408364346292271259238222219214198179178175170168168167161156152151151150150149149144139139139134131131130127126124121120118114114112109107104103102100949291887978767267664543362925141009,863

뿌리(깊이 0) 행이 각 트리가 가장 먼저 보는 피처다. 그룹별 설명력은 SHAP 절의 그림이 따로 답한다.

결과 — 연도별

정본 런 results/20260914_132405_selection/. 후보 모델 · 적응 상수 · 20분 모멘텀 규칙을 같은 날 집합에서.
연도n모델적응 상수규칙모델−상수그해 기저율
201917862.92%51.69%60.11%+11.2%p46.6%
202018357.92%56.28%54.10%+1.6%p40.4%
202119456.70%55.67%57.22%+1.0%p44.3%
202221060.48%50.00%59.52%+10.5%p51.4%
202319761.42%47.72%61.42%+13.7%p48.7%
202419659.69%60.71%59.69%−1.0%p39.3%
202519162.83%61.26%56.54%+1.6%p38.7%
202614261.27%58.45%61.97%+2.8%p41.6%
풀링1,49160.36%55.06%58.75%+5.3%p43.9%
연도별 승률 막대 그래프 — 모델·적응 상수·규칙
막대 위 숫자는 그 해 평가일 수. 2026은 09-11까지다. 모델의 우위는 기저율이 50% 근처인 해에 크고(2019·2022·2023), 기저율이 쏠린 해에는 작거나 음수다(2024). 당연하다 — 기저율이 39%면 “매일 하락”만으로 61%다. 대신 모델은 여덟 해 전부 56.7% 이상이고 상수는 47.7%까지 내려간다. 대회 구간의 레짐을 모르는 상태에서 어느 레짐에서도 무너지지 않는 쪽을 고르는 것이 이 선택의 근거다.

2024~2026만 따로 보면 모델 61.25% · 적응 상수 60.30%로 차이가 0.95%p뿐이다. 채택 판정은 사실상 2019·2022·2023이 만든 것이다 — 숨기지 않는다. 적응 상수 대비 짝지은 검정(McNemar)은 b=315 : c=236, p=0.0009(양측)이다.

누적 틱 곡선 — 후보 모델과 적응 상수
순위 동률 시의 타이브레이커. 후보 모델 +669틱. 2024년 구간에서 상수가 앞서는 것이 눈으로 보인다 — 위 표의 −2.0%p가 그 구간이다.
예측 확률 대 실제 상승 비율 곡선
확률 보정. 점 크기가 그 구간의 날 수다. 대각선에서 벗어나는 만큼 확률값 자체는 믿을 것이 못 된다 — 그래서 확신 필터로 날을 버리지 않는다. 대회는 커버리지 100%를 요구하고 미제출은 오답이다.

레짐 필터는 값을 했나

게이트가 열리면 모델 방향, 닫히면 적응 상수를 낸다. 게이트 종류와 문턱은 학습창의 폴드 밖 예측에서만 골랐다 — 시험연도를 보고 고르면 그건 필터가 아니라 사후 선택이다.

선정 티어·모델(T0_core/et)에서 게이트 종류별 워크포워드 승률. n=1,491.
게이트무엇을 보나승률필터 없음 대비
none필터 없음 — 대조군60.36%
vol후행 20일 실현변동성60.56%+0.20%p
trend60일 추세 강도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 게이트를 근거 없이 넣었을 수도 있었다 — 재서 빼는 것이 이 절의 성과다.

모델 열 종과 티어

티어와 모델 조합별 승률 히트맵
게이트 없음, 풀링 승률. 열끼리만 비교한다 — 티어마다 시험연도와 표본이 달라 행 방향 비교는 모델 비교가 아니라 표본 비교가 된다.

세 가지가 읽힌다.

  1. 강하게 무작위화한 배깅 트리가 부스팅을 이긴다. ExtraTrees 0.597~0.614, RandomForest 0.586~0.612 대 LightGBM 0.547~0.594, XGBoost 0.551~0.603. 표본이 작아 부스팅은 잔차를 좇다 과적합한다. ExtraTrees는 분할 지점까지 무작위로 뽑아 분산을 한 번 더 줄인다 — 신호 대 잡음이 극도로 낮은 이 문제의 모양과 맞는다.
  2. 선형 계열이 가장 약하다. 선형회귀 0.497~0.557, 릿지 0.518~0.555. SVM은 RBF 커널(0.576~0.598)이 선형 커널(0.542~0.573)보다 일관되게 높다 — 비선형이 필요하다는 같은 이야기다.
  3. 앙상블이 최고 단일 모델보다 낮다(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_3458P_ref와 24.97%의 날에 다르다.

교집합 529일(2024~2026)에서 상위 조합의 1·2위 차이는 1승뿐이다(1위 62.76%). 그래서 후보 선정을 여기서 하지 않는다 — 2026-09-14 개정(protocol.md). 같은 데이터에서 1위가 lgb/selectedlog/selected로 1승 차이에 뒤집혔고, 그 1위는 제 티어 1,491일에서 56.00%로 무너졌다. 529일 위의 argmax는 모델이 아니라 잡음을 고른다. 선정은 풀링 1,491일에서 적응 상수 대비 초과폭으로 하고(사전등록 문장이 원래 “풀링 승률 1위”였다), 교집합은 티어를 같은 날로 맞대 보는 자리로만 쓴다. 그 규칙이 T0_core · ExtraTrees · 게이트 없음을 뽑았다. 운영상 좋은 소식이다 — 라이브 수집기가 하나 늘 때마다 고장 지점이 하나 는다. 선물 1분봉과 일별 컨텍스트만으로 같은 성적이 나면, 대회 기간에 미국채·IRS·현물·환율 피드가 죽어도 제출이 멈추지 않는다.

해석 — SHAP

폴드마다 시험연도 행에서만 뽑아 이어 붙인 폴드 밖 SHAP이다(1,491일 × 70피처, TreeExplainer 정확 계산). 학습 표본 SHAP은 모델이 무엇을 외웠는지를 설명할 뿐 새 날에 무엇을 보고 찍는지를 설명하지 않는다.

데이터 그룹별 SHAP 합산 막대 그래프
어느 원천이 설명하나. 일별·캘린더 46.7% · 선물 1분봉 40.0% · 10년선물과 커브 13.3%. 절반 가까이가 그날의 시장 움직임이 아니라 달력에서 오는 정보다 — 입찰일인가, 롤 주간인가, 월중 어디인가.
피처별 평균 절대 SHAP 상위 18개 막대 그래프
전역 설명력 순위. 1위가 g6_is_auction_day(국고채 입찰일 플래그)이고, 2~4위가 g1_pos_20m·g1_pos_day·g1_pos_60m15:34 가격이 최근 고저 범위의 어디에 있는가다. 5위는 롤 주간, 6~8위는 10년선물과 3/10 커브. 눈여겨볼 것은 기저율 피처가 상위 8에 없다는 점이다 — 모델이 적응 상수를 흉내 내서 이기는 것이 아니다.
피처 값과 SHAP 기여의 분포 산점도
값이 크면 어느 쪽으로 미는가. 가로축이 로그오즈 기여, 색이 피처 값 (파랑 낮음, 빨강 높음)이다. g1_pos_*가 좌우로 길게 벌어진다 — 범위 상단에 있으면 한쪽, 하단이면 반대쪽으로 민다.
상위 네 피처의 값 대 SHAP 의존도 산점도 네 장
값이 변하면 기여가 어떻게 변하나. g1_pos_20m의 기울기가 단조에 가깝다 — 마감 직전 20분 범위에서의 위치가 그대로 방향 기여로 이어진다.
연도별 상위 여덟 피처의 평균 절대 SHAP 꺾은선
연도별 설명력. 순위가 해마다 크게 뒤집히면 그 피처는 레짐 종속이라 대회에서 믿을 수 없다. 상위 피처들의 상대 순위는 대체로 유지된다.
순열 중요도 상위 12개 막대 그래프
교차 검증용. SHAP과 순위가 완전히 같지는 않다 — 순열 쪽은 g1_ret_1m·g6_px_chg_20d·g6_dow를 위로 올린다. 두 방법이 다른 질문에 답하기 때문이다(SHAP은 “예측을 얼마나 밀었나”, 순열은 “섞으면 정확도가 얼마나 떨어지나”). 둘 다에서 살아남는 것은 선물 자체의 단기 모멘텀과 달력이다.
게이트가 열린 날과 닫힌 날의 SHAP 비교 막대 그래프
레짐 필터가 무엇을 거르나. 학습창이 고른 게이트가 닫은 날은 1,491일 중 76일뿐이다 — 그마저도 승률을 올리지 못했다(6절).

터진 것 — 승률 70.4%의 누설

첫 런의 결과는 60%가 아니라 70.4%였다. 이전 최고가 61%였으니 믿을 값이 아니었고, 그대로 제출 모델로 갔으면 대회에서 그대로 졌을 것이다.

단변량 진단(피처 부호만으로 찍었을 때의 승률)에서 범인이 드러났다 — g4_irs_2YMID_chg_since15부호만으로 69.7%. IRS 표는 09:00~15:30만 있어 “컷오프 이후 행”이 아예 없는데도 그랬다. 행의 타임스탬프가 그 값이 관측된 시각이 아니었던 것이다.

창 끝을 옮겨 가며 정답과의 상관을 재니 범인이 한 행으로 좁혀졌다(code/leak_scan.py).
창 끝정답과의 상관부호만으로 찍은 승률
→ 15:200.0480.501
→ 15:29−0.0580.539
→ 15:30−0.2630.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%였으니 걸린다).

한계

  1. 장중 수급 집계 지연 미실측. 15:30 컷오프로 4분 마진을 뒀지만 지연이 그보다 길면 T1 이상 티어는 여전히 누설이다. 후보 모델은 수급을 쓰지 않으므로 이 위험 밖에 있다 — 우연한 이득이다.
  2. 진짜 블라인드 구간이 없다. 2026-08-26 이전 데이터를 전부 봤다. 2026 시험(61.27%, n=142)도 사후 표본이다. 대회 라이브가 유일한 최종 OOS다.
  3. 다중비교. 게이트 문턱 탐색까지 합쳐 조합 9,306개를 재고 308개를 표로 보고했다. 그중 하나는 우연히 좋다. 그래서 게이트를 학습창에서 고르고, 후보 선정을 풀링 1,491일에서 적응 상수 대비 초과폭으로 하고(529일 위의 argmax가 일반화하지 않는 것을 실측하고 2026-09-14에 고쳤다), 짝지은 검정을 썼다. 그래도 선택 편의가 0이라고 주장하지 않는다 — 표본을 넓혀 줄였을 뿐이고, 대회 라이브가 유일한 최종 OOS다.
  4. 하이퍼파라미터 미탐색. 규제 설정을 사전등록하고 탐색하지 않았다. 더 나은 설정이 있을 수 있다.
  5. 대회 표본이 작다. 유효일 약 69일에서 ±1σ가 6%p다 — 대회 성적으로는 이 모델과 적응 상수를 구분할 수 없다. 판정은 워크포워드가 했고 대회는 운영 검증이다.
  6. 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.mdresults/20260914_132405_selection/metrics.json에서 옮겨 왔다. 어긋나면 저장소 쪽이 맞다. 데스크로 돌아가려면 데스크.