Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

10th-toy-team3

배당률이라는 "대중의 집단 예측"을 데이터 모델이 넘어설 수 있는가

KHUDA 10기 토이프로젝트 3조입니다. 한국마사회 서울 경주 데이터로 승률 예측 모델을 만들고, 배당률에서 환산한 시장 확률과 정면으로 비교했습니다.

경마는 대중의 예측이 실시간으로 집계되어 숫자로 공개되는 드문 분야입니다. 보통의 예측 문제는 "정답을 얼마나 맞혔나"만 재지만, 여기에는 수만 명이 자기 돈을 걸어 만든 확률 추정치가 매 경주마다 이미 존재합니다. 그래서 질문이 하나 더 생깁니다 — 그 기준선을 넘을 수 있는가.

마지막 갱신 2026-08-23 · 7단계까지 코드와 보고서 정리 완료


한 줄 결론

시장을 정면으로 이기는 데는 실패했습니다. 대신 시장이 구조적으로 약한 구간만 골라내는 필터로 방향을 틀어 의미 있는 결과를 얻었습니다.


프로젝트 흐름

폴더 번호가 아래 단계를 그대로 따릅니다. 같은 번호의 src/와 reports/가 짝을 이룹니다.

단계 결과 코드 보고서
1 승률 예측 모델 설계 RandomForest ROC-AUC 0.7495 src/2_승률모델 reports/2_승률모델
2 배당률과 대조 시장 0.817 > 모델 0.75 — 넘지 못함 02_market_gap.py 최종보고서
3 시장을 따라잡으려는 시도 확률 정확도만 근소하게 앞섬. 적중률·수익은 아님 src/3_시장대조 reports/3_시장대조
4 선행 연구 조사 시장을 이긴 사례가 사실상 없음 — 조건을 규명 — 문헌조사
5 방향 전환 — 이변 분석 Lift 1.81배. 유의미한 신호 확인 03_upset.py · notebooks reports/4_이변모델
6 이변에 영향을 주는 피처 최근 조교량 · 직전 성적 · 휴식일 src/4_이변모델 배당률 포함 모델
7 이변 탐지 모델과 베팅 전략 배당 10~50배 구간 ROI +32.0% src/5_이변모델_선정 reports/5_이변모델_선정

4단계에서 얻은 인사이트가 5단계로 넘어가는 근거입니다. 시장 전체를 이기려 하지 말고, 시장이 약한 구간만 노려라.


핵심 결과 셋

시장은 생각보다 훨씬 정확합니다

배당률만으로 ROC-AUC 0.816이 나옵니다. 우리가 만든 어떤 모델도 이 선을 넘지 못했습니다.

ROC-AUC
시장 (배당률) 0.816
RandomForest 0.7507
2단계 결합 모델 0.7081

피처를 20개로 줄여도, 100개로 늘려도 마찬가지였습니다. 피처 개수의 문제가 아니었습니다.

확률 정확도로는 아주 조금 앞섰습니다

시장 확률에 모델을 소량 섞고 온도 보정을 하면 확률 예측 정확도가 미세하게 개선됩니다. 2025-12-28 ~ 2026-08-09의 635경주를 최초 1회만 평가했습니다.

Race Log Loss ↓ Top-1 적중률
시장 (M0) 1.817348 37.80%
M2 XGBoost 후보 1.813446 37.48%

개선폭 +0.003902, 부트스트랩 95% 신뢰구간 [+0.001085, +0.006721]로 0을 포함하지 않습니다. 다만 Top-1 적중률은 오히려 시장이 높고, 기대수익이 양수인 후보가 없어 베팅 정책은 no_bet입니다. 개선폭 0.0039는 공제율 약 20%를 넘기에 턱없이 작습니다.

이변 구간에서는 의미 있는 결과가 나왔습니다

"이 말이 이긴다"를 맞히는 대신 "시장이 과소평가한 복병"을 골라내는 문제로 바꿨습니다.

선별력 Lift@10% 1.72배 [1.41, 2.02] — 상위 10%의 적중률이 전체 평균의 1.7배
전략 winOdds 10~50배 구간에서 모델 상위 10%만 단승
수익 ROI +32.0% · 부트스트랩 1,000회 중 87.9%에서 수익
이변마 유형 데뷔형 · 베테랑 복귀형 · 신예 깜짝형

모델의 가치는 예측이 아니라 필터에 있습니다. 다만 개선 실험 6가지 중 성공한 건 배당 구간 필터 하나뿐이었고 나머지는 ROI −7~−15%였습니다. 실전 판단의 근거로 쓰기엔 아직 표본이 부족합니다.


저장소 구조

