Files
edf/docs/technical_methodology.md

828 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 주식 변동성 기반 등급별 부도율 산출 — 기술 문서
> **프로젝트**: KRX 상장 한국 기업 대상 Equity Volatility → Default Rate by Rating
> **작성일**: 2026-03-11
> **버전**: v0.1 (초안)
---
## 목차
1. [이론적 기초](#1-이론적-기초)
2. [핵심 수학적 프레임워크](#2-핵심-수학적-프레임워크)
3. [한국 시장 등급 관측 문제 및 대안](#3-한국-시장-등급-관측-문제-및-대안)
4. [글로벌 접근 방법론 비교](#4-글로벌-접근-방법론-비교)
5. [구현 아키텍처](#5-구현-아키텍처)
6. [데이터 명세](#6-데이터-명세)
7. [알고리즘 상세](#7-알고리즘-상세)
8. [검증 방법론](#8-검증-방법론)
9. [한국 시장 특수 고려사항](#9-한국-시장-특수-고려사항)
10. [기술 스택 및 의존성](#10-기술-스택-및-의존성)
11. [참고 문헌](#11-참고-문헌)
---
## 1. 이론적 기초
### 1.1 구조적 모형(Structural Model) 계보
```
Black-Scholes (1973)
└─ Merton (1974) ─── 기업부도를 옵션으로 해석
├─ Black-Cox (1976) ─── First Passage Time (배리어 부도)
├─ Geske (1977) ─── 복합옵션 (쿠폰부 부채)
├─ Longstaff-Schwartz (1995) ─── 확률적 이자율
└─ KMV (Kealhofer-McQuown-Vasicek)
└─ Moody's Analytics EDF™ ─── 상용화
```
### 1.2 Merton 모형 (1974)
**핵심 가정:**
- 기업의 자산가치 `V(t)`는 기하 브라운 운동(GBM)을 따름
- 부채는 만기 `T`에 원금 `D`가 일시 상환되는 제로쿠폰 채권
- 자기자본 `E`는 자산 `V`에 대한 유럽형 콜옵션
**자산 역학:**
```
dV = μ·V·dt + σ_V·V·dW
```
- `μ`: 자산 기대수익률 (drift)
- `σ_V`: 자산 변동성
- `W`: 위너 과정
**부도 조건:**
```
Default ⟺ V(T) < D (만기 시점에 자산가치 < 부채)
```
**자기자본의 옵션 해석:**
```
E = Call(V, D, T) = V·N(d₁) - D·e^{-rT}·N(d₂)
```
### 1.3 KMV-Moody's EDF 모형
Merton 모형의 실무 확장:
| 구분 | Merton 원형 | KMV 수정 |
|------|-------------|----------|
| 부도점 | D (총부채) | STD + 0.5×LTD |
| 부도 시점 | 만기 T 시점만 | 임의 시점 (First Passage) |
| EDF 산출 | N(-DD) 이론값 | 경험적 부도 빈도 매핑 |
| 데이터 | 단일 시점 | 시계열 반복 추정 |
### 1.4 축약형 모형(Reduced-Form Model)
**CreditRisk+ (Credit Suisse)**
- 부도를 포아송 과정으로 모형화
- 부도율의 변동성을 명시적 반영 (부도율 자체가 확률변수)
- 섹터별 체계적 요인으로 부도 상관관계 간접 포착
- 장점: 구현 용이, 대규모 포트폴리오 적합
- 한계: 시장 데이터 반영 제한, 등급전이 미반영
**Jarrow-Turnbull / Duffie-Singleton**
- 부도 강도(hazard rate)가 시장 변수에 의존
- CDS/채권 스프레드에서 내재 부도확률 추출
- 한국 적용 한계: CDS 시장 유동성 부족
### 1.5 CreditMetrics 접근법
- 등급전이행렬(Rating Transition Matrix) 기반
- 잠재 변수 `Zt`를 통해 체계적 리스크 반영
- 전이확률 × 등급별 스프레드 → 포트폴리오 가치 분포
- 한국 시장: 신평사 발표 전이행렬과 연동 가능
---
## 2. 핵심 수학적 프레임워크
### 2.1 Merton 연립방정식
관측 가능한 `(E, σ_E)`로부터 비관측 `(V, σ_V)`를 추정:
**방정식 1 — 자기자본 가치:**
```
E = V·N(d₁) - D·e^{-rT}·N(d₂)
```
**방정식 2 — 변동성 관계 (Itô's Lemma):**
```
σ_E = (V/E)·N(d₁)·σ_V
```
**여기서:**
```
d₁ = [ln(V/D) + (r + σ²_V/2)·T] / (σ_V·√T)
d₂ = d₁ - σ_V·√T
```
### 2.2 Distance-to-Default (DD)
```
DD = [ln(V/DP) + (μ - σ²_V/2)·T] / (σ_V·√T)
```
- `DP = STD + 0.5 × LTD` (KMV 부도점)
- `μ`: 자산 기대수익률 (실무에서는 r 또는 과거 추정값 사용)
**해석:** DD는 자산가치가 부도점까지 하락하는 데 필요한 표준편차 수
### 2.3 EDF 산출
**이론적 EDF (정규분포 가정):**
```
EDF_theoretical = N(-DD) = Φ(-DD)
```
**경험적 EDF (KMV 방식):**
```
EDF_empirical = (DD 구간별 실제 부도 기업 수) / (DD 구간별 전체 기업 수)
```
**한국 시장 보정 EDF:**
```
EDF_KR = EDF_theoretical × Calibration_Factor(rating_grade)
```
### 2.4 주가 변동성 추정 방법
#### (a) 역사적 변동성 (Historical Volatility)
```
σ_E = √(252) × std(ln(P_t / P_{t-1}))
```
- 일별 로그수익률의 표준편차 × √252 (연환산)
- 추정 윈도우: 1년 (약 250 거래일)
#### (b) EWMA (Exponentially Weighted Moving Average)
```
σ²_t = λ·σ²_{t-1} + (1-λ)·r²_{t-1}
```
- `λ = 0.94` (RiskMetrics 표준)
- 최근 변동에 더 높은 가중치
#### (c) GARCH(1,1)
```
σ²_t = ω + α·ε²_{t-1} + β·σ²_{t-1}
```
- `ω, α, β`: 최대우도추정(MLE)으로 산출
- `α + β < 1` (정상성 조건)
- 변동성 클러스터링 반영 가능
---
## 3. 한국 시장 등급 관측 문제 및 대안
### 3.1 문제 진단
```
KRX 상장사 약 2,500개
├── 신용등급 보유: 약 500~600개 (주로 회사채/CP 발행사)
│ ├── 관측 가능 등급: BBB ~ A 중심 (약 70%)
│ ├── 고등급 (AAA~AA): 소수 (우량사, 등급 불필요)
│ └── 저등급 (B 이하): 극소수 (상장 유지 자체가 어려움)
└── 신용등급 미보유: 약 1,900개 (소형주, 미발행사)
```
**등급별 관측 분포 추정:**
| 등급군 | KRX 상장사 중 비율 | 부도 관측 가능성 | 주요 이슈 |
|--------|-------------------|-----------------|-----------|
| AAA~AA | ~5% | 극히 낮음 | 부도 사례 거의 0 |
| A | ~20% | 낮음 | 부도 희소하나 관측 가능 |
| BBB | ~40% | 보통 | 가장 관측 풍부 |
| BB | ~20% | 높음 | 투기등급 진입, 데이터 확보 |
| B 이하 | ~5% | 높으나 표본 부족 | 상장폐지와 혼재 |
| 무등급 | ~10% (등급보유 대비) | Shadow 필요 | 대부분 소형주 |
### 3.2 대안 전략 상세
#### 대안 1: Shadow Rating (내재등급) 모형
**목적:** DD 및 재무비율을 기반으로 무등급 기업에 내재등급 부여
**방법론 — Ordered Probit 모형:**
```
y* = β'X + ε, ε ~ N(0, 1)
y = k if τ_{k-1} < y* ≤ τ_k
여기서:
- y: 관측등급 (AAA=1, AA+=2, ..., D=n)
- X: [DD, log(총자산), 부채비율, 이자보상비율, EBITDA마진, ROA, 유동비율, 산업더미]
- τ_k: 등급 경계 절단점(cutoff)
```
**학습 과정:**
1. 등급 보유 기업의 (X, y) 쌍으로 β, τ를 MLE 추정
2. 등급 미보유 기업에 추정된 β'X를 적용하여 각 등급 확률 계산
3. 최대 확률 등급을 Shadow Rating으로 부여
**설명변수 후보:**
| 변수 | 정의 | 기대 부호 |
|------|------|-----------|
| DD | Distance-to-Default | + (높을수록 고등급) |
| log_assets | ln(총자산) | + 규모 효과 |
| leverage | 총부채/총자산 | - |
| int_coverage | EBITDA/이자비용 | + |
| ebitda_margin | EBITDA/매출 | + |
| roa | 순이익/총자산 | + |
| current_ratio | 유동자산/유동부채 | + |
| cash_ratio | 현금/유동부채 | + |
| industry | 산업 더미 | 산업별 상이 |
#### 대안 2: DD-Rating 직접 매핑
글로벌 벤치마크를 기반으로 DD 구간 → 등급 매핑:
| DD 범위 | Moody's 등급 | 한국 등급 (추정) | 이론적 EDF |
|---------|-------------|-----------------|------------|
| > 6.0 | Aaa ~ Aa1 | AAA ~ AA+ | < 0.02% |
| 5.0 ~ 6.0 | Aa2 ~ Aa3 | AA ~ AA- | 0.02% ~ 0.05% |
| 4.0 ~ 5.0 | A1 ~ A3 | A+ ~ A- | 0.05% ~ 0.20% |
| 3.0 ~ 4.0 | Baa1 ~ Baa2 | BBB+ ~ BBB | 0.20% ~ 0.70% |
| 2.5 ~ 3.0 | Baa3 | BBB- | 0.70% ~ 1.50% |
| 2.0 ~ 2.5 | Ba1 | BB+ | 1.50% ~ 3.00% |
| 1.5 ~ 2.0 | Ba2 ~ Ba3 | BB ~ BB- | 3.00% ~ 5.00% |
| 1.0 ~ 1.5 | B1 ~ B2 | B+ ~ B | 5.00% ~ 10.00% |
| 0.5 ~ 1.0 | B3 ~ Caa1 | B- ~ CCC+ | 10.00% ~ 20.00% |
| < 0.5 | Caa2 이하 | CCC 이하 | > 20.00% |
> 주의: 글로벌 매핑은 한국 시장에 직접 적용 시 보정(calibration) 필수
#### 대안 3: 등급군 병합(Grade Pooling)
표본 부족 등급을 인접 등급과 통합:
```
Pool 1: AAA + AA+ + AA + AA- → "최우량군" (Super-Prime)
Pool 2: A+ + A + A- → "우량군" (Prime)
Pool 3: BBB+ + BBB + BBB- → "투자적격군" (Investment)
Pool 4: BB+ + BB + BB- → "투기등급군" (Speculative)
Pool 5: B+ 이하 → "고위험군" (High-Risk)
```
**병합 기준:**
- 각 풀 내 최소 관측수: 30개 이상 (통계적 유의성)
- Hosmer-Lemeshow 검정 등으로 풀 내 균질성 확인
#### 대안 4: 글로벌 데이터 블렌딩
한국 데이터와 글로벌 벤치마크를 표본수 기반 가중 혼합:
```
DR_blended(g) = w(g) × DR_KR(g) + [1 - w(g)] × DR_Global(g)
w(g) = min(1, N_KR(g) / N_threshold)
```
- `DR_KR(g)`: 한국 등급 g의 관측 부도율
- `DR_Global(g)`: Moody's/S&P 등급 g의 글로벌 부도율
- `N_KR(g)`: 한국 등급 g의 관측 표본수
- `N_threshold`: 신뢰도 임계치 (예: 50)
#### 대안 5: 베이지안 보정
```
사전분포(Prior): π(θ_g) ~ Beta(α_0, β_0) ← 글로벌 부도율에서 유도
우도(Likelihood): L(data|θ_g) = θ_g^d × (1-θ_g)^{n-d}
사후분포(Posterior): π(θ_g|data) ~ Beta(α_0 + d, β_0 + n - d)
여기서:
- θ_g: 등급 g의 실제 부도율 (추정 대상)
- d: 한국 등급 g에서 관측된 부도 건수
- n: 한국 등급 g에서 관측된 전체 기업수
- α_0, β_0: 글로벌 데이터에서 유도된 사전 파라미터
```
**장점:** 표본 부족 등급에서 글로벌 Prior에 자연스럽게 의존, 표본 충분 등급에서는 한국 데이터 위주로 수렴
---
## 4. 글로벌 접근 방법론 비교
| 방법론 | 모형 유형 | 핵심 입력 | 장점 | 한계 | 한국 적용성 |
|--------|-----------|-----------|------|------|-------------|
| **Merton-KMV** | 구조적 | 주가, 부채 | 시장기반, 전향적 | 상장사 한정, 분포가정 | ★★★★★ |
| **CreditMetrics** | 전이행렬 | 등급전이, 스프레드 | 포트폴리오 리스크 | 등급 의존적 | ★★★☆☆ |
| **CreditRisk+** | 축약형 | 부도율, 변동성 | 구현 용이 | 시장 미반영 | ★★☆☆☆ |
| **Jarrow-Turnbull** | 축약형 | CDS스프레드 | 시장가격 반영 | CDS시장 미발달 | ★★☆☆☆ |
| **Altman Z-Score** | 판별분석 | 재무비율 | 간단, 검증됨 | 시장변동 미반영 | ★★★☆☆ |
| **ML (XGBoost)** | 비모수 | 다양한 데이터 | 유연, 비선형 | 해석부족, 과적합 | ★★★☆☆ |
| **Bharath-Shumway** | 구조적(간편) | 주가, 부채 | 단순 구현 | 정밀도 한계 | ★★★★☆ |
### Bharath-Shumway 간편 DD (Naïve DD)
반복 추정 없이 직접 DD 계산 (실무 빠른 적용용):
```
V_naive = E + D
σ_V_naive = (E/(E+D)) × σ_E + (D/(E+D)) × (0.05 + 0.25×σ_E)
DD_naive = [ln(V_naive/D) + (μ - σ²_V_naive/2)×T] / (σ_V_naive × √T)
```
- Bharath & Shumway (2008) 연구에서 반복추정 DD와 유사한 부도예측력 보고
- 대규모 데이터 처리 시 1차 필터로 유용
---
## 5. 구현 아키텍처
### 5.1 시스템 구조
```
EDF/
├── config/
│ ├── settings.yaml # 전역 설정 (기간, 파라미터)
│ └── rating_mapping.yaml # DD-등급 매핑 테이블
├── data/
│ ├── raw/ # 원본 데이터
│ ├── processed/ # 전처리된 데이터
│ └── external/ # 글로벌 부도율 통계
├── src/
│ ├── data/
│ │ ├── krx_fetcher.py # KRX 주가 수집
│ │ ├── dart_fetcher.py # DART 재무제표 수집
│ │ ├── rating_fetcher.py # 신용등급 수집
│ │ └── preprocessor.py # 데이터 전처리
│ ├── models/
│ │ ├── merton.py # Merton 연립방정식 풀이
│ │ ├── dd_calculator.py # DD/EDF 산출
│ │ ├── shadow_rating.py # Shadow Rating 모형
│ │ └── volatility.py # 변동성 추정 (Historical/EWMA/GARCH)
│ ├── calibration/
│ │ ├── global_benchmark.py # 글로벌 벤치마크 로딩
│ │ ├── blending.py # 블렌딩/베이지안 보정
│ │ └── grade_pooling.py # 등급군 병합
│ ├── validation/
│ │ ├── backtesting.py # 백테스팅
│ │ ├── discriminatory.py # 변별력 검증 (ROC, KS, CAP)
│ │ └── calibration_test.py # 보정력 검증 (Hosmer-Lemeshow)
│ └── utils/
│ ├── financial.py # 재무비율 계산
│ └── statistics.py # 통계 유틸리티
├── notebooks/
│ ├── 01_data_exploration.ipynb
│ ├── 02_merton_analysis.ipynb
│ ├── 03_shadow_rating.ipynb
│ └── 04_default_rate_output.ipynb
├── outputs/
│ ├── dd_results/ # DD/EDF 산출 결과
│ ├── rating_results/ # 등급별 부도율 결과
│ └── reports/ # 검증 보고서
├── docs/
│ └── technical_methodology.md # 이 문서
├── requirements.txt
└── README.md
```
### 5.2 처리 파이프라인
```
[Phase 1: 데이터 수집]
KRX 주가 → 전처리 → 주가변동성 산출
DART 재무제표 → 부채 구조(STD/LTD) 추출
신평사 등급 → 연도별 등급 스냅샷
[Phase 2: Merton-KMV 모형]
(E, σ_E, D, r, T) → 반복 추정 → (V, σ_V) → DP → DD → EDF
[Phase 3: Shadow Rating]
등급 보유 기업: (DD, 재무비율) ↔ 등급 매핑 학습
등급 미보유 기업: 학습 모형 → Shadow Rating 부여
[Phase 4: 등급별 부도율 집계]
실제등급 + Shadow Rating → 등급별 연간 부도율 집계
표본 부족 등급 → 글로벌 블렌딩 / 등급군 병합
[Phase 5: 검증]
백테스팅, 변별력/보정력 검증, CRA 발표 데이터 비교
```
---
## 6. 데이터 명세
### 6.1 필수 데이터
| 데이터 항목 | 소스 | 수집 주기 | 필수/선택 |
|-------------|------|-----------|-----------|
| 일별 종가 | KRX / pykrx | 일별 | 필수 |
| 시가총액 | KRX / pykrx | 일별 | 필수 |
| 발행주식수 | KRX / DART | 분기 | 필수 |
| 유동부채 (STD) | DART 재무제표 | 분기/연간 | 필수 |
| 비유동부채 (LTD) | DART 재무제표 | 분기/연간 | 필수 |
| 총자산 | DART 재무제표 | 분기/연간 | 필수 |
| 신용등급 | 한기평/한신평/나이스 | 연간 스냅샷 | 필수 |
| 무위험이자율 | 한국은행(ECOS) | 일별 | 필수 |
| 부도/워크아웃 이력 | KRX 상장폐지, 뉴스 | 사건 기반 | 필수 |
### 6.2 보조 데이터 (Shadow Rating 강화용)
| 데이터 항목 | 소스 | 용도 |
|-------------|------|------|
| EBITDA | DART | 이자보상비율, 마진 |
| 이자비용 | DART | 이자보상비율 |
| 매출액 | DART | EBITDA 마진 |
| 현금 및 현금성 자산 | DART | 유동성 비율 |
| 산업분류코드 (KSIC) | KRX / DART | 산업 더미 변수 |
| 거래량 | KRX | 유동성 필터링 |
### 6.3 글로벌 벤치마크 데이터
| 데이터 | 소스 | 내용 |
|--------|------|------|
| 등급별 연간 부도율 | Moody's Annual Default Study | Aaa~C 20년+ 평균 |
| 등급별 누적 부도율 | S&P Global Default Study | AAA~D 1~20년 |
| 한국 등급별 부도율 | 한기평/한신평/나이스 연간 발표 | 국내 기준 |
### 6.4 부도(Default) 정의
```
다음 이벤트 중 하나 이상 발생 시 "부도"로 정의:
1. 법정관리(회생절차) 개시 결정
2. 워크아웃(채권단 자율협약) 개시
3. 상장폐지 (재무 사유: 자본잠식, 감사의견 거절 등)
4. 부도어음/부도수표 발생
5. 기업회생절차 신청
6. 파산 선고
※ 제외: 합병·분할·자진 상장폐지 등 비재무적 사유
```
---
## 7. 알고리즘 상세
### 7.1 Merton 연립방정식 풀이
```python
import numpy as np
from scipy.optimize import fsolve
from scipy.stats import norm
def solve_merton(E: float, sigma_E: float, D: float,
r: float, T: float = 1.0) -> tuple[float, float]:
"""
Merton 연립방정식을 풀어 자산가치(V)와 자산변동성(σ_V)을 추정.
Parameters
----------
E : float
자기자본 시장가치 (시가총액, 억원)
sigma_E : float
주가수익률 변동성 (연환산, 예: 0.30 = 30%)
D : float
부도점 = STD + 0.5 × LTD (억원)
r : float
무위험이자율 (연, 예: 0.035 = 3.5%)
T : float
시간 수평선 (년, 기본 1.0)
Returns
-------
V : float
추정 자산가치 (억원)
sigma_V : float
추정 자산변동성 (연환산)
"""
def equations(params):
V, sigma_V = params
d1 = (np.log(V / D) + (r + 0.5 * sigma_V**2) * T) / (sigma_V * np.sqrt(T))
d2 = d1 - sigma_V * np.sqrt(T)
eq1 = V * norm.cdf(d1) - D * np.exp(-r * T) * norm.cdf(d2) - E
eq2 = (V / E) * norm.cdf(d1) * sigma_V - sigma_E
return [eq1, eq2]
# 초기값: V0 = E + D, sigma_V0 = sigma_E * E / (E + D)
V0 = E + D
sigma_V0 = sigma_E * E / (E + D)
solution = fsolve(equations, [V0, sigma_V0], full_output=True)
V, sigma_V = solution[0]
return max(V, E), max(sigma_V, 0.01) # 하한 설정
def calculate_dd(V: float, sigma_V: float, D: float,
mu: float, T: float = 1.0) -> float:
"""Distance-to-Default 산출"""
if D <= 0 or V <= 0 or sigma_V <= 0:
return np.nan
DD = (np.log(V / D) + (mu - 0.5 * sigma_V**2) * T) / (sigma_V * np.sqrt(T))
return DD
def calculate_edf(DD: float) -> float:
"""이론적 EDF 산출 (정규분포 가정)"""
if np.isnan(DD):
return np.nan
return norm.cdf(-DD)
```
### 7.2 변동성 추정
```python
def historical_volatility(prices: np.ndarray, window: int = 252) -> float:
"""역사적 변동성 (연환산)"""
log_returns = np.diff(np.log(prices))
if len(log_returns) < window:
window = len(log_returns)
return np.std(log_returns[-window:]) * np.sqrt(252)
def ewma_volatility(prices: np.ndarray, lmbda: float = 0.94) -> float:
"""EWMA 변동성 (연환산)"""
log_returns = np.diff(np.log(prices))
variance = log_returns[0]**2
for ret in log_returns[1:]:
variance = lmbda * variance + (1 - lmbda) * ret**2
return np.sqrt(variance * 252)
def garch_volatility(prices: np.ndarray) -> float:
"""GARCH(1,1) 변동성 (arch 패키지 사용)"""
from arch import arch_model
log_returns = np.diff(np.log(prices)) * 100 # 백분율
model = arch_model(log_returns, vol='Garch', p=1, q=1, dist='normal')
result = model.fit(disp='off')
# 최신 조건부 변동성을 연환산
cond_vol = result.conditional_volatility[-1] / 100
return cond_vol * np.sqrt(252)
```
### 7.3 Shadow Rating (Ordered Probit)
```python
import statsmodels.api as sm
import pandas as pd
def fit_shadow_rating_model(df_rated: pd.DataFrame,
feature_cols: list,
rating_col: str = 'rating_numeric') -> object:
"""
등급 보유 기업 데이터로 Ordered Probit 모형 적합.
Parameters
----------
df_rated : pd.DataFrame
등급 보유 기업 데이터 (DD, 재무비율, 등급 포함)
feature_cols : list
설명변수 컬럼명 리스트
rating_col : str
등급 숫자 컬럼 (1=AAA, 2=AA+, ...)
"""
X = df_rated[feature_cols]
y = df_rated[rating_col]
model = sm.OrderedModel(y, X, distr='probit')
result = model.fit(method='bfgs', disp=False)
return result
def predict_shadow_rating(model_result, df_unrated: pd.DataFrame,
feature_cols: list) -> pd.DataFrame:
"""등급 미보유 기업에 Shadow Rating 부여"""
X = df_unrated[feature_cols]
pred_probs = model_result.predict(X)
# 각 기업의 최대 확률 등급
df_unrated = df_unrated.copy()
df_unrated['shadow_rating_numeric'] = pred_probs.values.argmax(axis=1) + 1
return df_unrated
```
### 7.4 등급별 부도율 산출 (블렌딩)
```python
def compute_blended_default_rates(df: pd.DataFrame,
rating_col: str,
default_col: str,
global_dr: dict,
threshold: int = 50) -> pd.DataFrame:
"""
등급별 부도율을 한국 관측 + 글로벌 벤치마크 블렌딩으로 산출.
Parameters
----------
df : pd.DataFrame
전체 기업 데이터 (등급 + 부도여부 포함)
rating_col : str
등급 컬럼명
default_col : str
부도 여부 컬럼명 (0/1)
global_dr : dict
{등급: 글로벌 부도율} 매핑
threshold : int
블렌딩 전환 표본수 임계치
"""
results = []
for grade in sorted(df[rating_col].unique()):
subset = df[df[rating_col] == grade]
n = len(subset)
d = subset[default_col].sum()
kr_dr = d / n if n > 0 else 0
g_dr = global_dr.get(grade, kr_dr)
w = min(1.0, n / threshold)
blended = w * kr_dr + (1 - w) * g_dr
results.append({
'grade': grade,
'n_firms': n,
'n_defaults': d,
'korean_dr': kr_dr,
'global_dr': g_dr,
'weight_kr': w,
'blended_dr': blended
})
return pd.DataFrame(results)
```
### 7.5 베이지안 부도율 추정
```python
from scipy.stats import beta as beta_dist
def bayesian_default_rate(n: int, d: int,
prior_mean: float,
prior_strength: float = 50) -> dict:
"""
베이지안 방식 등급별 부도율 추정.
Parameters
----------
n : int
관측 기업수
d : int
부도 건수
prior_mean : float
사전 부도율 (글로벌 벤치마크)
prior_strength : float
사전 강도 (글로벌 표본수에 비례)
"""
# Beta prior 파라미터
alpha_0 = prior_mean * prior_strength
beta_0 = (1 - prior_mean) * prior_strength
# 사후 분포 (Beta-Binomial conjugacy)
alpha_post = alpha_0 + d
beta_post = beta_0 + (n - d)
# 사후 통계량
posterior_mean = alpha_post / (alpha_post + beta_post)
posterior_mode = (alpha_post - 1) / (alpha_post + beta_post - 2) \
if (alpha_post > 1 and beta_post > 1) else posterior_mean
ci_lower, ci_upper = beta_dist.ppf([0.025, 0.975], alpha_post, beta_post)
return {
'posterior_mean': posterior_mean,
'posterior_mode': posterior_mode,
'ci_95_lower': ci_lower,
'ci_95_upper': ci_upper,
'prior_mean': prior_mean,
'n_obs': n,
'n_defaults': d
}
```
---
## 8. 검증 방법론
### 8.1 변별력(Discriminatory Power) 검증
| 지표 | 설명 | 기준 |
|------|------|------|
| **AUROC** | ROC 곡선 하 면적 | > 0.70 (수용), > 0.80 (양호) |
| **KS 통계량** | 부도/비부도 분포 최대 이격 | > 0.30 (수용) |
| **CAP/AR** | 정확도 비율 | > 0.50 (수용) |
| **정보값(IV)** | 변수별 변별 기여도 | > 0.10 (유의미) |
### 8.2 보정력(Calibration) 검증
| 지표 | 설명 |
|------|------|
| **Hosmer-Lemeshow** | 예측 부도율 vs 실제 부도율 적합도 |
| **Binomial Test** | 등급별 실제 부도율이 예측 구간 내 존재 여부 |
| **Traffic Light** | Basel II 권장 — 녹색/황색/적색 신호 |
### 8.3 백테스팅 프로세스
```
for year in [T-5, T-4, T-3, T-2, T-1]:
1. year 말 기준 DD/EDF 산출
2. year+1 동안의 실제 부도 여부 관측
3. 예측 EDF vs 실현 부도율 비교
4. 변별력/보정력 지표 산출
```
### 8.4 CRA 발표 데이터와 비교
- 한기평·한신평·나이스 연간 발표 등급별 부도율과 본 모형 산출치 비교
- 등급별 편차(bias) 및 상관관계 분석
- 부도 시점 대비 DD 하락 시점의 선행성 분석
---
## 9. 한국 시장 특수 고려사항
### 9.1 데이터 관련
| 항목 | 고려사항 | 대응 |
|------|----------|------|
| KOSPI vs KOSDAQ | KOSDAQ 소형·고변동성 기업 다수 | 시장별 분리 분석 또는 통합+더미 |
| 금융업 | 부채 구조 상이 (예금 = 부채) | **분석 제외** 또는 별도 모형 |
| 등급 불일치 | 한기평·한신평·나이스 등급 차이 | 중위값 또는 최빈값 사용 |
| 분기 vs 연간 | 재무제표 시차 | 분기 데이터 우선, 없으면 연간 보간 |
| 상장폐지 | 부도 vs 비부도 폐지 구분 | 폐지 사유 코드로 필터링 |
### 9.2 구조적 특성
| 특성 | 영향 | 모형 반영 방법 |
|------|------|---------------|
| **재벌 계열** | 그룹 지원으로 개별 DD 대비 부도율 하락 | 계열사 더미 / 그룹 DD 산출 |
| **정부 지원** | 공기업 부도율 ≈ 0 | 정부지원 등급에서 제외 또는 별도 처리 |
| **채권단 자율협약** | 형식적 부도 회피 | 워크아웃 개시를 부도 사건에 포함 |
| **유상증자/CB** | 부도 직전 자본 확충으로 DD 왜곡 | 이벤트 전 DD 사용 또는 플래그 |
### 9.3 변동성 추정 주의사항
| 상황 | 문제 | 대응 |
|------|------|------|
| 장기 거래정지 | 변동성 과소추정 | 정지 기간 제외, 30일 이상 정지 시 분석 제외 |
| 저거래량 | 비유동성 프리미엄 혼재 | 거래량 하위 10% 제외 또는 유동성 보정 |
| 극단 이벤트 | 일시적 급등락으로 변동성 왜곡 | Winsorization (상하 1%) 또는 트리밍 |
| 공매도 제한 | 하락 변동성 억제 | 변동성 하향 편의 인지, 글로벌 대비 보정 |
---
## 10. 기술 스택 및 의존성
### 10.1 Python 패키지
```
# 핵심 (requirements.txt)
numpy>=1.24
pandas>=2.0
scipy>=1.10
statsmodels>=0.14
# 데이터 수집
pykrx>=1.0 # KRX 주가 데이터
OpenDartReader>=0.3 # DART 전자공시 API
# 변동성 모형
arch>=6.0 # GARCH/EWMA 모형
# 시각화
matplotlib>=3.7
plotly>=5.15
seaborn>=0.12
# 머신러닝 (선택)
scikit-learn>=1.3
xgboost>=2.0
# 유틸리티
tqdm>=4.65
pyyaml>=6.0
```
### 10.2 무위험이자율
- 한국은행 ECOS의 **국고채 1년물 금리** 사용
- 또는 통화안정증권(MSB) 1년물
### 10.3 데이터베이스
- 개발 단계: SQLite (로컬 파일)
- 운영 단계: PostgreSQL (필요시)
---
## 11. 참고 문헌
### 핵심 논문
1. Merton, R.C. (1974). "On the Pricing of Corporate Debt: The Risk Structure of Interest Rates." *Journal of Finance*, 29(2), 449-470.
2. Black, F. & Cox, J.C. (1976). "Valuing Corporate Securities: Some Effects of Bond Indenture Provisions." *Journal of Finance*, 31(2), 351-367.
3. Bharath, S.T. & Shumway, T. (2008). "Forecasting Default with the Merton Distance to Default Model." *Review of Financial Studies*, 21(3), 1339-1369.
4. Crosbie, P. & Bohn, J. (2003). "Modeling Default Risk." Moody's KMV Working Paper.
### 한국 시장 연구
5. 한국은행. "IRB 접근법 하에서의 장기 부도확률 추정."
6. 한국기업평가. "연간 부도율 통계" (korearatings.com)
7. 한국신용평가. "신용등급별 부도율 및 전이행렬" (kisrating.com)
### 기술 참고
8. Credit Suisse Financial Products. (1997). "CreditRisk+: A Credit Risk Management Framework."
9. JP Morgan. (1997). "CreditMetrics — Technical Document."
10. Basel Committee on Banking Supervision. "Studies on the Validation of Internal Rating Systems."