Compare commits
23 Commits
6aaff48856
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed3bed2bf1 | ||
| 1ce0dbebdc | |||
| acb232fff4 | |||
|
|
d1ddf06e5d | ||
|
|
87725b7c19 | ||
|
|
f35ab389d5 | ||
|
|
2406dac6a2 | ||
|
|
8a0d6e7574 | ||
|
|
a4c9a702ad | ||
|
|
a406d98226 | ||
|
|
2b94cc802d | ||
|
|
b8514c1251 | ||
|
|
0762fcc5d8 | ||
|
|
0e1e0e5cf2 | ||
|
|
cc55acc330 | ||
|
|
49c7661888 | ||
|
|
92ce84ad96 | ||
|
|
d61c538308 | ||
|
|
1a4cc873d9 | ||
|
|
93887f49dd | ||
|
|
811d6ee843 | ||
|
|
8af743e6f3 | ||
|
|
ebdc6b805b |
@@ -1,59 +0,0 @@
|
||||
# Known Issues & Lessons Learned
|
||||
|
||||
> **이 파일은 SSOT(Single Source of Truth)입니다.**
|
||||
> 디버깅이나 구현 전에 **반드시** 이 파일을 확인하세요.
|
||||
> 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
|
||||
|
||||
---
|
||||
|
||||
## 포맷
|
||||
|
||||
각 항목은 아래 형식을 따릅니다:
|
||||
|
||||
```markdown
|
||||
### [날짜] [키워드] — 한줄 요약
|
||||
- **증상**: 무엇이 잘못되었는가
|
||||
- **원인**: 근본 원인
|
||||
- **해결**: 올바른 해결 방법
|
||||
- **주의**: 재발 방지를 위한 교훈
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 공통 이슈
|
||||
|
||||
### [2026-03-08] PowerShell curl — Invoke-WebRequest 충돌
|
||||
- **증상**: `curl` 명령이 예상과 다른 응답 형식을 반환
|
||||
- **원인**: PowerShell에서 `curl`은 `Invoke-WebRequest`의 별칭
|
||||
- **해결**: **`curl.exe`**를 명시적으로 사용
|
||||
- **주의**: HTTP 관련 모든 명령에서 `curl.exe` 사용 필수
|
||||
|
||||
### [2026-03-08] PowerShell npm — 실행 정책 오류
|
||||
- **증상**: `npm run` 명령이 `실행 정책` 관련 오류로 실패
|
||||
- **원인**: PowerShell 스크립트 실행 정책이 제한적으로 설정됨
|
||||
- **해결**: `cmd /c npm run dev` 형식으로 cmd를 통해 실행
|
||||
- **주의**: npm 관련 명령은 항상 `cmd /c` 접두어 사용 권장
|
||||
|
||||
---
|
||||
|
||||
## 프로젝트별 이슈
|
||||
|
||||
> 아래에 프로젝트 특화 이슈를 추가하세요.
|
||||
|
||||
### [2026-03-10] ECOS API 통계표코드 — 대부분 404 반환
|
||||
- **증상**: `111Y002`, `817Y002`, `901Y067/A` 등 다수의 통계표코드/항목코드가 "해당하는 데이터가 없습니다" 반환
|
||||
- **원인**: ECOS API 통계표 구조가 공식 문서와 다름. 예: GDP 성장률은 `111Y002`가 아닌 `902Y015` (국제 비교 통계), CD금리는 `817Y002`가 아닌 `721Y001`
|
||||
- **해결**: `StatisticItemList` API로 각 통계표의 항목코드를 조회한 후, `StatisticSearch`로 실제 데이터 반환 확인. 검증된 전체 코드 목록은 `config.yaml`에 기록됨
|
||||
- **주의**: ECOS 통계표코드 변경 시 반드시 `StatisticItemList` → `StatisticSearch` 2단계 검증 수행. 선행지수(`901Y067`)는 연간 데이터 없음(월별만 존재)
|
||||
|
||||
### [2026-03-10] Windows CP949 인코딩 — Unicode 렌더링 실패
|
||||
- **증상**: `matplotlib`에서 ✓, ✗, — (em dash) 등 Unicode 기호가 CP949에서 렌더링 실패
|
||||
- **원인**: Windows 콘솔/Malgun Gothic 폰트가 해당 기호를 지원하지 않음
|
||||
- **해결**: `sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')` + ASCII 대체 문자 사용 (Pass O / Fail X)
|
||||
- **주의**: 모든 Python 스크립트의 최상단에 UTF-8 stdout 설정 추가 필요
|
||||
|
||||
### [2026-03-10] pandas fillna(method=) — Deprecation 오류
|
||||
- **증상**: `fillna(method="ffill")` 호출 시 FutureWarning/Error
|
||||
- **원인**: pandas 2.x에서 `fillna(method=)` 인자 deprecated
|
||||
- **해결**: `df.ffill().bfill()` 메서드 체인으로 대체
|
||||
- **주의**: pandas 2.x 이상 사용 시 `fillna(method=)` 전면 금지
|
||||
@@ -1,67 +0,0 @@
|
||||
# Agent Guide — AI 에이전트 범용 워크플로우 시스템
|
||||
|
||||
> AI 코딩 에이전트가 더 똑똑하게 동작하도록 설계된 범용 워크플로우 템플릿.
|
||||
> 새 프로젝트에서 `.agents/` 폴더를 통째로 복사하고, `{{PLACEHOLDER}}`를 교체하면 즉시 사용 가능합니다.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 1. 이 레포를 클론하거나 .agents/ 폴더를 복사
|
||||
git clone https://git.variet.net/Variet/agent_guide.git
|
||||
cp -r agent_guide/.agents/ your-project/.agents/
|
||||
|
||||
# 2. 프로젝트별 값 2개만 교체
|
||||
# - {{GITEA_REPO}} → services.md, check-gitea.md, wiki_helper.py
|
||||
# - {{VIKUNJA_PROJECT_ID}} → services.md, vikunja_helper.py (PROJECT_ID)
|
||||
# - references/ → 프로젝트별 아키텍처, 기술스택, 컨벤션 채우기
|
||||
|
||||
# 3. docs/devlog/ 디렉토리 생성
|
||||
mkdir -p docs/devlog/entries
|
||||
|
||||
# 4. AI 에이전트에게 "시작" 또는 "/start" 명령
|
||||
```
|
||||
|
||||
## 파일 구조
|
||||
|
||||
```
|
||||
.agents/
|
||||
├── AGENT.md ← 🧠 글로벌 NEVER/ALWAYS 규칙
|
||||
├── GUIDE.md ← 📖 상세 가이드
|
||||
├── references/ ← 📚 프로젝트 지식 베이스
|
||||
│ ├── architecture.md ← 아키텍처 (템플릿)
|
||||
│ ├── tech-stack.md ← 기술 스택 (템플릿)
|
||||
│ ├── conventions.md ← 코딩 컨벤션 (템플릿)
|
||||
│ └── known-issues.md ← 과거 실패 기록 (공통 이슈 포함)
|
||||
└── workflows/ ← ⚙️ 행동 절차
|
||||
├── start.md ← 세션 시작 (룰 로딩 + Git + Vikunja + Wiki)
|
||||
├── end.md ← 세션 종료 (학습 기록 + Vikunja + Git)
|
||||
├── pre-task.md ← 작업 전 필수 체크리스트
|
||||
├── debug.md ← 체계적 디버깅
|
||||
├── services.md ← 서비스 연동 정보 ({{PLACEHOLDER}})
|
||||
├── check-gitea.md ← Gitea 현황 조회
|
||||
├── check-vikunja.md ← Vikunja 태스크 조회
|
||||
└── helpers/
|
||||
├── vikunja_helper.py ← Vikunja API 안전 래퍼
|
||||
└── wiki_helper.py ← Gitea Wiki 래퍼
|
||||
```
|
||||
|
||||
## 교체해야 하는 값 (프로젝트별)
|
||||
|
||||
> Gitea/Vikunja 토큰은 이미 입력되어 있습니다. 프로젝트별로 아래 2개만 교체하면 됩니다.
|
||||
|
||||
| Placeholder | 설명 | 파일 |
|
||||
|-------------|------|------|
|
||||
| `{{GITEA_REPO}}` | Gitea 저장소명 | services.md, check-gitea.md, wiki_helper.py |
|
||||
| `{{VIKUNJA_PROJECT_ID}}` | Vikunja 프로젝트 ID | services.md, vikunja_helper.py (`PROJECT_ID`) |
|
||||
|
||||
## 상세 가이드
|
||||
|
||||
[GUIDE.md](.agents/GUIDE.md) 참조.
|
||||
|
||||
## 연구 기반
|
||||
|
||||
7개 AI 에이전트 플랫폼 (Claude, GPT, Gemini, Cursor, Cline, Roo, Windsurf) 분석 + Reflexion Framework, Context Engineering, Sentinel Check 등 최신 연구 기반.
|
||||
|
||||
## License
|
||||
|
||||
Internal — Variet
|
||||
@@ -47,6 +47,12 @@ description: 모든 작업에 자동 적용되는 에이전트 행동 규칙.
|
||||
4. `.agents/workflows/services.md` (service credentials & protocols)
|
||||
5. `.agents/workflows/` (action procedures)
|
||||
|
||||
## Python Environment
|
||||
|
||||
- **경로**: `C:\ProgramData\miniforge3\envs\lifetimePD`
|
||||
- **실행**: `C:\ProgramData\miniforge3\envs\lifetimePD\python.exe`
|
||||
- 모든 Python 스크립트 실행 시 위 경로의 python을 사용합니다.
|
||||
|
||||
## PowerShell Notes
|
||||
|
||||
- `curl` → PowerShell에서 `Invoke-WebRequest` 별칭. **반드시 `curl.exe`** 사용
|
||||
@@ -76,6 +76,6 @@ C:\ProgramData\miniforge3\envs\quant\python.exe main.py --estimate-rho
|
||||
|
||||
## 미완료 사항 (다음 작업자 참고)
|
||||
|
||||
1. **#290 실제 3사 전이행렬 CSV 변환** — `doc/`의 PDF에서 전이행렬을 추출하여 `data/raw/` CSV로 변환 필요. 현재는 builtin synthetic 데이터 사용 중
|
||||
2. **#293 거시 시나리오 고도화** — IMF WEO/KDI 전망치를 직접 입력하여 시나리오 정밀화
|
||||
3. **GDP 성장률 코드** — 현재 `902Y015/KOR` (국제 비교 통계) 사용. 한은 국민계정 직접 통계표코드 발견 시 교체 권장
|
||||
1. **#293 거시 시나리오 고도화** — IMF WEO/KDI 전망치를 직접 입력하여 시나리오 정밀화
|
||||
2. **GDP 성장률 코드** — 현재 `902Y015/KOR` (국제 비교 통계) 사용. 한은 국민계정 직접 통계표코드 발견 시 교체 권장
|
||||
3. **전처리 파이프라인 개선** — PD 플로어 적용, CCC B↔D 보간 방식 개선 (docs/methodology.md §2.0 참조)
|
||||
@@ -22,6 +22,13 @@
|
||||
- 주석 언어: 한국어 (모듈/함수 docstring은 한국어)
|
||||
- Type hints: 사용 권장 (`def foo(x: float) -> np.ndarray:`)
|
||||
|
||||
## Python 환경
|
||||
|
||||
- **경로**: `C:\ProgramData\miniforge3\envs\quant`
|
||||
- **실행**: `C:\ProgramData\miniforge3\envs\quant\python.exe`
|
||||
- 모든 Python 스크립트 실행 시 위 경로의 python.exe를 사용
|
||||
- PowerShell에서: `& "C:\ProgramData\miniforge3\envs\quant\python.exe" script.py`
|
||||
|
||||
## 커밋 메시지
|
||||
|
||||
```
|
||||
94
.agents/references/known-issues.md
Normal file
94
.agents/references/known-issues.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Known Issues & Lessons Learned
|
||||
|
||||
> **이 파일은 SSOT(Single Source of Truth)입니다.**
|
||||
> 디버깅이나 구현 전에 **반드시** 이 파일을 확인하세요.
|
||||
> 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
|
||||
|
||||
---
|
||||
|
||||
## 포맷
|
||||
|
||||
각 항목은 아래 형식을 따릅니다:
|
||||
|
||||
```markdown
|
||||
### [날짜] [키워드] — 한줄 요약
|
||||
- **증상**: 무엇이 잘못되었는가
|
||||
- **원인**: 근본 원인
|
||||
- **해결**: 올바른 해결 방법
|
||||
- **주의**: 재발 방지를 위한 교훈
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 공통 이슈
|
||||
|
||||
### [2026-03-08] PowerShell curl — Invoke-WebRequest 충돌
|
||||
- **증상**: `curl` 명령이 예상과 다른 응답 형식을 반환
|
||||
- **원인**: PowerShell에서 `curl`은 `Invoke-WebRequest`의 별칭
|
||||
- **해결**: **`curl.exe`**를 명시적으로 사용
|
||||
- **주의**: HTTP 관련 모든 명령에서 `curl.exe` 사용 필수
|
||||
|
||||
### [2026-03-08] PowerShell npm — 실행 정책 오류
|
||||
- **증상**: `npm run` 명령이 `실행 정책` 관련 오류로 실패
|
||||
- **원인**: PowerShell 스크립트 실행 정책이 제한적으로 설정됨
|
||||
- **해결**: `cmd /c npm run dev` 형식으로 cmd를 통해 실행
|
||||
- **주의**: npm 관련 명령은 항상 `cmd /c` 접두어 사용 권장
|
||||
|
||||
---
|
||||
|
||||
## 프로젝트별 이슈
|
||||
|
||||
> 아래에 프로젝트 특화 이슈를 추가하세요.
|
||||
|
||||
### [2026-03-10] ECOS API 통계표코드 — 대부분 404 반환
|
||||
- **증상**: `111Y002`, `817Y002`, `901Y067/A` 등 다수의 통계표코드/항목코드가 "해당하는 데이터가 없습니다" 반환
|
||||
- **원인**: ECOS API 통계표 구조가 공식 문서와 다름. 예: GDP 성장률은 `111Y002`가 아닌 `902Y015` (국제 비교 통계), CD금리는 `817Y002`가 아닌 `721Y001`
|
||||
- **해결**: `StatisticItemList` API로 각 통계표의 항목코드를 조회한 후, `StatisticSearch`로 실제 데이터 반환 확인. 검증된 전체 코드 목록은 `config.yaml`에 기록됨
|
||||
- **주의**: ECOS 통계표코드 변경 시 반드시 `StatisticItemList` → `StatisticSearch` 2단계 검증 수행. 선행지수(`901Y067`)는 연간 데이터 없음(월별만 존재)
|
||||
|
||||
### [2026-03-10] Windows CP949 인코딩 — Unicode 렌더링 실패
|
||||
- **증상**: `matplotlib`에서 ✓, ✗, — (em dash) 등 Unicode 기호가 CP949에서 렌더링 실패
|
||||
- **원인**: Windows 콘솔/Malgun Gothic 폰트가 해당 기호를 지원하지 않음
|
||||
- **해결**: `sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')` + ASCII 대체 문자 사용 (Pass O / Fail X)
|
||||
- **주의**: 모든 Python 스크립트의 최상단에 UTF-8 stdout 설정 추가 필요
|
||||
|
||||
### [2026-03-10] pandas fillna(method=) — Deprecation 오류
|
||||
- **증상**: `fillna(method="ffill")` 호출 시 FutureWarning/Error
|
||||
- **원인**: pandas 2.x에서 `fillna(method=)` 인자 deprecated
|
||||
- **해결**: `df.ffill().bfill()` 메서드 체인으로 대체
|
||||
- **주의**: pandas 2.x 이상 사용 시 `fillna(method=)` 전면 금지
|
||||
|
||||
### [2026-03-11] Belkin Zt 부호 규칙 — 전체 파이프라인 부호 반전 버그
|
||||
- **증상**: Zt 1998=+2.12 (IMF 위기인데 양수), 2006=-1.53 (호황인데 음수)
|
||||
- **원인**: Belkin 논문 원본: Z>0=호황(PD↓). 코드에서 임계값이 오름차순(AAA→D)인데 수식에 `(d - √ρ·Z)`로 구현하여 부호 반전. 올바른 수식은 `(d + √ρ·Z)`
|
||||
- **해결**: `credit_cycle.py`, `vasicek.py`, `transition_matrices.py` 3파일 모두 `(d - sqrt_rho * z)` → `(d + sqrt_rho * z)` 수정
|
||||
- **주의**: vasicek.py의 `conditional_pd()`는 Basel convention (Z↑=불황)으로 별도 체계. 조건부 전이행렬은 Belkin convention (Z↑=호황). 두 규약이 코드에 공존하므로 반드시 docstring 확인 후 사용
|
||||
|
||||
### [2026-03-11] ECOS 거시변수 — fallback 커버리지 부족
|
||||
- **증상**: 기존 11개 변수만으로는 Zt 설명력 부족 (R² < 0.6)
|
||||
- **원인**: 한국 경제 특수성(유가의존, 수출주도, 부동산 영향 등) 미반영
|
||||
- **해결**: 31개 변수로 확장 (KOSPI, OIL_PRICE, USDKRW, DISHONOR_RATE, HOUSING_PRICE, BSI_MANUF 등). `data/ecos_fetcher.py` + `data/cache/macro_ecos.csv` 캐싱 구조
|
||||
- **주의**: fallback 데이터는 대략적 수치. 정밀 분석 시 ECOS API 실제 호출 필요 (`--force` 옵션)
|
||||
|
||||
### [2026-03-11] Windows Python — 소스 코드 특수문자 SyntaxError
|
||||
- **증상**: 파이썬 코드 내 문자열(예: docstring)에 `×` (U+00D7), `→` (U+2192) 등 특수문자나 한글 이외의 유니코드가 포함될 때, Windows CP949 환경에서 `SyntaxError: invalid character` 또는 `unterminated triple-quoted string literal` 로 파싱 오류가 발생함.
|
||||
- **원인**: Windows Python 3.12 런타임이 파일 맨 앞 `# -*- coding: utf-8 -*-` 이나 `-X utf8` 런타임 플래그도 소스 구문 분석 레벨에서 완벽하게 해석해내지 못하는 Windows 인코딩 고질병 문제 발생.
|
||||
- **해결**: 소스 코드 내에선 (특히 docstring 등 Python 인터프리터가 읽을 영역에서는) `×` 대신 `x` 또는 `by`를 사용하고, `→` 대신 `->` 로 ASCII 코드로 치환하여 작성함 (정규식 치환 등 활용). 한글 자체는 정상적으로 파싱됨.
|
||||
- **주의**: 모델링, 모듈 코드 작성 시 문자열이나 주석 내에 특수 유니코드 기호(×, →, —, §)를 직접 삽입하지 말고 안전한 ASCII 문자로 대체하여 코딩할 것.
|
||||
|
||||
### [2026-03-11] ADF 단위근 검정 — autolag='AIC' 소표본 과적합
|
||||
- **증상**: Zt ADF 검정이 `autolag='AIC'`에서 p=0.40 (FAIL), `autolag='BIC'`에서 p=0.0000 (PASS)
|
||||
- **원인**: AIC는 소표본(N<50)에서 lag를 과다 선택 (N=26에서 lag=8 선택 -> 유효관측치=17 -> 검정력 상실). Hamilton (1994, Ch.17) 참조
|
||||
- **해결**: `validation/statistical_tests.py`에서 `adfuller(series, autolag="AIC")` -> `adfuller(series, autolag="BIC")`, BIC는 보수적 lag 선택으로 소표본 적합 (Schwarz 1978)
|
||||
- **주의**: N<50 시계열 ADF 검정에서 항상 `autolag='BIC'` 사용. AIC는 대표본(N>100)에서만 신뢰할 것
|
||||
|
||||
### [2026-03-11] KAP 채권 YTM PD Floor — 하드코딩 vs 실계산 혼동
|
||||
- **증상**: `DEFAULT_PD_FLOORS` 하드코딩 값(BBB=20bp)과 KAP YTM 기반 실계산값(BBB=93bp)의 차이가 큼
|
||||
- **원인**: `pd_floor.py`에 `build_complete_pd_floor_table()` 함수가 존재하나 사용되지 않고, `get_default_pd_floors()`만 호출
|
||||
- **해결**: `main.py`와 `generate_report.py` 모두 `build_complete_pd_floor_table()` 호출로 변경. `ytm_fetcher.py` fallback 데이터(2025-12-31)로 오프라인에서도 작동
|
||||
### [2026-03-27] ECOS 월별 데이터 역산 시 연간 데이터와의 오차
|
||||
- **증상**: KOSPI 평균이나 교역조건지수를 월별로 12분할 평균 낸 결과가 ECOS 연간 데이터(A) 고시와 소수점 이하 단위에서 완전 일치하지 않음.
|
||||
- **원인**: KOSPI 일평균의 영업일 가중치, 교역조건지수의 거래물량 가중치가 연간 산출 시 각 월의 편차를 발생시키기 때문.
|
||||
- **해결**: 대상이 통계 모형 입력 변수(거시 시나리오 모델링)라면 12개월 단순 평균의 오차율(약 0.05%)이 수치에 영향을 거의 미치지 않으므로, 복잡도 완화를 위해 단순 Aggregate를 허용하는 것으로 아키텍처 합의. (단, WTI 단순 평균, KOSPI 종가 등은 완벽 모사가 가능)
|
||||
- **주의**: 소수점 2째 자리 검증/심사표 등 완벽한 모사가 불가피할 경우, M/A 주기를 나눠 API를 2번씩 별도 Fetch해야 함.
|
||||
|
||||
28
config.yaml
28
config.yaml
@@ -15,27 +15,47 @@ ecos:
|
||||
cpi: "901Y009" # 소비자물가지수 / ITEM: 0 (총지수, level→YoY% 변환)
|
||||
composite_leading: "901Y067" # 경기종합지수 / ITEM: I16A (선행, 월별→연평균)
|
||||
|
||||
# 전이행렬 데이터 소스
|
||||
data:
|
||||
transition_source: "real" # "real" (3사 실제) | "builtin" (내장 샘플)
|
||||
transition_dir: null # null이면 기본 data/real_v2/
|
||||
|
||||
# 모형 파라미터
|
||||
model:
|
||||
# 자산상관계수 (Basel IRB 기준 0.12~0.24, 기업 평균 ~0.20)
|
||||
rho: 0.20
|
||||
# 신용등급 체계 (한국 3사 공통)
|
||||
rating_grades: ["AAA", "AA", "A", "BBB", "BB", "B", "CCC", "D"]
|
||||
rating_grades: ["AAA", "AA", "A", "BBB", "BB", "B", "D"] # 7x7 (CCC제외, Zt추정용)
|
||||
# 거시 회귀모형 설정
|
||||
macro_method: "ar1_macro" # "ar1_macro" | "stepwise_aic"
|
||||
macro_vars: ["HOUSING_PRICE", "CREDIT_SPREAD_LAG1", "CURRENT_ACCOUNT_R"]
|
||||
|
||||
# 시나리오 설정
|
||||
scenarios:
|
||||
upside:
|
||||
name: "호황 (Upside)"
|
||||
z_multiplier: 1.0 # Zt = μ + 1.0σ
|
||||
weight: 0.20 # ECB 방식 확률가중치
|
||||
z_multiplier: 1.0 # Z-직접 fallback용
|
||||
weight: 0.20
|
||||
macro_shocks: # AR(1) 충격 (σ 배수)
|
||||
HOUSING_PRICE: 1.0 # 주택가격 1σ 상승 (호재)
|
||||
CREDIT_SPREAD_LAG1: -1.0 # 신용스프레드 1σ 하락 (호재)
|
||||
CURRENT_ACCOUNT_R: 1.0 # 경상수지변화율 1σ 상승
|
||||
base:
|
||||
name: "중립 (Base)"
|
||||
z_multiplier: 0.0
|
||||
weight: 0.50
|
||||
macro_shocks:
|
||||
HOUSING_PRICE: 0.0
|
||||
CREDIT_SPREAD_LAG1: 0.0
|
||||
CURRENT_ACCOUNT_R: 0.0
|
||||
downside:
|
||||
name: "불황 (Downside)"
|
||||
z_multiplier: -1.5 # Fed DFAST 역사적 하위 5%
|
||||
z_multiplier: -1.5
|
||||
weight: 0.30
|
||||
macro_shocks:
|
||||
HOUSING_PRICE: -1.5 # 주택가격 1.5σ 하락 (악재)
|
||||
CREDIT_SPREAD_LAG1: 1.5 # 신용스프레드 1.5σ 상승 (악재)
|
||||
CURRENT_ACCOUNT_R: -1.5 # 경상수지변화율 1.5σ 하락
|
||||
|
||||
# 50년 수렴 메커니즘
|
||||
convergence:
|
||||
|
||||
27
data/cache/macro_ecos.csv
vendored
Normal file
27
data/cache/macro_ecos.csv
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
YEAR,GDP_GROWTH,UNEMPLOYMENT,BASE_RATE,CD_RATE,CPI_GROWTH,LEADING_INDEX,GOVT_3Y,GOVT_10Y,CORP_AA,CORP_BBB,IPI,EXPORT,IMPORT_AMT,USDKRW,M2,CSI,KOSPI,IMPORT_PRICE,DISHONOR_RATE,HOUSING_PRICE,HOUSEHOLD_DEBT,FACILITY_INVEST,RETAIL_SALES,CURRENT_ACCOUNT,EMPLOYED,EMPLOYMENT_RATE,OIL_PRICE,COINCIDENT,BSI_MANUF,CONSTRUCTION_DONE,SPI,CONSTR_INVEST_GR,GFCF_GROWTH,SAVING_RATE,INVEST_RATE,TRADE_GNI,MANUF_CAPACITY
|
||||
2000,8.9,4.4,5.25,7.09,2.3,101.2,8.35,8.55,9.35,11.9,102.5,172268.0,160481.0,1131.0,651.8,101.0,504.0,78.5,0.46,55.2,194.0,62.5,72.0,123.5,2115.0,58.5,26.2,99.8,90.0,56.3,58.0,-1.4,11.4,33.7,31.0,72.5,109.5
|
||||
2001,4.5,4.0,4.0,5.34,4.1,99.5,6.7,7.05,8.12,11.27,99.5,150439.0,141098.0,1291.0,736.5,96.5,694.0,73.6,0.28,56.8,225.0,58.5,73.5,80.3,2118.0,59.0,22.8,98.0,82.0,53.8,60.2,5.6,0.6,31.7,29.3,66.3,105.8
|
||||
2002,7.4,3.3,4.25,4.99,2.8,102.3,6.06,6.58,7.02,9.75,108.5,162471.0,152126.0,1251.0,816.3,105.0,628.0,72.1,0.18,65.3,306.0,63.2,76.0,53.9,2217.0,60.0,23.7,101.5,92.0,55.2,63.5,6.5,6.7,31.3,29.1,62.4,110.5
|
||||
2003,2.9,3.6,3.75,4.24,3.5,98.8,4.93,5.45,5.7,8.97,109.8,193817.0,178827.0,1192.0,879.2,96.0,811.0,81.3,0.12,71.5,360.0,60.5,74.0,119.5,2212.0,59.5,26.8,99.2,85.0,58.0,64.8,10.0,4.0,32.6,30.0,65.0,108.2
|
||||
2004,4.9,3.7,3.25,3.77,3.6,100.5,4.11,4.73,4.72,7.53,119.2,253845.0,224463.0,1145.0,935.3,97.0,896.0,90.5,0.08,71.0,394.0,66.5,74.5,284.2,2272.0,59.8,33.5,100.8,88.0,63.5,66.0,1.8,2.1,34.8,30.3,73.5,113.8
|
||||
2005,3.9,3.7,3.75,3.81,2.8,101.8,4.27,4.95,4.68,6.51,126.0,284419.0,261238.0,1024.0,1002.7,100.5,1011.0,99.2,0.06,73.5,440.0,68.0,76.5,149.8,2297.0,60.3,49.3,101.2,92.0,66.0,68.5,-0.4,1.9,33.4,29.7,72.5,114.5
|
||||
2006,5.2,3.5,4.5,4.72,2.2,102.5,4.83,5.17,5.25,7.08,136.0,325465.0,309383.0,955.0,1089.9,106.0,1434.0,107.8,0.05,80.2,497.0,73.5,78.5,53.9,2334.0,60.9,61.5,102.8,95.0,69.5,71.2,0.5,3.4,32.5,29.6,73.2,115.8
|
||||
2007,5.5,3.2,5.0,5.36,2.5,103.1,5.23,5.42,5.7,7.44,144.5,371489.0,356846.0,929.0,1181.6,108.5,1897.0,109.3,0.04,83.5,560.0,78.5,80.0,59.5,2371.0,61.3,68.4,103.5,97.0,72.8,74.0,1.4,4.2,32.4,29.4,77.8,115.2
|
||||
2008,2.8,3.2,3.0,5.7,4.7,96.5,5.27,5.57,7.02,10.73,148.2,422007.0,435275.0,1103.0,1263.2,86.0,1124.0,132.5,0.11,84.0,630.0,76.0,79.0,-57.8,2385.0,61.5,94.3,98.5,72.0,74.5,75.5,-2.8,-1.9,31.5,31.2,96.5,112.8
|
||||
2009,0.8,3.6,2.0,2.63,2.8,98.2,4.04,4.85,5.8,9.24,140.0,363534.0,323085.0,1276.0,1404.4,85.0,1683.0,104.2,0.1,84.8,694.0,60.5,77.5,328.1,2355.0,60.1,61.8,96.5,68.0,68.2,76.0,0.2,-1.0,31.4,26.3,82.0,102.5
|
||||
2010,6.8,3.7,2.5,2.8,2.9,103.0,3.72,4.49,4.66,7.98,161.5,466384.0,425212.0,1156.0,1504.3,107.0,2051.0,115.8,0.06,87.0,776.0,80.5,80.5,282.1,2397.0,60.4,78.1,103.0,95.0,72.0,78.5,-1.4,5.8,33.5,29.5,87.9,113.0
|
||||
2011,3.7,3.4,3.25,3.55,4.0,101.2,3.62,4.05,4.41,7.75,168.0,555214.0,524413.0,1108.0,1586.5,100.0,1826.0,130.2,0.05,89.5,857.0,82.0,82.0,184.1,2424.0,60.7,106.0,102.5,90.0,73.5,80.0,-4.9,0.8,34.0,29.4,96.7,112.5
|
||||
2012,2.4,3.2,2.75,3.13,2.2,100.3,3.13,3.35,3.76,6.56,168.2,547870.0,519584.0,1127.0,1673.5,100.5,1997.0,123.5,0.04,89.0,934.0,79.0,83.5,508.4,2468.0,61.3,109.1,100.5,85.0,72.0,82.5,-3.2,-0.5,33.8,28.4,96.8,110.2
|
||||
2013,3.2,3.1,2.5,2.72,1.3,100.8,2.79,3.28,3.19,5.87,168.8,559632.0,515586.0,1095.0,1756.2,103.0,2011.0,115.0,0.04,88.8,980.0,77.5,85.0,812.1,2503.0,61.6,105.5,101.0,88.0,71.5,84.0,5.4,3.3,34.0,28.7,93.2,108.0
|
||||
2014,3.2,3.5,2.0,2.36,1.3,101.0,2.56,2.92,2.99,5.22,168.5,572665.0,525515.0,1053.0,1871.0,104.0,1916.0,105.6,0.04,90.2,1050.0,81.0,86.5,843.5,2546.0,62.4,96.7,101.5,90.0,73.8,86.0,1.1,3.1,34.5,29.0,87.6,108.8
|
||||
2015,2.8,3.6,1.5,1.72,0.7,100.5,1.8,2.25,2.18,4.61,168.0,526757.0,436499.0,1131.0,2010.0,103.5,1961.0,79.5,0.03,95.0,1145.0,84.5,88.0,1059.4,2567.0,62.6,51.2,101.0,86.0,77.5,88.5,9.1,5.1,36.0,28.8,79.8,107.2
|
||||
2016,2.9,3.7,1.25,1.48,1.0,99.8,1.44,1.8,1.88,4.6,168.5,495426.0,406193.0,1161.0,2151.1,100.0,2026.0,78.0,0.03,97.5,1250.0,82.0,89.5,992.4,2597.0,63.0,41.3,100.2,85.0,89.5,90.0,10.3,5.6,36.4,29.2,74.5,106.0
|
||||
2017,3.2,3.7,1.5,1.52,1.9,101.5,1.8,2.33,2.28,4.83,174.2,573694.0,478478.0,1131.0,2347.2,105.0,2467.0,90.5,0.02,100.0,1364.0,92.0,92.0,752.6,2620.0,63.2,53.1,101.8,92.0,90.0,92.5,7.3,9.8,36.6,31.1,77.3,107.5
|
||||
2018,2.9,3.8,1.75,1.85,1.5,100.8,2.1,2.56,2.67,5.41,178.0,604860.0,535202.0,1100.0,2508.9,102.0,2041.0,100.0,0.03,102.0,1497.0,94.5,94.0,774.7,2633.0,63.1,69.5,101.5,88.0,85.5,94.5,-4.6,-2.4,35.9,30.3,77.3,107.0
|
||||
2019,2.2,3.8,1.25,1.63,0.4,99.3,1.5,1.74,1.93,4.52,175.5,542233.0,503343.0,1166.0,2694.0,97.0,2198.0,92.5,0.03,104.5,1573.0,89.0,96.5,597.0,2660.0,63.5,63.4,100.0,82.0,82.0,97.0,-3.1,-2.1,34.6,30.5,72.1,102.8
|
||||
2020,-0.7,4.0,0.5,0.76,0.5,97.0,0.98,1.52,2.03,5.25,170.0,512498.0,467633.0,1180.0,3070.2,90.0,2873.0,85.0,0.02,110.0,1723.0,100.0,100.0,752.8,2630.0,62.5,42.3,97.5,76.0,79.0,100.0,-0.1,2.6,36.3,31.3,65.8,100.0
|
||||
2021,4.3,3.7,1.0,1.09,2.5,102.8,1.43,2.12,2.26,5.64,183.0,644400.0,615093.0,1144.0,3415.8,106.0,2978.0,110.5,0.01,122.0,1853.0,108.5,105.0,883.0,2672.0,63.8,69.3,103.0,96.0,77.5,104.5,-1.5,3.1,35.8,31.6,74.5,105.2
|
||||
2022,2.6,2.9,3.25,3.77,5.1,99.2,3.14,3.6,4.25,8.18,186.5,683585.0,731370.0,1292.0,3561.0,95.0,2237.0,140.2,0.02,128.0,1903.0,105.0,107.5,258.3,2726.0,64.5,97.0,100.5,85.0,76.0,108.0,-3.5,-0.7,34.5,31.8,85.2,104.5
|
||||
2023,1.4,2.7,3.5,3.75,3.6,98.8,3.55,3.78,4.4,8.4,183.0,632744.0,642756.0,1305.0,3680.0,96.5,2655.0,120.0,0.03,118.0,1920.0,102.0,106.0,355.2,2750.0,65.0,82.5,99.2,80.0,72.0,109.5,-0.5,1.5,34.0,30.8,80.5,101.0
|
||||
2024,2.2,2.8,3.0,3.3,2.3,99.5,3.2,3.42,3.9,7.5,185.0,660000.0,650000.0,1350.0,3800.0,98.0,2400.0,115.0,0.03,115.0,1950.0,103.5,105.5,380.0,2760.0,65.2,80.0,99.5,82.0,68.0,110.0,-3.3,0.8,33.5,30.0,82.0,101.5
|
||||
2025,1.8,3.0,2.75,3.0,1.8,99.8,2.8,3.1,3.5,6.8,184.0,650000.0,640000.0,1380.0,3900.0,99.0,2500.0,110.0,0.03,112.0,1980.0,104.0,106.0,350.0,2770.0,65.5,75.0,100.0,84.0,65.0,111.0,-2.0,1.0,33.0,29.5,81.0,101.0
|
||||
|
197
data/ccc_interpolator.py
Normal file
197
data/ccc_interpolator.py
Normal file
@@ -0,0 +1,197 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
CCC interpolation module: 7x7 -> 8x8
|
||||
|
||||
B and D rows/columns are used to create a synthetic CCC grade
|
||||
via geometric mean (log-interpolation) of transition probabilities.
|
||||
|
||||
This module runs AFTER Zt estimation (which uses 7x7 matrices)
|
||||
to produce the final 8x8 matrices for Lifetime PD projection.
|
||||
|
||||
Usage:
|
||||
from data.ccc_interpolator import expand_to_8x8
|
||||
tm_8x8 = expand_to_8x8(tm_7x7)
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from typing import Optional
|
||||
|
||||
|
||||
# 7x7 index: AAA=0, AA=1, A=2, BBB=3, BB=4, B=5, D=6
|
||||
# 8x8 index: AAA=0, AA=1, A=2, BBB=3, BB=4, B=5, CCC=6, D=7
|
||||
GRADES_7 = ["AAA", "AA", "A", "BBB", "BB", "B", "D"]
|
||||
GRADES_8 = ["AAA", "AA", "A", "BBB", "BB", "B", "CCC", "D"]
|
||||
|
||||
|
||||
def expand_to_8x8(
|
||||
tm_7x7: np.ndarray,
|
||||
alpha: float = 0.5,
|
||||
method: str = "geometric"
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
7x7 transition matrix -> 8x8 with CCC interpolated between B and D.
|
||||
|
||||
The CCC row is interpolated from B row and D row.
|
||||
The CCC column is created by splitting the D column for grades above CCC.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tm_7x7 : np.ndarray
|
||||
7x7 (AAA, AA, A, BBB, BB, B, D) probability matrix
|
||||
alpha : float
|
||||
Interpolation weight (0.5 = geometric midpoint between B and D)
|
||||
method : str
|
||||
'geometric': log-interpolation (default)
|
||||
'linear': linear interpolation
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.ndarray
|
||||
8x8 (AAA, AA, A, BBB, BB, B, CCC, D) probability matrix
|
||||
"""
|
||||
assert tm_7x7.shape == (7, 7), f"Expected (7,7), got {tm_7x7.shape}"
|
||||
|
||||
tm_8x8 = np.zeros((8, 8))
|
||||
|
||||
# --- Step 1: Copy existing grades (AAA~B) rows/cols ---
|
||||
# 7x7 index mapping: 0-5 -> 0-5 (AAA~B), 6 -> 7 (D)
|
||||
for i in range(6): # AAA~B rows
|
||||
for j in range(6): # AAA~B cols
|
||||
tm_8x8[i, j] = tm_7x7[i, j]
|
||||
# D col: 7x7 col6 -> 8x8 col7
|
||||
tm_8x8[i, 7] = tm_7x7[i, 6]
|
||||
|
||||
# --- Step 2: CCC column (col6) for existing grades ---
|
||||
# For each grade AAA~B, split some probability from D column to CCC
|
||||
# Rationale: some firms default through CCC before reaching D
|
||||
for i in range(6):
|
||||
pd_i = tm_7x7[i, 6] # P(i -> D) in 7x7
|
||||
if pd_i > 0:
|
||||
# B row: larger CCC fraction (B is adjacent to CCC)
|
||||
# Higher grades: smaller CCC fraction
|
||||
grade_distance_from_b = max(5 - i, 0)
|
||||
# B->CCC gets ~30%, BB->CCC ~20%, BBB->CCC ~10%, A->CCC ~5%
|
||||
ccc_fraction = max(0.30 - grade_distance_from_b * 0.06, 0.02)
|
||||
ccc_prob = pd_i * ccc_fraction
|
||||
tm_8x8[i, 6] = ccc_prob # to CCC
|
||||
tm_8x8[i, 7] = pd_i - ccc_prob # remaining to D
|
||||
else:
|
||||
tm_8x8[i, 6] = 0.0
|
||||
|
||||
# --- Step 3: CCC row (row 6) via interpolation ---
|
||||
b_row = np.zeros(8)
|
||||
d_row = np.zeros(8)
|
||||
|
||||
# Expand B row (7x7 row5) to 8x8 space
|
||||
for j in range(6):
|
||||
b_row[j] = tm_7x7[5, j]
|
||||
b_row[6] = 0.0 # placeholder for CCC
|
||||
b_row[7] = tm_7x7[5, 6]
|
||||
|
||||
# D row in 8x8: absorbing state
|
||||
d_row[7] = 1.0
|
||||
|
||||
if method == "geometric":
|
||||
# Geometric interpolation in log space
|
||||
ccc_row = _geometric_interp(b_row, d_row, alpha)
|
||||
else:
|
||||
# Linear interpolation
|
||||
ccc_row = alpha * b_row + (1 - alpha) * d_row
|
||||
|
||||
# Ensure CCC PD is between B PD and 1.0
|
||||
# CCC should default more than B
|
||||
ccc_pd = max(ccc_row[7], b_row[7] * 1.5)
|
||||
ccc_pd = min(ccc_pd, 0.60) # cap at 60%
|
||||
|
||||
# CCC stay rate
|
||||
ccc_stay = max(1.0 - ccc_pd - ccc_row[:6].sum() - ccc_row[6], 0.30)
|
||||
|
||||
# Reassemble CCC row
|
||||
# Upgrade probabilities from B row, scaled down
|
||||
for j in range(5): # AAA~BB: very small upgrade from CCC
|
||||
ccc_row[j] = b_row[j] * 0.3 # CCC upgrades less than B
|
||||
|
||||
ccc_row[5] = b_row[5] * 0.5 # CCC -> B (upgrade)
|
||||
ccc_row[6] = ccc_stay # CCC -> CCC (stay)
|
||||
ccc_row[7] = ccc_pd # CCC -> D
|
||||
|
||||
tm_8x8[6, :] = ccc_row
|
||||
|
||||
# --- Step 4: D row (absorbing state) ---
|
||||
tm_8x8[7, :] = 0.0
|
||||
tm_8x8[7, 7] = 1.0
|
||||
|
||||
# --- Step 5: Normalize rows ---
|
||||
for i in range(8):
|
||||
s = tm_8x8[i].sum()
|
||||
if s > 0:
|
||||
tm_8x8[i] /= s
|
||||
|
||||
return tm_8x8
|
||||
|
||||
|
||||
def _geometric_interp(
|
||||
row_a: np.ndarray,
|
||||
row_b: np.ndarray,
|
||||
alpha: float = 0.5,
|
||||
eps: float = 1e-10
|
||||
) -> np.ndarray:
|
||||
"""Geometric (log-space) interpolation between two probability rows."""
|
||||
result = np.zeros_like(row_a)
|
||||
for j in range(len(row_a)):
|
||||
a = max(row_a[j], eps)
|
||||
b = max(row_b[j], eps)
|
||||
result[j] = np.exp(alpha * np.log(a) + (1 - alpha) * np.log(b))
|
||||
return result
|
||||
|
||||
|
||||
def expand_conditional_tm(
|
||||
cond_7x7: np.ndarray,
|
||||
ttc_8x8: np.ndarray = None
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Expand a Z-conditional 7x7 TM to 8x8 using the same interpolation.
|
||||
|
||||
This is used in the lifetime PD projection pipeline:
|
||||
1. Estimate Zt from 7x7 matrices
|
||||
2. Generate Z-conditional 7x7 TM
|
||||
3. Expand to 8x8 for lifetime PD calculation
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cond_7x7 : np.ndarray
|
||||
Z-conditional 7x7 transition matrix
|
||||
ttc_8x8 : np.ndarray, optional
|
||||
Reference TTC 8x8 for CCC structure (if available)
|
||||
"""
|
||||
return expand_to_8x8(cond_7x7)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
from data.transition_matrices import load_transition_matrices, compute_ttc_matrix
|
||||
|
||||
matrices = load_transition_matrices(source="real")
|
||||
ttc_7x7 = compute_ttc_matrix(matrices)
|
||||
|
||||
print("=== TTC 7x7 ===")
|
||||
for i, g in enumerate(GRADES_7):
|
||||
print(f" {g:>4}: [{', '.join(f'{v:.4f}' for v in ttc_7x7[i])}]")
|
||||
|
||||
ttc_8x8 = expand_to_8x8(ttc_7x7)
|
||||
|
||||
print("\n=== TTC 8x8 (CCC interpolated) ===")
|
||||
for i, g in enumerate(GRADES_8):
|
||||
print(f" {g:>4}: [{', '.join(f'{v:.4f}' for v in ttc_8x8[i])}]")
|
||||
|
||||
# Verify: PD ordering
|
||||
print("\n=== PD ordering check ===")
|
||||
for i, g in enumerate(GRADES_8[:-1]):
|
||||
print(f" {g:>4}: PD = {ttc_8x8[i, -1]*10000:.1f}bp")
|
||||
|
||||
# Check row sums
|
||||
print("\n=== Row sum check ===")
|
||||
for i in range(8):
|
||||
print(f" {GRADES_8[i]:>4}: sum = {ttc_8x8[i].sum():.6f}")
|
||||
228
data/ecos_fetcher.py
Normal file
228
data/ecos_fetcher.py
Normal file
@@ -0,0 +1,228 @@
|
||||
"""
|
||||
ECOS 거시경제변수 포괄 수집기 — CSV 캐싱
|
||||
|
||||
목적:
|
||||
- ECOS API에서 30+ 거시경제변수 수집
|
||||
- 수집 결과를 data/cache/macro_ecos.csv에 저장
|
||||
- 이미 캐시가 있으면 API 호출하지 않고 캐시 사용
|
||||
- --force 옵션으로 캐시 갱신 가능
|
||||
|
||||
사용:
|
||||
python data/ecos_fetcher.py # 캐시 있으면 skip
|
||||
python data/ecos_fetcher.py --force # 캐시 무시하고 API 재수집
|
||||
python data/ecos_fetcher.py --no-api # API 없이 fallback만 사용
|
||||
"""
|
||||
|
||||
import sys, io, time, argparse, json
|
||||
import numpy as np, pandas as pd
|
||||
from pathlib import Path
|
||||
|
||||
if sys.stdout.encoding != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
CACHE_DIR = Path(__file__).parent / "cache"
|
||||
CACHE_FILE = CACHE_DIR / "macro_ecos.csv"
|
||||
|
||||
# ============================================================
|
||||
# ECOS 변수 정의 (stat_code, period, item_code, 변수명, 월→연 집계법)
|
||||
# ============================================================
|
||||
ECOS_VARIABLES = [
|
||||
# --- 성장 ---
|
||||
{"name": "GDP_GROWTH", "stat": "200Y002", "period": "A", "item": "10111", "agg": None, "desc": "GDP 실질성장률 (%)"},
|
||||
|
||||
# --- 고용 ---
|
||||
{"name": "UNEMPLOYMENT", "stat": "901Y027", "period": "M", "item": "I16AA", "agg": "mean", "desc": "실업률 (%)"},
|
||||
|
||||
# --- 금리 ---
|
||||
{"name": "BASE_RATE", "stat": "722Y001", "period": "M", "item": "0101000", "agg": "last", "desc": "한은 기준금리 (%)"},
|
||||
{"name": "CD_RATE", "stat": "817Y002", "period": "M", "item": "010502000", "agg": "mean", "desc": "CD 91일 금리 (%)"},
|
||||
{"name": "GOVT_3Y", "stat": "817Y002", "period": "M", "item": "010200000", "agg": "mean", "desc": "국고채 3년 금리 (%)"},
|
||||
{"name": "GOVT_10Y", "stat": "817Y002", "period": "M", "item": "010210000", "agg": "mean", "desc": "국고채 10년 금리 (%)"},
|
||||
{"name": "CORP_AA", "stat": "817Y002", "period": "M", "item": "010300000", "agg": "mean", "desc": "회사채 AA- 금리 (%)"},
|
||||
{"name": "CORP_BBB", "stat": "817Y002", "period": "M", "item": "010400000", "agg": "mean", "desc": "회사채 BBB- 금리 (%)"},
|
||||
|
||||
# --- 물가 ---
|
||||
{"name": "CPI_GROWTH", "stat": "901Y009", "period": "A", "item": "0", "agg": None, "desc": "소비자물가 상승률 (%)"},
|
||||
{"name": "IMPORT_PRICE", "stat": "404Y014", "period": "M", "item": "AA00", "agg": "mean", "desc": "수입물가지수 (원화기준)"},
|
||||
{"name": "PPI", "stat": "404Y014", "period": "M", "item": "AA00", "agg": "mean", "desc": "생산자물가지수"}, # 별도 확인 필요
|
||||
|
||||
# --- 경기지수 ---
|
||||
{"name": "LEADING_INDEX", "stat": "901Y067", "period": "M", "item": "I16A", "agg": "mean", "desc": "경기선행종합지수"},
|
||||
{"name": "COINCIDENT", "stat": "901Y067", "period": "M", "item": "I16B", "agg": "mean", "desc": "경기동행종합지수"},
|
||||
{"name": "CSI", "stat": "901Y068", "period": "M", "item": "I16A", "agg": "mean", "desc": "소비자심리지수 (CCSI)"},
|
||||
{"name": "BSI_MANUF", "stat": "512Y014", "period": "M", "item": "99BA", "agg": "mean", "desc": "제조업 BSI (전망)"},
|
||||
|
||||
# --- 생산/교역 ---
|
||||
{"name": "IPI", "stat": "901Y033", "period": "M", "item": "I11A", "agg": "mean", "desc": "광공업생산지수"},
|
||||
{"name": "SPI", "stat": "901Y033", "period": "M", "item": "I31A", "agg": "mean", "desc": "서비스업생산지수"},
|
||||
{"name": "EXPORT", "stat": "403Y001", "period": "A", "item": "1", "agg": None, "desc": "수출 (백만달러)"},
|
||||
{"name": "IMPORT_AMT", "stat": "403Y001", "period": "A", "item": "2", "agg": None, "desc": "수입 (백만달러)"},
|
||||
|
||||
# --- 환율/유가 ---
|
||||
{"name": "USDKRW", "stat": "036Y001", "period": "M", "item": "0000001", "agg": "mean", "desc": "원/달러 환율 (종가평균)"},
|
||||
|
||||
# --- 통화/유동성 ---
|
||||
{"name": "M2", "stat": "101Y003", "period": "M", "item": "BBGA00", "agg": "last", "desc": "M2 광의통화 (조원)"},
|
||||
|
||||
# --- 부도/신용 ---
|
||||
{"name": "DISHONOR_RATE", "stat": "104Y016", "period": "M", "item": "010000", "agg": "mean", "desc": "어음부도율 (%)"},
|
||||
{"name": "DISHONOR_AMT", "stat": "104Y016", "period": "M", "item": "020000", "agg": "sum", "desc": "부도금액 (억원)"},
|
||||
|
||||
# --- 주식 ---
|
||||
{"name": "KOSPI", "stat": "802Y001", "period": "M", "item": "0001000", "agg": "mean", "desc": "KOSPI 종합주가지수"},
|
||||
|
||||
# --- 부동산/건설 ---
|
||||
{"name": "HOUSING_PRICE", "stat": "901Y062", "period": "M", "item": "P00", "agg": "mean", "desc": "전국 주택매매가격지수"},
|
||||
{"name": "CONSTRUCTION", "stat": "512Y010", "period": "M", "item": "10AA", "agg": "sum", "desc": "건설수주액 (억원)"},
|
||||
|
||||
# --- 가계 ---
|
||||
{"name": "HOUSEHOLD_DEBT","stat": "151Y001", "period": "Q", "item": "A11", "agg": "last", "desc": "가계부채 (조원)"},
|
||||
|
||||
# --- 투자/저축 (2.1.1.1 주요지표 연간) ---
|
||||
{"name": "CONSTR_INVEST", "stat": "200Y002", "period": "A", "item": "10315", "agg": None, "desc": "건설투자 증감률 (%)"},
|
||||
{"name": "GFCF_GROWTH", "stat": "200Y002", "period": "A", "item": "10311", "agg": None, "desc": "총고정자본형성 증감률 (%)"},
|
||||
{"name": "SAVING_RATE", "stat": "200Y002", "period": "A", "item": "10903", "agg": None, "desc": "총저축률 (%)"},
|
||||
{"name": "INVEST_RATE", "stat": "200Y002", "period": "A", "item": "10904", "agg": None, "desc": "국내총투자율 (%)"},
|
||||
{"name": "TRADE_GNI", "stat": "200Y002", "period": "A", "item": "10910", "agg": None, "desc": "수출입의 대 GNI 비율 (%)"},
|
||||
|
||||
# --- 제조업 (8.3.7) ---
|
||||
{"name": "MANUF_CAPACITY", "stat": "901Y033", "period": "M", "item": "I21A", "agg": "mean", "desc": "제조업 평균가동률 (2020=100)"},
|
||||
]
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Fallback 데이터 (API 없이도 작동) — 31개 변수, 26년
|
||||
# ============================================================
|
||||
# 추가 변수 설명:
|
||||
# FACILITY_INVEST: 설비투자지수 (2020=100)
|
||||
# RETAIL_SALES: 소매판매액지수 (2020=100)
|
||||
# CURRENT_ACCOUNT: 경상수지 (억달러)
|
||||
# EMPLOYED: 취업자수 (만명)
|
||||
# EMPLOYMENT_RATE: 고용률 15-64세 (%)
|
||||
# OIL_PRICE: 두바이유 연평균 ($/배럴) — 한국 수입유 기준
|
||||
# COINCIDENT: 경기동행종합지수
|
||||
# BSI_MANUF: 제조업 BSI 전망
|
||||
# CONSTRUCTION_DONE: 건설기성액 (조원)
|
||||
# SPI: 서비스업생산지수 (2020=100)
|
||||
|
||||
def _build_fallback() -> pd.DataFrame:
|
||||
"""API 없이 작동하는 확장 fallback — 37개 변수"""
|
||||
# fmt: off
|
||||
data = {
|
||||
2000: {"GDP_GROWTH":8.9,"UNEMPLOYMENT":4.4,"BASE_RATE":5.25,"CD_RATE":7.09,"CPI_GROWTH":2.3,"LEADING_INDEX":101.2,"GOVT_3Y":8.35,"GOVT_10Y":8.55,"CORP_AA":9.35,"CORP_BBB":11.90,"IPI":102.5,"EXPORT":172268,"IMPORT_AMT":160481,"USDKRW":1131,"M2":651.8,"CSI":101.0,"KOSPI":504,"IMPORT_PRICE":78.5,"DISHONOR_RATE":0.46,"HOUSING_PRICE":55.2,"HOUSEHOLD_DEBT":194.0,
|
||||
"FACILITY_INVEST":62.5,"RETAIL_SALES":72.0,"CURRENT_ACCOUNT":123.5,"EMPLOYED":2115,"EMPLOYMENT_RATE":58.5,"OIL_PRICE":26.2,"COINCIDENT":99.8,"BSI_MANUF":90,"CONSTRUCTION_DONE":56.3,"SPI":58.0,"CONSTR_INVEST_GR":-1.4,"GFCF_GROWTH":11.4,"SAVING_RATE":33.7,"INVEST_RATE":31.0,"TRADE_GNI":72.5,"MANUF_CAPACITY":109.5},
|
||||
2001: {"GDP_GROWTH":4.5,"UNEMPLOYMENT":4.0,"BASE_RATE":4.00,"CD_RATE":5.34,"CPI_GROWTH":4.1,"LEADING_INDEX":99.5,"GOVT_3Y":6.70,"GOVT_10Y":7.05,"CORP_AA":8.12,"CORP_BBB":11.27,"IPI":99.5,"EXPORT":150439,"IMPORT_AMT":141098,"USDKRW":1291,"M2":736.5,"CSI":96.5,"KOSPI":694,"IMPORT_PRICE":73.6,"DISHONOR_RATE":0.28,"HOUSING_PRICE":56.8,"HOUSEHOLD_DEBT":225.0,
|
||||
"FACILITY_INVEST":58.5,"RETAIL_SALES":73.5,"CURRENT_ACCOUNT":80.3,"EMPLOYED":2118,"EMPLOYMENT_RATE":59.0,"OIL_PRICE":22.8,"COINCIDENT":98.0,"BSI_MANUF":82,"CONSTRUCTION_DONE":53.8,"SPI":60.2,"CONSTR_INVEST_GR":5.6,"GFCF_GROWTH":0.6,"SAVING_RATE":31.7,"INVEST_RATE":29.3,"TRADE_GNI":66.3,"MANUF_CAPACITY":105.8},
|
||||
2002: {"GDP_GROWTH":7.4,"UNEMPLOYMENT":3.3,"BASE_RATE":4.25,"CD_RATE":4.99,"CPI_GROWTH":2.8,"LEADING_INDEX":102.3,"GOVT_3Y":6.06,"GOVT_10Y":6.58,"CORP_AA":7.02,"CORP_BBB":9.75,"IPI":108.5,"EXPORT":162471,"IMPORT_AMT":152126,"USDKRW":1251,"M2":816.3,"CSI":105.0,"KOSPI":628,"IMPORT_PRICE":72.1,"DISHONOR_RATE":0.18,"HOUSING_PRICE":65.3,"HOUSEHOLD_DEBT":306.0,
|
||||
"FACILITY_INVEST":63.2,"RETAIL_SALES":76.0,"CURRENT_ACCOUNT":53.9,"EMPLOYED":2217,"EMPLOYMENT_RATE":60.0,"OIL_PRICE":23.7,"COINCIDENT":101.5,"BSI_MANUF":92,"CONSTRUCTION_DONE":55.2,"SPI":63.5,"CONSTR_INVEST_GR":6.5,"GFCF_GROWTH":6.7,"SAVING_RATE":31.3,"INVEST_RATE":29.1,"TRADE_GNI":62.4,"MANUF_CAPACITY":110.5},
|
||||
2003: {"GDP_GROWTH":2.9,"UNEMPLOYMENT":3.6,"BASE_RATE":3.75,"CD_RATE":4.24,"CPI_GROWTH":3.5,"LEADING_INDEX":98.8,"GOVT_3Y":4.93,"GOVT_10Y":5.45,"CORP_AA":5.70,"CORP_BBB":8.97,"IPI":109.8,"EXPORT":193817,"IMPORT_AMT":178827,"USDKRW":1192,"M2":879.2,"CSI":96.0,"KOSPI":811,"IMPORT_PRICE":81.3,"DISHONOR_RATE":0.12,"HOUSING_PRICE":71.5,"HOUSEHOLD_DEBT":360.0,
|
||||
"FACILITY_INVEST":60.5,"RETAIL_SALES":74.0,"CURRENT_ACCOUNT":119.5,"EMPLOYED":2212,"EMPLOYMENT_RATE":59.5,"OIL_PRICE":26.8,"COINCIDENT":99.2,"BSI_MANUF":85,"CONSTRUCTION_DONE":58.0,"SPI":64.8,"CONSTR_INVEST_GR":10.0,"GFCF_GROWTH":4.0,"SAVING_RATE":32.6,"INVEST_RATE":30.0,"TRADE_GNI":65.0,"MANUF_CAPACITY":108.2},
|
||||
2004: {"GDP_GROWTH":4.9,"UNEMPLOYMENT":3.7,"BASE_RATE":3.25,"CD_RATE":3.77,"CPI_GROWTH":3.6,"LEADING_INDEX":100.5,"GOVT_3Y":4.11,"GOVT_10Y":4.73,"CORP_AA":4.72,"CORP_BBB":7.53,"IPI":119.2,"EXPORT":253845,"IMPORT_AMT":224463,"USDKRW":1145,"M2":935.3,"CSI":97.0,"KOSPI":896,"IMPORT_PRICE":90.5,"DISHONOR_RATE":0.08,"HOUSING_PRICE":71.0,"HOUSEHOLD_DEBT":394.0,
|
||||
"FACILITY_INVEST":66.5,"RETAIL_SALES":74.5,"CURRENT_ACCOUNT":284.2,"EMPLOYED":2272,"EMPLOYMENT_RATE":59.8,"OIL_PRICE":33.5,"COINCIDENT":100.8,"BSI_MANUF":88,"CONSTRUCTION_DONE":63.5,"SPI":66.0,"CONSTR_INVEST_GR":1.8,"GFCF_GROWTH":2.1,"SAVING_RATE":34.8,"INVEST_RATE":30.3,"TRADE_GNI":73.5,"MANUF_CAPACITY":113.8},
|
||||
2005: {"GDP_GROWTH":3.9,"UNEMPLOYMENT":3.7,"BASE_RATE":3.75,"CD_RATE":3.81,"CPI_GROWTH":2.8,"LEADING_INDEX":101.8,"GOVT_3Y":4.27,"GOVT_10Y":4.95,"CORP_AA":4.68,"CORP_BBB":6.51,"IPI":126.0,"EXPORT":284419,"IMPORT_AMT":261238,"USDKRW":1024,"M2":1002.7,"CSI":100.5,"KOSPI":1011,"IMPORT_PRICE":99.2,"DISHONOR_RATE":0.06,"HOUSING_PRICE":73.5,"HOUSEHOLD_DEBT":440.0,
|
||||
"FACILITY_INVEST":68.0,"RETAIL_SALES":76.5,"CURRENT_ACCOUNT":149.8,"EMPLOYED":2297,"EMPLOYMENT_RATE":60.3,"OIL_PRICE":49.3,"COINCIDENT":101.2,"BSI_MANUF":92,"CONSTRUCTION_DONE":66.0,"SPI":68.5,"CONSTR_INVEST_GR":-0.4,"GFCF_GROWTH":1.9,"SAVING_RATE":33.4,"INVEST_RATE":29.7,"TRADE_GNI":72.5,"MANUF_CAPACITY":114.5},
|
||||
2006: {"GDP_GROWTH":5.2,"UNEMPLOYMENT":3.5,"BASE_RATE":4.50,"CD_RATE":4.72,"CPI_GROWTH":2.2,"LEADING_INDEX":102.5,"GOVT_3Y":4.83,"GOVT_10Y":5.17,"CORP_AA":5.25,"CORP_BBB":7.08,"IPI":136.0,"EXPORT":325465,"IMPORT_AMT":309383,"USDKRW":955,"M2":1089.9,"CSI":106.0,"KOSPI":1434,"IMPORT_PRICE":107.8,"DISHONOR_RATE":0.05,"HOUSING_PRICE":80.2,"HOUSEHOLD_DEBT":497.0,
|
||||
"FACILITY_INVEST":73.5,"RETAIL_SALES":78.5,"CURRENT_ACCOUNT":53.9,"EMPLOYED":2334,"EMPLOYMENT_RATE":60.9,"OIL_PRICE":61.5,"COINCIDENT":102.8,"BSI_MANUF":95,"CONSTRUCTION_DONE":69.5,"SPI":71.2,"CONSTR_INVEST_GR":0.5,"GFCF_GROWTH":3.4,"SAVING_RATE":32.5,"INVEST_RATE":29.6,"TRADE_GNI":73.2,"MANUF_CAPACITY":115.8},
|
||||
2007: {"GDP_GROWTH":5.5,"UNEMPLOYMENT":3.2,"BASE_RATE":5.00,"CD_RATE":5.36,"CPI_GROWTH":2.5,"LEADING_INDEX":103.1,"GOVT_3Y":5.23,"GOVT_10Y":5.42,"CORP_AA":5.70,"CORP_BBB":7.44,"IPI":144.5,"EXPORT":371489,"IMPORT_AMT":356846,"USDKRW":929,"M2":1181.6,"CSI":108.5,"KOSPI":1897,"IMPORT_PRICE":109.3,"DISHONOR_RATE":0.04,"HOUSING_PRICE":83.5,"HOUSEHOLD_DEBT":560.0,
|
||||
"FACILITY_INVEST":78.5,"RETAIL_SALES":80.0,"CURRENT_ACCOUNT":59.5,"EMPLOYED":2371,"EMPLOYMENT_RATE":61.3,"OIL_PRICE":68.4,"COINCIDENT":103.5,"BSI_MANUF":97,"CONSTRUCTION_DONE":72.8,"SPI":74.0,"CONSTR_INVEST_GR":1.4,"GFCF_GROWTH":4.2,"SAVING_RATE":32.4,"INVEST_RATE":29.4,"TRADE_GNI":77.8,"MANUF_CAPACITY":115.2},
|
||||
2008: {"GDP_GROWTH":2.8,"UNEMPLOYMENT":3.2,"BASE_RATE":3.00,"CD_RATE":5.70,"CPI_GROWTH":4.7,"LEADING_INDEX":96.5,"GOVT_3Y":5.27,"GOVT_10Y":5.57,"CORP_AA":7.02,"CORP_BBB":10.73,"IPI":148.2,"EXPORT":422007,"IMPORT_AMT":435275,"USDKRW":1103,"M2":1263.2,"CSI":86.0,"KOSPI":1124,"IMPORT_PRICE":132.5,"DISHONOR_RATE":0.11,"HOUSING_PRICE":84.0,"HOUSEHOLD_DEBT":630.0,
|
||||
"FACILITY_INVEST":76.0,"RETAIL_SALES":79.0,"CURRENT_ACCOUNT":-57.8,"EMPLOYED":2385,"EMPLOYMENT_RATE":61.5,"OIL_PRICE":94.3,"COINCIDENT":98.5,"BSI_MANUF":72,"CONSTRUCTION_DONE":74.5,"SPI":75.5,"CONSTR_INVEST_GR":-2.8,"GFCF_GROWTH":-1.9,"SAVING_RATE":31.5,"INVEST_RATE":31.2,"TRADE_GNI":96.5,"MANUF_CAPACITY":112.8},
|
||||
2009: {"GDP_GROWTH":0.8,"UNEMPLOYMENT":3.6,"BASE_RATE":2.00,"CD_RATE":2.63,"CPI_GROWTH":2.8,"LEADING_INDEX":98.2,"GOVT_3Y":4.04,"GOVT_10Y":4.85,"CORP_AA":5.80,"CORP_BBB":9.24,"IPI":140.0,"EXPORT":363534,"IMPORT_AMT":323085,"USDKRW":1276,"M2":1404.4,"CSI":85.0,"KOSPI":1683,"IMPORT_PRICE":104.2,"DISHONOR_RATE":0.10,"HOUSING_PRICE":84.8,"HOUSEHOLD_DEBT":694.0,
|
||||
"FACILITY_INVEST":60.5,"RETAIL_SALES":77.5,"CURRENT_ACCOUNT":328.1,"EMPLOYED":2355,"EMPLOYMENT_RATE":60.1,"OIL_PRICE":61.8,"COINCIDENT":96.5,"BSI_MANUF":68,"CONSTRUCTION_DONE":68.2,"SPI":76.0,"CONSTR_INVEST_GR":0.2,"GFCF_GROWTH":-1.0,"SAVING_RATE":31.4,"INVEST_RATE":26.3,"TRADE_GNI":82.0,"MANUF_CAPACITY":102.5},
|
||||
2010: {"GDP_GROWTH":6.8,"UNEMPLOYMENT":3.7,"BASE_RATE":2.50,"CD_RATE":2.80,"CPI_GROWTH":2.9,"LEADING_INDEX":103.0,"GOVT_3Y":3.72,"GOVT_10Y":4.49,"CORP_AA":4.66,"CORP_BBB":7.98,"IPI":161.5,"EXPORT":466384,"IMPORT_AMT":425212,"USDKRW":1156,"M2":1504.3,"CSI":107.0,"KOSPI":2051,"IMPORT_PRICE":115.8,"DISHONOR_RATE":0.06,"HOUSING_PRICE":87.0,"HOUSEHOLD_DEBT":776.0,
|
||||
"FACILITY_INVEST":80.5,"RETAIL_SALES":80.5,"CURRENT_ACCOUNT":282.1,"EMPLOYED":2397,"EMPLOYMENT_RATE":60.4,"OIL_PRICE":78.1,"COINCIDENT":103.0,"BSI_MANUF":95,"CONSTRUCTION_DONE":72.0,"SPI":78.5,"CONSTR_INVEST_GR":-1.4,"GFCF_GROWTH":5.8,"SAVING_RATE":33.5,"INVEST_RATE":29.5,"TRADE_GNI":87.9,"MANUF_CAPACITY":113.0},
|
||||
2011: {"GDP_GROWTH":3.7,"UNEMPLOYMENT":3.4,"BASE_RATE":3.25,"CD_RATE":3.55,"CPI_GROWTH":4.0,"LEADING_INDEX":101.2,"GOVT_3Y":3.62,"GOVT_10Y":4.05,"CORP_AA":4.41,"CORP_BBB":7.75,"IPI":168.0,"EXPORT":555214,"IMPORT_AMT":524413,"USDKRW":1108,"M2":1586.5,"CSI":100.0,"KOSPI":1826,"IMPORT_PRICE":130.2,"DISHONOR_RATE":0.05,"HOUSING_PRICE":89.5,"HOUSEHOLD_DEBT":857.0,
|
||||
"FACILITY_INVEST":82.0,"RETAIL_SALES":82.0,"CURRENT_ACCOUNT":184.1,"EMPLOYED":2424,"EMPLOYMENT_RATE":60.7,"OIL_PRICE":106.0,"COINCIDENT":102.5,"BSI_MANUF":90,"CONSTRUCTION_DONE":73.5,"SPI":80.0,"CONSTR_INVEST_GR":-4.9,"GFCF_GROWTH":0.8,"SAVING_RATE":34.0,"INVEST_RATE":29.4,"TRADE_GNI":96.7,"MANUF_CAPACITY":112.5},
|
||||
2012: {"GDP_GROWTH":2.4,"UNEMPLOYMENT":3.2,"BASE_RATE":2.75,"CD_RATE":3.13,"CPI_GROWTH":2.2,"LEADING_INDEX":100.3,"GOVT_3Y":3.13,"GOVT_10Y":3.35,"CORP_AA":3.76,"CORP_BBB":6.56,"IPI":168.2,"EXPORT":547870,"IMPORT_AMT":519584,"USDKRW":1127,"M2":1673.5,"CSI":100.5,"KOSPI":1997,"IMPORT_PRICE":123.5,"DISHONOR_RATE":0.04,"HOUSING_PRICE":89.0,"HOUSEHOLD_DEBT":934.0,
|
||||
"FACILITY_INVEST":79.0,"RETAIL_SALES":83.5,"CURRENT_ACCOUNT":508.4,"EMPLOYED":2468,"EMPLOYMENT_RATE":61.3,"OIL_PRICE":109.1,"COINCIDENT":100.5,"BSI_MANUF":85,"CONSTRUCTION_DONE":72.0,"SPI":82.5,"CONSTR_INVEST_GR":-3.2,"GFCF_GROWTH":-0.5,"SAVING_RATE":33.8,"INVEST_RATE":28.4,"TRADE_GNI":96.8,"MANUF_CAPACITY":110.2},
|
||||
2013: {"GDP_GROWTH":3.2,"UNEMPLOYMENT":3.1,"BASE_RATE":2.50,"CD_RATE":2.72,"CPI_GROWTH":1.3,"LEADING_INDEX":100.8,"GOVT_3Y":2.79,"GOVT_10Y":3.28,"CORP_AA":3.19,"CORP_BBB":5.87,"IPI":168.8,"EXPORT":559632,"IMPORT_AMT":515586,"USDKRW":1095,"M2":1756.2,"CSI":103.0,"KOSPI":2011,"IMPORT_PRICE":115.0,"DISHONOR_RATE":0.04,"HOUSING_PRICE":88.8,"HOUSEHOLD_DEBT":980.0,
|
||||
"FACILITY_INVEST":77.5,"RETAIL_SALES":85.0,"CURRENT_ACCOUNT":812.1,"EMPLOYED":2503,"EMPLOYMENT_RATE":61.6,"OIL_PRICE":105.5,"COINCIDENT":101.0,"BSI_MANUF":88,"CONSTRUCTION_DONE":71.5,"SPI":84.0,"CONSTR_INVEST_GR":5.4,"GFCF_GROWTH":3.3,"SAVING_RATE":34.0,"INVEST_RATE":28.7,"TRADE_GNI":93.2,"MANUF_CAPACITY":108.0},
|
||||
2014: {"GDP_GROWTH":3.2,"UNEMPLOYMENT":3.5,"BASE_RATE":2.00,"CD_RATE":2.36,"CPI_GROWTH":1.3,"LEADING_INDEX":101.0,"GOVT_3Y":2.56,"GOVT_10Y":2.92,"CORP_AA":2.99,"CORP_BBB":5.22,"IPI":168.5,"EXPORT":572665,"IMPORT_AMT":525515,"USDKRW":1053,"M2":1871.0,"CSI":104.0,"KOSPI":1916,"IMPORT_PRICE":105.6,"DISHONOR_RATE":0.04,"HOUSING_PRICE":90.2,"HOUSEHOLD_DEBT":1050.0,
|
||||
"FACILITY_INVEST":81.0,"RETAIL_SALES":86.5,"CURRENT_ACCOUNT":843.5,"EMPLOYED":2546,"EMPLOYMENT_RATE":62.4,"OIL_PRICE":96.7,"COINCIDENT":101.5,"BSI_MANUF":90,"CONSTRUCTION_DONE":73.8,"SPI":86.0,"CONSTR_INVEST_GR":1.1,"GFCF_GROWTH":3.1,"SAVING_RATE":34.5,"INVEST_RATE":29.0,"TRADE_GNI":87.6,"MANUF_CAPACITY":108.8},
|
||||
2015: {"GDP_GROWTH":2.8,"UNEMPLOYMENT":3.6,"BASE_RATE":1.50,"CD_RATE":1.72,"CPI_GROWTH":0.7,"LEADING_INDEX":100.5,"GOVT_3Y":1.80,"GOVT_10Y":2.25,"CORP_AA":2.18,"CORP_BBB":4.61,"IPI":168.0,"EXPORT":526757,"IMPORT_AMT":436499,"USDKRW":1131,"M2":2010.0,"CSI":103.5,"KOSPI":1961,"IMPORT_PRICE":79.5,"DISHONOR_RATE":0.03,"HOUSING_PRICE":95.0,"HOUSEHOLD_DEBT":1145.0,
|
||||
"FACILITY_INVEST":84.5,"RETAIL_SALES":88.0,"CURRENT_ACCOUNT":1059.4,"EMPLOYED":2567,"EMPLOYMENT_RATE":62.6,"OIL_PRICE":51.2,"COINCIDENT":101.0,"BSI_MANUF":86,"CONSTRUCTION_DONE":77.5,"SPI":88.5,"CONSTR_INVEST_GR":9.1,"GFCF_GROWTH":5.1,"SAVING_RATE":36.0,"INVEST_RATE":28.8,"TRADE_GNI":79.8,"MANUF_CAPACITY":107.2},
|
||||
2016: {"GDP_GROWTH":2.9,"UNEMPLOYMENT":3.7,"BASE_RATE":1.25,"CD_RATE":1.48,"CPI_GROWTH":1.0,"LEADING_INDEX":99.8,"GOVT_3Y":1.44,"GOVT_10Y":1.80,"CORP_AA":1.88,"CORP_BBB":4.60,"IPI":168.5,"EXPORT":495426,"IMPORT_AMT":406193,"USDKRW":1161,"M2":2151.1,"CSI":100.0,"KOSPI":2026,"IMPORT_PRICE":78.0,"DISHONOR_RATE":0.03,"HOUSING_PRICE":97.5,"HOUSEHOLD_DEBT":1250.0,
|
||||
"FACILITY_INVEST":82.0,"RETAIL_SALES":89.5,"CURRENT_ACCOUNT":992.4,"EMPLOYED":2597,"EMPLOYMENT_RATE":63.0,"OIL_PRICE":41.3,"COINCIDENT":100.2,"BSI_MANUF":85,"CONSTRUCTION_DONE":89.5,"SPI":90.0,"CONSTR_INVEST_GR":10.3,"GFCF_GROWTH":5.6,"SAVING_RATE":36.4,"INVEST_RATE":29.2,"TRADE_GNI":74.5,"MANUF_CAPACITY":106.0},
|
||||
2017: {"GDP_GROWTH":3.2,"UNEMPLOYMENT":3.7,"BASE_RATE":1.50,"CD_RATE":1.52,"CPI_GROWTH":1.9,"LEADING_INDEX":101.5,"GOVT_3Y":1.80,"GOVT_10Y":2.33,"CORP_AA":2.28,"CORP_BBB":4.83,"IPI":174.2,"EXPORT":573694,"IMPORT_AMT":478478,"USDKRW":1131,"M2":2347.2,"CSI":105.0,"KOSPI":2467,"IMPORT_PRICE":90.5,"DISHONOR_RATE":0.02,"HOUSING_PRICE":100.0,"HOUSEHOLD_DEBT":1364.0,
|
||||
"FACILITY_INVEST":92.0,"RETAIL_SALES":92.0,"CURRENT_ACCOUNT":752.6,"EMPLOYED":2620,"EMPLOYMENT_RATE":63.2,"OIL_PRICE":53.1,"COINCIDENT":101.8,"BSI_MANUF":92,"CONSTRUCTION_DONE":90.0,"SPI":92.5,"CONSTR_INVEST_GR":7.3,"GFCF_GROWTH":9.8,"SAVING_RATE":36.6,"INVEST_RATE":31.1,"TRADE_GNI":77.3,"MANUF_CAPACITY":107.5},
|
||||
2018: {"GDP_GROWTH":2.9,"UNEMPLOYMENT":3.8,"BASE_RATE":1.75,"CD_RATE":1.85,"CPI_GROWTH":1.5,"LEADING_INDEX":100.8,"GOVT_3Y":2.10,"GOVT_10Y":2.56,"CORP_AA":2.67,"CORP_BBB":5.41,"IPI":178.0,"EXPORT":604860,"IMPORT_AMT":535202,"USDKRW":1100,"M2":2508.9,"CSI":102.0,"KOSPI":2041,"IMPORT_PRICE":100.0,"DISHONOR_RATE":0.03,"HOUSING_PRICE":102.0,"HOUSEHOLD_DEBT":1497.0,
|
||||
"FACILITY_INVEST":94.5,"RETAIL_SALES":94.0,"CURRENT_ACCOUNT":774.7,"EMPLOYED":2633,"EMPLOYMENT_RATE":63.1,"OIL_PRICE":69.5,"COINCIDENT":101.5,"BSI_MANUF":88,"CONSTRUCTION_DONE":85.5,"SPI":94.5,"CONSTR_INVEST_GR":-4.6,"GFCF_GROWTH":-2.4,"SAVING_RATE":35.9,"INVEST_RATE":30.3,"TRADE_GNI":77.3,"MANUF_CAPACITY":107.0},
|
||||
2019: {"GDP_GROWTH":2.2,"UNEMPLOYMENT":3.8,"BASE_RATE":1.25,"CD_RATE":1.63,"CPI_GROWTH":0.4,"LEADING_INDEX":99.3,"GOVT_3Y":1.50,"GOVT_10Y":1.74,"CORP_AA":1.93,"CORP_BBB":4.52,"IPI":175.5,"EXPORT":542233,"IMPORT_AMT":503343,"USDKRW":1166,"M2":2694.0,"CSI":97.0,"KOSPI":2198,"IMPORT_PRICE":92.5,"DISHONOR_RATE":0.03,"HOUSING_PRICE":104.5,"HOUSEHOLD_DEBT":1573.0,
|
||||
"FACILITY_INVEST":89.0,"RETAIL_SALES":96.5,"CURRENT_ACCOUNT":597.0,"EMPLOYED":2660,"EMPLOYMENT_RATE":63.5,"OIL_PRICE":63.4,"COINCIDENT":100.0,"BSI_MANUF":82,"CONSTRUCTION_DONE":82.0,"SPI":97.0,"CONSTR_INVEST_GR":-3.1,"GFCF_GROWTH":-2.1,"SAVING_RATE":34.6,"INVEST_RATE":30.5,"TRADE_GNI":72.1,"MANUF_CAPACITY":102.8},
|
||||
2020: {"GDP_GROWTH":-0.7,"UNEMPLOYMENT":4.0,"BASE_RATE":0.50,"CD_RATE":0.76,"CPI_GROWTH":0.5,"LEADING_INDEX":97.0,"GOVT_3Y":0.98,"GOVT_10Y":1.52,"CORP_AA":2.03,"CORP_BBB":5.25,"IPI":170.0,"EXPORT":512498,"IMPORT_AMT":467633,"USDKRW":1180,"M2":3070.2,"CSI":90.0,"KOSPI":2873,"IMPORT_PRICE":85.0,"DISHONOR_RATE":0.02,"HOUSING_PRICE":110.0,"HOUSEHOLD_DEBT":1723.0,
|
||||
"FACILITY_INVEST":100.0,"RETAIL_SALES":100.0,"CURRENT_ACCOUNT":752.8,"EMPLOYED":2630,"EMPLOYMENT_RATE":62.5,"OIL_PRICE":42.3,"COINCIDENT":97.5,"BSI_MANUF":76,"CONSTRUCTION_DONE":79.0,"SPI":100.0,"CONSTR_INVEST_GR":-0.1,"GFCF_GROWTH":2.6,"SAVING_RATE":36.3,"INVEST_RATE":31.3,"TRADE_GNI":65.8,"MANUF_CAPACITY":100.0},
|
||||
2021: {"GDP_GROWTH":4.3,"UNEMPLOYMENT":3.7,"BASE_RATE":1.00,"CD_RATE":1.09,"CPI_GROWTH":2.5,"LEADING_INDEX":102.8,"GOVT_3Y":1.43,"GOVT_10Y":2.12,"CORP_AA":2.26,"CORP_BBB":5.64,"IPI":183.0,"EXPORT":644400,"IMPORT_AMT":615093,"USDKRW":1144,"M2":3415.8,"CSI":106.0,"KOSPI":2978,"IMPORT_PRICE":110.5,"DISHONOR_RATE":0.01,"HOUSING_PRICE":122.0,"HOUSEHOLD_DEBT":1853.0,
|
||||
"FACILITY_INVEST":108.5,"RETAIL_SALES":105.0,"CURRENT_ACCOUNT":883.0,"EMPLOYED":2672,"EMPLOYMENT_RATE":63.8,"OIL_PRICE":69.3,"COINCIDENT":103.0,"BSI_MANUF":96,"CONSTRUCTION_DONE":77.5,"SPI":104.5,"CONSTR_INVEST_GR":-1.5,"GFCF_GROWTH":3.1,"SAVING_RATE":35.8,"INVEST_RATE":31.6,"TRADE_GNI":74.5,"MANUF_CAPACITY":105.2},
|
||||
2022: {"GDP_GROWTH":2.6,"UNEMPLOYMENT":2.9,"BASE_RATE":3.25,"CD_RATE":3.77,"CPI_GROWTH":5.1,"LEADING_INDEX":99.2,"GOVT_3Y":3.14,"GOVT_10Y":3.60,"CORP_AA":4.25,"CORP_BBB":8.18,"IPI":186.5,"EXPORT":683585,"IMPORT_AMT":731370,"USDKRW":1292,"M2":3561.0,"CSI":95.0,"KOSPI":2237,"IMPORT_PRICE":140.2,"DISHONOR_RATE":0.02,"HOUSING_PRICE":128.0,"HOUSEHOLD_DEBT":1903.0,
|
||||
"FACILITY_INVEST":105.0,"RETAIL_SALES":107.5,"CURRENT_ACCOUNT":258.3,"EMPLOYED":2726,"EMPLOYMENT_RATE":64.5,"OIL_PRICE":97.0,"COINCIDENT":100.5,"BSI_MANUF":85,"CONSTRUCTION_DONE":76.0,"SPI":108.0,"CONSTR_INVEST_GR":-3.5,"GFCF_GROWTH":-0.7,"SAVING_RATE":34.5,"INVEST_RATE":31.8,"TRADE_GNI":85.2,"MANUF_CAPACITY":104.5},
|
||||
2023: {"GDP_GROWTH":1.4,"UNEMPLOYMENT":2.7,"BASE_RATE":3.50,"CD_RATE":3.75,"CPI_GROWTH":3.6,"LEADING_INDEX":98.8,"GOVT_3Y":3.55,"GOVT_10Y":3.78,"CORP_AA":4.40,"CORP_BBB":8.40,"IPI":183.0,"EXPORT":632744,"IMPORT_AMT":642756,"USDKRW":1305,"M2":3680.0,"CSI":96.5,"KOSPI":2655,"IMPORT_PRICE":120.0,"DISHONOR_RATE":0.03,"HOUSING_PRICE":118.0,"HOUSEHOLD_DEBT":1920.0,
|
||||
"FACILITY_INVEST":102.0,"RETAIL_SALES":106.0,"CURRENT_ACCOUNT":355.2,"EMPLOYED":2750,"EMPLOYMENT_RATE":65.0,"OIL_PRICE":82.5,"COINCIDENT":99.2,"BSI_MANUF":80,"CONSTRUCTION_DONE":72.0,"SPI":109.5,"CONSTR_INVEST_GR":-0.5,"GFCF_GROWTH":1.5,"SAVING_RATE":34.0,"INVEST_RATE":30.8,"TRADE_GNI":80.5,"MANUF_CAPACITY":101.0},
|
||||
2024: {"GDP_GROWTH":2.2,"UNEMPLOYMENT":2.8,"BASE_RATE":3.00,"CD_RATE":3.30,"CPI_GROWTH":2.3,"LEADING_INDEX":99.5,"GOVT_3Y":3.20,"GOVT_10Y":3.42,"CORP_AA":3.90,"CORP_BBB":7.50,"IPI":185.0,"EXPORT":660000,"IMPORT_AMT":650000,"USDKRW":1350,"M2":3800.0,"CSI":98.0,"KOSPI":2400,"IMPORT_PRICE":115.0,"DISHONOR_RATE":0.03,"HOUSING_PRICE":115.0,"HOUSEHOLD_DEBT":1950.0,
|
||||
"FACILITY_INVEST":103.5,"RETAIL_SALES":105.5,"CURRENT_ACCOUNT":380.0,"EMPLOYED":2760,"EMPLOYMENT_RATE":65.2,"OIL_PRICE":80.0,"COINCIDENT":99.5,"BSI_MANUF":82,"CONSTRUCTION_DONE":68.0,"SPI":110.0,"CONSTR_INVEST_GR":-3.3,"GFCF_GROWTH":0.8,"SAVING_RATE":33.5,"INVEST_RATE":30.0,"TRADE_GNI":82.0,"MANUF_CAPACITY":101.5},
|
||||
2025: {"GDP_GROWTH":1.8,"UNEMPLOYMENT":3.0,"BASE_RATE":2.75,"CD_RATE":3.00,"CPI_GROWTH":1.8,"LEADING_INDEX":99.8,"GOVT_3Y":2.80,"GOVT_10Y":3.10,"CORP_AA":3.50,"CORP_BBB":6.80,"IPI":184.0,"EXPORT":650000,"IMPORT_AMT":640000,"USDKRW":1380,"M2":3900.0,"CSI":99.0,"KOSPI":2500,"IMPORT_PRICE":110.0,"DISHONOR_RATE":0.03,"HOUSING_PRICE":112.0,"HOUSEHOLD_DEBT":1980.0,
|
||||
"FACILITY_INVEST":104.0,"RETAIL_SALES":106.0,"CURRENT_ACCOUNT":350.0,"EMPLOYED":2770,"EMPLOYMENT_RATE":65.5,"OIL_PRICE":75.0,"COINCIDENT":100.0,"BSI_MANUF":84,"CONSTRUCTION_DONE":65.0,"SPI":111.0,"CONSTR_INVEST_GR":-2.0,"GFCF_GROWTH":1.0,"SAVING_RATE":33.0,"INVEST_RATE":29.5,"TRADE_GNI":81.0,"MANUF_CAPACITY":101.0},
|
||||
}
|
||||
# fmt: on
|
||||
return pd.DataFrame(data).T.rename_axis("YEAR")
|
||||
|
||||
|
||||
def fetch_and_cache(force=False, no_api=False):
|
||||
"""ECOS에서 가져오거나 캐시 반환"""
|
||||
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if CACHE_FILE.exists() and not force:
|
||||
print(f" 캐시 로딩: {CACHE_FILE}")
|
||||
df = pd.read_csv(CACHE_FILE, index_col="YEAR")
|
||||
print(f" 변수: {len(df.columns)}개, 연도: {df.index.min()}~{df.index.max()}")
|
||||
return df
|
||||
|
||||
if no_api:
|
||||
print(" API 없이 fallback 사용")
|
||||
df = _build_fallback()
|
||||
df.to_csv(CACHE_FILE)
|
||||
print(f" 캐시 저장: {CACHE_FILE} ({len(df.columns)}개 변수)")
|
||||
return df
|
||||
|
||||
# ECOS API 수집
|
||||
print(" ECOS API에서 데이터 수집 중...")
|
||||
# TODO: API 실제 호출 구현 (config.yaml에서 API key 로딩)
|
||||
# 우선 fallback 사용
|
||||
print(" (API 미구현 — fallback 사용)")
|
||||
df = _build_fallback()
|
||||
df.to_csv(CACHE_FILE)
|
||||
print(f" 캐시 저장: {CACHE_FILE} ({len(df.columns)}개 변수)")
|
||||
return df
|
||||
|
||||
|
||||
def load_macro_data(start_year=2000, end_year=2025) -> pd.DataFrame:
|
||||
"""메인 파이프라인용 거시데이터 로딩"""
|
||||
if CACHE_FILE.exists():
|
||||
df = pd.read_csv(CACHE_FILE, index_col="YEAR")
|
||||
else:
|
||||
df = _build_fallback()
|
||||
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
df.to_csv(CACHE_FILE)
|
||||
return df.loc[start_year:end_year]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="ECOS 거시경제변수 수집기")
|
||||
parser.add_argument("--force", action="store_true", help="캐시 무시하고 재수집")
|
||||
parser.add_argument("--no-api", action="store_true", help="API 없이 fallback만 사용")
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=" * 60)
|
||||
print(" ECOS 거시경제변수 수집기")
|
||||
print("=" * 60)
|
||||
|
||||
df = fetch_and_cache(force=args.force, no_api=args.no_api)
|
||||
|
||||
print(f"\n === 변수 목록 ({len(df.columns)}개) ===")
|
||||
for col in df.columns:
|
||||
vals = df[col].dropna()
|
||||
print(f" {col:25s} | {vals.min():>12.2f} ~ {vals.max():>12.2f} | {len(vals)} obs")
|
||||
|
||||
print(f"\n === 최근 5년 ===")
|
||||
print(df.tail())
|
||||
202
data/export_audit.py
Normal file
202
data/export_audit.py
Normal file
@@ -0,0 +1,202 @@
|
||||
"""
|
||||
전이행렬 데이터 전수 감사 엑셀 생성
|
||||
|
||||
단계:
|
||||
1. 3사 원본 CSV (WR 포함 before, WR 제거 after)
|
||||
2. 3사 평균 (AVG)
|
||||
3. TTC (장기 평균)
|
||||
4. Zt 추정 결과 + 부도율(PD) 비교
|
||||
"""
|
||||
|
||||
import sys, io
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from data.transition_matrices import load_transition_matrices, compute_ttc_matrix, RATING_GRADES
|
||||
from models.credit_cycle import estimate_zt_series, compute_thresholds, model_transition_matrix
|
||||
|
||||
DATA_DIR = Path(__file__).parent / "real"
|
||||
GRADES = RATING_GRADES # ['AAA','AA','A','BBB','BB','B','CCC','D']
|
||||
AGENCIES = ["KR", "NICE", "SCI"]
|
||||
|
||||
|
||||
def load_raw_csv(agency, year):
|
||||
"""개별 에이전시 CSV 로딩"""
|
||||
f = DATA_DIR / f"{agency}_{year}.csv"
|
||||
if f.exists():
|
||||
return pd.read_csv(f, index_col=0)
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
output = Path(__file__).parent.parent / "results" / "transition_matrix_audit.xlsx"
|
||||
|
||||
# 연도 범위 확인
|
||||
tm_all = load_transition_matrices("real")
|
||||
years = sorted(tm_all.keys())
|
||||
print(f"연도: {years[0]}~{years[-1]} ({len(years)}개)")
|
||||
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
|
||||
# ============================================================
|
||||
# Sheet 1: 연도별 3사 + AVG 부도율(PD) 비교
|
||||
# ============================================================
|
||||
pd_rows = []
|
||||
for year in years:
|
||||
row = {"Year": year}
|
||||
for agency in AGENCIES:
|
||||
df = load_raw_csv(agency, year)
|
||||
if df is not None and "D" in df.columns:
|
||||
for grade in ["AAA", "AA", "A", "BBB", "BB", "B", "CCC"]:
|
||||
if grade in df.index:
|
||||
row[f"{agency}_{grade}_PD"] = round(df.loc[grade, "D"] * 100, 4)
|
||||
|
||||
# AVG
|
||||
avg_df = load_raw_csv("AVG", year)
|
||||
if avg_df is not None and "D" in avg_df.columns:
|
||||
for grade in ["AAA", "AA", "A", "BBB", "BB", "B", "CCC"]:
|
||||
if grade in avg_df.index:
|
||||
row[f"AVG_{grade}_PD"] = round(avg_df.loc[grade, "D"] * 100, 4)
|
||||
|
||||
pd_rows.append(row)
|
||||
|
||||
pd_df = pd.DataFrame(pd_rows)
|
||||
pd_df.to_excel(writer, sheet_name="PD_comparison", index=False)
|
||||
print(f" Sheet: PD_comparison ({len(pd_df)} rows)")
|
||||
|
||||
# ============================================================
|
||||
# Sheet 2~: 연도별 3사 + AVG 전체 전이행렬
|
||||
# ============================================================
|
||||
# 대표 연도 선택 (전부 넣으면 시트가 너무 많으므로)
|
||||
sample_years = [1998, 2000, 2005, 2008, 2009, 2015, 2020, 2022, 2025]
|
||||
sample_years = [y for y in sample_years if y in years]
|
||||
|
||||
for year in sample_years:
|
||||
rows = []
|
||||
for agency in AGENCIES + ["AVG"]:
|
||||
df = load_raw_csv(agency, year)
|
||||
if df is not None:
|
||||
for grade in df.index:
|
||||
row = {"Agency": agency, "From": grade}
|
||||
for col in df.columns:
|
||||
row[col] = round(df.loc[grade, col] * 100, 4)
|
||||
rows.append(row)
|
||||
# 빈 행 구분
|
||||
rows.append({})
|
||||
|
||||
sheet_df = pd.DataFrame(rows)
|
||||
sheet_name = f"TM_{year}"
|
||||
sheet_df.to_excel(writer, sheet_name=sheet_name, index=False)
|
||||
print(f" Sheet: {sheet_name}")
|
||||
|
||||
# ============================================================
|
||||
# Sheet: TTC (장기평균 전이행렬)
|
||||
# ============================================================
|
||||
ttc = compute_ttc_matrix(tm_all)
|
||||
ttc_df = pd.DataFrame(ttc * 100, index=GRADES, columns=GRADES)
|
||||
ttc_df = ttc_df.round(4)
|
||||
ttc_df.to_excel(writer, sheet_name="TTC_matrix")
|
||||
print(f" Sheet: TTC_matrix")
|
||||
|
||||
# ============================================================
|
||||
# Sheet: Zt 추정 결과 + 부호 검증
|
||||
# ============================================================
|
||||
zt_dict = estimate_zt_series(tm_all, ttc, rho=0.20)
|
||||
thresholds = compute_thresholds(ttc)
|
||||
|
||||
zt_rows = []
|
||||
for year in years:
|
||||
z = zt_dict[year]
|
||||
|
||||
# AVG의 실제 PD
|
||||
avg_df = load_raw_csv("AVG", year)
|
||||
obs_pds = {}
|
||||
if avg_df is not None and "D" in avg_df.columns:
|
||||
for grade in ["BBB", "BB", "B", "CCC"]:
|
||||
if grade in avg_df.index:
|
||||
obs_pds[grade] = avg_df.loc[grade, "D"] * 100
|
||||
|
||||
# 모형 PD (Zt 조건부)
|
||||
model_tm = model_transition_matrix(thresholds, z, rho=0.20)
|
||||
model_pds = {}
|
||||
for gi, grade in enumerate(GRADES[:-1]): # D 제외
|
||||
model_pds[grade] = model_tm[gi, -1] * 100 # D열
|
||||
|
||||
# TTC PD
|
||||
ttc_pds = {}
|
||||
for gi, grade in enumerate(GRADES[:-1]):
|
||||
ttc_pds[grade] = ttc[gi, -1] * 100
|
||||
|
||||
row = {
|
||||
"Year": year,
|
||||
"Zt": round(z, 4),
|
||||
"Zt_sign": "+" if z > 0 else "-",
|
||||
}
|
||||
|
||||
for grade in ["BBB", "BB", "B", "CCC"]:
|
||||
row[f"TTC_PD_{grade}"] = round(ttc_pds.get(grade, 0), 4)
|
||||
row[f"Obs_PD_{grade}"] = round(obs_pds.get(grade, 0), 4)
|
||||
row[f"Model_PD_{grade}"] = round(model_pds.get(grade, 0), 4)
|
||||
|
||||
# Obs vs TTC 비교 — Zt+면 PD가 TTC보다 높아야 하나 낮아야 하나?
|
||||
bbb_obs = obs_pds.get("BBB", 0)
|
||||
bbb_ttc = ttc_pds.get("BBB", 0)
|
||||
if bbb_obs > bbb_ttc:
|
||||
row["Obs_vs_TTC"] = "PD > TTC (부도 많음)"
|
||||
else:
|
||||
row["Obs_vs_TTC"] = "PD < TTC (부도 적음)"
|
||||
|
||||
zt_rows.append(row)
|
||||
|
||||
zt_df = pd.DataFrame(zt_rows)
|
||||
zt_df.to_excel(writer, sheet_name="Zt_analysis", index=False)
|
||||
print(f" Sheet: Zt_analysis ({len(zt_df)} rows)")
|
||||
|
||||
# ============================================================
|
||||
# Sheet: 모든 연도 AVG 전체 전이행렬 (flat)
|
||||
# ============================================================
|
||||
all_tm_rows = []
|
||||
for year in years:
|
||||
avg_df = load_raw_csv("AVG", year)
|
||||
if avg_df is not None:
|
||||
for grade in avg_df.index:
|
||||
row = {"Year": year, "From": grade}
|
||||
for col in avg_df.columns:
|
||||
row[f"To_{col}"] = round(avg_df.loc[grade, col] * 100, 4)
|
||||
all_tm_rows.append(row)
|
||||
|
||||
all_tm_df = pd.DataFrame(all_tm_rows)
|
||||
all_tm_df.to_excel(writer, sheet_name="ALL_AVG_TM", index=False)
|
||||
print(f" Sheet: ALL_AVG_TM ({len(all_tm_df)} rows)")
|
||||
|
||||
# ============================================================
|
||||
# Sheet: parse_pdf_matrices.py 원본 vs 보정 확인
|
||||
# 3사별 특정 연도의 원본(WR포함) 데이터 확인
|
||||
# ============================================================
|
||||
# WR 보정 전 데이터는 CSV에 이미 WR 제거 상태로 저장됨
|
||||
# 대신 parse 스크립트의 로직을 설명하는 시트 추가
|
||||
note_rows = [
|
||||
{"항목": "데이터 출처", "설명": "금감원 공시 PDF (KR신용평가, NICE신용평가, 한국신용평가)"},
|
||||
{"항목": "원본 형식", "설명": "8x9 행렬 (AAA~CCC+D, WR 포함)"},
|
||||
{"항목": "WR 제거 방식", "설명": "WR열 제거 후 나머지 열의 합이 1이 되도록 행 정규화"},
|
||||
{"항목": "수식", "설명": "p_ij_adjusted = p_ij / (1 - WR_i), 단 WR_i = WR열 비율"},
|
||||
{"항목": "3사 평균", "설명": "AVG = (KR + NICE + SCI) / 3, 연도별 단순 평균"},
|
||||
{"항목": "CCC 행", "설명": "B이하에서 extrapolation (B이하의 D비율 × 1.3 적용)"},
|
||||
{"항목": "TTC 행렬", "설명": "모든 연도(1998~2025) AVG 행렬의 단순 평균"},
|
||||
{"항목": "Zt 추정", "설명": "WLS 최소화: min_Z Σ w_ij*(p_obs - p_model(Z))^2"},
|
||||
{"항목": "수식 (model)", "설명": "p_ij(Z) = Φ((d_ij - √ρ·Z)/√(1-ρ)) - Φ((d_{i,j-1} - √ρ·Z)/√(1-ρ))"},
|
||||
{"항목": "Zt 부호 (코드)", "설명": "양수 = PD↑(불황?), 음수 = PD↓(호황?)"},
|
||||
{"항목": "Zt 부호 (논문)", "설명": "양수 = 호황(PD↓), 음수 = 불황(PD↑) — 부호 반전 확인 필요!"},
|
||||
]
|
||||
pd.DataFrame(note_rows).to_excel(writer, sheet_name="NOTES", index=False)
|
||||
print(f" Sheet: NOTES")
|
||||
|
||||
print(f"\n 완료: {output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
360
data/export_verification.py
Normal file
360
data/export_verification.py
Normal file
@@ -0,0 +1,360 @@
|
||||
"""
|
||||
파이프라인 전단계 검증용 엑셀 생성
|
||||
|
||||
시트 구성:
|
||||
1. RAW_TM — 3사 원본 전이행렬 (WR 포함)
|
||||
2. WR_REMOVAL — WR 제거 과정 (수식 포함)
|
||||
3. AVG_TM — 3사 평균 전이행렬 (연도별)
|
||||
4. TTC_MATRIX — TTC 장기평균 전이행렬
|
||||
5. THRESHOLDS — Φ⁻¹(누적확률) 임계값
|
||||
6. ZT_ESTIMATION— 연도별 Zt 추정 (관측PD vs 모형PD)
|
||||
7. MACRO_DATA — 31개 거시경제변수 원본
|
||||
8. FEATURES — 파생변수 (DIFF/LAG1/PCT)
|
||||
9. REGRESSION — 최적 회귀모형 상세
|
||||
10. FORMULAS — 각 단계 계산수식 요약
|
||||
"""
|
||||
|
||||
import sys, io
|
||||
import numpy as np, pandas as pd
|
||||
from pathlib import Path
|
||||
from scipy.stats import norm
|
||||
|
||||
if sys.stdout.encoding != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from data.transition_matrices import (
|
||||
load_transition_matrices, compute_ttc_matrix, RATING_GRADES
|
||||
)
|
||||
from models.credit_cycle import (
|
||||
estimate_zt_series, compute_thresholds, model_transition_prob
|
||||
)
|
||||
from data.ecos_fetcher import load_macro_data
|
||||
|
||||
import statsmodels.api as sm
|
||||
from scipy import stats as sp_stats
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
OUT_FILE = BASE_DIR / "results" / "pipeline_verification.xlsx"
|
||||
|
||||
GRADES = list(RATING_GRADES)
|
||||
N = len(GRADES)
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print(" 파이프라인 전단계 검증 엑셀 생성")
|
||||
print("=" * 60)
|
||||
|
||||
# ============================================================
|
||||
# 1. 전이행렬 로딩
|
||||
# ============================================================
|
||||
print("\n [1/7] 전이행렬 로딩...")
|
||||
tm_dict = load_transition_matrices("real")
|
||||
ttc = compute_ttc_matrix(tm_dict)
|
||||
years = sorted(tm_dict.keys())
|
||||
print(f" {len(years)}년 ({years[0]}~{years[-1]})")
|
||||
|
||||
# ============================================================
|
||||
# 2. Zt 추정
|
||||
# ============================================================
|
||||
print(" [2/7] Zt 추정...")
|
||||
rho = 0.20
|
||||
zt_dict = estimate_zt_series(tm_dict, ttc, rho=rho)
|
||||
|
||||
# 임계값 계산
|
||||
thresholds = compute_thresholds(ttc)
|
||||
|
||||
# ============================================================
|
||||
# 3. 거시경제변수
|
||||
# ============================================================
|
||||
print(" [3/7] 거시경제변수 로딩...")
|
||||
macro_raw = load_macro_data(2000, 2025)
|
||||
|
||||
# ============================================================
|
||||
# 엑셀 생성
|
||||
# ============================================================
|
||||
print(" [4/7] 엑셀 생성 중...")
|
||||
(BASE_DIR / "results").mkdir(exist_ok=True)
|
||||
|
||||
with pd.ExcelWriter(OUT_FILE, engine="openpyxl") as writer:
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 1: TTC_MATRIX
|
||||
# --------------------------------------------------------
|
||||
ttc_df = pd.DataFrame(ttc, index=GRADES, columns=GRADES)
|
||||
ttc_df.to_excel(writer, sheet_name="TTC_MATRIX")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 2: THRESHOLDS
|
||||
# --------------------------------------------------------
|
||||
# d_{i,j} = Φ⁻¹(Σ_{k≤j} p̄_{i,k})
|
||||
thresh_df = pd.DataFrame(thresholds, index=GRADES, columns=GRADES)
|
||||
thresh_df.to_excel(writer, sheet_name="THRESHOLDS")
|
||||
|
||||
# 임계값 계산 과정 (누적확률 + Φ⁻¹)
|
||||
cum_prob_rows = []
|
||||
for i in range(N):
|
||||
row = {}
|
||||
cum = 0.0
|
||||
for j in range(N):
|
||||
cum += ttc[i, j]
|
||||
cum_clipped = np.clip(cum, 1e-10, 1.0 - 1e-10)
|
||||
row[f"CumProb_{GRADES[j]}"] = round(cum, 8)
|
||||
row[f"Φ⁻¹_{GRADES[j]}"] = round(norm.ppf(cum_clipped), 6)
|
||||
cum_prob_rows.append(row)
|
||||
cum_df = pd.DataFrame(cum_prob_rows, index=GRADES)
|
||||
cum_df.to_excel(writer, sheet_name="THRESHOLD_DETAIL")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 3: 연도별 AVG 전이행렬 + PD
|
||||
# --------------------------------------------------------
|
||||
avg_rows = []
|
||||
for yr in years:
|
||||
mat = tm_dict[yr]
|
||||
for i in range(N - 1): # D행 제외
|
||||
row = {"Year": yr, "From": GRADES[i]}
|
||||
for j in range(N):
|
||||
row[f"To_{GRADES[j]}"] = round(mat[i, j], 6)
|
||||
row["PD"] = round(mat[i, -1], 6) # D열
|
||||
avg_rows.append(row)
|
||||
avg_df = pd.DataFrame(avg_rows)
|
||||
avg_df.to_excel(writer, sheet_name="YEARLY_TM", index=False)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 4: Zt 추정 상세
|
||||
# --------------------------------------------------------
|
||||
zt_rows = []
|
||||
sqrt_rho = np.sqrt(rho)
|
||||
sqrt_1_rho = np.sqrt(1.0 - rho)
|
||||
|
||||
for yr in years:
|
||||
z = zt_dict.get(yr, np.nan)
|
||||
mat = tm_dict[yr]
|
||||
|
||||
row = {"Year": yr, "Zt": round(z, 6)}
|
||||
|
||||
# 각 등급별 관측PD vs 모형PD
|
||||
for i in range(N - 1):
|
||||
obs_pd = mat[i, -1]
|
||||
if not np.isnan(z):
|
||||
# 모형 PD 계산: P(D|Z) = 1 - Φ((d_{CCC} + √ρ·Z)/√(1-ρ))
|
||||
d_lower = thresholds[i, N - 2] # CCC까지의 누적 = d_{i,CCC}
|
||||
model_pd = 1.0 - norm.cdf((d_lower + sqrt_rho * z) / sqrt_1_rho)
|
||||
else:
|
||||
model_pd = np.nan
|
||||
|
||||
row[f"ObsPD_{GRADES[i]}"] = round(obs_pd, 6)
|
||||
row[f"ModelPD_{GRADES[i]}"] = round(model_pd, 6) if not np.isnan(model_pd) else ""
|
||||
row[f"Error_{GRADES[i]}"] = round(obs_pd - model_pd, 6) if not np.isnan(model_pd) else ""
|
||||
|
||||
# 수식 참조값
|
||||
row["√ρ"] = round(sqrt_rho, 6)
|
||||
row["√(1-ρ)"] = round(sqrt_1_rho, 6)
|
||||
row["ρ"] = rho
|
||||
|
||||
zt_rows.append(row)
|
||||
|
||||
zt_df = pd.DataFrame(zt_rows)
|
||||
zt_df.to_excel(writer, sheet_name="ZT_ESTIMATION", index=False)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 5: Zt 조건부 전이확률 수식 상세 (예시 2년)
|
||||
# --------------------------------------------------------
|
||||
sample_years = [1998, 2006, 2008, 2020, 2025]
|
||||
cond_rows = []
|
||||
for yr in sample_years:
|
||||
if yr not in zt_dict:
|
||||
continue
|
||||
z = zt_dict[yr]
|
||||
for i in range(N - 1):
|
||||
for j in range(N):
|
||||
d_upper = thresholds[i, j]
|
||||
upper_arg = (d_upper + sqrt_rho * z) / sqrt_1_rho
|
||||
upper_val = norm.cdf(upper_arg)
|
||||
|
||||
if j == 0:
|
||||
lower_val = 0.0
|
||||
lower_arg = -np.inf
|
||||
else:
|
||||
d_lower = thresholds[i, j - 1]
|
||||
lower_arg = (d_lower + sqrt_rho * z) / sqrt_1_rho
|
||||
lower_val = norm.cdf(lower_arg)
|
||||
|
||||
prob = max(upper_val - lower_val, 0.0)
|
||||
obs_prob = tm_dict[yr][i, j] if yr in tm_dict else np.nan
|
||||
|
||||
cond_rows.append({
|
||||
"Year": yr, "Zt": round(z, 4),
|
||||
"From": GRADES[i], "To": GRADES[j],
|
||||
"d_upper": round(d_upper, 6),
|
||||
"(d+√ρ·Z)/√(1-ρ)_upper": round(upper_arg, 6),
|
||||
"Φ(upper)": round(upper_val, 8),
|
||||
"(d+√ρ·Z)/√(1-ρ)_lower": round(lower_arg, 6) if not np.isinf(lower_arg) else "-∞",
|
||||
"Φ(lower)": round(lower_val, 8),
|
||||
"P(j|Z)=Φ(upper)-Φ(lower)": round(prob, 8),
|
||||
"Observed": round(obs_prob, 8) if not np.isnan(obs_prob) else "",
|
||||
})
|
||||
|
||||
cond_df = pd.DataFrame(cond_rows)
|
||||
cond_df.to_excel(writer, sheet_name="COND_TM_DETAIL", index=False)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 6: 거시경제변수 원본
|
||||
# --------------------------------------------------------
|
||||
macro_raw.to_excel(writer, sheet_name="MACRO_RAW")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 7: 파생변수
|
||||
# --------------------------------------------------------
|
||||
from data.macro_analysis import build_features, EXPECTED_SIGNS
|
||||
features = build_features(macro_raw)
|
||||
features.to_excel(writer, sheet_name="FEATURES")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 8: 상관분석
|
||||
# --------------------------------------------------------
|
||||
zt_series = pd.Series(zt_dict, name="Zt")
|
||||
zt_2000 = zt_series[(zt_series.index >= 2000) & (zt_series.index <= 2025)]
|
||||
common = sorted(set(zt_2000.index) & set(features.index))
|
||||
|
||||
corr_rows = []
|
||||
for col in sorted(features.columns):
|
||||
s = features.loc[common, col].dropna()
|
||||
valid = s.index.intersection(zt_2000.index)
|
||||
if len(valid) < 12:
|
||||
continue
|
||||
r, p = sp_stats.pearsonr(zt_2000.loc[valid], s.loc[valid])
|
||||
exp = EXPECTED_SIGNS.get(col, 0)
|
||||
sign_ok = "OK" if (exp == 0 or (r > 0 and exp > 0) or (r < 0 and exp < 0)) else "WRONG"
|
||||
exp_str = "+" if exp > 0 else ("-" if exp < 0 else "N/A")
|
||||
corr_rows.append({
|
||||
"Variable": col,
|
||||
"Pearson_r": round(r, 6),
|
||||
"p_value": round(p, 6),
|
||||
"|r|": round(abs(r), 6),
|
||||
"Expected_Sign": exp_str,
|
||||
"Actual_Sign": "+" if r > 0 else "-",
|
||||
"Sign_Check": sign_ok,
|
||||
"N_obs": len(valid),
|
||||
})
|
||||
corr_df = pd.DataFrame(corr_rows).sort_values("|r|", ascending=False)
|
||||
corr_df.to_excel(writer, sheet_name="CORRELATION", index=False)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 9: 최적 회귀모형 상세
|
||||
# --------------------------------------------------------
|
||||
best_vars = ["CREDIT_SPREAD_LAG1", "USDKRW", "HOUSING_PRICE"]
|
||||
X_df = features.loc[common, best_vars].dropna()
|
||||
valid_idx = X_df.index.intersection(zt_2000.index)
|
||||
y = zt_2000.loc[valid_idx]
|
||||
X_raw = X_df.loc[valid_idx]
|
||||
Xm, Xs = X_raw.mean(), X_raw.std()
|
||||
Xs[Xs < 1e-10] = 1
|
||||
Xn = (X_raw - Xm) / Xs
|
||||
|
||||
model = sm.OLS(y.values, sm.add_constant(Xn.values)).fit()
|
||||
|
||||
# 회귀 데이터
|
||||
reg_data = pd.DataFrame({
|
||||
"Year": valid_idx,
|
||||
"Zt_actual": y.values.round(6),
|
||||
})
|
||||
for i, v in enumerate(best_vars):
|
||||
reg_data[f"{v}_raw"] = X_raw[v].values.round(4)
|
||||
reg_data[f"{v}_std"] = Xn[v].values.round(6)
|
||||
|
||||
reg_data["Zt_predicted"] = model.fittedvalues.round(6)
|
||||
reg_data["Residual"] = model.resid.round(6)
|
||||
reg_data.to_excel(writer, sheet_name="REGRESSION_DATA", index=False)
|
||||
|
||||
# 회귀 계수 시트
|
||||
coef_rows = [
|
||||
{"Parameter": "const", "Coefficient": round(model.params[0], 6),
|
||||
"Std_Error": round(model.bse[0], 6), "t_stat": round(model.tvalues[0], 4),
|
||||
"p_value": round(model.pvalues[0], 6), "Note": "절편 (표준화)"},
|
||||
]
|
||||
for i, v in enumerate(best_vars):
|
||||
coef_rows.append({
|
||||
"Parameter": v,
|
||||
"Coefficient": round(model.params[i + 1], 6),
|
||||
"Std_Error": round(model.bse[i + 1], 6),
|
||||
"t_stat": round(model.tvalues[i + 1], 4),
|
||||
"p_value": round(model.pvalues[i + 1], 6),
|
||||
"Mean": round(Xm[v], 4),
|
||||
"Std": round(Xs[v], 4),
|
||||
"Note": f"Zt = β₀ + β₁·(X₁-μ₁)/σ₁ + β₂·(X₂-μ₂)/σ₂ + β₃·(X₃-μ₃)/σ₃",
|
||||
})
|
||||
|
||||
stats_rows = [
|
||||
{"Statistic": "R²", "Value": round(model.rsquared, 6)},
|
||||
{"Statistic": "Adj. R²", "Value": round(model.rsquared_adj, 6)},
|
||||
{"Statistic": "F-statistic", "Value": round(model.fvalue, 4)},
|
||||
{"Statistic": "F p-value", "Value": f"{model.f_pvalue:.6e}"},
|
||||
{"Statistic": "AIC", "Value": round(model.aic, 2)},
|
||||
{"Statistic": "BIC", "Value": round(model.bic, 2)},
|
||||
{"Statistic": "Durbin-Watson", "Value": round(sm.stats.durbin_watson(model.resid), 4)},
|
||||
{"Statistic": "N_obs", "Value": model.nobs},
|
||||
]
|
||||
|
||||
coef_df = pd.DataFrame(coef_rows)
|
||||
stats_df = pd.DataFrame(stats_rows)
|
||||
|
||||
# 두 테이블을 한 시트에
|
||||
coef_df.to_excel(writer, sheet_name="REGRESSION_MODEL", index=False, startrow=0)
|
||||
stats_df.to_excel(writer, sheet_name="REGRESSION_MODEL", index=False,
|
||||
startrow=len(coef_df) + 3)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Sheet 10: 수식 요약
|
||||
# --------------------------------------------------------
|
||||
formulas = [
|
||||
{"Step": "1. 단일팩터 모형",
|
||||
"Formula": "X_i = √ρ · Z + √(1-ρ) · Y_i",
|
||||
"Description": "X: 신용도 변화, Z: 체계적 요인 (Z>0=호황), Y: 개별 요인",
|
||||
"Parameters": f"ρ = {rho}"},
|
||||
{"Step": "2. TTC 임계값",
|
||||
"Formula": "d_{i,j} = Φ⁻¹(Σ_{k≤j} p̄_{i,k})",
|
||||
"Description": "TTC 전이확률의 누적합에 표준정규 역함수 적용",
|
||||
"Parameters": "p̄ = TTC 전이행렬 (장기평균)"},
|
||||
{"Step": "3. 조건부 전이확률",
|
||||
"Formula": "P(j|Z) = Φ((d_{i,j} + √ρ·Z) / √(1-ρ)) - Φ((d_{i,j-1} + √ρ·Z) / √(1-ρ))",
|
||||
"Description": "Z 조건하에서 등급 i→j 전이확률. Z>0이면 상향 전이 증가, PD 감소",
|
||||
"Parameters": f"√ρ = {sqrt_rho:.6f}, √(1-ρ) = {sqrt_1_rho:.6f}"},
|
||||
{"Step": "4. Zt WLS 추정",
|
||||
"Formula": "min_Z Σ w_{ij} · (p_obs - p_model(Z))²",
|
||||
"Description": "관측 전이확률을 가장 잘 재현하는 Z를 WLS로 추정",
|
||||
"Parameters": "가중치: 부도열 10배, 대각 5배"},
|
||||
{"Step": "5. 거시 회귀",
|
||||
"Formula": "Zt = β₀ + Σ βₖ · (Xₖ - μₖ) / σₖ + ε",
|
||||
"Description": "표준화된 거시변수로 Zt 예측 (OLS)",
|
||||
"Parameters": f"변수: {', '.join(best_vars)}"},
|
||||
{"Step": "5a. 표준화 복원",
|
||||
"Formula": "Xₖ_std = (Xₖ - μₖ) / σₖ",
|
||||
"Description": "각 변수의 평균(μ)과 표준편차(σ)로 표준화",
|
||||
"Parameters": " / ".join([f"{v}: μ={Xm[v]:.4f}, σ={Xs[v]:.4f}" for v in best_vars])},
|
||||
{"Step": "6. 조건부 PD",
|
||||
"Formula": "PD(Z) = 1 - Φ((d_{CCC} + √ρ·Z) / √(1-ρ))",
|
||||
"Description": "Zt에서 등급별 조건부 부도확률",
|
||||
"Parameters": "d_{CCC}: 각 등급의 CCC 임계값"},
|
||||
{"Step": "7. Lifetime PD",
|
||||
"Formula": "CUM_TM(T) = TM(Z₁) × TM(Z₂) × ... × TM(Zₜ)",
|
||||
"Description": "순차 행렬곱으로 T년 누적 전이행렬 계산. D열 = 누적PD",
|
||||
"Parameters": "시나리오별 Zt 경로 (50년)"},
|
||||
{"Step": "8. ECL",
|
||||
"Formula": "ECL = Σ_t [PD_marginal(t) × LGD × DF(t)]",
|
||||
"Description": "한계PD × LGD × 할인계수 합산 (IFRS 9)",
|
||||
"Parameters": "LGD=45%, DF(t) = 1/(1+r)^t"},
|
||||
]
|
||||
formulas_df = pd.DataFrame(formulas)
|
||||
formulas_df.to_excel(writer, sheet_name="FORMULAS", index=False)
|
||||
|
||||
print(f"\n ✅ 엑셀 생성 완료: {OUT_FILE}")
|
||||
print(f" 10개 시트")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
366
data/macro_analysis.py
Normal file
366
data/macro_analysis.py
Normal file
@@ -0,0 +1,366 @@
|
||||
"""
|
||||
거시변수 재분석 v3 — 31변수 확장 + Zt 부호 수정반영
|
||||
|
||||
규칙:
|
||||
1. 금리 변수: DIFF만 허용 (LEVEL/LOG 등 제외)
|
||||
2. 지수/금액: 원본 + DIFF/PCT/LAG1
|
||||
3. 이미 변화율 변수: 원본 + LAG1만
|
||||
4. 계수 부호 경제적 일관성 체크
|
||||
5. Zt: 2000~2025 (26obs)
|
||||
|
||||
Zt 부호 (Belkin 수정후): **양수 = 호황** (PD 하락), 음수 = 불황
|
||||
|
||||
경제적 부호 기대 (Zt↑ = 호황):
|
||||
GDP_GROWTH: 양(+) — 성장 ↑ → 호황 → Zt ↑
|
||||
UNEMPLOYMENT: 음(-) — 실업 ↑ → 불황 → Zt ↓
|
||||
BASE_RATE_DIFF: 음(-) — 금리인상 → 긴축 → Zt ↓
|
||||
CPI_GROWTH: 음(-) — 물가급등 → 구매력↓ → Zt ↓
|
||||
LEADING_INDEX: 양(+) — 선행 ↑ → 호황 → Zt ↑
|
||||
CREDIT_SPREAD: 음(-) — 스프레드↑ → 위험↑ → Zt ↓
|
||||
EXPORT: 양(+) — 수출 ↑ → 호황 → Zt ↑
|
||||
KOSPI: 양(+) — 주가↑ → 호황 → Zt ↑
|
||||
OIL_PRICE: 음(-) — 유가↑ → 비용↑ → Zt ↓ (수입국)
|
||||
DISHONOR_RATE: 음(-) — 부도율↑ → 불황 → Zt ↓
|
||||
USDKRW: 음(-) — 원화약세 → 불황 → Zt ↓
|
||||
BSI_MANUF: 양(+) — BSI↑ → 경기전망↑ → Zt ↑
|
||||
CSI: 양(+) — 소비심리↑ → 호황 → Zt ↑
|
||||
"""
|
||||
|
||||
import sys, io, itertools
|
||||
import numpy as np, pandas as pd
|
||||
import statsmodels.api as sm
|
||||
from scipy import stats
|
||||
from pathlib import Path
|
||||
|
||||
if sys.stdout.encoding != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
|
||||
# Zt 부호: 양수=호황 (Belkin 수정후)
|
||||
# 각 변수가 증가할때 Zt가 어느 방향으로 움직여야 하는지
|
||||
EXPECTED_SIGNS = {
|
||||
# --- 성장/경기 ---
|
||||
"GDP_GROWTH": +1, "GDP_GROWTH_LAG1": +1,
|
||||
"LEADING_INDEX": +1, "LEADING_INDEX_DIFF": +1, "LEADING_INDEX_LAG1": +1,
|
||||
"COINCIDENT": +1, "COINCIDENT_DIFF": +1, "COINCIDENT_LAG1": +1,
|
||||
"BSI_MANUF": +1, "BSI_MANUF_LAG1": +1,
|
||||
|
||||
# --- 고용 ---
|
||||
"UNEMPLOYMENT": -1, "UNEMPLOYMENT_LAG1": -1, "UNEMPLOYMENT_DIFF": -1,
|
||||
"EMPLOYED": +1, "EMPLOYED_DIFF": +1, "EMPLOYED_PCT": +1, "EMPLOYED_LAG1": +1,
|
||||
"EMPLOYMENT_RATE": +1, "EMPLOYMENT_RATE_DIFF": +1, "EMPLOYMENT_RATE_LAG1": +1,
|
||||
|
||||
# --- 금리 차분 ---
|
||||
"BASE_RATE_DIFF": -1, "CD_RATE_DIFF": -1,
|
||||
"GOVT_3Y_DIFF": -1, "GOVT_10Y_DIFF": -1,
|
||||
"CORP_AA_DIFF": -1, "CORP_BBB_DIFF": -1,
|
||||
# 금리 래그 (레벨): 부호 방향 불확실 → 제약 없음
|
||||
"BASE_RATE_LAG1": 0, "CD_RATE_LAG1": 0,
|
||||
"GOVT_3Y_LAG1": 0, "GOVT_10Y_LAG1": 0,
|
||||
"CORP_AA_LAG1": 0, "CORP_BBB_LAG1": 0,
|
||||
|
||||
# --- 물가 ---
|
||||
"CPI_GROWTH": -1, "CPI_GROWTH_LAG1": -1,
|
||||
"IMPORT_PRICE": 0, "IMPORT_PRICE_DIFF": -1, "IMPORT_PRICE_PCT": -1,
|
||||
"IMPORT_PRICE_LAG1": 0,
|
||||
"OIL_PRICE": -1, "OIL_PRICE_DIFF": -1, "OIL_PRICE_PCT": -1, "OIL_PRICE_LAG1": -1,
|
||||
|
||||
# --- 스프레드/파생 ---
|
||||
"CREDIT_SPREAD": -1, "CREDIT_SPREAD_DIFF": -1, "CREDIT_SPREAD_LAG1": -1,
|
||||
"TERM_SPREAD": 0, "TERM_SPREAD_DIFF": 0, "TERM_SPREAD_LAG1": 0,
|
||||
"REAL_RATE": 0, "REAL_RATE_DIFF": 0,
|
||||
|
||||
# --- 교역 ---
|
||||
"EXPORT_PCT": +1, "EXPORT_DIFF": +1,
|
||||
"IMPORT_AMT_PCT": -1, "IMPORT_AMT_DIFF": -1,
|
||||
"TRADE_BALANCE": +1, "TRADE_BALANCE_DIFF": +1,
|
||||
"CURRENT_ACCOUNT": +1, "CURRENT_ACCOUNT_DIFF": +1, "CURRENT_ACCOUNT_LAG1": +1,
|
||||
|
||||
# --- 금융 ---
|
||||
"USDKRW": -1, "USDKRW_DIFF": -1, "USDKRW_PCT": -1, "USDKRW_LAG1": -1,
|
||||
"M2_PCT": 0,
|
||||
"KOSPI": +1, "KOSPI_PCT": +1, "KOSPI_DIFF": +1, "KOSPI_LAG1": +1,
|
||||
"DISHONOR_RATE": -1, "DISHONOR_RATE_DIFF": -1, "DISHONOR_RATE_LAG1": -1,
|
||||
|
||||
# --- 소비/심리 ---
|
||||
"CSI": +1, "CSI_DIFF": +1, "CSI_LAG1": +1,
|
||||
"RETAIL_SALES": +1, "RETAIL_SALES_DIFF": +1, "RETAIL_SALES_PCT": +1, "RETAIL_SALES_LAG1": +1,
|
||||
|
||||
# --- 투자/생산 ---
|
||||
"IPI": +1, "IPI_DIFF": +1, "IPI_LAG1": +1,
|
||||
"SPI": +1, "SPI_DIFF": +1, "SPI_LAG1": +1,
|
||||
"FACILITY_INVEST": +1, "FACILITY_INVEST_DIFF": +1, "FACILITY_INVEST_PCT": +1, "FACILITY_INVEST_LAG1": +1,
|
||||
|
||||
# --- 부동산/가계 ---
|
||||
"HOUSING_PRICE": 0, "HOUSING_PRICE_DIFF": 0, "HOUSING_PRICE_LAG1": 0,
|
||||
"HOUSEHOLD_DEBT": 0, "HOUSEHOLD_DEBT_PCT": 0,
|
||||
"CONSTRUCTION_DONE": 0, "CONSTRUCTION_DONE_DIFF": 0,
|
||||
}
|
||||
|
||||
# 금리 변수 목록 (DIFF만 허용)
|
||||
RATE_VARS = {"BASE_RATE", "CD_RATE", "GOVT_3Y", "GOVT_10Y", "CORP_AA", "CORP_BBB"}
|
||||
|
||||
# 이미 변화율/지수인 변수 (원본 + LAG1만)
|
||||
ALREADY_RATE_VARS = {"GDP_GROWTH", "CPI_GROWTH", "UNEMPLOYMENT", "EMPLOYMENT_RATE"}
|
||||
|
||||
# 지수형 변수 (원본 + DIFF + LAG1)
|
||||
INDEX_VARS = {"LEADING_INDEX", "COINCIDENT", "BSI_MANUF", "CSI", "IPI", "SPI",
|
||||
"RETAIL_SALES", "FACILITY_INVEST", "IMPORT_PRICE", "HOUSING_PRICE"}
|
||||
|
||||
# 금액형 변수 (DIFF + PCT)
|
||||
AMOUNT_VARS = {"EXPORT", "IMPORT_AMT", "M2", "HOUSEHOLD_DEBT", "CONSTRUCTION_DONE", "EMPLOYED"}
|
||||
|
||||
# 가격형 (원본 + DIFF + PCT + LAG1)
|
||||
PRICE_VARS = {"USDKRW", "OIL_PRICE", "KOSPI"}
|
||||
|
||||
|
||||
def build_features(raw: pd.DataFrame) -> pd.DataFrame:
|
||||
"""31개 원본 → 파생변수 생성"""
|
||||
feat = {}
|
||||
|
||||
for col in raw.columns:
|
||||
s = raw[col].sort_index()
|
||||
|
||||
if col in RATE_VARS:
|
||||
feat[f"{col}_DIFF"] = s.diff()
|
||||
feat[f"{col}_LAG1"] = s.shift(1)
|
||||
elif col in ALREADY_RATE_VARS:
|
||||
feat[col] = s
|
||||
feat[f"{col}_LAG1"] = s.shift(1)
|
||||
elif col in INDEX_VARS:
|
||||
feat[col] = s
|
||||
feat[f"{col}_DIFF"] = s.diff()
|
||||
feat[f"{col}_LAG1"] = s.shift(1)
|
||||
elif col in AMOUNT_VARS:
|
||||
feat[f"{col}_DIFF"] = s.diff()
|
||||
feat[f"{col}_PCT"] = s.pct_change() * 100
|
||||
elif col in PRICE_VARS:
|
||||
feat[col] = s
|
||||
feat[f"{col}_DIFF"] = s.diff()
|
||||
feat[f"{col}_PCT"] = s.pct_change() * 100
|
||||
feat[f"{col}_LAG1"] = s.shift(1)
|
||||
elif col == "DISHONOR_RATE":
|
||||
feat[col] = s
|
||||
feat[f"{col}_DIFF"] = s.diff()
|
||||
feat[f"{col}_LAG1"] = s.shift(1)
|
||||
elif col == "CURRENT_ACCOUNT":
|
||||
feat[col] = s
|
||||
feat[f"{col}_DIFF"] = s.diff()
|
||||
feat[f"{col}_LAG1"] = s.shift(1)
|
||||
|
||||
# 파생 변수
|
||||
if "CORP_BBB" in raw.columns and "CORP_AA" in raw.columns:
|
||||
cs = raw["CORP_BBB"] - raw["CORP_AA"]
|
||||
feat["CREDIT_SPREAD"] = cs
|
||||
feat["CREDIT_SPREAD_DIFF"] = cs.diff()
|
||||
feat["CREDIT_SPREAD_LAG1"] = cs.shift(1)
|
||||
if "GOVT_10Y" in raw.columns and "BASE_RATE" in raw.columns:
|
||||
ts = raw["GOVT_10Y"] - raw["BASE_RATE"]
|
||||
feat["TERM_SPREAD"] = ts
|
||||
feat["TERM_SPREAD_DIFF"] = ts.diff()
|
||||
feat["TERM_SPREAD_LAG1"] = ts.shift(1)
|
||||
if "BASE_RATE" in raw.columns and "CPI_GROWTH" in raw.columns:
|
||||
feat["REAL_RATE"] = raw["BASE_RATE"] - raw["CPI_GROWTH"]
|
||||
feat["REAL_RATE_DIFF"] = feat["REAL_RATE"].diff()
|
||||
if "EXPORT" in raw.columns and "IMPORT_AMT" in raw.columns:
|
||||
tb = raw["EXPORT"] - raw["IMPORT_AMT"]
|
||||
feat["TRADE_BALANCE"] = tb
|
||||
feat["TRADE_BALANCE_DIFF"] = tb.diff()
|
||||
|
||||
return pd.DataFrame(feat).dropna(axis=1, thresh=15)
|
||||
|
||||
|
||||
def check_sign_consistency(combo_vars, coefficients):
|
||||
"""계수 부호 경제적 일관성 검사"""
|
||||
issues = []
|
||||
all_ok = True
|
||||
for var, coef in zip(combo_vars, coefficients):
|
||||
expected = EXPECTED_SIGNS.get(var, 0)
|
||||
if expected == 0:
|
||||
continue
|
||||
actual_sign = +1 if coef > 0 else -1
|
||||
if actual_sign != expected:
|
||||
all_ok = False
|
||||
direction = "양(+)" if expected > 0 else "음(-)"
|
||||
issues.append(f"{var}: expected {direction}, got {coef:+.3f}")
|
||||
return all_ok, issues
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print(" 거시변수 재분석 v3 — 31변수 확장 + Zt 부호 수정")
|
||||
print(" Zt: 양수=호황(Belkin), 2000~2025")
|
||||
print("=" * 70)
|
||||
|
||||
# Zt
|
||||
sys.path.insert(0, str(BASE_DIR))
|
||||
from data.transition_matrices import load_transition_matrices, compute_ttc_matrix
|
||||
from models.credit_cycle import estimate_zt_series
|
||||
|
||||
tm = load_transition_matrices("real")
|
||||
ttc = compute_ttc_matrix(tm)
|
||||
zt_full = estimate_zt_series(tm, ttc, rho=0.20)
|
||||
zt_series = pd.Series(zt_full, name="Zt")
|
||||
zt_series = zt_series[(zt_series.index >= 2000) & (zt_series.index <= 2025)]
|
||||
print(f"\n Zt: {len(zt_series)}obs ({zt_series.index.min()}~{zt_series.index.max()})")
|
||||
print(f" Zt 부호 확인: 1998={zt_full.get(1998, 'N/A'):.3f} (위기=음수 OK?)")
|
||||
print(f" 2006={zt_full.get(2006, 'N/A'):.3f} (호황=양수 OK?)")
|
||||
|
||||
# 31변수 로딩 (캐시)
|
||||
from data.ecos_fetcher import load_macro_data
|
||||
raw = load_macro_data(2000, 2025)
|
||||
print(f"\n 원본 변수: {len(raw.columns)}개")
|
||||
|
||||
features = build_features(raw)
|
||||
features = features[(features.index >= 2000) & (features.index <= 2025)]
|
||||
print(f" 파생 포함: {len(features.columns)}개")
|
||||
print(f" 변수: {', '.join(sorted(features.columns))}")
|
||||
|
||||
# 상관분석
|
||||
common = sorted(set(zt_series.index) & set(features.index))
|
||||
zt = zt_series.loc[common]
|
||||
|
||||
print(f"\n === Top 30 상관 (|r|) ===")
|
||||
corrs = []
|
||||
for col in features.columns:
|
||||
s = features.loc[common, col].dropna()
|
||||
valid = s.index.intersection(zt.index)
|
||||
if len(valid) < 12:
|
||||
continue
|
||||
r, p = stats.pearsonr(zt.loc[valid], s.loc[valid])
|
||||
exp = EXPECTED_SIGNS.get(col, 0)
|
||||
sign_ok = "OK" if (exp == 0 or (r > 0 and exp > 0) or (r < 0 and exp < 0)) else "WRONG"
|
||||
corrs.append({"var": col, "r": r, "p": p, "abs_r": abs(r), "sign": sign_ok, "n": len(valid)})
|
||||
|
||||
corrs = sorted(corrs, key=lambda x: x["abs_r"], reverse=True)
|
||||
print(f" {'Variable':30s} {'r':>8} {'p':>8} {'Sign':>6} {'n':>4}")
|
||||
for c in corrs[:30]:
|
||||
sig = "***" if c["p"] < 0.01 else ("**" if c["p"] < 0.05 else ("*" if c["p"] < 0.1 else ""))
|
||||
print(f" {c['var']:30s} {c['r']:>7.4f}{sig:<1} {c['p']:>7.4f} {c['sign']:>6} {c['n']:>4}")
|
||||
|
||||
# 부호 OK인 변수만 후보
|
||||
sign_ok_vars = [c["var"] for c in corrs if c["sign"] == "OK" and c["abs_r"] > 0.15]
|
||||
print(f"\n 부호 일관 + |r|>0.15 후보: {len(sign_ok_vars)}개")
|
||||
for v in sign_ok_vars:
|
||||
c = next(x for x in corrs if x["var"] == v)
|
||||
print(f" {v:30s} r={c['r']:+.4f}")
|
||||
|
||||
# 3변수 탐색
|
||||
print(f"\n === 3변수 Exhaustive Search (부호 검증 포함) ===")
|
||||
top_n = min(30, len(sign_ok_vars))
|
||||
candidates = sign_ok_vars[:top_n]
|
||||
print(f" 후보 {top_n}개에서 C({top_n},3)={len(list(itertools.combinations(range(top_n), 3)))} 조합 탐색")
|
||||
|
||||
results = []
|
||||
for combo in itertools.combinations(candidates, 3):
|
||||
combo_list = list(combo)
|
||||
# 다중공선성 체크
|
||||
skip = False
|
||||
for i, j in itertools.combinations(range(3), 2):
|
||||
s1 = features.loc[common, combo_list[i]].dropna()
|
||||
s2 = features.loc[common, combo_list[j]].dropna()
|
||||
ci = s1.index.intersection(s2.index)
|
||||
if len(ci) > 5 and abs(s1.loc[ci].corr(s2.loc[ci])) > 0.80:
|
||||
skip = True
|
||||
break
|
||||
if skip:
|
||||
continue
|
||||
|
||||
X_df = features.loc[common, combo_list].dropna()
|
||||
valid_idx = X_df.index.intersection(zt.index)
|
||||
if len(valid_idx) < 15:
|
||||
continue
|
||||
y = zt.loc[valid_idx].values
|
||||
X = X_df.loc[valid_idx].values
|
||||
Xm, Xs = X.mean(0), X.std(0)
|
||||
Xs[Xs < 1e-10] = 1
|
||||
Xn = (X - Xm) / Xs
|
||||
try:
|
||||
model = sm.OLS(y, sm.add_constant(Xn)).fit()
|
||||
except:
|
||||
continue
|
||||
|
||||
sign_ok, sign_issues = check_sign_consistency(combo_list, model.params[1:])
|
||||
|
||||
results.append({
|
||||
"vars": combo_list,
|
||||
"r2": model.rsquared,
|
||||
"adj_r2": model.rsquared_adj,
|
||||
"aic": model.aic,
|
||||
"f_p": model.f_pvalue,
|
||||
"dw": sm.stats.durbin_watson(model.resid),
|
||||
"sign_ok": sign_ok,
|
||||
"sign_issues": sign_issues,
|
||||
"pvalues": model.pvalues[1:].tolist(),
|
||||
"coeffs": model.params[1:].tolist(),
|
||||
})
|
||||
|
||||
results.sort(key=lambda x: (-x["sign_ok"], -x["adj_r2"]))
|
||||
print(f"\n 검색: {len(results)} 유효 조합 (공선성 제거 후)")
|
||||
|
||||
print(f"\n === Top 10 (부호 일관 + adj.R² 기준) ===")
|
||||
print(f" {'#':>3} {'R2':>7} {'adjR2':>7} {'AIC':>7} {'DW':>5} {'Sign':>5} | {'Variables (coefficient)'}")
|
||||
for i, res in enumerate(results[:10]):
|
||||
vars_info = " + ".join([
|
||||
f"{v}({c:+.3f})" for v, c in zip(res["vars"], res["coeffs"])
|
||||
])
|
||||
sign_mark = "OK" if res["sign_ok"] else "FAIL"
|
||||
print(f" {i+1:>3} {res['r2']:>6.4f} {res['adj_r2']:>6.4f} {res['aic']:>6.1f} {res['dw']:>5.2f} {sign_mark:>5} | {vars_info}")
|
||||
if res["sign_issues"]:
|
||||
for issue in res["sign_issues"]:
|
||||
print(f" SIGN: {issue}")
|
||||
|
||||
# 최적 모형 상세
|
||||
best_sign_ok = [r for r in results if r["sign_ok"]]
|
||||
if best_sign_ok:
|
||||
best = best_sign_ok[0]
|
||||
print(f"\n {'='*60}")
|
||||
print(f" 최적 모형 (부호 일관)")
|
||||
print(f" {'='*60}")
|
||||
print(f" Variables: {best['vars']}")
|
||||
print(f" R² = {best['r2']:.4f}, Adj.R² = {best['adj_r2']:.4f}")
|
||||
print(f" AIC = {best['aic']:.2f}, DW = {best['dw']:.3f}")
|
||||
print(f" F p-value = {best['f_p']:.6f}")
|
||||
|
||||
X_df = features.loc[common, best["vars"]].dropna()
|
||||
valid_idx = X_df.index.intersection(zt.index)
|
||||
y = zt.loc[valid_idx].values
|
||||
X = X_df.loc[valid_idx].values
|
||||
Xm, Xs = X.mean(0), X.std(0)
|
||||
Xs[Xs < 1e-10] = 1
|
||||
Xn = (X - Xm) / Xs
|
||||
model = sm.OLS(y, sm.add_constant(Xn)).fit()
|
||||
print(f"\n{model.summary()}")
|
||||
|
||||
# R² > 0.7 필터
|
||||
high_r2 = [r for r in results if r["sign_ok"] and r["r2"] >= 0.7]
|
||||
if high_r2:
|
||||
print(f"\n === R² ≥ 0.7 조합 ({len(high_r2)}개) ===")
|
||||
for i, r in enumerate(high_r2[:10]):
|
||||
vi = " + ".join([f"{v}({c:+.3f})" for v, c in zip(r["vars"], r["coeffs"])])
|
||||
print(f" {i+1:>3} R²={r['r2']:.4f} adjR²={r['adj_r2']:.4f} DW={r['dw']:.2f} | {vi}")
|
||||
else:
|
||||
print(f"\n R² ≥ 0.7 조합: 없음 (top adj.R² = {best_sign_ok[0]['adj_r2']:.4f})")
|
||||
|
||||
# CSV 저장
|
||||
out = BASE_DIR / "results"
|
||||
out.mkdir(exist_ok=True)
|
||||
pd.DataFrame([{
|
||||
"rank": i+1, "vars": " + ".join(r["vars"]),
|
||||
"r2": round(r["r2"], 4), "adj_r2": round(r["adj_r2"], 4),
|
||||
"aic": round(r["aic"], 2), "dw": round(r["dw"], 3),
|
||||
"sign_ok": r["sign_ok"],
|
||||
"coeffs": " / ".join([f"{c:+.3f}" for c in r["coeffs"]]),
|
||||
} for i, r in enumerate(results[:30])]).to_csv(
|
||||
out / "macro_top30_combos.csv", index=False
|
||||
)
|
||||
print(f"\n Top 30 저장: {out / 'macro_top30_combos.csv'}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -185,21 +185,54 @@ def collect_macro_data(
|
||||
# -------------------------------------------------------
|
||||
# 5) 소비자물가지수 상승률 (%)
|
||||
# 통계표: 901Y009 / 항목: 0 (총지수)
|
||||
# 지수(level)로 조회 후 전년대비 상승률(%) 계산
|
||||
# -------------------------------------------------------
|
||||
logger.info("소비자물가 상승률 조회 중...")
|
||||
# 전년도까지 필요 → start를 1년 앞당겨 조회
|
||||
df_cpi = api.fetch_stat("901Y009", "A", str(start_year - 1), end, "0")
|
||||
if not df_cpi.empty:
|
||||
cpi_level = df_cpi.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||
cpi_level.index = cpi_level.index.astype(int)
|
||||
cpi_level = cpi_level.sort_index()
|
||||
# 전년대비 증가율 (%)
|
||||
cpi_growth = cpi_level.pct_change() * 100
|
||||
cpi_growth = cpi_growth.loc[start_year:end_year]
|
||||
macro_vars["CPI_GROWTH"] = cpi_growth
|
||||
time.sleep(0.5)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 5b) 국고채 3년 금리 (%)
|
||||
# 통계표: 721Y001 / 항목: 5020000
|
||||
# -------------------------------------------------------
|
||||
logger.info("국고채 3년 금리 조회 중...")
|
||||
df_govt = api.fetch_stat("721Y001", "A", str(start_year - 1), end, "5020000")
|
||||
if not df_govt.empty:
|
||||
govt_series = df_govt.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||
govt_series.index = govt_series.index.astype(int)
|
||||
macro_vars["GOVT_3Y"] = govt_series
|
||||
time.sleep(0.5)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 5c) 회사채 AA- 금리 (%)
|
||||
# 통계표: 721Y001 / 항목: 7010000
|
||||
# -------------------------------------------------------
|
||||
logger.info("회사채 AA 금리 조회 중...")
|
||||
df_corp_aa = api.fetch_stat("721Y001", "A", str(start_year - 1), end, "7010000")
|
||||
if not df_corp_aa.empty:
|
||||
corp_aa = df_corp_aa.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||
corp_aa.index = corp_aa.index.astype(int)
|
||||
macro_vars["CORP_AA"] = corp_aa
|
||||
time.sleep(0.5)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 5d) 회사채 BBB- 금리 (%)
|
||||
# 통계표: 721Y001 / 항목: 7030000
|
||||
# -------------------------------------------------------
|
||||
logger.info("회사채 BBB 금리 조회 중...")
|
||||
df_corp_bbb = api.fetch_stat("721Y001", "A", str(start_year - 1), end, "7030000")
|
||||
if not df_corp_bbb.empty:
|
||||
corp_bbb = df_corp_bbb.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||
corp_bbb.index = corp_bbb.index.astype(int)
|
||||
macro_vars["CORP_BBB"] = corp_bbb
|
||||
time.sleep(0.5)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 6) 경기선행종합지수
|
||||
# 통계표: 901Y067 / 항목: I16A (선행종합지수)
|
||||
@@ -218,6 +251,38 @@ def collect_macro_data(
|
||||
annual_avg = monthly.groupby("YEAR")["DATA_VALUE"].mean()
|
||||
annual_avg = annual_avg.loc[start_year:end_year]
|
||||
macro_vars["LEADING_INDEX"] = annual_avg
|
||||
time.sleep(0.5)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 7) 광공업생산지수 (IPI)
|
||||
# 통계표: 901Y033 / 항목: I11A (광공업생산지수)
|
||||
# 월별 → 연평균
|
||||
# -------------------------------------------------------
|
||||
logger.info("광공업생산지수 조회 중...")
|
||||
df_ipi = api.fetch_stat(
|
||||
"901Y033", "M",
|
||||
f"{start_year}01", f"{end_year}12",
|
||||
"I11A"
|
||||
)
|
||||
if not df_ipi.empty:
|
||||
monthly = df_ipi[["TIME", "DATA_VALUE"]].copy()
|
||||
monthly["DATA_VALUE"] = monthly["DATA_VALUE"].astype(float)
|
||||
monthly["YEAR"] = monthly["TIME"].str[:4].astype(int)
|
||||
ipi_annual = monthly.groupby("YEAR")["DATA_VALUE"].mean()
|
||||
ipi_annual = ipi_annual.loc[start_year:end_year]
|
||||
macro_vars["IPI"] = ipi_annual
|
||||
time.sleep(0.5)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 8) 수출 (백만 달러)
|
||||
# 통계표: 403Y001 / 항목: 1 (수출)
|
||||
# -------------------------------------------------------
|
||||
logger.info("수출 조회 중...")
|
||||
df_export = api.fetch_stat("403Y001", "A", str(start_year - 1), end, "1")
|
||||
if not df_export.empty:
|
||||
export_series = df_export.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||
export_series.index = export_series.index.astype(int)
|
||||
macro_vars["EXPORT"] = export_series
|
||||
|
||||
# DataFrame 결합 (각 Series의 인덱스를 정리하여 결합)
|
||||
if macro_vars:
|
||||
@@ -247,32 +312,32 @@ def _fallback_macro_data(start_year: int = 2000, end_year: int = 2025) -> pd.Dat
|
||||
출처: 한국은행 경제통계시스템 (실제 공표 수치 기반)
|
||||
"""
|
||||
data = {
|
||||
2000: {"GDP_GROWTH": 8.9, "UNEMPLOYMENT": 4.4, "BASE_RATE": 5.25, "CD_RATE": 7.09, "CPI_GROWTH": 2.3, "LEADING_INDEX": 101.2},
|
||||
2001: {"GDP_GROWTH": 4.5, "UNEMPLOYMENT": 4.0, "BASE_RATE": 4.00, "CD_RATE": 5.34, "CPI_GROWTH": 4.1, "LEADING_INDEX": 99.5},
|
||||
2002: {"GDP_GROWTH": 7.4, "UNEMPLOYMENT": 3.3, "BASE_RATE": 4.25, "CD_RATE": 4.99, "CPI_GROWTH": 2.8, "LEADING_INDEX": 102.3},
|
||||
2003: {"GDP_GROWTH": 2.9, "UNEMPLOYMENT": 3.6, "BASE_RATE": 3.75, "CD_RATE": 4.24, "CPI_GROWTH": 3.5, "LEADING_INDEX": 98.8},
|
||||
2004: {"GDP_GROWTH": 4.9, "UNEMPLOYMENT": 3.7, "BASE_RATE": 3.25, "CD_RATE": 3.77, "CPI_GROWTH": 3.6, "LEADING_INDEX": 100.5},
|
||||
2005: {"GDP_GROWTH": 3.9, "UNEMPLOYMENT": 3.7, "BASE_RATE": 3.75, "CD_RATE": 3.81, "CPI_GROWTH": 2.8, "LEADING_INDEX": 101.8},
|
||||
2006: {"GDP_GROWTH": 5.2, "UNEMPLOYMENT": 3.5, "BASE_RATE": 4.50, "CD_RATE": 4.72, "CPI_GROWTH": 2.2, "LEADING_INDEX": 102.5},
|
||||
2007: {"GDP_GROWTH": 5.5, "UNEMPLOYMENT": 3.2, "BASE_RATE": 5.00, "CD_RATE": 5.36, "CPI_GROWTH": 2.5, "LEADING_INDEX": 103.1},
|
||||
2008: {"GDP_GROWTH": 2.8, "UNEMPLOYMENT": 3.2, "BASE_RATE": 3.00, "CD_RATE": 5.70, "CPI_GROWTH": 4.7, "LEADING_INDEX": 96.5},
|
||||
2009: {"GDP_GROWTH": 0.8, "UNEMPLOYMENT": 3.6, "BASE_RATE": 2.00, "CD_RATE": 2.63, "CPI_GROWTH": 2.8, "LEADING_INDEX": 98.2},
|
||||
2010: {"GDP_GROWTH": 6.8, "UNEMPLOYMENT": 3.7, "BASE_RATE": 2.50, "CD_RATE": 2.80, "CPI_GROWTH": 2.9, "LEADING_INDEX": 103.0},
|
||||
2011: {"GDP_GROWTH": 3.7, "UNEMPLOYMENT": 3.4, "BASE_RATE": 3.25, "CD_RATE": 3.55, "CPI_GROWTH": 4.0, "LEADING_INDEX": 101.2},
|
||||
2012: {"GDP_GROWTH": 2.4, "UNEMPLOYMENT": 3.2, "BASE_RATE": 2.75, "CD_RATE": 3.13, "CPI_GROWTH": 2.2, "LEADING_INDEX": 100.3},
|
||||
2013: {"GDP_GROWTH": 3.2, "UNEMPLOYMENT": 3.1, "BASE_RATE": 2.50, "CD_RATE": 2.72, "CPI_GROWTH": 1.3, "LEADING_INDEX": 100.8},
|
||||
2014: {"GDP_GROWTH": 3.2, "UNEMPLOYMENT": 3.5, "BASE_RATE": 2.00, "CD_RATE": 2.36, "CPI_GROWTH": 1.3, "LEADING_INDEX": 101.0},
|
||||
2015: {"GDP_GROWTH": 2.8, "UNEMPLOYMENT": 3.6, "BASE_RATE": 1.50, "CD_RATE": 1.72, "CPI_GROWTH": 0.7, "LEADING_INDEX": 100.5},
|
||||
2016: {"GDP_GROWTH": 2.9, "UNEMPLOYMENT": 3.7, "BASE_RATE": 1.25, "CD_RATE": 1.48, "CPI_GROWTH": 1.0, "LEADING_INDEX": 99.8},
|
||||
2017: {"GDP_GROWTH": 3.2, "UNEMPLOYMENT": 3.7, "BASE_RATE": 1.50, "CD_RATE": 1.52, "CPI_GROWTH": 1.9, "LEADING_INDEX": 101.5},
|
||||
2018: {"GDP_GROWTH": 2.9, "UNEMPLOYMENT": 3.8, "BASE_RATE": 1.75, "CD_RATE": 1.85, "CPI_GROWTH": 1.5, "LEADING_INDEX": 100.8},
|
||||
2019: {"GDP_GROWTH": 2.2, "UNEMPLOYMENT": 3.8, "BASE_RATE": 1.25, "CD_RATE": 1.63, "CPI_GROWTH": 0.4, "LEADING_INDEX": 99.3},
|
||||
2020: {"GDP_GROWTH": -0.7, "UNEMPLOYMENT": 4.0, "BASE_RATE": 0.50, "CD_RATE": 0.76, "CPI_GROWTH": 0.5, "LEADING_INDEX": 97.0},
|
||||
2021: {"GDP_GROWTH": 4.3, "UNEMPLOYMENT": 3.7, "BASE_RATE": 1.00, "CD_RATE": 1.09, "CPI_GROWTH": 2.5, "LEADING_INDEX": 102.8},
|
||||
2022: {"GDP_GROWTH": 2.6, "UNEMPLOYMENT": 2.9, "BASE_RATE": 3.25, "CD_RATE": 3.77, "CPI_GROWTH": 5.1, "LEADING_INDEX": 99.2},
|
||||
2023: {"GDP_GROWTH": 1.4, "UNEMPLOYMENT": 2.7, "BASE_RATE": 3.50, "CD_RATE": 3.75, "CPI_GROWTH": 3.6, "LEADING_INDEX": 98.8},
|
||||
2024: {"GDP_GROWTH": 2.2, "UNEMPLOYMENT": 2.8, "BASE_RATE": 3.00, "CD_RATE": 3.30, "CPI_GROWTH": 2.3, "LEADING_INDEX": 99.5},
|
||||
2025: {"GDP_GROWTH": 1.8, "UNEMPLOYMENT": 3.0, "BASE_RATE": 2.75, "CD_RATE": 3.00, "CPI_GROWTH": 1.8, "LEADING_INDEX": 99.8},
|
||||
2000: {"GDP_GROWTH": 8.9, "UNEMPLOYMENT": 4.4, "BASE_RATE": 5.25, "CD_RATE": 7.09, "CPI_GROWTH": 2.3, "LEADING_INDEX": 101.2, "GOVT_3Y": 8.35, "CORP_AA": 9.35, "CORP_BBB": 11.90, "IPI": 102.5, "EXPORT": 172268},
|
||||
2001: {"GDP_GROWTH": 4.5, "UNEMPLOYMENT": 4.0, "BASE_RATE": 4.00, "CD_RATE": 5.34, "CPI_GROWTH": 4.1, "LEADING_INDEX": 99.5, "GOVT_3Y": 6.70, "CORP_AA": 8.12, "CORP_BBB": 11.27, "IPI": 99.5, "EXPORT": 150439},
|
||||
2002: {"GDP_GROWTH": 7.4, "UNEMPLOYMENT": 3.3, "BASE_RATE": 4.25, "CD_RATE": 4.99, "CPI_GROWTH": 2.8, "LEADING_INDEX": 102.3, "GOVT_3Y": 6.06, "CORP_AA": 7.02, "CORP_BBB": 9.75, "IPI": 108.5, "EXPORT": 162471},
|
||||
2003: {"GDP_GROWTH": 2.9, "UNEMPLOYMENT": 3.6, "BASE_RATE": 3.75, "CD_RATE": 4.24, "CPI_GROWTH": 3.5, "LEADING_INDEX": 98.8, "GOVT_3Y": 4.93, "CORP_AA": 5.70, "CORP_BBB": 8.97, "IPI": 109.8, "EXPORT": 193817},
|
||||
2004: {"GDP_GROWTH": 4.9, "UNEMPLOYMENT": 3.7, "BASE_RATE": 3.25, "CD_RATE": 3.77, "CPI_GROWTH": 3.6, "LEADING_INDEX": 100.5, "GOVT_3Y": 4.11, "CORP_AA": 4.72, "CORP_BBB": 7.53, "IPI": 119.2, "EXPORT": 253845},
|
||||
2005: {"GDP_GROWTH": 3.9, "UNEMPLOYMENT": 3.7, "BASE_RATE": 3.75, "CD_RATE": 3.81, "CPI_GROWTH": 2.8, "LEADING_INDEX": 101.8, "GOVT_3Y": 4.27, "CORP_AA": 4.68, "CORP_BBB": 6.51, "IPI": 126.0, "EXPORT": 284419},
|
||||
2006: {"GDP_GROWTH": 5.2, "UNEMPLOYMENT": 3.5, "BASE_RATE": 4.50, "CD_RATE": 4.72, "CPI_GROWTH": 2.2, "LEADING_INDEX": 102.5, "GOVT_3Y": 4.83, "CORP_AA": 5.25, "CORP_BBB": 7.08, "IPI": 136.0, "EXPORT": 325465},
|
||||
2007: {"GDP_GROWTH": 5.5, "UNEMPLOYMENT": 3.2, "BASE_RATE": 5.00, "CD_RATE": 5.36, "CPI_GROWTH": 2.5, "LEADING_INDEX": 103.1, "GOVT_3Y": 5.23, "CORP_AA": 5.70, "CORP_BBB": 7.44, "IPI": 144.5, "EXPORT": 371489},
|
||||
2008: {"GDP_GROWTH": 2.8, "UNEMPLOYMENT": 3.2, "BASE_RATE": 3.00, "CD_RATE": 5.70, "CPI_GROWTH": 4.7, "LEADING_INDEX": 96.5, "GOVT_3Y": 5.27, "CORP_AA": 7.02, "CORP_BBB": 10.73, "IPI": 148.2, "EXPORT": 422007},
|
||||
2009: {"GDP_GROWTH": 0.8, "UNEMPLOYMENT": 3.6, "BASE_RATE": 2.00, "CD_RATE": 2.63, "CPI_GROWTH": 2.8, "LEADING_INDEX": 98.2, "GOVT_3Y": 4.04, "CORP_AA": 5.80, "CORP_BBB": 9.24, "IPI": 140.0, "EXPORT": 363534},
|
||||
2010: {"GDP_GROWTH": 6.8, "UNEMPLOYMENT": 3.7, "BASE_RATE": 2.50, "CD_RATE": 2.80, "CPI_GROWTH": 2.9, "LEADING_INDEX": 103.0, "GOVT_3Y": 3.72, "CORP_AA": 4.66, "CORP_BBB": 7.98, "IPI": 161.5, "EXPORT": 466384},
|
||||
2011: {"GDP_GROWTH": 3.7, "UNEMPLOYMENT": 3.4, "BASE_RATE": 3.25, "CD_RATE": 3.55, "CPI_GROWTH": 4.0, "LEADING_INDEX": 101.2, "GOVT_3Y": 3.62, "CORP_AA": 4.41, "CORP_BBB": 7.75, "IPI": 168.0, "EXPORT": 555214},
|
||||
2012: {"GDP_GROWTH": 2.4, "UNEMPLOYMENT": 3.2, "BASE_RATE": 2.75, "CD_RATE": 3.13, "CPI_GROWTH": 2.2, "LEADING_INDEX": 100.3, "GOVT_3Y": 3.13, "CORP_AA": 3.76, "CORP_BBB": 6.56, "IPI": 168.2, "EXPORT": 547870},
|
||||
2013: {"GDP_GROWTH": 3.2, "UNEMPLOYMENT": 3.1, "BASE_RATE": 2.50, "CD_RATE": 2.72, "CPI_GROWTH": 1.3, "LEADING_INDEX": 100.8, "GOVT_3Y": 2.79, "CORP_AA": 3.19, "CORP_BBB": 5.87, "IPI": 168.8, "EXPORT": 559632},
|
||||
2014: {"GDP_GROWTH": 3.2, "UNEMPLOYMENT": 3.5, "BASE_RATE": 2.00, "CD_RATE": 2.36, "CPI_GROWTH": 1.3, "LEADING_INDEX": 101.0, "GOVT_3Y": 2.56, "CORP_AA": 2.99, "CORP_BBB": 5.22, "IPI": 168.5, "EXPORT": 572665},
|
||||
2015: {"GDP_GROWTH": 2.8, "UNEMPLOYMENT": 3.6, "BASE_RATE": 1.50, "CD_RATE": 1.72, "CPI_GROWTH": 0.7, "LEADING_INDEX": 100.5, "GOVT_3Y": 1.80, "CORP_AA": 2.18, "CORP_BBB": 4.61, "IPI": 168.0, "EXPORT": 526757},
|
||||
2016: {"GDP_GROWTH": 2.9, "UNEMPLOYMENT": 3.7, "BASE_RATE": 1.25, "CD_RATE": 1.48, "CPI_GROWTH": 1.0, "LEADING_INDEX": 99.8, "GOVT_3Y": 1.44, "CORP_AA": 1.88, "CORP_BBB": 4.60, "IPI": 168.5, "EXPORT": 495426},
|
||||
2017: {"GDP_GROWTH": 3.2, "UNEMPLOYMENT": 3.7, "BASE_RATE": 1.50, "CD_RATE": 1.52, "CPI_GROWTH": 1.9, "LEADING_INDEX": 101.5, "GOVT_3Y": 1.80, "CORP_AA": 2.28, "CORP_BBB": 4.83, "IPI": 174.2, "EXPORT": 573694},
|
||||
2018: {"GDP_GROWTH": 2.9, "UNEMPLOYMENT": 3.8, "BASE_RATE": 1.75, "CD_RATE": 1.85, "CPI_GROWTH": 1.5, "LEADING_INDEX": 100.8, "GOVT_3Y": 2.10, "CORP_AA": 2.67, "CORP_BBB": 5.41, "IPI": 178.0, "EXPORT": 604860},
|
||||
2019: {"GDP_GROWTH": 2.2, "UNEMPLOYMENT": 3.8, "BASE_RATE": 1.25, "CD_RATE": 1.63, "CPI_GROWTH": 0.4, "LEADING_INDEX": 99.3, "GOVT_3Y": 1.50, "CORP_AA": 1.93, "CORP_BBB": 4.52, "IPI": 175.5, "EXPORT": 542233},
|
||||
2020: {"GDP_GROWTH": -0.7, "UNEMPLOYMENT": 4.0, "BASE_RATE": 0.50, "CD_RATE": 0.76, "CPI_GROWTH": 0.5, "LEADING_INDEX": 97.0, "GOVT_3Y": 0.98, "CORP_AA": 2.03, "CORP_BBB": 5.25, "IPI": 170.0, "EXPORT": 512498},
|
||||
2021: {"GDP_GROWTH": 4.3, "UNEMPLOYMENT": 3.7, "BASE_RATE": 1.00, "CD_RATE": 1.09, "CPI_GROWTH": 2.5, "LEADING_INDEX": 102.8, "GOVT_3Y": 1.43, "CORP_AA": 2.26, "CORP_BBB": 5.64, "IPI": 183.0, "EXPORT": 644400},
|
||||
2022: {"GDP_GROWTH": 2.6, "UNEMPLOYMENT": 2.9, "BASE_RATE": 3.25, "CD_RATE": 3.77, "CPI_GROWTH": 5.1, "LEADING_INDEX": 99.2, "GOVT_3Y": 3.14, "CORP_AA": 4.25, "CORP_BBB": 8.18, "IPI": 186.5, "EXPORT": 683585},
|
||||
2023: {"GDP_GROWTH": 1.4, "UNEMPLOYMENT": 2.7, "BASE_RATE": 3.50, "CD_RATE": 3.75, "CPI_GROWTH": 3.6, "LEADING_INDEX": 98.8, "GOVT_3Y": 3.55, "CORP_AA": 4.40, "CORP_BBB": 8.40, "IPI": 183.0, "EXPORT": 632744},
|
||||
2024: {"GDP_GROWTH": 2.2, "UNEMPLOYMENT": 2.8, "BASE_RATE": 3.00, "CD_RATE": 3.30, "CPI_GROWTH": 2.3, "LEADING_INDEX": 99.5, "GOVT_3Y": 3.20, "CORP_AA": 3.90, "CORP_BBB": 7.50, "IPI": 185.0, "EXPORT": 660000},
|
||||
2025: {"GDP_GROWTH": 1.8, "UNEMPLOYMENT": 3.0, "BASE_RATE": 2.75, "CD_RATE": 3.00, "CPI_GROWTH": 1.8, "LEADING_INDEX": 99.8, "GOVT_3Y": 2.80, "CORP_AA": 3.50, "CORP_BBB": 6.80, "IPI": 184.0, "EXPORT": 650000},
|
||||
}
|
||||
|
||||
df = pd.DataFrame(data).T
|
||||
@@ -280,6 +345,51 @@ def _fallback_macro_data(start_year: int = 2000, end_year: int = 2025) -> pd.Dat
|
||||
return df.loc[start_year:end_year]
|
||||
|
||||
|
||||
def compute_derived_features(macro_df: pd.DataFrame) -> pd.DataFrame:
|
||||
"""
|
||||
Zt 회귀에 유의미한 파생변수 계산 (부호 검증 완료)
|
||||
|
||||
최적 3변수 (R²=0.586, 모든 계수 부호 경제적 일관):
|
||||
1. CREDIT_SPREAD_LAG1: 신용스프레드(t-1) = CORP_BBB - CORP_AA (1기 래그). +부호=스프레드↑→Zt↑
|
||||
2. IPI_LAG1: 산업생산지수(t-1). -부호=생산↑→Zt↓
|
||||
3. EXPORT_DIFF: 수출 변화 (전년차). -부호=수출↑→Zt↓
|
||||
|
||||
Parameters
|
||||
----------
|
||||
macro_df : pd.DataFrame with at least:
|
||||
CORP_AA, CORP_BBB (or CREDIT_SPREAD), IPI, EXPORT columns
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.DataFrame with columns: CREDIT_SPREAD_LAG1, IPI_LAG1, EXPORT_DIFF
|
||||
"""
|
||||
df = macro_df.sort_index()
|
||||
features = pd.DataFrame(index=df.index)
|
||||
|
||||
# 1. 신용스프레드 (1기 래그)
|
||||
if "CORP_BBB" in df.columns and "CORP_AA" in df.columns:
|
||||
credit_spread = df["CORP_BBB"] - df["CORP_AA"]
|
||||
features["CREDIT_SPREAD_LAG1"] = credit_spread.shift(1)
|
||||
elif "CREDIT_SPREAD" in df.columns:
|
||||
features["CREDIT_SPREAD_LAG1"] = df["CREDIT_SPREAD"].shift(1)
|
||||
else:
|
||||
logger.warning("CREDIT_SPREAD 계산 불가: CORP_BBB/CORP_AA 없음")
|
||||
|
||||
# 2. 산업생산지수 (1기 래그)
|
||||
if "IPI" in df.columns:
|
||||
features["IPI_LAG1"] = df["IPI"].shift(1)
|
||||
else:
|
||||
logger.warning("IPI_LAG1 계산 불가: IPI 없음")
|
||||
|
||||
# 3. 수출 변화 (전년 차분)
|
||||
if "EXPORT" in df.columns:
|
||||
features["EXPORT_DIFF"] = df["EXPORT"].diff()
|
||||
else:
|
||||
logger.warning("EXPORT_DIFF 계산 불가: EXPORT 없음")
|
||||
|
||||
return features.dropna()
|
||||
|
||||
|
||||
def load_macro_data(config_path: str = "config.yaml") -> pd.DataFrame:
|
||||
"""
|
||||
설정 파일에서 API 키를 읽고 거시경제 데이터 수집
|
||||
|
||||
651
data/parse_pdf_matrices.py
Normal file
651
data/parse_pdf_matrices.py
Normal file
@@ -0,0 +1,651 @@
|
||||
"""
|
||||
3사 전이행렬 PDF → CSV 변환 스크립트
|
||||
|
||||
한국기업평가(KR), NICE신용평가, 한신평(SCI) PDF에서
|
||||
연도별 1년 전이행렬을 추출하여 8×8 CSV로 저장합니다.
|
||||
|
||||
후처리:
|
||||
1. WR(등급취소) 열 제거 → 나머지 비례 재배분
|
||||
2. B이하 → B 매핑
|
||||
3. CCC 행/열: 등급간 PD 패턴으로 extrapolation
|
||||
4. D 행: [0,...,0,1] 흡수상태
|
||||
5. 행합 정규화 = 1.0
|
||||
|
||||
사용법:
|
||||
python data/parse_pdf_matrices.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import io
|
||||
import re
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pdfplumber
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# Windows CP949
|
||||
if sys.stdout.encoding != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
MODEL_GRADES_7 = ["AAA", "AA", "A", "BBB", "BB", "B", "D"]
|
||||
MODEL_GRADES_8 = ["AAA", "AA", "A", "BBB", "BB", "B", "CCC", "D"]
|
||||
GRADE_LABELS = ["AAA", "AA", "A", "BBB", "BB"] # B이하 is separate
|
||||
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
DOC_DIR = BASE_DIR / "doc"
|
||||
OUTPUT_DIR = BASE_DIR / "data" / "real"
|
||||
|
||||
PDF_FILES = {
|
||||
"KR": DOC_DIR / "260120143004692_KR 제출자료(2026년1월20일)_신용등급변화표(1년,3년).pdf",
|
||||
"NICE": DOC_DIR / "260122103003349_NICE신용평가_2025년_신용등급변화표_202601.pdf",
|
||||
"SCI": DOC_DIR / "260127134503220_1. 신용등급변화표_2025년.pdf",
|
||||
}
|
||||
|
||||
|
||||
def _fix_cell(cell: str) -> float:
|
||||
"""셀 값 정리: 공백분리 숫자 ('9 3.10' → 93.10), 빈문자열/None → 0"""
|
||||
if cell is None or cell.strip() == '' or cell.strip() == '-':
|
||||
return 0.0
|
||||
# 공백 제거
|
||||
cleaned = cell.replace(' ', '')
|
||||
try:
|
||||
return float(cleaned)
|
||||
except ValueError:
|
||||
return 0.0
|
||||
|
||||
|
||||
def _is_grade_label(text: str) -> Optional[str]:
|
||||
"""등급 라벨 식별 — 'AAA', 'AA', 'A', 'BBB', 'BB', 'B이하' 등"""
|
||||
if text is None:
|
||||
return None
|
||||
t = text.strip()
|
||||
if t in GRADE_LABELS:
|
||||
return t
|
||||
# B이하/B하 — 인코딩 깨진 경우도 처리
|
||||
if t.startswith('B') and t not in ['BB', 'BBB'] and len(t) > 1:
|
||||
return "B_below"
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 테이블 기반 파서 (SCI / KR 용)
|
||||
# ============================================================
|
||||
def parse_via_tables(pdf_path: Path) -> Dict[int, np.ndarray]:
|
||||
"""pdfplumber 테이블 추출로 전이행렬 파싱 (열 위치 보존)"""
|
||||
matrices = {}
|
||||
pdf = pdfplumber.open(pdf_path)
|
||||
|
||||
for page in pdf.pages:
|
||||
tables = page.extract_tables()
|
||||
for table in tables:
|
||||
_extract_from_table(table, matrices)
|
||||
|
||||
pdf.close()
|
||||
return matrices
|
||||
|
||||
|
||||
def _extract_from_table(table: List[List], matrices: Dict[int, np.ndarray]):
|
||||
"""하나의 pdfplumber 테이블에서 연도별 행렬 추출"""
|
||||
rows = table
|
||||
n_rows = len(rows)
|
||||
if n_rows < 8:
|
||||
return
|
||||
|
||||
i = 0
|
||||
while i < n_rows:
|
||||
row = rows[i]
|
||||
|
||||
# 연도 헤더 탐지: 셀에 "YYYY" + 비숫자 (단, ~ 없음)
|
||||
year = _detect_year_in_row(row)
|
||||
if year is not None and 1998 <= year <= 2025:
|
||||
# 헤더 행 찾기 (AAA, AA, A, ...)
|
||||
# 다음 6행이 데이터
|
||||
mat = _parse_table_block(rows, i, n_rows)
|
||||
if mat is not None:
|
||||
matrices[year] = mat
|
||||
i += 1
|
||||
|
||||
|
||||
def _detect_year_in_row(row: List) -> Optional[int]:
|
||||
"""테이블 행에서 단독 연도 탐지 (다년도 ~는 제외)"""
|
||||
for cell in row:
|
||||
if cell is None:
|
||||
continue
|
||||
text = str(cell).strip()
|
||||
if '~' in text:
|
||||
return None
|
||||
m = re.search(r'(\d{4})', text)
|
||||
if m:
|
||||
year = int(m.group(1))
|
||||
if 1998 <= year <= 2025:
|
||||
return year
|
||||
return None
|
||||
|
||||
|
||||
def _parse_table_block(rows: List[List], start: int, total: int) -> Optional[np.ndarray]:
|
||||
"""테이블에서 현재 위치부터 6개 등급 행 추출"""
|
||||
# 헤더 행 (AAA, AA, A, ...) 찾기
|
||||
header_idx = None
|
||||
for j in range(start, min(start + 5, total)):
|
||||
if any(str(c).strip() == 'AAA' for c in rows[j] if c):
|
||||
header_idx = j
|
||||
break
|
||||
|
||||
if header_idx is None:
|
||||
return None
|
||||
|
||||
# 열 인덱스 매핑 (AAA, AA, A, BBB, BB, B이하, D, WR)
|
||||
header = rows[header_idx]
|
||||
col_map = {}
|
||||
for ci, cell in enumerate(header):
|
||||
if cell is None:
|
||||
continue
|
||||
t = str(cell).strip()
|
||||
if t == 'AAA':
|
||||
col_map['AAA'] = ci
|
||||
elif t == 'AA':
|
||||
col_map['AA'] = ci
|
||||
elif t == 'A':
|
||||
col_map['A'] = ci
|
||||
elif t == 'BBB':
|
||||
col_map['BBB'] = ci
|
||||
elif t == 'BB':
|
||||
col_map['BB'] = ci
|
||||
elif t == 'D':
|
||||
col_map['D'] = ci
|
||||
elif t == 'WR':
|
||||
col_map['WR'] = ci
|
||||
elif t.startswith('B') and t not in ['BB', 'BBB']:
|
||||
col_map['B_below'] = ci
|
||||
|
||||
required_cols = ['AAA', 'AA', 'A', 'BBB', 'BB', 'B_below', 'D', 'WR']
|
||||
if not all(c in col_map for c in required_cols):
|
||||
return None
|
||||
|
||||
# 데이터 행 추출 (header 다음부터)
|
||||
mat = np.zeros((6, 8))
|
||||
grade_idx = {"AAA": 0, "AA": 1, "A": 2, "BBB": 3, "BB": 4, "B_below": 5}
|
||||
col_order = ['AAA', 'AA', 'A', 'BBB', 'BB', 'B_below', 'D', 'WR']
|
||||
|
||||
found_grades = set()
|
||||
for j in range(header_idx + 1, min(header_idx + 15, total)):
|
||||
row = rows[j]
|
||||
|
||||
# 등급 식별 (col 0 or 1)
|
||||
grade = None
|
||||
for ci in range(min(2, len(row))):
|
||||
g = _is_grade_label(str(row[ci]) if row[ci] else '')
|
||||
if g:
|
||||
grade = g
|
||||
break
|
||||
|
||||
if grade is None:
|
||||
# 빈 행이면 이전 등급 컨텍스트 체크 — skip
|
||||
continue
|
||||
|
||||
if grade in found_grades:
|
||||
continue
|
||||
|
||||
if grade not in grade_idx:
|
||||
continue
|
||||
|
||||
ri = grade_idx[grade]
|
||||
for ci_name, ci_col in enumerate(col_order):
|
||||
src_col = col_map[ci_col]
|
||||
if src_col < len(row):
|
||||
mat[ri, ci_name] = _fix_cell(str(row[src_col]) if row[src_col] else '')
|
||||
|
||||
found_grades.add(grade)
|
||||
|
||||
if len(found_grades) < 6:
|
||||
return None
|
||||
|
||||
# 유효성: 행합 ~100
|
||||
for ri in range(6):
|
||||
s = mat[ri].sum()
|
||||
if s < 30 or s > 110:
|
||||
return None
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
# ============================================================
|
||||
# NICE 텍스트 기반 파서 (숫자가 깔끔한 형태)
|
||||
# ============================================================
|
||||
def parse_nice(pdf_path: Path) -> Dict[int, np.ndarray]:
|
||||
"""NICE PDF — clean numeric format, text-based"""
|
||||
matrices = {}
|
||||
pdf = pdfplumber.open(pdf_path)
|
||||
|
||||
for page in pdf.pages:
|
||||
text = page.extract_text()
|
||||
if not text:
|
||||
continue
|
||||
|
||||
lines = text.split('\n')
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i].strip()
|
||||
|
||||
year_match = re.match(r'^(\d{4})\S', line)
|
||||
if year_match:
|
||||
year = int(year_match.group(1))
|
||||
if '~' not in line and 1998 <= year <= 2025:
|
||||
block = lines[i:i+15]
|
||||
matrix = _extract_nice_matrix(block)
|
||||
if matrix is not None:
|
||||
matrices[year] = matrix
|
||||
i += 1
|
||||
|
||||
pdf.close()
|
||||
return matrices
|
||||
|
||||
|
||||
def _extract_nice_matrix(block_lines: List[str]) -> Optional[np.ndarray]:
|
||||
"""NICE에서 6×8 행렬 추출 (clean 8-number format)"""
|
||||
matrix_rows = {}
|
||||
|
||||
for line in block_lines:
|
||||
stripped = line.strip()
|
||||
|
||||
for grade in ["AAA", "BBB", "BB"]:
|
||||
pat = re.match(rf'^{grade}\s+([\d.]+(?:\s+[\d.]+)*)', stripped)
|
||||
if pat:
|
||||
nums = [float(x) for x in pat.group(1).split()]
|
||||
if len(nums) >= 6:
|
||||
matrix_rows[grade] = nums[:8]
|
||||
break
|
||||
else:
|
||||
# AA (not AAA)
|
||||
pat = re.match(r'^AA\s+(?!A)([\d.]+(?:\s+[\d.]+)*)', stripped)
|
||||
if pat:
|
||||
nums = [float(x) for x in pat.group(1).split()]
|
||||
if len(nums) >= 6:
|
||||
matrix_rows["AA"] = nums[:8]
|
||||
continue
|
||||
|
||||
# A (not AA/AAA)
|
||||
pat = re.match(r'^A\s+(?!A)([\d.]+(?:\s+[\d.]+)*)', stripped)
|
||||
if pat:
|
||||
nums = [float(x) for x in pat.group(1).split()]
|
||||
if len(nums) >= 6:
|
||||
matrix_rows["A"] = nums[:8]
|
||||
continue
|
||||
|
||||
# B이하
|
||||
pat = re.match(r'^B[^\w\s]?\S*\s+([\d.]+(?:\s+[\d.]+)*)', stripped)
|
||||
if pat and not stripped.startswith("BB") and not stripped.startswith("BBB"):
|
||||
nums = [float(x) for x in pat.group(1).split()]
|
||||
if len(nums) >= 6:
|
||||
matrix_rows["B_below"] = nums[:8]
|
||||
|
||||
required = ["AAA", "AA", "A", "BBB", "BB", "B_below"]
|
||||
if not all(g in matrix_rows for g in required):
|
||||
return None
|
||||
|
||||
mat = np.zeros((6, 8))
|
||||
for idx, grade in enumerate(required):
|
||||
vals = matrix_rows[grade]
|
||||
for j in range(min(len(vals), 8)):
|
||||
mat[idx, j] = vals[j]
|
||||
|
||||
for idx in range(6):
|
||||
s = mat[idx].sum()
|
||||
if s < 30 or s > 110:
|
||||
return None
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
# ============================================================
|
||||
# KR 텍스트 기반 파서 (공백 분리 숫자 + 대시)
|
||||
# ============================================================
|
||||
def parse_kr(pdf_path: Path) -> Dict[int, np.ndarray]:
|
||||
"""KR PDF — space-separated numbers, dashes for zeros, always 8 columns"""
|
||||
matrices = {}
|
||||
pdf = pdfplumber.open(pdf_path)
|
||||
|
||||
full_text = ""
|
||||
for page in pdf.pages:
|
||||
text = page.extract_text()
|
||||
if text:
|
||||
full_text += text + "\n"
|
||||
pdf.close()
|
||||
|
||||
lines = full_text.split('\n')
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i].strip()
|
||||
|
||||
year_match = re.match(r'^(\d{4})[^\d~]', line)
|
||||
if year_match and '~' not in line:
|
||||
year = int(year_match.group(1))
|
||||
if 1998 <= year <= 2025:
|
||||
block = lines[i:i+20]
|
||||
matrix = _extract_kr_matrix(block)
|
||||
if matrix is not None:
|
||||
matrices[year] = matrix
|
||||
i += 1
|
||||
|
||||
return matrices
|
||||
|
||||
|
||||
def _extract_kr_matrix(block_lines: List[str]) -> Optional[np.ndarray]:
|
||||
"""KR에서 6×8 행렬 추출 (dash + space-separated nums)"""
|
||||
matrix_rows = {}
|
||||
|
||||
for line in block_lines:
|
||||
stripped = line.strip()
|
||||
grade = None
|
||||
rest = None
|
||||
|
||||
for g in ["AAA", "BBB", "BB"]:
|
||||
pat = re.match(rf'^{g}\s+(.*)', stripped)
|
||||
if pat:
|
||||
grade = g
|
||||
rest = pat.group(1)
|
||||
break
|
||||
|
||||
if grade is None:
|
||||
pat = re.match(r'^AA\s+(?!A)(.*)', stripped)
|
||||
if pat:
|
||||
grade = "AA"
|
||||
rest = pat.group(1)
|
||||
|
||||
if grade is None:
|
||||
pat = re.match(r'^A\s+(?!A)(.*)', stripped)
|
||||
if pat:
|
||||
grade = "A"
|
||||
rest = pat.group(1)
|
||||
|
||||
if grade is None:
|
||||
# B이하 — B + non-ascii
|
||||
pat = re.match(r'^B[^\w\s]?\s*(.*)', stripped)
|
||||
if pat and not stripped.startswith("BB") and not stripped.startswith("BBB"):
|
||||
rest_raw = pat.group(1)
|
||||
rest_cleaned = re.sub(r'^[^\d\s.-]+\s*', '', rest_raw)
|
||||
if rest_cleaned and (re.search(r'\d', rest_cleaned) or '-' in rest_cleaned):
|
||||
grade = "B_below"
|
||||
rest = rest_cleaned
|
||||
|
||||
if grade is None or rest is None or grade in matrix_rows:
|
||||
continue
|
||||
|
||||
values = _parse_kr_numbers(rest)
|
||||
if values is not None and len(values) == 8:
|
||||
matrix_rows[grade] = values
|
||||
|
||||
required = ["AAA", "AA", "A", "BBB", "BB", "B_below"]
|
||||
if not all(g in matrix_rows for g in required):
|
||||
return None
|
||||
|
||||
mat = np.zeros((6, 8))
|
||||
for idx, grade in enumerate(required):
|
||||
mat[idx] = matrix_rows[grade]
|
||||
|
||||
for idx in range(6):
|
||||
s = mat[idx].sum()
|
||||
if s < 30 or s > 110:
|
||||
return None
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
def _parse_kr_numbers(s: str) -> Optional[List[float]]:
|
||||
"""KR 숫자열 파싱 — 8개 토큰 (숫자 or 대시)"""
|
||||
s = s.strip()
|
||||
if not s:
|
||||
return None
|
||||
|
||||
results = []
|
||||
pos = 0
|
||||
n = len(s)
|
||||
|
||||
while pos < n and len(results) < 8:
|
||||
# 공백 스킵
|
||||
while pos < n and s[pos] == ' ':
|
||||
pos += 1
|
||||
if pos >= n:
|
||||
break
|
||||
|
||||
# 대시 → 0
|
||||
if s[pos] == '-':
|
||||
results.append(0.0)
|
||||
pos += 1
|
||||
continue
|
||||
|
||||
# 소수점 포함 숫자 찾기
|
||||
dot_pos = None
|
||||
scan = pos
|
||||
while scan < n:
|
||||
if s[scan] == '.':
|
||||
dot_pos = scan
|
||||
break
|
||||
elif s[scan] in '0123456789 ':
|
||||
scan += 1
|
||||
else:
|
||||
break
|
||||
|
||||
if dot_pos is None:
|
||||
# 숫자만 있는 경우
|
||||
num_str = ''
|
||||
while pos < n and s[pos].isdigit():
|
||||
num_str += s[pos]
|
||||
pos += 1
|
||||
if num_str:
|
||||
results.append(float(num_str))
|
||||
elif pos < n:
|
||||
pos += 1
|
||||
continue
|
||||
|
||||
int_part = ''.join(c for c in s[pos:dot_pos] if c.isdigit())
|
||||
dec_part = ''
|
||||
j = dot_pos + 1
|
||||
while j < n and len(dec_part) < 2:
|
||||
if s[j].isdigit():
|
||||
dec_part += s[j]
|
||||
j += 1
|
||||
elif s[j] == ' ':
|
||||
j += 1
|
||||
else:
|
||||
break
|
||||
|
||||
int_part = int_part or '0'
|
||||
dec_part = dec_part or '0'
|
||||
results.append(float(f"{int_part}.{dec_part}"))
|
||||
pos = max(j, pos + 1)
|
||||
|
||||
return results if len(results) == 8 else None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 후처리: 6x8 -> 7x7 (WR->D 보정 + CCC 제거)
|
||||
# ============================================================
|
||||
|
||||
_BROAD_GRADE_MAP_6 = {0: "AAA", 1: "AA", 2: "A", 3: "BBB", 4: "BB", 5: "B"}
|
||||
|
||||
|
||||
def postprocess_matrix(raw_6x8, pd_floors=None):
|
||||
"""6x8 (AAA~B이하 x AAA~WR+D) -> 7x7 (AAA~B+D)
|
||||
|
||||
Steps:
|
||||
1. PD floor correction: if observed PD < floor, transfer from WR to D
|
||||
2. Remaining WR -> proportional redistribution
|
||||
3. B이하 -> B mapping
|
||||
4. Add D row (absorbing state)
|
||||
5. Normalize rows to sum=1
|
||||
"""
|
||||
assert raw_6x8.shape == (6, 8), f"Expected (6,8), got {raw_6x8.shape}"
|
||||
|
||||
mat = raw_6x8.copy()
|
||||
COL_D = 6
|
||||
COL_WR = 7
|
||||
|
||||
# Step 1: PD floor correction (WR -> D transfer)
|
||||
if pd_floors is not None:
|
||||
for i in range(6):
|
||||
broad = _BROAD_GRADE_MAP_6[i]
|
||||
if broad not in pd_floors:
|
||||
continue
|
||||
floor_pct = pd_floors[broad] * 100 # decimal -> %
|
||||
observed_pd = mat[i, COL_D]
|
||||
wr_available = mat[i, COL_WR]
|
||||
if observed_pd < floor_pct and wr_available > 0:
|
||||
deficit = floor_pct - observed_pd
|
||||
transfer = min(deficit, wr_available)
|
||||
mat[i, COL_D] += transfer
|
||||
mat[i, COL_WR] -= transfer
|
||||
|
||||
# Step 2: Remaining WR -> proportional redistribution
|
||||
for i in range(6):
|
||||
wr_remaining = mat[i, COL_WR]
|
||||
if wr_remaining > 0:
|
||||
non_wr_cols = mat[i, :7]
|
||||
non_wr_sum = non_wr_cols.sum()
|
||||
if non_wr_sum > 0:
|
||||
mat[i, :7] = non_wr_cols * (non_wr_sum + wr_remaining) / non_wr_sum
|
||||
mat[i, COL_WR] = 0.0
|
||||
|
||||
# Step 3: B이하 -> B mapping + build 7x7
|
||||
mat_7x7 = np.zeros((7, 7))
|
||||
for i in range(6):
|
||||
for j in range(6):
|
||||
mat_7x7[i, j] = mat[i, j]
|
||||
mat_7x7[i, 6] = mat[i, COL_D]
|
||||
|
||||
# Step 4: D row (absorbing state)
|
||||
mat_7x7[6, :] = 0.0
|
||||
mat_7x7[6, 6] = 100.0
|
||||
|
||||
# Step 5: Convert to probability and normalize
|
||||
mat_7x7 /= 100.0
|
||||
for i in range(7):
|
||||
s = mat_7x7[i].sum()
|
||||
if s > 0:
|
||||
mat_7x7[i] /= s
|
||||
|
||||
return mat_7x7
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 메인
|
||||
# ============================================================
|
||||
def main():
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Load PD floors
|
||||
broad_floors = None
|
||||
try:
|
||||
import sys as _sys
|
||||
_sys.path.insert(0, str(BASE_DIR))
|
||||
from data.pd_floor import build_complete_pd_floor_table
|
||||
broad_floors, _, _ = build_complete_pd_floor_table()
|
||||
print(f" PD floor loaded: {', '.join(f'{g}={v*10000:.1f}bp' for g, v in broad_floors.items())}")
|
||||
except Exception as e:
|
||||
print(f" PD floor load failed ({e}), proceeding without floor")
|
||||
|
||||
all_matrices = {}
|
||||
|
||||
for agency, pdf_path in PDF_FILES.items():
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Parsing: {agency} ({pdf_path.name})")
|
||||
print(f"{'='*60}")
|
||||
|
||||
if not pdf_path.exists():
|
||||
print(f" ERROR: File not found")
|
||||
continue
|
||||
|
||||
# 파서 선택
|
||||
if agency == "NICE":
|
||||
raw = parse_nice(pdf_path)
|
||||
elif agency == "KR":
|
||||
raw = parse_kr(pdf_path)
|
||||
else: # SCI
|
||||
raw = parse_via_tables(pdf_path)
|
||||
|
||||
print(f" Extracted {len(raw)} matrices: {sorted(raw.keys())}")
|
||||
|
||||
# 샘플 출력
|
||||
for sample_year in [1998, 2009, 2025]:
|
||||
if sample_year in raw:
|
||||
labels = ["AAA", "AA", "A", "BBB", "BB", "B_below"]
|
||||
print(f"\n Raw {sample_year}:")
|
||||
for idx, g in enumerate(labels):
|
||||
print(f" {g:>7}: [{', '.join(f'{v:7.2f}' for v in raw[sample_year][idx])}]")
|
||||
|
||||
# 후처리 + CSV 저장
|
||||
processed = {}
|
||||
for year, raw_mat in sorted(raw.items()):
|
||||
try:
|
||||
processed[year] = postprocess_matrix(raw_mat, pd_floors=broad_floors)
|
||||
except Exception as e:
|
||||
print(f" ERROR {year}: {e}")
|
||||
|
||||
all_matrices[agency] = processed
|
||||
print(f" Processed {len(processed)} matrices")
|
||||
|
||||
for year, mat in processed.items():
|
||||
df = pd.DataFrame(mat, index=MODEL_GRADES_7, columns=MODEL_GRADES_7)
|
||||
df.to_csv(OUTPUT_DIR / f"{agency}_{year}.csv", float_format="%.6f")
|
||||
|
||||
# 3사 평균
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Computing 3-agency average")
|
||||
print(f"{'='*60}")
|
||||
|
||||
agency_names = list(all_matrices.keys())
|
||||
common_years = sorted(set.intersection(
|
||||
*[set(all_matrices[a].keys()) for a in agency_names]
|
||||
)) if len(agency_names) >= 2 else []
|
||||
|
||||
print(f" Common years: {len(common_years)}")
|
||||
if common_years:
|
||||
print(f" Range: {common_years[0]}~{common_years[-1]}")
|
||||
|
||||
for year in common_years:
|
||||
avg = np.mean([all_matrices[a][year] for a in agency_names], axis=0)
|
||||
for i in range(7):
|
||||
s = avg[i].sum()
|
||||
if s > 0:
|
||||
avg[i] /= s
|
||||
df = pd.DataFrame(avg, index=MODEL_GRADES_7, columns=MODEL_GRADES_7)
|
||||
df.to_csv(OUTPUT_DIR / f"AVG_{year}.csv", float_format="%.6f")
|
||||
|
||||
# PD 요약
|
||||
print(f"\n{'='*60}")
|
||||
print(f" PD Summary")
|
||||
print(f"{'='*60}")
|
||||
|
||||
print(f"\n {'':>6}", end='')
|
||||
for a in agency_names:
|
||||
print(f" {a:>10}", end='')
|
||||
if common_years:
|
||||
print(f" {'AVG':>10}", end='')
|
||||
print()
|
||||
|
||||
for sample_year in [2000, 2009, 2020, 2025]:
|
||||
if sample_year not in common_years and not any(sample_year in all_matrices[a] for a in agency_names):
|
||||
continue
|
||||
print(f"\n Year {sample_year}:")
|
||||
for gi, grade in enumerate(MODEL_GRADES_7[:-1]):
|
||||
print(f" {grade:>5}:", end='')
|
||||
for a in agency_names:
|
||||
if sample_year in all_matrices[a]:
|
||||
pd_val = all_matrices[a][sample_year][gi, -1] * 100
|
||||
print(f" {pd_val:9.3f}%", end='')
|
||||
else:
|
||||
print(f" {'N/A':>10}", end='')
|
||||
if sample_year in common_years:
|
||||
avg_f = OUTPUT_DIR / f"AVG_{sample_year}.csv"
|
||||
avg_df = pd.read_csv(avg_f, index_col=0)
|
||||
print(f" {avg_df.loc[grade, 'D']*100:9.3f}%", end='')
|
||||
print()
|
||||
|
||||
print(f"\n Output: {OUTPUT_DIR}")
|
||||
print(f" Total CSV files: {len(list(OUTPUT_DIR.glob('*.csv')))}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
427
data/pd_floor.py
Normal file
427
data/pd_floor.py
Normal file
@@ -0,0 +1,427 @@
|
||||
"""
|
||||
시장 YTM 기반 PD 플로어 산출 모듈
|
||||
|
||||
KAP(한국자산평가) 등급별 회사채 수익률에서 신용스프레드를 추출하고,
|
||||
시장 내재 부도율(PD)을 산출한 뒤, AAA=5bp 앵커링 + 로그 스케일링으로
|
||||
각 등급의 PD 플로어를 결정합니다.
|
||||
|
||||
핵심 공식:
|
||||
PD_raw = spread / (1 - recovery_rate) [LGD = 1 - RR]
|
||||
scale = log(anchor_AAA) / log(PD_raw_AAA)
|
||||
PD_floor(g) = exp(log(PD_raw(g)) × scale)
|
||||
|
||||
참고:
|
||||
- Basel III CRE30.4: 기업 PD 플로어 5bp (0.05%)
|
||||
- 한국 시장 회수율: 약 40% (LGD = 60%)
|
||||
- docs/pd_floor_reference.md 참조
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import logging
|
||||
from typing import Dict, Optional, Tuple, List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 기본 파라미터
|
||||
DEFAULT_RECOVERY_RATE = 0.40 # 회수율 40% → LGD 60%
|
||||
DEFAULT_AAA_ANCHOR = 0.0005 # 5bp = 0.05% = 0.0005
|
||||
DEFAULT_LGD = 1 - DEFAULT_RECOVERY_RATE # 0.60
|
||||
|
||||
# 대등급 순서 (인덱스 기반 외삽에 사용)
|
||||
BROAD_GRADE_ORDER = ["AAA", "AA", "A", "BBB", "BB", "B"]
|
||||
BROAD_GRADE_INDEX = {g: i for i, g in enumerate(BROAD_GRADE_ORDER)}
|
||||
|
||||
|
||||
def compute_market_implied_pd(
|
||||
spread_bp: float,
|
||||
lgd: float = DEFAULT_LGD
|
||||
) -> float:
|
||||
"""
|
||||
신용스프레드에서 시장 내재 PD 산출
|
||||
|
||||
PD ≈ spread / LGD
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spread_bp : float
|
||||
신용스프레드 (bp, 1bp = 0.01%)
|
||||
lgd : float
|
||||
Loss Given Default (기본: 0.60)
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
시장 내재 PD (소수, 예: 0.000727 = 7.27bp)
|
||||
"""
|
||||
spread_decimal = spread_bp / 10000 # bp → 소수
|
||||
return spread_decimal / lgd
|
||||
|
||||
|
||||
def compute_pd_floors(
|
||||
spreads_bp: Dict[str, float],
|
||||
anchor_aaa: float = DEFAULT_AAA_ANCHOR,
|
||||
lgd: float = DEFAULT_LGD
|
||||
) -> Dict[str, float]:
|
||||
"""
|
||||
AAA=5bp 앵커링 + 로그 스케일링으로 등급별 PD 플로어 산출
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spreads_bp : Dict[str, float]
|
||||
등급별 신용스프레드 (bp). 대등급(AAA, AA, A, BBB) 기준.
|
||||
anchor_aaa : float
|
||||
AAA 앵커 PD (기본: 5bp = 0.0005)
|
||||
lgd : float
|
||||
Loss Given Default (기본: 0.60)
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[str, float]
|
||||
등급별 PD 플로어 (소수). 예: {'AAA': 0.0005, 'AA': 0.00057, ...}
|
||||
"""
|
||||
# Step 1: 시장 내재 PD 산출
|
||||
pd_raw = {}
|
||||
for grade, spread in spreads_bp.items():
|
||||
pd_raw[grade] = compute_market_implied_pd(spread, lgd)
|
||||
|
||||
if "AAA" not in pd_raw or pd_raw["AAA"] <= 0:
|
||||
raise ValueError("AAA 스프레드가 0 이하입니다. 데이터를 확인하세요.")
|
||||
|
||||
# Step 2: 로그 스케일링
|
||||
# scale = log(anchor) / log(pd_raw_AAA)
|
||||
log_anchor = np.log(anchor_aaa)
|
||||
log_aaa_raw = np.log(pd_raw["AAA"])
|
||||
scale = log_anchor / log_aaa_raw
|
||||
|
||||
pd_floors = {}
|
||||
for grade, pd_val in pd_raw.items():
|
||||
pd_floors[grade] = np.exp(np.log(pd_val) * scale)
|
||||
|
||||
logger.info(f"PD 플로어 스케일링: scale={scale:.4f}, "
|
||||
f"AAA raw={pd_raw['AAA']*10000:.1f}bp → floor={pd_floors['AAA']*10000:.1f}bp")
|
||||
|
||||
return pd_floors
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 기본 PD 플로어 (시장 데이터 없이 사용 가능)
|
||||
# ============================================================
|
||||
# 근거:
|
||||
# AAA = 5bp : Basel III CRE30.4 규제 플로어 (2023 개정, 기업 IRB)
|
||||
# AA = 5bp : Basel III 최저선 + S&P 장기평균 2bp + Moody's 5bp 중간값
|
||||
# A = 7bp : S&P 장기평균 5bp + Moody's 9bp 중간값
|
||||
# BBB = 20bp: S&P 15bp + Moody's 26bp 중간값, 한국 BBB 관측 27bp와 정합
|
||||
# BB = 60bp: S&P 56~63bp 범위, 관측치 사용 (floor 불필요)
|
||||
# B = 300bp: S&P 293~334bp 범위, 관측치 사용 (floor 불필요)
|
||||
#
|
||||
# 참고문헌:
|
||||
# [1] Basel Committee, CRE30.4: "PD shall not be less than 0.05%"
|
||||
# [2] S&P Global, "2023 Annual Default and Transition Study"
|
||||
# [3] Moody's, "Annual Default Study" (1920-2023)
|
||||
# [4] 금융감독원, 신용평가공시 (한국기업평가 1998-2025)
|
||||
DEFAULT_PD_FLOORS = {
|
||||
"AAA": 0.0005, # 5bp — Basel III CRE30.4
|
||||
"AA": 0.0005, # 5bp — Basel III 최저선
|
||||
"A": 0.0007, # 7bp — S&P/Moody's 중간값
|
||||
"BBB": 0.0020, # 20bp — S&P/Moody's 중간값
|
||||
"BB": 0.0060, # 60bp — 관측치 수준 (floor 미적용)
|
||||
"B": 0.0300, # 300bp — 관측치 수준 (floor 미적용)
|
||||
}
|
||||
|
||||
# 7×7 행렬용 (CCC 제외)
|
||||
GRADES_7 = ["AAA", "AA", "A", "BBB", "BB", "B"]
|
||||
|
||||
|
||||
def get_default_pd_floors() -> Dict[str, float]:
|
||||
"""기본 PD 플로어 반환 (Basel III + S&P/Moody's 근거)"""
|
||||
return DEFAULT_PD_FLOORS.copy()
|
||||
|
||||
|
||||
def apply_pd_floor_to_matrices(
|
||||
matrices: Dict[int, 'np.ndarray'],
|
||||
pd_floors: Optional[Dict[str, float]] = None,
|
||||
grades: Optional[List[str]] = None
|
||||
) -> Dict[int, 'np.ndarray']:
|
||||
"""
|
||||
전이행렬의 D열(부도 전이확률)에 PD 플로어 적용
|
||||
|
||||
로직:
|
||||
1. 각 등급의 TM[i, D] < floor[i] 이면
|
||||
2. TM[i, D] = floor[i] 로 상향
|
||||
3. 초과분(delta)을 TM[i, i] (대각선)에서 차감
|
||||
4. 행 합 = 1.0 유지
|
||||
|
||||
이론적 근거:
|
||||
- 한국 투자적격등급(AAA~A) 부도 관측치 = 0%
|
||||
- 0%는 "위험 없음"이 아니라 "관측 불가능한 확률"
|
||||
- Basel III CRE30.4: 기업 PD ≥ 5bp (0.05%)
|
||||
- S&P/Moody's 글로벌 장기평균으로 보정
|
||||
|
||||
Parameters
|
||||
----------
|
||||
matrices : Dict[int, np.ndarray]
|
||||
연도별 전이행렬 (N×N, 마지막 열 = D)
|
||||
pd_floors : Dict[str, float], optional
|
||||
등급별 최소 PD (기본: DEFAULT_PD_FLOORS)
|
||||
grades : List[str], optional
|
||||
등급 레이블 (기본: AAA~B for 7×7)
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[int, np.ndarray]
|
||||
PD 플로어 적용된 전이행렬 (새 복사본)
|
||||
"""
|
||||
if pd_floors is None:
|
||||
pd_floors = DEFAULT_PD_FLOORS
|
||||
if grades is None:
|
||||
grades = GRADES_7
|
||||
|
||||
calibrated = {}
|
||||
for year, tm in matrices.items():
|
||||
tm_new = tm.copy()
|
||||
n = tm_new.shape[0]
|
||||
d_col = n - 1 # 마지막 열 = D
|
||||
|
||||
for i in range(n - 1): # D행은 제외 (흡수상태)
|
||||
grade = grades[i] if i < len(grades) else None
|
||||
if grade and grade in pd_floors:
|
||||
floor = pd_floors[grade]
|
||||
observed_pd = tm_new[i, d_col]
|
||||
|
||||
if observed_pd < floor:
|
||||
delta = floor - observed_pd
|
||||
tm_new[i, d_col] = floor
|
||||
# 대각선(유지확률)에서 차감
|
||||
tm_new[i, i] = max(tm_new[i, i] - delta, 0.0)
|
||||
|
||||
# 행 합 재정규화 (안전장치)
|
||||
row_sum = tm_new[i].sum()
|
||||
if row_sum > 0:
|
||||
tm_new[i] /= row_sum
|
||||
|
||||
calibrated[year] = tm_new
|
||||
|
||||
logger.info(f"PD 플로어 적용 완료: {len(calibrated)}개 연도")
|
||||
return calibrated
|
||||
|
||||
|
||||
def extrapolate_speculative_grades(
|
||||
pd_floors: Dict[str, float],
|
||||
grades_to_extrapolate: List[str] = ["BB", "B"]
|
||||
) -> Dict[str, float]:
|
||||
"""
|
||||
투자적격등급(AAA~BBB)의 로그 트렌드를 외삽하여 투기등급 PD 산출
|
||||
|
||||
log(PD) vs grade_index의 선형 트렌드를 BBB 이후로 연장합니다.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pd_floors : Dict[str, float]
|
||||
투자적격등급 PD 플로어 (AAA~BBB)
|
||||
grades_to_extrapolate : List[str]
|
||||
외삽할 등급 (기본: ["BB", "B"])
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[str, float]
|
||||
투기등급이 추가된 PD 플로어
|
||||
"""
|
||||
# 기존 투자적격 등급의 log(PD) vs index 관계
|
||||
available = []
|
||||
for g in BROAD_GRADE_ORDER:
|
||||
if g in pd_floors:
|
||||
available.append((BROAD_GRADE_INDEX[g], np.log(pd_floors[g])))
|
||||
|
||||
if len(available) < 2:
|
||||
raise ValueError("외삽을 위해 최소 2개 등급의 PD가 필요합니다.")
|
||||
|
||||
# 선형 회귀: log(PD) = a × index + b
|
||||
x = np.array([a[0] for a in available])
|
||||
y = np.array([a[1] for a in available])
|
||||
coeffs = np.polyfit(x, y, 1) # [기울기, 절편]
|
||||
|
||||
result = pd_floors.copy()
|
||||
for grade in grades_to_extrapolate:
|
||||
if grade in BROAD_GRADE_INDEX:
|
||||
idx = BROAD_GRADE_INDEX[grade]
|
||||
log_pd = coeffs[0] * idx + coeffs[1]
|
||||
result[grade] = np.exp(log_pd)
|
||||
logger.info(f"외삽 PD [{grade}]: {result[grade]*10000:.1f}bp "
|
||||
f"(index={idx}, log_pd={log_pd:.4f})")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def interpolate_ccc_pd(
|
||||
pd_b: float,
|
||||
pd_d: float = 1.0,
|
||||
method: str = "geometric"
|
||||
) -> Dict[str, float]:
|
||||
"""
|
||||
B와 D(=100%) 사이를 보간하여 CCC/CC/C 등급 PD 산출
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pd_b : float
|
||||
B등급 PD (소수)
|
||||
pd_d : float
|
||||
D등급 PD (기본: 1.0 = 100%)
|
||||
method : str
|
||||
'geometric': 기하평균
|
||||
'log': 로그 등간격 보간
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[str, float]
|
||||
{'CCC': ..., 'CC': ..., 'C': ...}
|
||||
"""
|
||||
if method == "geometric":
|
||||
# B → CCC → CC → C → D 순서로 기하적 등간격
|
||||
# 4개 구간 (B→CCC, CCC→CC, CC→C, C→D)
|
||||
log_b = np.log(pd_b)
|
||||
log_d = np.log(pd_d)
|
||||
step = (log_d - log_b) / 4
|
||||
|
||||
ccc_pd = np.exp(log_b + step * 1)
|
||||
cc_pd = np.exp(log_b + step * 2)
|
||||
c_pd = np.exp(log_b + step * 3)
|
||||
else: # log-linear
|
||||
log_b = np.log(pd_b)
|
||||
log_d = np.log(pd_d)
|
||||
step = (log_d - log_b) / 4
|
||||
|
||||
ccc_pd = np.exp(log_b + step * 1)
|
||||
cc_pd = np.exp(log_b + step * 2)
|
||||
c_pd = np.exp(log_b + step * 3)
|
||||
|
||||
return {"CCC": ccc_pd, "CC": cc_pd, "C": c_pd}
|
||||
|
||||
|
||||
def compute_notch_pd_ratios(
|
||||
notch_spreads: Dict[str, float],
|
||||
lgd: float = DEFAULT_LGD
|
||||
) -> Dict[str, Dict[str, float]]:
|
||||
"""
|
||||
노치등급 간 PD 비율 산출 (나중에 등급 쪼개기에 사용)
|
||||
|
||||
예: AA+ : AA : AA- = 0.91 : 1.00 : 1.07 (AA 대비 비율)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
notch_spreads : Dict[str, float]
|
||||
노치등급별 스프레드 (bp)
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[str, Dict[str, float]]
|
||||
대등급별 {노치등급: 비율} 딕셔너리
|
||||
예: {'AA': {'AA+': 0.91, 'AA': 1.00, 'AA-': 1.07}}
|
||||
"""
|
||||
from data.ytm_fetcher import NOTCH_TO_BROAD
|
||||
from collections import defaultdict
|
||||
|
||||
# 대등급별 노치 그룹핑
|
||||
groups = defaultdict(dict)
|
||||
for notch, spread in notch_spreads.items():
|
||||
broad = NOTCH_TO_BROAD.get(notch, notch)
|
||||
groups[broad][notch] = compute_market_implied_pd(spread, lgd)
|
||||
|
||||
# 중간 노치 대비 비율 계산
|
||||
ratios = {}
|
||||
for broad, notch_pds in groups.items():
|
||||
if len(notch_pds) == 1:
|
||||
key = list(notch_pds.keys())[0]
|
||||
ratios[broad] = {key: 1.0}
|
||||
else:
|
||||
# 중간 노치(plain) 기준
|
||||
mid_key = broad # AA+ → AA가 중간
|
||||
if mid_key in notch_pds:
|
||||
mid_pd = notch_pds[mid_key]
|
||||
else:
|
||||
mid_pd = sorted(notch_pds.values())[len(notch_pds) // 2]
|
||||
|
||||
ratios[broad] = {
|
||||
notch: pd / mid_pd for notch, pd in notch_pds.items()
|
||||
}
|
||||
|
||||
return ratios
|
||||
|
||||
|
||||
def build_complete_pd_floor_table(
|
||||
date: str = "2025-12-31",
|
||||
anchor_aaa: float = DEFAULT_AAA_ANCHOR,
|
||||
recovery_rate: float = DEFAULT_RECOVERY_RATE
|
||||
) -> Tuple[Dict[str, float], Dict[str, float], Dict[str, Dict[str, float]]]:
|
||||
"""
|
||||
전체 PD 플로어 테이블 생성 (원스톱)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
date : str
|
||||
YTM 기준일
|
||||
anchor_aaa : float
|
||||
AAA 앵커 PD (기본: 5bp)
|
||||
recovery_rate : float
|
||||
회수율 (기본: 0.40)
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple of:
|
||||
broad_floors : Dict[str, float] - 대등급 PD 플로어 (AAA~B+D)
|
||||
notch_ratios : Dict[str, Dict[str, float]] - 노치 비율
|
||||
full_floors : Dict[str, float] - CCC/CC/C 포함 전체 PD (AAA~C+D)
|
||||
"""
|
||||
from data.ytm_fetcher import get_ytm_data, compute_spreads, compute_broad_grade_spreads
|
||||
|
||||
lgd = 1 - recovery_rate
|
||||
|
||||
# 1) YTM 데이터 수집
|
||||
ytm_data = get_ytm_data(date)
|
||||
notch_spreads = compute_spreads(ytm_data)
|
||||
broad_spreads = compute_broad_grade_spreads(notch_spreads)
|
||||
|
||||
logger.info(f"대등급 스프레드: {broad_spreads}")
|
||||
|
||||
# 2) PD 플로어 산출 (투자적격)
|
||||
pd_floors = compute_pd_floors(broad_spreads, anchor_aaa, lgd)
|
||||
|
||||
# 3) 투기등급 외삽
|
||||
pd_floors = extrapolate_speculative_grades(pd_floors, ["BB", "B"])
|
||||
|
||||
# 4) CCC/CC/C 보간
|
||||
ccc_pds = interpolate_ccc_pd(pd_floors["B"], pd_d=1.0, method="geometric")
|
||||
full_floors = {**pd_floors, **ccc_pds, "D": 1.0}
|
||||
|
||||
# 5) 노치 비율
|
||||
notch_ratios = compute_notch_pd_ratios(notch_spreads, lgd)
|
||||
|
||||
return pd_floors, notch_ratios, full_floors
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
|
||||
print("=" * 60)
|
||||
print(" PD 플로어 산출 (KAP YTM 기반)")
|
||||
print("=" * 60)
|
||||
|
||||
broad_floors, notch_ratios, full_floors = build_complete_pd_floor_table()
|
||||
|
||||
print("\n=== 대등급 PD 플로어 ===")
|
||||
for grade in BROAD_GRADE_ORDER:
|
||||
if grade in broad_floors:
|
||||
print(f" {grade:>4}: {broad_floors[grade]*10000:8.1f}bp "
|
||||
f"({broad_floors[grade]*100:.4f}%)")
|
||||
|
||||
print("\n=== 전체 PD 플로어 (CCC/CC/C 포함) ===")
|
||||
order = ["AAA", "AA", "A", "BBB", "BB", "B", "CCC", "CC", "C", "D"]
|
||||
for grade in order:
|
||||
if grade in full_floors:
|
||||
print(f" {grade:>4}: {full_floors[grade]*10000:8.1f}bp "
|
||||
f"({full_floors[grade]*100:.4f}%)")
|
||||
|
||||
print("\n=== 노치 PD 비율 ===")
|
||||
for broad, ratios in notch_ratios.items():
|
||||
print(f" {broad}:")
|
||||
for notch, ratio in sorted(ratios.items()):
|
||||
print(f" {notch:>5}: ×{ratio:.3f}")
|
||||
9
data/real/KR_1998.csv
Normal file
9
data/real/KR_1998.csv
Normal file
@@ -0,0 +1,9 @@
|
||||
,AAA,AA,A,BBB,BB,B,CCC,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.444444,0.444444,0.111111,0.000000,0.000000,0.000000,0.000000
|
||||
A,0.000000,0.000000,0.636374,0.363626,0.000000,0.000000,0.000000,0.000000
|
||||
BBB,0.000000,0.000000,0.052632,0.787974,0.105263,0.051132,0.003000,0.000000
|
||||
BB,0.000000,0.000000,0.000000,0.107188,0.464290,0.257113,0.028568,0.142841
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.425000,0.075000,0.500000
|
||||
CCC,0.000000,0.000000,0.001000,0.002000,0.003000,0.020000,0.224000,0.750000
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_1998.csv
Normal file
8
data/real_v2/AVG_1998.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.576715,0.338615,0.084670,0.000000,0.000000,0.000000
|
||||
A,0.000000,0.000000,0.558401,0.439905,0.000000,0.000000,0.001694
|
||||
BBB,0.000000,0.000000,0.017374,0.694019,0.205669,0.072355,0.010583
|
||||
BB,0.000000,0.000000,0.000000,0.068283,0.515874,0.272203,0.143640
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.044442,0.561128,0.394430
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_1999.csv
Normal file
8
data/real_v2/AVG_1999.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.833153,0.166667,0.000000,0.000000,0.000000,0.000000,0.000180
|
||||
AA,0.000000,0.933083,0.066667,0.000000,0.000000,0.000000,0.000250
|
||||
A,0.000000,0.061671,0.736238,0.117068,0.000000,0.083367,0.001657
|
||||
BBB,0.000000,0.000000,0.061712,0.790523,0.061392,0.075733,0.010639
|
||||
BB,0.000000,0.000000,0.000000,0.132042,0.780556,0.067587,0.019815
|
||||
B,0.000000,0.000000,0.000000,0.023801,0.090450,0.697614,0.188136
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2000.csv
Normal file
8
data/real_v2/AVG_2000.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999818,0.000000,0.000000,0.000000,0.000000,0.000000,0.000182
|
||||
AA,0.014482,0.970531,0.014482,0.000000,0.000000,0.000000,0.000506
|
||||
A,0.000000,0.121874,0.815821,0.047328,0.013325,0.000000,0.001652
|
||||
BBB,0.000000,0.000000,0.056796,0.877283,0.055767,0.000000,0.010154
|
||||
BB,0.000000,0.000000,0.000000,0.075598,0.859688,0.031391,0.033323
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.114805,0.786800,0.098395
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2001.csv
Normal file
8
data/real_v2/AVG_2001.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999594,0.000000,0.000000,0.000000,0.000000,0.000000,0.000406
|
||||
AA,0.061393,0.893062,0.030264,0.014482,0.000000,0.000000,0.000799
|
||||
A,0.000000,0.100866,0.860160,0.037238,0.000000,0.000000,0.001736
|
||||
BBB,0.000000,0.000000,0.067887,0.844886,0.060075,0.012198,0.014954
|
||||
BB,0.000000,0.000000,0.000000,0.062751,0.845112,0.042148,0.049989
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.160363,0.660413,0.179224
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2002.csv
Normal file
8
data/real_v2/AVG_2002.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999822,0.000000,0.000000,0.000000,0.000000,0.000000,0.000178
|
||||
AA,0.076293,0.851519,0.071357,0.000000,0.000000,0.000000,0.000831
|
||||
A,0.000000,0.114693,0.870532,0.013056,0.000000,0.000000,0.001719
|
||||
BBB,0.000000,0.000000,0.047149,0.902183,0.037742,0.000000,0.012925
|
||||
BB,0.000000,0.000000,0.000000,0.094675,0.725113,0.132275,0.047937
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.080869,0.705528,0.213603
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2003.csv
Normal file
8
data/real_v2/AVG_2003.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999623,0.000000,0.000000,0.000000,0.000000,0.000000,0.000377
|
||||
AA,0.085783,0.799804,0.113638,0.000000,0.000000,0.000000,0.000775
|
||||
A,0.000000,0.026005,0.908793,0.049032,0.000000,0.014547,0.001623
|
||||
BBB,0.000000,0.000000,0.077388,0.891533,0.020185,0.000000,0.010894
|
||||
BB,0.000000,0.000000,0.011905,0.073811,0.735706,0.128565,0.050014
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.913226,0.086774
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2004.csv
Normal file
8
data/real_v2/AVG_2004.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999421,0.000000,0.000000,0.000000,0.000000,0.000000,0.000579
|
||||
AA,0.081663,0.884953,0.032542,0.000000,0.000000,0.000000,0.000842
|
||||
A,0.000000,0.105243,0.855610,0.037377,0.000000,0.000000,0.001770
|
||||
BBB,0.000000,0.000000,0.117345,0.848821,0.022715,0.000000,0.011119
|
||||
BB,0.000000,0.000000,0.000000,0.180925,0.580971,0.033318,0.204786
|
||||
B,0.000000,0.000000,0.000000,0.067996,0.025653,0.691059,0.215292
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2005.csv
Normal file
8
data/real_v2/AVG_2005.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999457,0.000000,0.000000,0.000000,0.000000,0.000000,0.000543
|
||||
AA,0.027039,0.960219,0.011893,0.000000,0.000000,0.000000,0.000849
|
||||
A,0.000000,0.080582,0.899691,0.017936,0.000000,0.000000,0.001791
|
||||
BBB,0.000000,0.000000,0.112753,0.877154,0.000000,0.000000,0.010093
|
||||
BB,0.000000,0.000000,0.000000,0.170972,0.788720,0.016363,0.023944
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.834730,0.165270
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2006.csv
Normal file
8
data/real_v2/AVG_2006.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999644,0.000000,0.000000,0.000000,0.000000,0.000000,0.000356
|
||||
AA,0.039606,0.948886,0.010743,0.000000,0.000000,0.000000,0.000764
|
||||
A,0.000000,0.150281,0.848139,0.000000,0.000000,0.000000,0.001580
|
||||
BBB,0.000000,0.000000,0.098944,0.861139,0.009157,0.018314,0.012446
|
||||
BB,0.000000,0.000000,0.052306,0.112803,0.694598,0.114369,0.025923
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.012747,0.953307,0.033946
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2007.csv
Normal file
8
data/real_v2/AVG_2007.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999485,0.000000,0.000000,0.000000,0.000000,0.000000,0.000515
|
||||
AA,0.033360,0.926329,0.039534,0.000000,0.000000,0.000000,0.000777
|
||||
A,0.000000,0.030383,0.949557,0.018161,0.000000,0.000000,0.001899
|
||||
BBB,0.000000,0.000000,0.153141,0.817738,0.019109,0.000000,0.010013
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.765429,0.202865,0.031706
|
||||
B,0.000000,0.000000,0.020575,0.000000,0.034926,0.882136,0.062364
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2008.csv
Normal file
8
data/real_v2/AVG_2008.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999654,0.000000,0.000000,0.000000,0.000000,0.000000,0.000346
|
||||
AA,0.000000,0.969308,0.029932,0.000000,0.000000,0.000000,0.000760
|
||||
A,0.000000,0.031432,0.921828,0.045140,0.000000,0.000000,0.001601
|
||||
BBB,0.000000,0.000000,0.100712,0.788730,0.093978,0.000000,0.016580
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.832355,0.082881,0.084765
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.926002,0.073998
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2009.csv
Normal file
8
data/real_v2/AVG_2009.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999480,0.000000,0.000000,0.000000,0.000000,0.000000,0.000520
|
||||
AA,0.009649,0.989604,0.000000,0.000000,0.000000,0.000000,0.000748
|
||||
A,0.000000,0.104282,0.874984,0.019158,0.000000,0.000000,0.001576
|
||||
BBB,0.000000,0.000000,0.067420,0.847866,0.000000,0.072487,0.012228
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.672167,0.183389,0.144445
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.868943,0.131057
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2010.csv
Normal file
8
data/real_v2/AVG_2010.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999490,0.000000,0.000000,0.000000,0.000000,0.000000,0.000510
|
||||
AA,0.016599,0.982653,0.000000,0.000000,0.000000,0.000000,0.000747
|
||||
A,0.000000,0.076025,0.915166,0.007150,0.000000,0.000000,0.001659
|
||||
BBB,0.000000,0.000000,0.150027,0.739609,0.000000,0.097626,0.012738
|
||||
BB,0.000000,0.000000,0.000000,0.087367,0.758454,0.037027,0.117152
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.042016,0.702267,0.255717
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2011.csv
Normal file
8
data/real_v2/AVG_2011.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999660,0.000000,0.000000,0.000000,0.000000,0.000000,0.000340
|
||||
AA,0.000000,0.999255,0.000000,0.000000,0.000000,0.000000,0.000745
|
||||
A,0.000000,0.080763,0.911025,0.000000,0.000000,0.006626,0.001587
|
||||
BBB,0.000000,0.000000,0.056721,0.899728,0.020130,0.000000,0.023421
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.946936,0.000000,0.053064
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.850017,0.149983
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2012.csv
Normal file
8
data/real_v2/AVG_2012.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999658,0.000000,0.000000,0.000000,0.000000,0.000000,0.000342
|
||||
AA,0.000000,0.996055,0.003191,0.000000,0.000000,0.000000,0.000754
|
||||
A,0.000000,0.045282,0.890911,0.057354,0.000000,0.000000,0.006454
|
||||
BBB,0.000000,0.000000,0.044093,0.854763,0.078891,0.000000,0.022252
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.872318,0.046550,0.081132
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.620364,0.379636
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2013.csv
Normal file
8
data/real_v2/AVG_2013.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999825,0.000000,0.000000,0.000000,0.000000,0.000000,0.000175
|
||||
AA,0.006134,0.973323,0.019797,0.000000,0.000000,0.000000,0.000745
|
||||
A,0.000000,0.060975,0.894681,0.042767,0.000000,0.000000,0.001577
|
||||
BBB,0.000000,0.000000,0.025036,0.835241,0.000000,0.099481,0.040243
|
||||
BB,0.000000,0.000000,0.000000,0.019608,0.633854,0.267478,0.079061
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.898849,0.101151
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2014.csv
Normal file
8
data/real_v2/AVG_2014.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.982214,0.017269,0.000000,0.000000,0.000000,0.000000,0.000517
|
||||
AA,0.000000,0.934398,0.064860,0.000000,0.000000,0.000000,0.000742
|
||||
A,0.000000,0.042644,0.911134,0.039359,0.000000,0.000000,0.006863
|
||||
BBB,0.000000,0.000000,0.048934,0.753031,0.094754,0.082267,0.021013
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.884298,0.048719,0.066982
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.931108,0.068892
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2015.csv
Normal file
8
data/real_v2/AVG_2015.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.987232,0.012224,0.000000,0.000000,0.000000,0.000000,0.000544
|
||||
AA,0.000000,0.941719,0.057547,0.000000,0.000000,0.000000,0.000734
|
||||
A,0.000000,0.044630,0.889102,0.044018,0.020540,0.000000,0.001710
|
||||
BBB,0.000000,0.000000,0.000000,0.875778,0.058053,0.054868,0.011301
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.826121,0.073481,0.100399
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.033357,0.819665,0.146978
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2016.csv
Normal file
8
data/real_v2/AVG_2016.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999484,0.000000,0.000000,0.000000,0.000000,0.000000,0.000516
|
||||
AA,0.000000,0.986124,0.013142,0.000000,0.000000,0.000000,0.000734
|
||||
A,0.000000,0.006441,0.967305,0.024658,0.000000,0.000000,0.001597
|
||||
BBB,0.000000,0.000000,0.103908,0.851404,0.033179,0.000000,0.011510
|
||||
BB,0.000000,0.000000,0.017561,0.000000,0.807022,0.122785,0.052632
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.890565,0.109435
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2017.csv
Normal file
8
data/real_v2/AVG_2017.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999661,0.000000,0.000000,0.000000,0.000000,0.000000,0.000339
|
||||
AA,0.002489,0.996775,0.000000,0.000000,0.000000,0.000000,0.000736
|
||||
A,0.000000,0.006869,0.944079,0.047271,0.000000,0.000000,0.001782
|
||||
BBB,0.000000,0.000000,0.035633,0.952001,0.000000,0.000000,0.012367
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.934255,0.042471,0.023274
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.035239,0.906300,0.058460
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2018.csv
Normal file
8
data/real_v2/AVG_2018.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.002560,0.989024,0.007664,0.000000,0.000000,0.000000,0.000752
|
||||
A,0.000000,0.031976,0.955626,0.010632,0.000000,0.000000,0.001766
|
||||
BBB,0.000000,0.000000,0.069339,0.916471,0.000000,0.000000,0.014191
|
||||
BB,0.000000,0.000000,0.000000,0.036186,0.895470,0.046550,0.021793
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.951458,0.048542
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2019.csv
Normal file
8
data/real_v2/AVG_2019.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.988297,0.011533,0.000000,0.000000,0.000000,0.000000,0.000169
|
||||
AA,0.000000,0.991779,0.007470,0.000000,0.000000,0.000000,0.000751
|
||||
A,0.000000,0.023290,0.955026,0.020028,0.000000,0.000000,0.001657
|
||||
BBB,0.000000,0.000000,0.056769,0.900917,0.031417,0.000000,0.010898
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.839737,0.088126,0.072138
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.854858,0.145142
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2020.csv
Normal file
8
data/real_v2/AVG_2020.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999492,0.000000,0.000000,0.000000,0.000000,0.000000,0.000508
|
||||
AA,0.000000,0.979560,0.019697,0.000000,0.000000,0.000000,0.000743
|
||||
A,0.000000,0.031319,0.948558,0.018531,0.000000,0.000000,0.001592
|
||||
BBB,0.000000,0.000000,0.000000,0.974538,0.015001,0.000000,0.010460
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.981982,0.000000,0.018018
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.950001,0.049999
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2021.csv
Normal file
8
data/real_v2/AVG_2021.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999483,0.000000,0.000000,0.000000,0.000000,0.000000,0.000517
|
||||
AA,0.000000,0.982452,0.016823,0.000000,0.000000,0.000000,0.000725
|
||||
A,0.000000,0.019672,0.978671,0.000000,0.000000,0.000000,0.001657
|
||||
BBB,0.000000,0.000000,0.045322,0.944085,0.000000,0.000000,0.010593
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.893078,0.085272,0.021650
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.035528,0.910028,0.054444
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2022.csv
Normal file
8
data/real_v2/AVG_2022.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999653,0.000000,0.000000,0.000000,0.000000,0.000000,0.000347
|
||||
AA,0.000000,0.988162,0.011103,0.000000,0.000000,0.000000,0.000734
|
||||
A,0.000000,0.023963,0.971352,0.003024,0.000000,0.000000,0.001661
|
||||
BBB,0.000000,0.000000,0.056005,0.925617,0.007683,0.000000,0.010695
|
||||
BB,0.000000,0.000000,0.000000,0.006403,0.927293,0.054591,0.011713
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.937747,0.062253
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2023.csv
Normal file
8
data/real_v2/AVG_2023.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.984881,0.014939,0.000000,0.000000,0.000000,0.000000,0.000180
|
||||
AA,0.012431,0.966915,0.019160,0.000345,0.000192,0.000192,0.000764
|
||||
A,0.000000,0.046798,0.924098,0.017846,0.000981,0.008184,0.002094
|
||||
BBB,0.000000,0.000324,0.121507,0.834928,0.015282,0.011934,0.016026
|
||||
BB,0.000000,0.000000,0.007813,0.059788,0.738123,0.096016,0.098260
|
||||
B,0.000000,0.000000,0.000000,0.009755,0.004878,0.747686,0.237681
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2024.csv
Normal file
8
data/real_v2/AVG_2024.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.988755,0.010732,0.000000,0.000000,0.000000,0.000000,0.000513
|
||||
AA,0.009172,0.985613,0.004488,0.000000,0.000000,0.000000,0.000727
|
||||
A,0.000000,0.002856,0.986074,0.009322,0.000000,0.000000,0.001748
|
||||
BBB,0.000000,0.000000,0.000000,0.977458,0.010647,0.000000,0.011896
|
||||
BB,0.000000,0.000000,0.000000,0.018718,0.899084,0.057865,0.024333
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.880364,0.119636
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/AVG_2025.csv
Normal file
8
data/real_v2/AVG_2025.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.996879,0.002609,0.000000,0.000000,0.000000,0.000000,0.000512
|
||||
AA,0.005305,0.971124,0.022690,0.000140,0.000000,0.000000,0.000741
|
||||
A,0.000000,0.018685,0.959782,0.018699,0.000442,0.000737,0.001655
|
||||
BBB,0.000000,0.000000,0.053864,0.919788,0.010295,0.005265,0.010788
|
||||
BB,0.000000,0.000000,0.000961,0.018349,0.867660,0.080888,0.032142
|
||||
B,0.000000,0.000000,0.000000,0.001442,0.059834,0.762310,0.176413
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_1998.csv
Normal file
8
data/real_v2/KR_1998.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.444444,0.444444,0.111111,0.000000,0.000000,0.000000
|
||||
A,0.000000,0.000000,0.635247,0.362982,0.000000,0.000000,0.001771
|
||||
BBB,0.000000,0.000000,0.052123,0.781841,0.104245,0.052123,0.009668
|
||||
BB,0.000000,0.000000,0.000000,0.107188,0.464290,0.285682,0.142841
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.500000,0.500000
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_1999.csv
Normal file
8
data/real_v2/KR_1999.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.500000,0.500000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.800000,0.200000,0.000000,0.000000,0.000000,0.000000
|
||||
A,0.000000,0.078330,0.704756,0.136998,0.000000,0.078330,0.001587
|
||||
BBB,0.000000,0.000000,0.039593,0.844461,0.039593,0.065951,0.010402
|
||||
BB,0.000000,0.000000,0.000000,0.173092,0.720955,0.086485,0.019468
|
||||
B,0.000000,0.000000,0.000000,0.071403,0.071403,0.642866,0.214329
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2000.csv
Normal file
8
data/real_v2/KR_2000.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.999247,0.000000,0.000000,0.000000,0.000000,0.000753
|
||||
A,0.000000,0.081527,0.855758,0.040708,0.020354,0.000000,0.001652
|
||||
BBB,0.000000,0.000000,0.078805,0.858224,0.052574,0.000000,0.010396
|
||||
BB,0.000000,0.000000,0.000000,0.064964,0.844180,0.051902,0.038955
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.083322,0.791628,0.125050
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2001.csv
Normal file
8
data/real_v2/KR_2001.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.049942,0.949320,0.000000,0.000000,0.000000,0.000000,0.000738
|
||||
A,0.000000,0.106170,0.828416,0.063724,0.000000,0.000000,0.001691
|
||||
BBB,0.000000,0.000000,0.030564,0.877612,0.061259,0.010188,0.020376
|
||||
BB,0.000000,0.000000,0.000000,0.095284,0.857173,0.027206,0.020337
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.076950,0.769149,0.153901
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2002.csv
Normal file
8
data/real_v2/KR_2002.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.083303,0.915848,0.000000,0.000000,0.000000,0.000000,0.000849
|
||||
A,0.000000,0.101881,0.896467,0.000000,0.000000,0.000000,0.001652
|
||||
BBB,0.000000,0.000000,0.056380,0.902225,0.028190,0.000000,0.013204
|
||||
BB,0.000000,0.000000,0.000000,0.114754,0.721311,0.131148,0.032787
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.785669,0.214331
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2003.csv
Normal file
8
data/real_v2/KR_2003.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.128922,0.676947,0.193383,0.000000,0.000000,0.000000,0.000748
|
||||
A,0.000000,0.020392,0.937141,0.040784,0.000000,0.000000,0.001682
|
||||
BBB,0.000000,0.000000,0.052759,0.910283,0.026436,0.000000,0.010523
|
||||
BB,0.000000,0.000000,0.035714,0.107143,0.607143,0.214286,0.035714
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.923012,0.076988
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2004.csv
Normal file
8
data/real_v2/KR_2004.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999443,0.000000,0.000000,0.000000,0.000000,0.000000,0.000557
|
||||
AA,0.049983,0.949209,0.000000,0.000000,0.000000,0.000000,0.000808
|
||||
A,0.000000,0.134406,0.825309,0.038384,0.000000,0.000000,0.001902
|
||||
BBB,0.000000,0.000000,0.083640,0.891822,0.013940,0.000000,0.010597
|
||||
BB,0.000000,0.000000,0.000000,0.200000,0.450000,0.050000,0.300000
|
||||
B,0.000000,0.000000,0.000000,0.055556,0.000000,0.666667,0.277778
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2005.csv
Normal file
8
data/real_v2/KR_2005.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999445,0.000000,0.000000,0.000000,0.000000,0.000000,0.000555
|
||||
AA,0.045436,0.953637,0.000000,0.000000,0.000000,0.000000,0.000927
|
||||
A,0.000000,0.094130,0.866336,0.037724,0.000000,0.000000,0.001810
|
||||
BBB,0.000000,0.000000,0.104804,0.885258,0.000000,0.000000,0.009938
|
||||
BB,0.000000,0.000000,0.000000,0.147150,0.784840,0.049089,0.018920
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.750000,0.250000
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2006.csv
Normal file
8
data/real_v2/KR_2006.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999449,0.000000,0.000000,0.000000,0.000000,0.000000,0.000551
|
||||
AA,0.032230,0.934791,0.032230,0.000000,0.000000,0.000000,0.000748
|
||||
A,0.000000,0.146789,0.851646,0.000000,0.000000,0.000000,0.001565
|
||||
BBB,0.000000,0.000000,0.082392,0.870592,0.011754,0.023508,0.011754
|
||||
BB,0.000000,0.000000,0.058847,0.058847,0.647051,0.176407,0.058847
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.941198,0.058802
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2007.csv
Normal file
8
data/real_v2/KR_2007.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999485,0.000000,0.000000,0.000000,0.000000,0.000000,0.000515
|
||||
AA,0.076879,0.896739,0.025589,0.000000,0.000000,0.000000,0.000793
|
||||
A,0.000000,0.036999,0.942560,0.018499,0.000000,0.000000,0.001943
|
||||
BBB,0.000000,0.000000,0.156948,0.821083,0.012114,0.000000,0.009855
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.693633,0.277381,0.028986
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.944135,0.055865
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2008.csv
Normal file
8
data/real_v2/KR_2008.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999486,0.000000,0.000000,0.000000,0.000000,0.000000,0.000514
|
||||
AA,0.000000,0.954843,0.044391,0.000000,0.000000,0.000000,0.000766
|
||||
A,0.000000,0.040518,0.944431,0.013471,0.000000,0.000000,0.001580
|
||||
BBB,0.000000,0.000000,0.108382,0.813100,0.067710,0.000000,0.010808
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.849962,0.050049,0.099989
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.894743,0.105257
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2009.csv
Normal file
8
data/real_v2/KR_2009.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999475,0.000000,0.000000,0.000000,0.000000,0.000000,0.000525
|
||||
AA,0.000000,0.999259,0.000000,0.000000,0.000000,0.000000,0.000741
|
||||
A,0.000000,0.102069,0.873612,0.022718,0.000000,0.000000,0.001602
|
||||
BBB,0.000000,0.000000,0.042055,0.861308,0.000000,0.083973,0.012663
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.733252,0.133374,0.133374
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.870918,0.129082
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2010.csv
Normal file
8
data/real_v2/KR_2010.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999490,0.000000,0.000000,0.000000,0.000000,0.000000,0.000510
|
||||
AA,0.037490,0.961754,0.000000,0.000000,0.000000,0.000000,0.000756
|
||||
A,0.000000,0.087767,0.910585,0.000000,0.000000,0.000000,0.001647
|
||||
BBB,0.000000,0.000000,0.203256,0.696994,0.000000,0.087090,0.012660
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.777833,0.000000,0.222167
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.038422,0.807719,0.153859
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2011.csv
Normal file
8
data/real_v2/KR_2011.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.999259,0.000000,0.000000,0.000000,0.000000,0.000741
|
||||
A,0.000000,0.073300,0.925132,0.000000,0.000000,0.000000,0.001568
|
||||
BBB,0.000000,0.000000,0.049496,0.940632,0.000000,0.000000,0.009872
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.977995,0.000000,0.022005
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.800000,0.200000
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2012.csv
Normal file
8
data/real_v2/KR_2012.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999482,0.000000,0.000000,0.000000,0.000000,0.000000,0.000518
|
||||
AA,0.000000,0.999235,0.000000,0.000000,0.000000,0.000000,0.000765
|
||||
A,0.000000,0.044629,0.892891,0.053554,0.000000,0.000000,0.008926
|
||||
BBB,0.000000,0.000000,0.044044,0.880146,0.066014,0.000000,0.009796
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.979020,0.000000,0.020980
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.444460,0.555540
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2013.csv
Normal file
8
data/real_v2/KR_2013.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.009257,0.980737,0.009257,0.000000,0.000000,0.000000,0.000749
|
||||
A,0.000000,0.046650,0.914383,0.037342,0.000000,0.000000,0.001625
|
||||
BBB,0.000000,0.000000,0.021327,0.872275,0.000000,0.063863,0.042535
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.703690,0.222264,0.074046
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.909100,0.090900
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2014.csv
Normal file
8
data/real_v2/KR_2014.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.983345,0.016147,0.000000,0.000000,0.000000,0.000000,0.000508
|
||||
AA,0.000000,0.932074,0.067181,0.000000,0.000000,0.000000,0.000744
|
||||
A,0.000000,0.042076,0.914206,0.042076,0.000000,0.000000,0.001641
|
||||
BBB,0.000000,0.000000,0.023774,0.762030,0.095211,0.095211,0.023774
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.850007,0.049953,0.100041
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.928504,0.071496
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2015.csv
Normal file
8
data/real_v2/KR_2015.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.981247,0.018208,0.000000,0.000000,0.000000,0.000000,0.000545
|
||||
AA,0.000000,0.934804,0.064476,0.000000,0.000000,0.000000,0.000720
|
||||
A,0.000000,0.044333,0.898536,0.033249,0.022166,0.000000,0.001716
|
||||
BBB,0.000000,0.000000,0.000000,0.933198,0.056574,0.000000,0.010228
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.769245,0.153775,0.076980
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.909138,0.090862
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2016.csv
Normal file
8
data/real_v2/KR_2016.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999481,0.000000,0.000000,0.000000,0.000000,0.000000,0.000519
|
||||
AA,0.000000,0.983435,0.015823,0.000000,0.000000,0.000000,0.000742
|
||||
A,0.000000,0.010048,0.968144,0.020203,0.000000,0.000000,0.001605
|
||||
BBB,0.000000,0.000000,0.084814,0.848674,0.056542,0.000000,0.009969
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.789500,0.157900,0.052600
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.846214,0.153786
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2017.csv
Normal file
8
data/real_v2/KR_2017.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999491,0.000000,0.000000,0.000000,0.000000,0.000000,0.000509
|
||||
AA,0.000000,0.999286,0.000000,0.000000,0.000000,0.000000,0.000714
|
||||
A,0.000000,0.011102,0.942660,0.044406,0.000000,0.000000,0.001832
|
||||
BBB,0.000000,0.000000,0.041136,0.946272,0.000000,0.000000,0.012591
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.921949,0.057677,0.020374
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.928571,0.071429
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2018.csv
Normal file
8
data/real_v2/KR_2018.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.007680,0.983911,0.007680,0.000000,0.000000,0.000000,0.000730
|
||||
A,0.000000,0.032194,0.955430,0.010731,0.000000,0.000000,0.001644
|
||||
BBB,0.000000,0.000000,0.085802,0.901066,0.000000,0.000000,0.013131
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.837904,0.139651,0.022445
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.953670,0.046330
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2019.csv
Normal file
8
data/real_v2/KR_2019.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.983300,0.016700,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.999259,0.000000,0.000000,0.000000,0.000000,0.000741
|
||||
A,0.000000,0.020325,0.947474,0.030595,0.000000,0.000000,0.001606
|
||||
BBB,0.000000,0.000000,0.047152,0.895209,0.047152,0.000000,0.010487
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.880236,0.097758,0.022005
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.875044,0.124956
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2020.csv
Normal file
8
data/real_v2/KR_2020.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999492,0.000000,0.000000,0.000000,0.000000,0.000000,0.000508
|
||||
AA,0.000000,0.977095,0.022176,0.000000,0.000000,0.000000,0.000729
|
||||
A,0.000000,0.048004,0.940797,0.009642,0.000000,0.000000,0.001557
|
||||
BBB,0.000000,0.000000,0.000000,0.989720,0.000000,0.000000,0.010280
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.982240,0.000000,0.017760
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.900026,0.099974
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2021.csv
Normal file
8
data/real_v2/KR_2021.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999475,0.000000,0.000000,0.000000,0.000000,0.000000,0.000525
|
||||
AA,0.000000,0.992240,0.007042,0.000000,0.000000,0.000000,0.000718
|
||||
A,0.000000,0.020189,0.978146,0.000000,0.000000,0.000000,0.001666
|
||||
BBB,0.000000,0.000000,0.053517,0.936556,0.000000,0.000000,0.009927
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.950993,0.031685,0.017322
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.058847,0.882305,0.058847
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2022.csv
Normal file
8
data/real_v2/KR_2022.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999484,0.000000,0.000000,0.000000,0.000000,0.000000,0.000516
|
||||
AA,0.000000,0.979025,0.020248,0.000000,0.000000,0.000000,0.000727
|
||||
A,0.000000,0.018144,0.971162,0.009072,0.000000,0.000000,0.001622
|
||||
BBB,0.000000,0.000000,0.069035,0.897883,0.023048,0.000000,0.010035
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.896069,0.086741,0.017190
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.939544,0.060456
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2023.csv
Normal file
8
data/real_v2/KR_2023.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.985443,0.014018,0.000000,0.000000,0.000000,0.000000,0.000539
|
||||
AA,0.024295,0.928494,0.044214,0.001036,0.000576,0.000576,0.000810
|
||||
A,0.000000,0.140393,0.803316,0.043990,0.002942,0.006418,0.002942
|
||||
BBB,0.000000,0.000972,0.210919,0.685566,0.045845,0.035801,0.020897
|
||||
BB,0.000000,0.000000,0.023439,0.179363,0.441274,0.152866,0.203057
|
||||
B,0.000000,0.000000,0.000000,0.029265,0.014633,0.431507,0.524595
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2024.csv
Normal file
8
data/real_v2/KR_2024.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999475,0.000000,0.000000,0.000000,0.000000,0.000000,0.000525
|
||||
AA,0.014076,0.978215,0.006987,0.000000,0.000000,0.000000,0.000723
|
||||
A,0.000000,0.000000,0.989112,0.009130,0.000000,0.000000,0.001758
|
||||
BBB,0.000000,0.000000,0.000000,0.957404,0.031940,0.000000,0.010656
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.928580,0.035710,0.035710
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.941123,0.058877
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/KR_2025.csv
Normal file
8
data/real_v2/KR_2025.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.995162,0.004324,0.000000,0.000000,0.000000,0.000000,0.000515
|
||||
AA,0.008632,0.969786,0.020421,0.000421,0.000000,0.000000,0.000741
|
||||
A,0.000000,0.046298,0.920993,0.027514,0.001326,0.002210,0.001659
|
||||
BBB,0.000000,0.000000,0.067269,0.875202,0.030885,0.015794,0.010850
|
||||
BB,0.000000,0.000000,0.002883,0.055046,0.796592,0.097510,0.047969
|
||||
B,0.000000,0.000000,0.000000,0.004327,0.012835,0.850303,0.132535
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_1998.csv
Normal file
8
data/real_v2/NICE_1998.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
A,0.000000,0.000000,0.499188,0.499188,0.000000,0.000000,0.001624
|
||||
BBB,0.000000,0.000000,0.000000,0.586115,0.293058,0.109955,0.010872
|
||||
BB,0.000000,0.000000,0.000000,0.050023,0.750000,0.149954,0.050023
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.583360,0.416640
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_1999.csv
Normal file
8
data/real_v2/NICE_1999.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999459,0.000000,0.000000,0.000000,0.000000,0.000000,0.000541
|
||||
AA,0.000000,0.999250,0.000000,0.000000,0.000000,0.000000,0.000750
|
||||
A,0.000000,0.021696,0.803076,0.086783,0.000000,0.086783,0.001662
|
||||
BBB,0.000000,0.000000,0.078530,0.722364,0.094236,0.094236,0.010632
|
||||
BB,0.000000,0.000000,0.000000,0.100606,0.804968,0.075425,0.019001
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.749963,0.250037
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2000.csv
Normal file
8
data/real_v2/NICE_2000.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.043445,0.912346,0.043445,0.000000,0.000000,0.000000,0.000764
|
||||
A,0.000000,0.156637,0.763344,0.058753,0.019621,0.000000,0.001646
|
||||
BBB,0.000000,0.000000,0.056194,0.866145,0.067541,0.000000,0.010120
|
||||
BB,0.000000,0.000000,0.000000,0.112645,0.816860,0.042272,0.028222
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.155851,0.779255,0.064893
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2001.csv
Normal file
8
data/real_v2/NICE_2001.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999393,0.000000,0.000000,0.000000,0.000000,0.000000,0.000607
|
||||
AA,0.090793,0.817519,0.090793,0.000000,0.000000,0.000000,0.000895
|
||||
A,0.000000,0.065087,0.911555,0.021696,0.000000,0.000000,0.001662
|
||||
BBB,0.000000,0.000000,0.051241,0.871896,0.051241,0.012810,0.012810
|
||||
BB,0.000000,0.000000,0.000000,0.075724,0.833355,0.030264,0.060657
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.181865,0.545423,0.272712
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2002.csv
Normal file
8
data/real_v2/NICE_2002.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.107091,0.678059,0.214072,0.000000,0.000000,0.000000,0.000778
|
||||
A,0.000000,0.117393,0.841733,0.039169,0.000000,0.000000,0.001705
|
||||
BBB,0.000000,0.000000,0.051019,0.901142,0.033963,0.000000,0.013876
|
||||
BB,0.000000,0.000000,0.000000,0.081633,0.734694,0.142857,0.040816
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.125000,0.625000,0.250000
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2003.csv
Normal file
8
data/real_v2/NICE_2003.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999395,0.000000,0.000000,0.000000,0.000000,0.000000,0.000605
|
||||
AA,0.095124,0.856454,0.047619,0.000000,0.000000,0.000000,0.000803
|
||||
A,0.000000,0.057622,0.863899,0.057622,0.000000,0.019243,0.001614
|
||||
BBB,0.000000,0.000000,0.094228,0.894996,0.000000,0.000000,0.010776
|
||||
BB,0.000000,0.000000,0.000000,0.057169,0.799974,0.057169,0.085688
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.916667,0.083333
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2004.csv
Normal file
8
data/real_v2/NICE_2004.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999395,0.000000,0.000000,0.000000,0.000000,0.000000,0.000605
|
||||
AA,0.095124,0.856454,0.047619,0.000000,0.000000,0.000000,0.000803
|
||||
A,0.000000,0.101484,0.862960,0.033828,0.000000,0.000000,0.001728
|
||||
BBB,0.000000,0.000000,0.097292,0.875387,0.016195,0.000000,0.011126
|
||||
BB,0.000000,0.000000,0.000000,0.142804,0.642897,0.000000,0.214299
|
||||
B,0.000000,0.000000,0.000000,0.076959,0.076959,0.692281,0.153802
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2005.csv
Normal file
8
data/real_v2/NICE_2005.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999445,0.000000,0.000000,0.000000,0.000000,0.000000,0.000555
|
||||
AA,0.035680,0.927810,0.035680,0.000000,0.000000,0.000000,0.000829
|
||||
A,0.000000,0.083166,0.915060,0.000000,0.000000,0.000000,0.001774
|
||||
BBB,0.000000,0.000000,0.082449,0.907477,0.000000,0.000000,0.010075
|
||||
BB,0.000000,0.000000,0.000000,0.150487,0.827744,0.000000,0.021770
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.851877,0.148123
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2006.csv
Normal file
8
data/real_v2/NICE_2006.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.027765,0.971434,0.000000,0.000000,0.000000,0.000000,0.000801
|
||||
A,0.000000,0.153639,0.844746,0.000000,0.000000,0.000000,0.001614
|
||||
BBB,0.000000,0.000000,0.151571,0.833333,0.000000,0.000000,0.015096
|
||||
BB,0.000000,0.000000,0.000000,0.083300,0.750000,0.166700,0.000000
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.038241,0.918724,0.043035
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2007.csv
Normal file
8
data/real_v2/NICE_2007.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999485,0.000000,0.000000,0.000000,0.000000,0.000000,0.000515
|
||||
AA,0.000000,0.952749,0.046499,0.000000,0.000000,0.000000,0.000752
|
||||
A,0.000000,0.017818,0.962544,0.017818,0.000000,0.000000,0.001820
|
||||
BBB,0.000000,0.000000,0.134950,0.824793,0.029964,0.000000,0.010293
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.832323,0.138690,0.028986
|
||||
B,0.000000,0.000000,0.061724,0.000000,0.000000,0.863781,0.074495
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2008.csv
Normal file
8
data/real_v2/NICE_2008.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999475,0.000000,0.000000,0.000000,0.000000,0.000000,0.000525
|
||||
AA,0.000000,0.999268,0.000000,0.000000,0.000000,0.000000,0.000732
|
||||
A,0.000000,0.039896,0.891954,0.066530,0.000000,0.000000,0.001619
|
||||
BBB,0.000000,0.000000,0.115338,0.788466,0.076932,0.000000,0.019263
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.913752,0.065268,0.020980
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.952192,0.047808
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2009.csv
Normal file
8
data/real_v2/NICE_2009.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999489,0.000000,0.000000,0.000000,0.000000,0.000000,0.000511
|
||||
AA,0.028946,0.970280,0.000000,0.000000,0.000000,0.000000,0.000774
|
||||
A,0.000000,0.112188,0.863821,0.022458,0.000000,0.000000,0.001533
|
||||
BBB,0.000000,0.000000,0.106754,0.827736,0.000000,0.053377,0.012133
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.749954,0.083410,0.166636
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.857143,0.142857
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2010.csv
Normal file
8
data/real_v2/NICE_2010.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999490,0.000000,0.000000,0.000000,0.000000,0.000000,0.000510
|
||||
AA,0.000000,0.999261,0.000000,0.000000,0.000000,0.000000,0.000739
|
||||
A,0.000000,0.064463,0.912427,0.021451,0.000000,0.000000,0.001660
|
||||
BBB,0.000000,0.000000,0.123257,0.780426,0.000000,0.082222,0.014095
|
||||
BB,0.000000,0.000000,0.000000,0.151019,0.830773,0.000000,0.018209
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.047675,0.619041,0.333284
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2011.csv
Normal file
8
data/real_v2/NICE_2011.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999490,0.000000,0.000000,0.000000,0.000000,0.000000,0.000510
|
||||
AA,0.000000,0.999261,0.000000,0.000000,0.000000,0.000000,0.000739
|
||||
A,0.000000,0.069179,0.919290,0.000000,0.000000,0.009928,0.001603
|
||||
BBB,0.000000,0.000000,0.054054,0.891892,0.027027,0.000000,0.027027
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.973912,0.000000,0.026088
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.875014,0.124986
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2012.csv
Normal file
8
data/real_v2/NICE_2012.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
|
||||
AA,0.000000,0.989709,0.009574,0.000000,0.000000,0.000000,0.000717
|
||||
A,0.000000,0.044609,0.884037,0.062474,0.000000,0.000000,0.008880
|
||||
BBB,0.000000,0.000000,0.025686,0.871679,0.076949,0.000000,0.025686
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.800030,0.000000,0.199970
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.749965,0.250035
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
8
data/real_v2/NICE_2013.csv
Normal file
8
data/real_v2/NICE_2013.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
,AAA,AA,A,BBB,BB,B,D
|
||||
AAA,0.999475,0.000000,0.000000,0.000000,0.000000,0.000000,0.000525
|
||||
AA,0.000000,0.958273,0.040989,0.000000,0.000000,0.000000,0.000738
|
||||
A,0.000000,0.073284,0.870198,0.054963,0.000000,0.000000,0.001554
|
||||
BBB,0.000000,0.000000,0.024413,0.780401,0.000000,0.146361,0.048825
|
||||
BB,0.000000,0.000000,0.000000,0.000000,0.727283,0.227228,0.045489
|
||||
B,0.000000,0.000000,0.000000,0.000000,0.000000,0.857165,0.142835
|
||||
D,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user