10th-toy-team3/
├── data/                          원천 데이터와 전처리 8버전
│   ├── race_entries.csv.gz          API 수집 원천 56,648행 × 156열
│   └── 전처리_데이터셋/              v1~v8 × train/valid/test (gz)
│       ├── 분할안내.md                왜 시간순으로 잘랐는지 · 어떻게 불러 쓰는지
│       └── 사용정책.md                v5~v8을 신규 학습에 쓰면 안 되는 이유
│
├── src/                           파이프라인 코드 — 폴더마다 01부터 실행 순서
│   ├── 0_데이터수집/                KRA API 수집 → 용도별 분리          01~02
│   ├── 1_전처리/                    결측·이상치·다중공선성 → 8버전      01~07
│   │   └── 버전비교/                  v1~v8 중 무엇을 쓸지 실험           01
│   ├── 2_승률모델/                  [1·2단계] 1차 모델링 · 시장 괴리     01~05
│   ├── 3_시장대조/                  [3단계] 시장을 따라잡으려는 시도
│   │   ├── 방향실험/                  확률보정 · 시장결합 · 이변예측      01~04
│   │   ├── 비시장모델/                배당률 없이 피처만으로              01~06
│   │   └── 시장확률보정/              ★ 시장에 가장 가까이 간 독립 패키지
│   ├── 4_이변모델/                  [6단계] 배당률을 넣은 이변 예측      01~10
│   └── 5_이변모델_선정/             [7단계] 개선·배당필터·배분 전략      01~05
│
├── notebooks/                     탐색 분석 (노트북은 GitHub에서 바로 렌더링됩니다)
├── reports/                       단계별 보고서 — 번호가 읽는 순서입니다
│                                  docx 마다 같은 이름의 pdf 가 있어 클릭하면 바로 보입니다
└── docs/                          데이터 명세와 사용 지침

파일 이름 규칙

src/ 스크립트 01_ 부터 폴더 안에서의 실행 순서. 폴더가 바뀌면 다시 01부터
config.py · kra_client.py 실행하는 파일이 아니라 불러 쓰는 모듈이라 번호를 붙이지 않았습니다
reports/ 문서 01_ 부터 읽는 순서. .docx와 .pdf가 같은 이름으로 짝을 이룹니다
결과 .csv · .png 코드가 이 이름 그대로 읽고 써서 영문 원본을 유지했습니다. 뜻은 각 폴더 README.md의 표에 있습니다

config.py는 7개 폴더에 같은 내용으로 복사돼 있습니다. 각 파이프라인이 다른 폴더에 의존하지 않고 혼자 돌아가도록 한 것이라 중복이 아니라 의도입니다.

각 src/·reports/ 하위 폴더에 무엇이 들어 있고 어떤 순서로 실행하는지 적은 README.md가 있습니다.


시작하기

git clone https://github.com/khuda-data/10th-toy-team3.git
cd 10th-toy-team3
pip install -r requirements.txt
import pandas as pd

VERSION = "v1_base"          # 트리 모델 (RF · XGBoost · LightGBM)
# VERSION = "v2_standard"    # 로지스틱 회귀 · SVM

BASE = f"data/전처리_데이터셋/{VERSION}"
dtypes = {"hrNo": str, "jkNo": str, "trNo": str, "owNo": str}

train = pd.read_csv(f"{BASE}/train.csv.gz", encoding="utf-8-sig", dtype=dtypes)
valid = pd.read_csv(f"{BASE}/valid.csv.gz", encoding="utf-8-sig", dtype=dtypes)
test  = pd.read_csv(f"{BASE}/test.csv.gz",  encoding="utf-8-sig", dtype=dtypes)

시작 전에 꼭 읽을 것

docs/데이터.md의 「쓸 때 지킬 것」 절입니다. 아래 다섯 가지를 놓치면 결과가 조용히 틀립니다.

1 hrNo 등 식별자는 문자열로 읽기. 숫자로 읽으면 앞자리 0이 날아가 조인이 깨집니다
2 경주 결과·사후 정보를 피처에 넣지 않기
3 현재 경주의 배당·베팅 정보를 피처에 넣지 않기. 이 프로젝트의 핵심 제약입니다
4 시간순 분할 지키기. train_test_split으로 섞으면 안 됩니다
5 class_weight·오버샘플링 쓰지 않기. 확률 자체가 분석 대상이라 보정이 무너집니다

데이터

출처 한국마사회 공공데이터 API
기간 2023-08 ~ 2026-08
원천 56,648 출전행 × 156열 (서울 + 부경)
모델링 서울 32,888행 × 111열 — 경주일 순 60 : 20 : 20 분할
예측 대상 1착 여부 win (양성 9.6%) · 이변 upset_A upset_B

파리뮤추얼 방식이라 배당률은 주최측이 매긴 값이 아니라 사람들이 어디에 얼마를 걸었는지의 결과입니다. 경주 내 1/배당률 합이 약 1.25이고, 이 초과분(오버라운드)이 주최측 몫입니다. 환급률 약 80%.

열 156개 설명과 분할 기준은 docs/데이터.md, data/전처리_데이터셋/분할안내.md에 있습니다.


팀원

이름 GitHub 담당
김찬진 cj001212 저장소 개설 · 시장확률 보정 파이프라인
윤우진 PLAUROS 데이터 수집 · 전처리 · 문헌 조사 · 저장소 정리
박준석 junseok EDA · 전처리 · 승률 모델링 · 이변 예측 모델과 베팅 전략
홍정의 a01063950463-svg 비시장 모델 피처 수 실험
이종원 happy97908a-cmyk 3차 모델링 6종 비교 · LightGBM 5-fold 검증
전유림 yoolim-jeon 이변 유형별 특징 분석

본 자료는 연구 결과이며 실제 베팅이나 금융 조언이 아닙니다.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages