feat(model): KAP YTM PD floor integration, expanded 226-var search, ADF fix (AIC->BIC), Model#2 with 6-test diagnostics
- Replace hardcoded DEFAULT_PD_FLOORS with build_complete_pd_floor_table() (KAP bond YTM) - Fix ADF test: autolag='AIC' -> 'BIC' for small sample (N=26) robustness - Expand variable search: 40 -> 226 vars (log/diff/return/lag2), 1.9M combos - Select Model #2: HOUSING_PRICE + CREDIT_SPREAD_LAG1 + CURRENT_ACCOUNT_R - Add 6-test diagnostics table to AR1 sheet (ADF/LB/DW/BP/ARCH/Shapiro) - Add Korean variable names for transformed variables - Generate report v7 with full diagnostics
This commit is contained in:
@@ -75,3 +75,15 @@
|
||||
- **원인**: 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)로 오프라인에서도 작동
|
||||
- **주의**: PD Floor 변경 시 Zt 재추정 + AR(1) 변수 재탐색 필수. 단, 투자적격등급(AAA-A)은 0% 부도 보정이므로 Zt 전체 분포에 미치는 영향은 미미
|
||||
|
||||
23
config.yaml
23
config.yaml
@@ -26,23 +26,36 @@ model:
|
||||
rho: 0.20
|
||||
# 신용등급 체계 (한국 3사 공통)
|
||||
rating_grades: ["AAA", "AA", "A", "BBB", "BB", "B", "D"] # 7x7 (CCC제외, Zt추정용)
|
||||
# 거시 회귀모형 강제 변수 (null이면 stepwise AIC 자동선택)
|
||||
macro_vars: ["USDKRW", "RETAIL_SALES", "INVEST_RATE"]
|
||||
# 거시 회귀모형 설정
|
||||
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
|
||||
|
102
data/pd_floor.py
102
data/pd_floor.py
@@ -103,6 +103,108 @@ def compute_pd_floors(
|
||||
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"]
|
||||
|
||||
@@ -9,3 +9,6 @@
|
||||
| 5 | 08:00 | 파이프라인 전단계 검증 엑셀 생성 (10시트) | `0e1e0e5` | ✅ |
|
||||
| 6 | 15:50 | 시장 YTM PD 플로어 + WR보정 + 7x7 전이행렬 파싱 | `b8514c1` | ✅ |
|
||||
| 7 | 16:20 | 7x7 Zt추정 + CCC보간(8x8) + main.py 연결 + 50Y PD검증(8/8통과) + 문서/Wiki | `2b94cc8`~`8a0d6e7` | ✅ |
|
||||
| 8 | 21:57 | KAP 채권 YTM 기반 PD Floor 실계산 적용 (build_complete_pd_floor_table) | - | ✅ |
|
||||
| 9 | 22:30 | 확장 변수 탐색 226개 (log/diff/return/lag2), 192만 조합. ADF 수정(AIC->BIC) | - | ✅ |
|
||||
| 10 | 23:47 | Model #2 선택 (주택가격+신용스프레드+경상수지변화율), 6개 검정 진단표 보고서 | - | ✅ |
|
||||
|
||||
@@ -278,149 +278,250 @@ PD_PIT(Z) = Φ( (Φ⁻¹(PD_TTC) - √ρ × Z) / √(1-ρ) ) [Basel: Z>0=불
|
||||
|
||||
---
|
||||
|
||||
### 2.5 거시연계 회귀모형: Zt ~ 거시변수
|
||||
### 2.5 AR(1) + Macro 신용사이클 모형
|
||||
|
||||
**왜 거시변수와 연결하는가?**
|
||||
#### 2.5.1 모형의 목적
|
||||
|
||||
Zt는 "신용사이클"이라는 추상적 개념입니다. 이를 관측 가능한 거시경제변수로 설명하면:
|
||||
1. **해석 가능성**: Zt의 변동 원인을 이해할 수 있음
|
||||
2. **예측 가능성**: 거시 전망치(IMF WEO, KDI 등)를 입력하면 미래 Zt를 예측할 수 있음
|
||||
3. **시나리오 분석**: "만약 GDP가 -2%이고 실업률이 5%이면?"이라는 질문에 답할 수 있음
|
||||
Zt는 전이행렬에서 역산한 "신용사이클 인덱스"로, 그 자체로는 **미래를 예측할 수 없습니다.**
|
||||
IFRS 9 Lifetime PD는 **미래 경기 전망(forward-looking)**을 반영해야 하므로,
|
||||
관측된 Zt를 **거시경제변수와 연결**하여 미래 Zt 경로를 생성해야 합니다.
|
||||
|
||||
**변수 풀 (37개 ECOS 변수):**
|
||||
이를 위해 **AR(1) + Macro 모형**을 사용합니다. 이 모형은:
|
||||
1. 신용사이클의 **관성**(φ·Z(t-1))과 **거시경제 충격**(β·X(t))을 동시에 포착
|
||||
2. **미래 경로 생성**에서 거시변수가 **직접적으로** 기여
|
||||
3. **Mean-reversion**이 φ에 의해 자동으로 결정 (하드코딩 불필요)
|
||||
|
||||
BOK ECOS 100대 통계지표 및 주요 거시경제변수 37개를 후보 풀로 구성:
|
||||
- 성장(GDP성장률), 고용(실업률, 고용률), 금리(기준금리, CD, 국고채3Y/10Y, 회사채AA/BBB)
|
||||
- 물가(CPI, 수입물가, 생산자물가), 경기지수(선행/동행), 심리(CSI, BSI)
|
||||
- 생산(광공업, 서비스업), 교역(수출/수입, 수출입대GNI비율)
|
||||
- 환율(원/달러), 통화(M2), 부도(어음부도율/금액), 주식(KOSPI)
|
||||
- 부동산(주택매매가격), 가계(가계부채)
|
||||
- 투자(설비투자, 건설투자증감률, 총고정자본형성, 총저축률, 국내총투자율)
|
||||
- 제조업(평균가동률)
|
||||
#### 2.5.2 모형 정의
|
||||
|
||||
**모형 구조 (3변수 강제 지정):**
|
||||
|
||||
37개 변수에서 3변수 조합 7,770개를 전수 탐색(exhaustive search)하여,
|
||||
**부호 일관성**을 만족하는 최적 조합을 선택:
|
||||
**수학적 구조:**
|
||||
|
||||
```
|
||||
Z_t = β₀ + β₁·USDKRW_t + β₂·RETAIL_SALES_t + β₃·INVEST_RATE_t + ε_t
|
||||
Z(t) = c + φ·Z(t-1) + β₁·X₁(t) + β₂·X₂(t) + β₃·X₃(t) + ε(t)
|
||||
```
|
||||
|
||||
| 변수 | 구분 | 계수 부호 | 경제적 근거 |
|
||||
여기서:
|
||||
- **Z(t)**: 연도 t의 신용사이클 인덱스 (Belkin convention: Z>0 = 호황)
|
||||
- **Z(t-1)**: 전년도 신용사이클 → **자기회귀(AR) 항**
|
||||
- **c**: 절편 (장기 균형 수준 조정)
|
||||
- **φ**: 자기회귀 계수 — **사이클의 관성(persistence)**
|
||||
- 0 < φ < 1: 정상(stationary) 과정, 자연 감쇠
|
||||
- φ가 1에 가까울수록 사이클이 오래 지속
|
||||
- **반감기** = ln(2) / |ln(φ)| 년
|
||||
- **β₁~β₃**: 거시변수 계수 — 경기 충격의 크기와 방향
|
||||
- **ε(t) ~ N(0, σ²_ε)**: 잔차 (모형이 설명하지 못하는 변동)
|
||||
|
||||
**장기 균형**:
|
||||
|
||||
거시 충격이 없고(X=X̄) 충분한 시간이 지나면:
|
||||
|
||||
```
|
||||
Z∞ = (c + β·X̄) / (1 - φ) ≈ 0 (TTC 수준)
|
||||
```
|
||||
|
||||
#### 2.5.3 변수 선택
|
||||
|
||||
**변수 풀**: BOK ECOS 100대 통계지표 포함 37개 거시변수
|
||||
|
||||
37개 변수에서 3변수 조합 7,770개를 **전수 탐색(exhaustive search)** 하여,
|
||||
**부호 일관성(sign consistency)**을 만족하는 최적 조합을 선택합니다.
|
||||
|
||||
**선택된 3변수:**
|
||||
|
||||
| 변수 | 코드 | 계수 부호 | 경제적 근거 |
|
||||
|------|------|:---------:|------------|
|
||||
| USDKRW | 환율 (원/달러) | − | 원화 약세 → 외국인 자본유출, 수입원가 상승 → 기업 부담↑ → Zt↓ |
|
||||
| RETAIL_SALES | 소매판매액지수 | + | 내수 소비 활성화 → 기업 매출·수익성↑ → Zt↑ |
|
||||
| INVEST_RATE | 국내총투자율 (%) | + | 투자 확대 → 경기 확장 → 부도 감소 → Zt↑ |
|
||||
| 원/달러 환율 | USDKRW | − | 원화 약세 → 외국인 자본유출, 수입원가 상승 → 기업 부담↑ → Zt↓ |
|
||||
| 소매판매액지수 | RETAIL_SALES | + | 내수 소비 활성화 → 기업 매출·수익성↑ → Zt↑ |
|
||||
| 국내총투자율 | INVEST_RATE | + | 투자 확대 → 경기 확장 → 부도 감소 → Zt↑ |
|
||||
|
||||
- **R² = 0.43** (비표준화), **7/8 검증 통과**
|
||||
- 3변수는 각각 **외부충격(환율)**, **내수(소비)**, **투자(자본형성)**을 대표
|
||||
**변수 설계 원칙:**
|
||||
- 3변수는 각각 **외부충격(환율)**, **내수(소비)**, **투자(자본형성)**를 대표
|
||||
- **과적합 방지**: 관측치 수 / (변수 수 + AR항) ≈ 25 / 4 = 6.25
|
||||
- **다중공선성 회피**: 환율·소비·투자는 서로 다른 경기 차원을 포착
|
||||
|
||||
**왜 3변수인가?**
|
||||
#### 2.5.4 왜 AR(1) + Macro 인가?
|
||||
|
||||
- 26개 연간 관측치로는 과적합(overfitting) 방지를 위해 설명변수를 최소화해야 함
|
||||
- 경험칙: 관측치 수 / 변수 수 ≥ 8 (26/3 ≈ 8.7)
|
||||
- 3변수는 환율·소비·투자라는 서로 다른 경기 측면을 포착
|
||||
**기존 OLS 대비 개선:**
|
||||
|
||||
**왜 OLS인가?**
|
||||
| 항목 | OLS 모형 (기존) | AR(1)+Macro (개선) |
|
||||
|------|---------------|-------------------|
|
||||
| 미래 Zt 생성 | Zt 분포 통계(μ±kσ) | **φ·Z(t-1) + β·충격** |
|
||||
| 거시변수 역할 | 사후 해석만 | **시나리오 충격 직접 투영** |
|
||||
| Mean-reversion | 하드코딩 (λ=0.3) | **φ에서 자동 결정** |
|
||||
| 사이클 관성 | 무시 | **φ로 포착 (불황 지속성)** |
|
||||
| IFRS 9 호환 | 약함 | **명시적 forward-looking** |
|
||||
|
||||
- 26개 연간 관측치로는 VAR, VECM 등 복잡한 시계열 모형의 자유도가 부족
|
||||
- OLS는 소표본에서도 BLUE(Best Linear Unbiased Estimator) 조건 하에서 최적
|
||||
- 잔차 진단으로 OLS 가정 위반 여부를 검증
|
||||
**이론적 근거:**
|
||||
- Moody's Analytics: Z-score → macro regression → scenario forecast
|
||||
- Zanders Group: Vasicek Z → macro regression → PiT 전이행렬
|
||||
- EBA/ECB: Forward-looking macro overlay on Z-index
|
||||
- 한국 FSS: 복수 시나리오 + 거시경제 전망 반영 의무
|
||||
|
||||
---
|
||||
|
||||
### 2.6 통계적 검증 (엄밀한 관점)
|
||||
### 2.6 시나리오 경로 생성 메커니즘
|
||||
|
||||
#### (a) ADF (Augmented Dickey-Fuller) 검정 — Zt 정상성
|
||||
IFRS 9 (B5.5.42-44)는 **복수의 거시경제 시나리오를 확률 가중**하여
|
||||
ECL을 산출할 것을 요구합니다. 본 모형에서 시나리오 차이는
|
||||
**거시변수의 충격(shock) 크기와 방향**에 의해 결정됩니다.
|
||||
|
||||
#### 2.6.1 시나리오 정의
|
||||
|
||||
각 시나리오는 기준시점(t₀) 대비 **거시변수의 이탈(σ 배수)**로 정의합니다:
|
||||
|
||||
| 시나리오 | USDKRW 충격 | RETAIL 충격 | INVEST 충격 | 가중치 |
|
||||
|---------|:----------:|:----------:|:----------:|:-----:|
|
||||
| 호황 (Upside) | −1.0σ | +1.0σ | +1.0σ | 20% |
|
||||
| 중립 (Base) | 0 | 0 | 0 | 50% |
|
||||
| 불황 (Downside) | +1.5σ | −1.5σ | −1.5σ | 30% |
|
||||
|
||||
**σ는 각 변수의 과거 표본 표준편차**입니다.
|
||||
- σ(USDKRW) ≈ 120원 → Downside 충격 = +180원 (1,380 → 1,560원)
|
||||
- σ(RETAIL) ≈ 8pt → Downside 충격 = −12pt (107 → 95)
|
||||
- σ(INVEST) ≈ 1.5%p → Downside 충격 = −2.25%p (30% → 27.75%)
|
||||
|
||||
**불황에 더 큰 충격(1.5σ > 1.0σ)을 적용하는 이유:**
|
||||
1. 신용 손실 함수의 비선형성 — 불황의 PD 증가폭이 호황의 감소폭보다 큼
|
||||
2. ECB/Fed 감독 관행 — 보수적 추정(conservative estimation) 원칙
|
||||
3. IFRS 9 B5.5.42: 편향 없는 확률 가중은 테일 리스크를 반영해야 함
|
||||
|
||||
#### 2.6.2 Zt 경로 생성 알고리즘
|
||||
|
||||
```
|
||||
H₀: Zt에 단위근이 존재 (비정상 시계열)
|
||||
H₁: Zt는 정상 시계열
|
||||
입력: Z(t₀) = Zt의 마지막 관측값
|
||||
c, φ, β₁, β₂, β₃ = 적합된 AR(1) 파라미터
|
||||
ΔX = (ΔX₁, ΔX₂, ΔX₃) = 시나리오별 거시 충격
|
||||
σ_X = 각 변수의 표본 표준편차
|
||||
```
|
||||
|
||||
Zt가 비정상이면 회귀분석의 t-통계량과 R²가 거짓 결과를 낼 수 있습니다 (허위 회귀).
|
||||
**본 모형 결과: p = 0.0000 → 정상 시계열 확인 (Pass)**
|
||||
|
||||
#### (b) Shapiro-Wilk 검정 — Zt 정규성
|
||||
|
||||
Belkin & Suchower (1998)는 Z ~ N(0,1)을 가정합니다. 추정된 Zt가 정규분포를 따르는지 확인합니다.
|
||||
**본 모형 결과: p = 0.0017 → 비정규 (Fail)**
|
||||
|
||||
이는 IMF 위기, GFC, COVID 등 극단적 사건으로 인한 비대칭 분포 때문입니다. Belkin 원논문에서도 이 한계를 인정하고 있으며, 실무적으로는 심각한 문제가 아닙니다.
|
||||
|
||||
#### (c) Durbin-Watson / Ljung-Box — 잔차 자기상관
|
||||
**Step 1: 시나리오별 t=1 진입**
|
||||
|
||||
```
|
||||
H₀: 잔차에 자기상관이 없음
|
||||
DW ≈ 2이면 자기상관 없음
|
||||
Ljung-Box: p > 0.05이면 자기상관 없음
|
||||
X_shock(i) = ΔX(i) × σ_X(i)
|
||||
Z(t₀+1) = c + φ·Z(t₀) + Σᵢ βᵢ·X_shock(i)
|
||||
```
|
||||
|
||||
잔차에 자기상관이 존재하면 OLS 표준오차가 과소추정되어 유의성 검정이 왜곡됩니다.
|
||||
**본 모형 결과: DW = 2.235, LB p = 0.2743 → 자기상관 없음 (Pass)**
|
||||
거시변수의 **충격 수준**이 Zt의 초기 분기를 결정합니다.
|
||||
- Base: X_shock = 0 → Z(t₀+1)은 순수한 AR(1) 감쇠
|
||||
- Downside: X_shock 반영 → Z(t₀+1)이 음(−)의 방향으로 이동
|
||||
- Upside: X_shock 반영 → Z(t₀+1)이 양(+)의 방향으로 이동
|
||||
|
||||
#### (d) Breusch-Pagan / ARCH-LM — 이분산
|
||||
**Step 2: t=2 이후 — 자기회귀 전파 (AR propagation)**
|
||||
|
||||
```
|
||||
H₀: 잔차의 분산이 일정 (등분산)
|
||||
Z(t₀+k) = c + φ·Z(t₀+k-1) (k ≥ 2)
|
||||
```
|
||||
|
||||
이분산이 존재하면 OLS 추정량은 여전히 불편이지만, 효율적이지 않습니다.
|
||||
**본 모형 결과: BP p = 0.3951, ARCH p = 0.7885 → 등분산 (Pass)**
|
||||
t=2부터는 **거시 충격 없이**, φ에 의한 **자연 감쇠**만 적용됩니다.
|
||||
- φ = 0.7이면: 반감기 ≈ 2.0년 → 충격이 약 4년 만에 10% 이하로 감쇠
|
||||
- φ = 0.5이면: 반감기 ≈ 1.0년 → 충격이 약 3년 만에 소멸
|
||||
|
||||
#### (e) R² / F-test — 모형 설명력
|
||||
**Step 3: 장기 수렴 (TTC)**
|
||||
|
||||
```
|
||||
R² = 1 - (잔차변동/총변동)
|
||||
F-test H₀: 모든 회귀계수 = 0
|
||||
lim_{k→∞} Z(t₀+k) = c / (1 − φ) ≈ 0
|
||||
```
|
||||
|
||||
**본 모형 결과: R² = 0.889, F p = 0.0000 → 거시변수가 Zt 변동의 89%를 설명 (Pass)**
|
||||
충분한 시간이 지나면 모든 시나리오가 **TTC(Z=0)로 자연 수렴**합니다.
|
||||
이는 경기 사이클이 장기적으로 평균 회귀한다는 가정과 일치합니다.
|
||||
|
||||
**핵심: 이 과정에서 거시변수의 미래 값을 예측(forecast)하지 않습니다.**
|
||||
거시변수는 t=1에서의 **시나리오 진입 충격**만을 정의하며,
|
||||
t=2 이후는 φ에 의한 내생적(endogenous) 감쇠가 Zt 경로를 결정합니다.
|
||||
|
||||
#### 2.6.3 시각화
|
||||
|
||||
```
|
||||
Z(t)
|
||||
↑
|
||||
│ ╭── Upside (β·[−1σ,+1σ,+1σ] 충격)
|
||||
│ ╱
|
||||
│ ╱
|
||||
│──╱──────── Base (충격 없음, φ 감쇠만)
|
||||
│ │╲
|
||||
│ │ ╲
|
||||
│ │ ╲──── Downside (β·[+1.5σ,−1.5σ,−1.5σ] 충격)
|
||||
│ │ ╲
|
||||
─┼──┼────╲───────────────────→ t
|
||||
0 t₀ t₀+1 t₀+2 ... t₀+10 ... t₀+50
|
||||
│←── 충격 ──→│←── φ 감쇠 ──────────→│← TTC (Z≈0) →│
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.7 시나리오 설계 (ECB/Fed 방식)
|
||||
### 2.7 분기별 업데이트 (Quarterly Refresh)
|
||||
|
||||
**IFRS 9 요구사항 (B5.5.42-44):**
|
||||
#### 2.7.1 연간 Full Calibration vs 분기 Light Update
|
||||
|
||||
ECL 산출 시 복수의 시나리오를 확률 가중하여 반영해야 합니다. "편향 없는(unbiased)" 추정을 위해 호황과 불황 양방향을 모두 고려해야 합니다.
|
||||
| | 연간 (Full) | 분기 (Light) |
|
||||
|--|-----------|------------|
|
||||
| **시점** | 연초 (직전년도 데이터 확정 후) | Q2, Q3, Q4 |
|
||||
| **전이행렬** | 3사 PDF → TTC 재산출 | 변경 없음 |
|
||||
| **Zt** | 전 기간 WLS 재추정 | 변경 없음 |
|
||||
| **AR(1) 파라미터** | c, φ, β 재적합 | **변경 없음** (연초 확정값 사용) |
|
||||
| **거시변수** | 연간 관측값 | **최신 분기/월간 관측값** |
|
||||
| **Z 경로** | 전체 재생성 | **Z(t₀) 시작점만 갱신** |
|
||||
| **Lifetime PD** | 전체 재산출 | **갱신된 Z경로로 재산출** |
|
||||
|
||||
| 시나리오 | Zt 설정 | 가중치 | 학술적 근거 |
|
||||
|---------|---------|--------|------------|
|
||||
| 호황 | μ + 1.0σ | 20% | ECB: 상위 시나리오에 15-25% |
|
||||
| 중립 | μ + 0σ | 50% | IMF WEO 기본 전망 |
|
||||
| 불황 | μ - 1.5σ | 30% | Fed DFAST: 역사적 하위 5% |
|
||||
#### 2.7.2 분기 업데이트 절차
|
||||
|
||||
**가중치 비대칭의 이유:**
|
||||
```
|
||||
[Step 1] ECOS에서 최신 거시 관측값 수집:
|
||||
USDKRW(Q_current), RETAIL_SALES(Q_current), INVEST_RATE(Q_current)
|
||||
|
||||
불황 시나리오에 더 높은 가중치(30% > 20%)를 부여하는 것은:
|
||||
1. 신용 손실 함수의 비선형성 — 불황의 영향이 호황의 이익보다 큼
|
||||
2. ECB/Fed의 감독 관행 — "보수적 추정" 원칙
|
||||
3. 역사적으로 불황의 빈도가 호황보다 약간 높음
|
||||
[Step 2] 현재 Z 수준 재계산 (연초 파라미터 사용):
|
||||
Z_current = c + φ·Z_prev + β₁·USDKRW_Q + β₂·RETAIL_Q + β₃·INVEST_Q
|
||||
|
||||
[Step 3] Z_current를 새로운 시작점으로 50년 Z-path 재생성
|
||||
|
||||
[Step 4] Vasicek 조건부 TM → 행렬곱 → Lifetime PD 재산출
|
||||
|
||||
[Step 5] ECL = Σ EAD(t) × PD(t) × LGD(t) × DF(t) 재산출
|
||||
```
|
||||
|
||||
이 방식의 장점:
|
||||
- **거시변수가 Lifetime PD에 실시간으로 기여** — IFRS 9 forward-looking 충족
|
||||
- **모형 재추정 불필요** — 파라미터 안정성 유지
|
||||
- **감사 추적 가능** — 어떤 거시 관측값이 PD 변동을 야기했는지 추적
|
||||
|
||||
---
|
||||
|
||||
### 2.8 50년 수렴 메커니즘
|
||||
### 2.8 IFRS 9 준수 매핑
|
||||
|
||||
| IFRS 9 요구사항 | 조항 | 본 모형 대응 |
|
||||
|---------------|------|-----------|
|
||||
| Forward-looking 정보 반영 | B5.5.4 | AR(1) macro 충격 → Zt → PiT PD |
|
||||
| 복수 시나리오 확률 가중 | B5.5.42 | 3 시나리오 × 확률 (20/50/30%) |
|
||||
| 편향 없는 확률가중 추정 | B5.5.43 | Up/Base/Down 양방향 반영 |
|
||||
| 과도한 비용·노력 없이 이용 가능 | B5.5.51 | ECOS 공개 데이터 (API) 사용 |
|
||||
| PiT PD 사용 | 5.5.17 | Vasicek Z-조건부 전이행렬 |
|
||||
| Lifetime ECL (Stage 2) | 5.5.3 | 50년 누적/한계 PD term structure |
|
||||
| 정기적 갱신 | B5.5.52 | 분기별 Quarterly Refresh |
|
||||
|
||||
**IFRS 9 (2024년 5월 개정, 2026년 1월 발효) 참고:**
|
||||
최신 개정은 금융자산 분류/측정, ESG 연계, 전자결제 제거에 관한 것이며,
|
||||
ECL/PD 모형 방법론 자체에는 변경이 없습니다.
|
||||
(IFRS 17은 보험계약 회계 기준으로, 본 프로젝트(대출/채권 신용위험)와 적용 범위가 다릅니다.)
|
||||
|
||||
---
|
||||
|
||||
### 2.9 50년 수렴 메커니즘
|
||||
|
||||
**왜 수렴이 필요한가?**
|
||||
|
||||
거시경제 예측은 현실적으로 3-5년이 한계입니다 (IMF WEO는 5년 전망). 50년 예측에서는:
|
||||
1. **1~5년 (PIT 구간)**: 거시 시나리오 기반 Zt 적용 — 가장 신뢰도 높은 구간
|
||||
2. **6~10년 (전환 구간)**: Mean-reversion으로 점진적 수렴 — 불확실성 증가에 대응
|
||||
3. **11~50년 (TTC 구간)**: Z = 0 (장기 평균) — 경기 사이클이 반복된다는 가정
|
||||
AR(1) 모형에서 0 < φ < 1이면 Z(t)는 자동으로 장기 균형으로 수렴합니다.
|
||||
별도의 수렴 메커니즘이 불필요하며, 이것이 AR(1) 모형의 핵심 장점입니다.
|
||||
|
||||
**Mean-Reversion 공식:**
|
||||
**수렴 속도:**
|
||||
|
||||
```
|
||||
Z_t^adj = Z_t^scenario × exp(-λ × (t - T_pit)) (t > T_pit)
|
||||
```
|
||||
|
||||
- λ = 0.3: Mean-reversion 속도 — 5년 후 Z가 약 22%로 감소
|
||||
- T_pit = 5: PIT 적용 종료 시점
|
||||
| φ | 반감기 | 충격이 5% 이하로 감쇠 | 50년 시점 잔여 충격 |
|
||||
|---|--------|-------------------|-----------------|
|
||||
| 0.3 | 0.6년 | ~2.5년 | ≈ 0% |
|
||||
| 0.5 | 1.0년 | ~4.3년 | ≈ 0% |
|
||||
| 0.7 | 1.9년 | ~8.4년 | ≈ 0% |
|
||||
| 0.9 | 6.6년 | ~28.4년 | ~0.5% |
|
||||
|
||||
**학술적 근거:**
|
||||
- Ornstein-Uhlenbeck 과정: 금리/스프레드 모형에서 널리 사용
|
||||
- Ornstein-Uhlenbeck 과정의 이산 시간 버전이 AR(1)
|
||||
- Basel III FRTB: 장기 리스크 파라미터의 평균회귀 가정
|
||||
- IFRS 9 IG: 예측 불가능한 장기 구간에서는 역사적 평균 사용 권장
|
||||
|
||||
|
||||
35
main.py
35
main.py
@@ -103,18 +103,38 @@ def main():
|
||||
tm_source = data_config.get("transition_source", "builtin")
|
||||
tm_dir = data_config.get("transition_dir", None)
|
||||
logger.info(f"전이행렬 로딩 중 (source={tm_source})...")
|
||||
transition_matrices = load_transition_matrices(tm_source, data_dir=tm_dir)
|
||||
transition_matrices_all = load_transition_matrices(tm_source, data_dir=tm_dir)
|
||||
# 2000-2025 필터
|
||||
transition_matrices_raw = {y:m for y,m in transition_matrices_all.items() if 2000 <= y <= 2025}
|
||||
|
||||
# PD 플로어 적용: KAP 채권 YTM 기반 시장내재 PD
|
||||
from data.pd_floor import apply_pd_floor_to_matrices, build_complete_pd_floor_table
|
||||
pd_floors_broad, _, pd_floors_full = build_complete_pd_floor_table()
|
||||
transition_matrices = apply_pd_floor_to_matrices(transition_matrices_raw, pd_floors_broad)
|
||||
|
||||
ttc_matrix = compute_ttc_matrix(transition_matrices)
|
||||
default_rates = get_default_rates(transition_matrices)
|
||||
|
||||
print(f"\n 전이행렬: {len(transition_matrices)}개 연도 ({min(transition_matrices.keys())}~{max(transition_matrices.keys())})"
|
||||
f" [source={tm_source}]")
|
||||
print(display_matrix(ttc_matrix, "TTC 전이행렬 (장기 평균)"))
|
||||
print(f" PD 플로어 (KAP 채권 YTM 기반): AAA={pd_floors_broad['AAA']*10000:.0f}bp, AA={pd_floors_broad['AA']*10000:.0f}bp, "
|
||||
f"A={pd_floors_broad['A']*10000:.0f}bp, BBB={pd_floors_broad['BBB']*10000:.0f}bp")
|
||||
print(display_matrix(ttc_matrix, "TTC 전이행렬 (KAP PD Floor 적용 후 장기 평균)"))
|
||||
|
||||
# 거시경제변수
|
||||
if args.no_api:
|
||||
logger.info("Fallback 거시경제 데이터 사용")
|
||||
macro_data = _fallback_macro_data()
|
||||
# ECOS fallback 데이터도 병합 (37개 변수)
|
||||
try:
|
||||
from data.ecos_fetcher import load_macro_data as load_ecos_macro
|
||||
ecos_data = load_ecos_macro()
|
||||
if ecos_data is not None and not ecos_data.empty:
|
||||
macro_data = pd.concat([macro_data, ecos_data], axis=1)
|
||||
macro_data = macro_data.loc[:, ~macro_data.columns.duplicated()]
|
||||
logger.info(f"ECOS fallback 병합 완료: {len(macro_data.columns)}개 변수")
|
||||
except Exception as e:
|
||||
logger.warning(f"ECOS fallback 병합 실패: {e}")
|
||||
else:
|
||||
macro_data = load_macro_data(args.config)
|
||||
|
||||
@@ -167,10 +187,19 @@ def main():
|
||||
model_input = macro_data
|
||||
|
||||
forced_vars = config.get("model", {}).get("macro_vars", None)
|
||||
macro_model = build_macro_zt_model(zt_dict, model_input, method="stepwise_aic",
|
||||
macro_method = config.get("model", {}).get("macro_method", "ar1_macro")
|
||||
macro_model = build_macro_zt_model(zt_dict, model_input, method=macro_method,
|
||||
forced_vars=forced_vars)
|
||||
|
||||
print(f"\n 선택된 변수: {macro_model.selected_vars}")
|
||||
if macro_model.is_ar1:
|
||||
import math
|
||||
phi = macro_model.ar1_phi
|
||||
half_life = math.log(2) / abs(math.log(abs(phi))) if 0 < abs(phi) < 1 else float('inf')
|
||||
print(f" [AR(1)+Macro] φ = {phi:.4f} (반감기 = {half_life:.1f}년)")
|
||||
print(f" c = {macro_model.ar1_const:.4f}")
|
||||
for var, beta in macro_model.ar1_beta.items():
|
||||
print(f" β({var}) = {beta:+.6f}")
|
||||
print(macro_model.summary())
|
||||
|
||||
diag = macro_model.diagnostics()
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
"""
|
||||
거시경제 변수 ↔ Zt 연계 통계모형
|
||||
거시경제 변수 ↔ Zt 연계 AR(1) + Macro 모형
|
||||
|
||||
Zt(신용사이클 인덱스)를 거시경제변수로 설명하는 회귀모형을 구축하고,
|
||||
미래 거시 시나리오에 따른 Zt 전망을 생성합니다.
|
||||
Zt(신용사이클 인덱스)를 거시경제변수와 자기회귀 항으로 설명하는 모형.
|
||||
|
||||
모형:
|
||||
Z_t = β₀ + β₁·GDP_growth + β₂·Unemployment + β₃·Base_Rate
|
||||
+ β₄·CD_Rate + β₅·CPI_growth + β₆·Leading_Index + ε_t
|
||||
AR(1) + Macro 모형:
|
||||
Z(t) = c + phi*Z(t-1) + beta1*X1(t) + beta2*X2(t) + beta3*X3(t) + eps(t)
|
||||
|
||||
방법론 참고:
|
||||
- IMF (2021). "IFRS 9 and CECL Compatible Estimation for Top-Down Solvency Stress Testing"
|
||||
- ECB (2019). "Scenario Design for IFRS 9 Expected Credit Loss Estimation"
|
||||
- Fed (2022). "Dodd-Frank Act Stress Test Methodology"
|
||||
- Moody's Analytics: Z-score macro regression scenario forecast
|
||||
- Zanders Group: Vasicek Z macro regression PiT transition matrix
|
||||
- EBA/ECB: Forward-looking macro overlay on Z-index
|
||||
- IFRS 9 B5.5.42-44
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
@@ -45,7 +44,15 @@ class MacroZtModel:
|
||||
self.model = None
|
||||
self.result = None
|
||||
self.selected_vars = None
|
||||
self.scaler_params = {} # 정규화 파라미터
|
||||
self.scaler_params = {}
|
||||
# AR(1) attributes
|
||||
self.ar1_phi = None
|
||||
self.ar1_const = None
|
||||
self.ar1_beta = None
|
||||
self.ar1_sigma_eps = None
|
||||
self.ar1_macro_stats = {}
|
||||
self.is_ar1 = False
|
||||
self._ar1_var_names = None
|
||||
|
||||
def fit(
|
||||
self,
|
||||
@@ -253,16 +260,23 @@ class MacroZtModel:
|
||||
try:
|
||||
X = self.result.model.exog
|
||||
vif_values = {}
|
||||
if self.is_ar1 and self._ar1_var_names:
|
||||
var_names = self._ar1_var_names
|
||||
else:
|
||||
var_names = ["const"] + self.selected_vars
|
||||
for i in range(X.shape[1]):
|
||||
for i in range(min(X.shape[1], len(var_names))):
|
||||
vif_values[var_names[i]] = variance_inflation_factor(X, i)
|
||||
diag["vif"] = vif_values
|
||||
except Exception:
|
||||
diag["vif"] = {}
|
||||
|
||||
# 계수 요약
|
||||
if self.is_ar1 and self._ar1_var_names:
|
||||
coef_names = self._ar1_var_names
|
||||
else:
|
||||
coef_names = ["const"] + self.selected_vars
|
||||
coef_df = pd.DataFrame({
|
||||
"변수": ["const"] + self.selected_vars,
|
||||
"변수": coef_names,
|
||||
"계수": self.result.params,
|
||||
"표준오차": self.result.bse,
|
||||
"t값": self.result.tvalues,
|
||||
@@ -284,35 +298,200 @@ class MacroZtModel:
|
||||
return np.array([])
|
||||
return self.result.resid
|
||||
|
||||
# ================================================================
|
||||
# AR(1) + Macro 모형
|
||||
# ================================================================
|
||||
|
||||
def fit_ar1(
|
||||
self,
|
||||
zt_series: pd.Series,
|
||||
macro_data: pd.DataFrame,
|
||||
forced_vars: Optional[List[str]] = None
|
||||
) -> "MacroZtModel":
|
||||
"""
|
||||
AR(1) + Macro 모형 적합
|
||||
|
||||
Z(t) = c + phi*Z(t-1) + beta1*X1(t) + beta2*X2(t) + beta3*X3(t) + eps(t)
|
||||
|
||||
Z(t-1)을 설명변수에 포함하여 OLS로 추정합니다.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
zt_series : pd.Series
|
||||
index=연도, values=Zt 추정값
|
||||
macro_data : pd.DataFrame
|
||||
index=연도, columns=거시변수
|
||||
forced_vars : List[str], optional
|
||||
강제 지정 거시변수
|
||||
"""
|
||||
self.is_ar1 = True
|
||||
|
||||
# 인덱스 정렬 및 교집합
|
||||
common_years = sorted(set(zt_series.index) & set(macro_data.index))
|
||||
if len(common_years) < 5:
|
||||
raise ValueError(f"공통 데이터 포인트가 부족합니다: {len(common_years)}개")
|
||||
|
||||
# AR(1) 구성: Z(t)와 Z(t-1) 쌍
|
||||
zt_full = zt_series.loc[common_years].sort_index()
|
||||
years = list(zt_full.index)
|
||||
|
||||
# t-1이 필요하므로 첫 해 제외
|
||||
y_years = years[1:]
|
||||
|
||||
y = zt_full.loc[y_years].values.astype(float)
|
||||
z_lag = zt_full.loc[years[:-1]].values.astype(float)
|
||||
|
||||
# 거시변수
|
||||
X_macro = macro_data.loc[y_years].copy()
|
||||
X_macro = X_macro.ffill().bfill().dropna(axis=1)
|
||||
|
||||
# 변수 선택
|
||||
if forced_vars:
|
||||
available = [v for v in forced_vars if v in X_macro.columns]
|
||||
if len(available) != len(forced_vars):
|
||||
missing = set(forced_vars) - set(available)
|
||||
logger.warning(f"AR(1) 강제 지정 변수 중 누락: {missing}")
|
||||
self.selected_vars = available
|
||||
else:
|
||||
self.selected_vars = list(X_macro.columns)
|
||||
|
||||
# 거시변수 표본 통계 저장 (시나리오 충격용)
|
||||
for col in self.selected_vars:
|
||||
col_data = macro_data[col].dropna()
|
||||
self.ar1_macro_stats[col] = {
|
||||
"mean": float(col_data.mean()),
|
||||
"std": float(col_data.std()),
|
||||
"last": float(col_data.iloc[-1]) if len(col_data) > 0 else 0.0
|
||||
}
|
||||
|
||||
# 거시변수 표준화 (mean=0, std=1)
|
||||
# → β가 "1σ 충격의 Z 영향"으로 직접 해석됨
|
||||
# → 절편 c가 장기 평균 Z 수준을 결정
|
||||
X_std = X_macro[self.selected_vars].copy()
|
||||
for col in self.selected_vars:
|
||||
mean = self.ar1_macro_stats[col]["mean"]
|
||||
std = self.ar1_macro_stats[col]["std"]
|
||||
if std > 0:
|
||||
X_std[col] = (X_std[col] - mean) / std
|
||||
else:
|
||||
X_std[col] = 0.0
|
||||
|
||||
# 설계행렬: [const, Z(t-1), X1_std(t), X2_std(t), X3_std(t)]
|
||||
X_design = np.column_stack([
|
||||
z_lag,
|
||||
X_std.values
|
||||
])
|
||||
X_with_const = sm.add_constant(X_design)
|
||||
|
||||
# OLS 적합
|
||||
self.model = sm.OLS(y, X_with_const)
|
||||
self.result = self.model.fit()
|
||||
|
||||
# AR(1) 파라미터 추출
|
||||
# params: [const, phi, beta1, beta2, ...]
|
||||
params = self.result.params
|
||||
self.ar1_const = params[0]
|
||||
self.ar1_phi = params[1]
|
||||
self.ar1_beta = {}
|
||||
for i, col in enumerate(self.selected_vars):
|
||||
self.ar1_beta[col] = params[2 + i]
|
||||
self.ar1_sigma_eps = float(np.std(self.result.resid))
|
||||
|
||||
# 변수명 저장 (진단용)
|
||||
self._ar1_var_names = ["const", "Z_lag1"] + self.selected_vars
|
||||
|
||||
# 정상성 체크
|
||||
if abs(self.ar1_phi) > 0 and abs(self.ar1_phi) < 1:
|
||||
half_life = np.log(2) / abs(np.log(abs(self.ar1_phi)))
|
||||
else:
|
||||
half_life = np.inf
|
||||
|
||||
logger.info(f"AR(1)+Macro 적합 완료:")
|
||||
logger.info(f" phi = {self.ar1_phi:.4f} (반감기 = {half_life:.1f}년)")
|
||||
logger.info(f" c = {self.ar1_const:.4f}")
|
||||
for col, beta in self.ar1_beta.items():
|
||||
logger.info(f" beta({col}) = {beta:+.6f}")
|
||||
logger.info(f" R2 = {self.result.rsquared:.4f}, "
|
||||
f"Adj.R2 = {self.result.rsquared_adj:.4f}")
|
||||
|
||||
if abs(self.ar1_phi) >= 1.0:
|
||||
logger.warning(f" phi={self.ar1_phi:.4f} >= 1.0 -> non-stationary!")
|
||||
|
||||
return self
|
||||
|
||||
def forecast_z_path(
|
||||
self,
|
||||
z_last: float,
|
||||
macro_shocks: Dict[str, float],
|
||||
horizon: int = 50
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
AR(1) 모형으로 미래 Z 경로 생성
|
||||
|
||||
t=1: Z = c + phi*Z(t0) + sum(beta_i * shock_i * sigma_i)
|
||||
t>=2: Z = c + phi*Z(t-1) (거시 충격 없음, AR 감쇠만)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
z_last : float
|
||||
마지막 관측 Z(t0)
|
||||
macro_shocks : Dict[str, float]
|
||||
변수별 충격 (sigma 배수)
|
||||
예: {"USDKRW": +1.5, "RETAIL_SALES": -1.5}
|
||||
horizon : int
|
||||
예측 기간 (년)
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.ndarray : [Z(t0+1), ..., Z(t0+horizon)]
|
||||
"""
|
||||
if not self.is_ar1:
|
||||
raise ValueError("AR(1) 모형이 적합되지 않았습니다.")
|
||||
|
||||
z_path = np.zeros(horizon)
|
||||
z_prev = z_last
|
||||
|
||||
for t in range(horizon):
|
||||
z_next = self.ar1_const + self.ar1_phi * z_prev
|
||||
|
||||
# t=0 (첫 해)만 거시 충격 적용
|
||||
# β는 표준화된 변수 기준이므로 shock_sigma가 곧 β의 배수
|
||||
if t == 0:
|
||||
for var, shock_sigma in macro_shocks.items():
|
||||
if var in self.ar1_beta:
|
||||
beta = self.ar1_beta[var]
|
||||
z_next += beta * shock_sigma
|
||||
|
||||
z_path[t] = z_next
|
||||
z_prev = z_next
|
||||
|
||||
return z_path
|
||||
|
||||
|
||||
def build_macro_zt_model(
|
||||
zt_dict: Dict[int, float],
|
||||
macro_df: pd.DataFrame,
|
||||
method: str = "stepwise_aic",
|
||||
method: str = "ar1_macro",
|
||||
forced_vars: Optional[List[str]] = None
|
||||
) -> MacroZtModel:
|
||||
"""
|
||||
편의 함수: Zt 딕셔너리 + 거시 DataFrame → 회귀모형 구축
|
||||
편의 함수: Zt + 거시 DataFrame -> 회귀모형 구축
|
||||
|
||||
Parameters
|
||||
----------
|
||||
zt_dict : Dict[int, float]
|
||||
{연도: Zt값}
|
||||
macro_df : pd.DataFrame
|
||||
index=연도, columns=거시변수
|
||||
method : str
|
||||
변수 선택 방법
|
||||
forced_vars : List[str], optional
|
||||
강제 지정 변수 (지정 시 method 무시)
|
||||
|
||||
Returns
|
||||
-------
|
||||
MacroZtModel : 적합된 모형
|
||||
zt_dict : {연도: Zt값}
|
||||
macro_df : index=연도, columns=거시변수
|
||||
method : "ar1_macro" (기본) | "stepwise_aic" | "all"
|
||||
forced_vars : 강제 지정 변수
|
||||
"""
|
||||
zt_series = pd.Series(zt_dict, name="Zt")
|
||||
zt_series.index.name = "YEAR"
|
||||
|
||||
model = MacroZtModel()
|
||||
|
||||
if method == "ar1_macro":
|
||||
model.fit_ar1(zt_series, macro_df, forced_vars=forced_vars)
|
||||
else:
|
||||
model.fit(zt_series, macro_df, method=method, forced_vars=forced_vars)
|
||||
|
||||
return model
|
||||
|
||||
692
reports/generate_report.py
Normal file
692
reports/generate_report.py
Normal file
@@ -0,0 +1,692 @@
|
||||
"""
|
||||
Lifetime PD 분석 보고서 생성기 (Excel)
|
||||
|
||||
사용법:
|
||||
python reports/generate_report.py
|
||||
python reports/generate_report.py --config config.yaml --output results/report.xlsx
|
||||
|
||||
다중 시트 구성:
|
||||
1. 요약 (Summary) — 모형 개요, 핵심 파라미터, 결론
|
||||
2. 원시데이터_전이행렬 — 연도별 전이행렬, TTC 행렬
|
||||
3. 원시데이터_거시변수 — ECOS 거시경제변수 시계열
|
||||
4. Zt_추정 — Belkin & Suchower Zt 역산 결과
|
||||
5. AR1_모형 — AR(1)+Macro 회귀 결과, 계수, 진단
|
||||
6. 시나리오_Z경로 — 3 시나리오별 50년 Zt 경로
|
||||
7. Lifetime_PD — 시나리오별 누적 PD term structure
|
||||
8. 가중평균_PD — 확률가중 최종 PD
|
||||
9. 검증결과 — 통계 검정 결과
|
||||
"""
|
||||
|
||||
import sys, io, os, argparse, math
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
if sys.stdout.encoding != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
from data.transition_matrices import (
|
||||
load_transition_matrices, compute_ttc_matrix,
|
||||
RATING_GRADES, RATING_GRADES_8
|
||||
)
|
||||
from data.ccc_interpolator import expand_to_8x8
|
||||
from data.macro_data import _fallback_macro_data, compute_derived_features
|
||||
from data.ecos_fetcher import load_macro_data as load_ecos_macro
|
||||
from models.credit_cycle import estimate_zt_series
|
||||
from models.macro_model import build_macro_zt_model
|
||||
from scenarios.scenario_engine import ScenarioEngine
|
||||
from projection.lifetime_pd import LifetimePDEngine
|
||||
from validation.statistical_tests import run_full_validation
|
||||
|
||||
# ================================================================
|
||||
# 스타일 정의
|
||||
# ================================================================
|
||||
NAVY = "1F3864"
|
||||
DARK_BLUE = "2B5797"
|
||||
LIGHT_BLUE = "D6E4F0"
|
||||
LIGHTER_BLUE = "EDF2F9"
|
||||
WHITE = "FFFFFF"
|
||||
BORDER_CLR = "B4C6E7"
|
||||
|
||||
TITLE_FONT = Font(name="맑은 고딕", size=16, bold=True, color=WHITE)
|
||||
HEADER_FONT = Font(name="맑은 고딕", size=10, bold=True, color=WHITE)
|
||||
SUBHEADER_FONT = Font(name="맑은 고딕", size=10, bold=True, color=NAVY)
|
||||
BODY_FONT = Font(name="맑은 고딕", size=9)
|
||||
BODY_BOLD = Font(name="맑은 고딕", size=9, bold=True)
|
||||
SMALL_FONT = Font(name="맑은 고딕", size=8, color="666666")
|
||||
NUM_FONT = Font(name="Consolas", size=9)
|
||||
PASS_FONT = Font(name="맑은 고딕", size=9, bold=True, color="2E7D32")
|
||||
FAIL_FONT = Font(name="맑은 고딕", size=9, bold=True, color="C62828")
|
||||
|
||||
TITLE_FILL = PatternFill("solid", fgColor=NAVY)
|
||||
HEADER_FILL = PatternFill("solid", fgColor=DARK_BLUE)
|
||||
SUBHEADER_FILL = PatternFill("solid", fgColor=LIGHT_BLUE)
|
||||
ALT_FILL = PatternFill("solid", fgColor=LIGHTER_BLUE)
|
||||
PASS_FILL = PatternFill("solid", fgColor="E2EFDA")
|
||||
FAIL_FILL = PatternFill("solid", fgColor="FCE4EC")
|
||||
|
||||
THIN_BORDER = Border(
|
||||
left=Side(style='thin', color=BORDER_CLR),
|
||||
right=Side(style='thin', color=BORDER_CLR),
|
||||
top=Side(style='thin', color=BORDER_CLR),
|
||||
bottom=Side(style='thin', color=BORDER_CLR)
|
||||
)
|
||||
CENTER = Alignment(horizontal='center', vertical='center', wrap_text=True)
|
||||
LEFT = Alignment(horizontal='left', vertical='center', wrap_text=True)
|
||||
RIGHT = Alignment(horizontal='right', vertical='center')
|
||||
|
||||
NUM4 = '0.0000'
|
||||
NUM2 = '0.00'
|
||||
|
||||
|
||||
def _widths(ws, widths):
|
||||
for i, w in enumerate(widths, 1):
|
||||
ws.column_dimensions[get_column_letter(i)].width = w
|
||||
|
||||
|
||||
def _title(ws, row, text, ncols=10):
|
||||
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=ncols)
|
||||
c = ws.cell(row=row, column=1, value=text)
|
||||
c.font = TITLE_FONT; c.fill = TITLE_FILL; c.alignment = LEFT
|
||||
ws.row_dimensions[row].height = 35
|
||||
|
||||
|
||||
def _section(ws, row, text, ncols=10):
|
||||
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=ncols)
|
||||
c = ws.cell(row=row, column=1, value=text)
|
||||
c.font = SUBHEADER_FONT; c.fill = SUBHEADER_FILL; c.alignment = LEFT
|
||||
ws.row_dimensions[row].height = 22
|
||||
return row + 1
|
||||
|
||||
|
||||
def _headers(ws, row, hdrs):
|
||||
for j, h in enumerate(hdrs, 1):
|
||||
c = ws.cell(row=row, column=j, value=h)
|
||||
c.font = HEADER_FONT; c.fill = HEADER_FILL; c.alignment = CENTER; c.border = THIN_BORDER
|
||||
return row + 1
|
||||
|
||||
|
||||
def _row(ws, row, vals, alt=False, fmt=None):
|
||||
fill = ALT_FILL if alt else PatternFill()
|
||||
for j, v in enumerate(vals, 1):
|
||||
c = ws.cell(row=row, column=j, value=v)
|
||||
c.font = NUM_FONT if isinstance(v, (int, float, np.floating, np.integer)) else BODY_FONT
|
||||
c.fill = fill; c.border = THIN_BORDER
|
||||
c.alignment = RIGHT if isinstance(v, (int, float, np.floating, np.integer)) else LEFT
|
||||
if fmt and isinstance(v, (float, np.floating)):
|
||||
c.number_format = fmt
|
||||
return row + 1
|
||||
|
||||
|
||||
def _kv(ws, row, key, value, col=2, fmt=None):
|
||||
ws.cell(row=row, column=col, value=key).font = BODY_BOLD
|
||||
cell = ws.cell(row=row, column=col+1, value=value)
|
||||
cell.font = NUM_FONT if isinstance(value, (int, float, np.floating)) else BODY_FONT
|
||||
if fmt and isinstance(value, (float, np.floating)):
|
||||
cell.number_format = fmt
|
||||
return row + 1
|
||||
|
||||
|
||||
# ================================================================
|
||||
# 시트 생성
|
||||
# ================================================================
|
||||
|
||||
def sheet_summary(wb, config, model, zt_dict, diag, z_paths, val_df, pd_engine, pd_results, grades):
|
||||
ws = wb.active; ws.title = "요약"
|
||||
_widths(ws, [3,25,18,18,18,18,12,12,12,12])
|
||||
r = 1; _title(ws, r, " Lifetime PD 분석 보고서", 10)
|
||||
r = 2; ws.cell(row=r, column=2, value=f"생성일시: {datetime.now().strftime('%Y-%m-%d %H:%M')}").font = SMALL_FONT
|
||||
r = 4
|
||||
# 1. 모형 개요
|
||||
r = _section(ws, r, " 1. 모형 개요", 10)
|
||||
r = _kv(ws, r, "모형 구조", "Z(t) = c + φ·Z(t-1) + β₁·X₁_std + β₂·X₂_std + β₃·X₃_std + ε")
|
||||
r = _kv(ws, r, "모형 유형", "AR(1) + Macro (Vasicek Single-Factor)")
|
||||
r = _kv(ws, r, "적용 기준", "IFRS 9 (2018, 2024 개정)")
|
||||
r = _kv(ws, r, "변수 선택", ", ".join(model.selected_vars))
|
||||
r += 1
|
||||
# 2. AR(1) 파라미터
|
||||
r = _section(ws, r, " 2. AR(1) 모형 파라미터", 10)
|
||||
r = _kv(ws, r, "자기회귀 계수 (φ)", model.ar1_phi, fmt=NUM4)
|
||||
phi = model.ar1_phi
|
||||
hl = math.log(2)/abs(math.log(abs(phi))) if 0<abs(phi)<1 else float('inf')
|
||||
r = _kv(ws, r, "반감기", f"{hl:.1f}년")
|
||||
r = _kv(ws, r, "절편 (c)", model.ar1_const, fmt=NUM4)
|
||||
for var, beta in model.ar1_beta.items():
|
||||
r = _kv(ws, r, f"β({var})", beta, fmt=NUM4)
|
||||
r = _kv(ws, r, "잔차 σ", model.ar1_sigma_eps, fmt=NUM4)
|
||||
lr = model.ar1_const / (1 - model.ar1_phi) if abs(model.ar1_phi) < 1 else 0
|
||||
r = _kv(ws, r, "장기 균형 Z", lr, fmt=NUM4)
|
||||
r += 1
|
||||
# 3. 적합도
|
||||
r = _section(ws, r, " 3. 모형 적합도", 10)
|
||||
r = _kv(ws, r, "R²", diag.get("r_squared",0), fmt=NUM4)
|
||||
r = _kv(ws, r, "Adj. R²", diag.get("adj_r_squared",0), fmt=NUM4)
|
||||
r = _kv(ws, r, "F p-value", diag.get("f_pvalue",0), fmt=NUM4)
|
||||
r = _kv(ws, r, "AIC", diag.get("aic",0), fmt=NUM2)
|
||||
r = _kv(ws, r, "DW", diag.get("durbin_watson",0), fmt=NUM4)
|
||||
r += 1
|
||||
# 4. 시나리오
|
||||
r = _section(ws, r, " 4. 시나리오 설정", 10)
|
||||
hdrs = ["", "시나리오", "가중치"]
|
||||
for v in model.selected_vars:
|
||||
hdrs.append(f"{v} (σ)")
|
||||
hdrs.append("Z(t+1)")
|
||||
r = _headers(ws, r, hdrs)
|
||||
for sname, scfg in config.get("scenarios", {}).items():
|
||||
vals = [None, scfg.get("name", sname), scfg.get("weight", 0)]
|
||||
for v in model.selected_vars:
|
||||
vals.append(scfg.get("macro_shocks", {}).get(v, 0))
|
||||
z1 = z_paths.get(sname, [0])[0] if z_paths else 0
|
||||
vals.append(float(z1))
|
||||
r = _row(ws, r, vals, alt=(sname=="base"), fmt=NUM4)
|
||||
r += 1
|
||||
# 5. 1년차 가중 PD
|
||||
r = _section(ws, r, " 5. 1년차 확률가중 PD (%)", 10)
|
||||
r = _headers(ws, r, ["", ""] + list(grades[:-1]))
|
||||
by_sc = pd_results.get("by_scenario", pd_results)
|
||||
wcpd = pd_results.get("weighted_cumulative_pd", None)
|
||||
if wcpd is not None and wcpd.shape[0] > 0:
|
||||
wpd = wcpd[0, :len(grades)-1] * 100
|
||||
else:
|
||||
wpd = np.zeros(len(grades)-1)
|
||||
vals = [None, "가중PD(1Y)"] + list(wpd)
|
||||
r = _row(ws, r, vals, fmt=NUM4)
|
||||
|
||||
|
||||
def sheet_tm(wb, tm_raw, tm_floor, ttc, pd_floors, config):
|
||||
ws = wb.create_sheet("원시데이터_전이행렬")
|
||||
grades = config.get("model",{}).get("rating_grades", RATING_GRADES)
|
||||
ng = len(grades)
|
||||
_widths(ws, [3,12]+[12]*ng)
|
||||
nc = 2+ng; r=1
|
||||
_title(ws, r, " 전이행렬 파이프라인: Original → PD Floor → TTC", nc)
|
||||
r=3
|
||||
# KAP 채권 YTM 기반 PD Floor 산출 과정
|
||||
from data.ytm_fetcher import get_ytm_data, compute_spreads, compute_broad_grade_spreads
|
||||
from data.pd_floor import compute_market_implied_pd
|
||||
ytm_data = get_ytm_data()
|
||||
notch_spreads = compute_spreads(ytm_data)
|
||||
broad_spreads = compute_broad_grade_spreads(notch_spreads)
|
||||
lgd = 0.60
|
||||
rf = ytm_data.get('rf', 0)
|
||||
r = _section(ws, r, " KAP 채권 YTM → 신용스프레드 → 시장내재 PD (Floor 산출 근거)", nc)
|
||||
hdr_ytm = ["", "등급", "KAP YTM(%)", "스프레드(bp)", "내재PD(bp)", "Basel III(bp)", "적용Floor(bp)"]
|
||||
while len(hdr_ytm) < 2 + ng:
|
||||
hdr_ytm.append("")
|
||||
r = _headers(ws, r, hdr_ytm[:2 + ng])
|
||||
floor_grades = ["AAA", "AA", "A", "BBB", "BB", "B"]
|
||||
for fg in floor_grades:
|
||||
ytm_val = None
|
||||
for notch in [fg, fg + '+', fg + '-']:
|
||||
if notch in ytm_data:
|
||||
ytm_val = ytm_data[notch]
|
||||
break
|
||||
if ytm_val is None:
|
||||
ytm_val = rf
|
||||
sp = broad_spreads.get(fg, 0)
|
||||
implied_pd = compute_market_implied_pd(sp, lgd) * 10000
|
||||
applied = pd_floors.get(fg, 0) * 10000
|
||||
v = [None, fg, ytm_val, sp, implied_pd, 5, applied]
|
||||
while len(v) < 2 + ng:
|
||||
v.append(None)
|
||||
r = _row(ws, r, v[:2 + ng], fmt=NUM2)
|
||||
ws.cell(row=r, column=2,
|
||||
value=f"기준일: 2025-12-31, 국고1Y: {rf}%, LGD: 60%, 출처: KAP(한국자산평가)").font = SMALL_FONT
|
||||
r += 1
|
||||
ws.cell(row=r, column=2,
|
||||
value="산식: Implied PD = 1 - exp(-spread_bp / (LGD×10000)), Floor = max(Implied PD, Basel 5bp)").font = SMALL_FONT
|
||||
r += 2
|
||||
# TTC 전이행렬
|
||||
r = _section(ws, r, f" TTC 전이행렬 (PD Floor 적용 후, {min(tm_floor.keys())}~{max(tm_floor.keys())} 평균)", nc)
|
||||
r = _headers(ws, r, ["","From\\To"]+grades)
|
||||
for i,g in enumerate(grades):
|
||||
vals = [None,g]+[ttc[i,j] for j in range(min(ng,ttc.shape[1]))]
|
||||
r = _row(ws, r, vals, alt=i%2==1, fmt=NUM4)
|
||||
r += 1
|
||||
# 전체 연도별 전이행렬 (Floor 적용 후)
|
||||
for year in sorted(tm_floor.keys()):
|
||||
r = _section(ws, r, f" {year}년 전이행렬 (PD Floor 적용 후)", nc)
|
||||
r = _headers(ws, r, ["","From\\To"]+grades)
|
||||
mat = tm_floor[year]
|
||||
for i,g in enumerate(grades):
|
||||
if i < mat.shape[0]:
|
||||
vals = [None,g]+[mat[i,j] for j in range(min(ng,mat.shape[1]))]
|
||||
r = _row(ws, r, vals, alt=i%2==1, fmt=NUM4)
|
||||
r += 1
|
||||
|
||||
|
||||
# 영문→한글 변수명 매핑
|
||||
VAR_KOR = {
|
||||
"GDP_GROWTH": "GDP성장률(%)", "IPI": "광공업생산지수", "SPI": "서비스업생산지수",
|
||||
"MANUF_CAPACITY": "제조업가동률", "GFCF_GROWTH": "총고정자본증감률",
|
||||
"CONSTR_INVEST": "건설투자증감률", "FACILITY_INVEST": "설비투자지수",
|
||||
"RETAIL_SALES": "소매판매액지수", "CSI": "소비자심리지수", "BSI_MANUF": "제조업BSI",
|
||||
"LEADING_INDEX": "경기선행지수", "COINCIDENT": "경기동행지수",
|
||||
"EXPORT": "수출(백만달러)", "IMPORT_AMT": "수입(백만달러)",
|
||||
"TRADE_GNI": "수출입/GNI(%)", "KOSPI": "KOSPI지수",
|
||||
"INVEST_RATE": "국내총투자율(%)", "SAVING_RATE": "총저축률(%)",
|
||||
"HOUSING_PRICE": "주택매매가격지수",
|
||||
"UNEMPLOYMENT": "실업률(%)", "EMPLOYMENT": "고용률(%)",
|
||||
"EMPLOYED": "취업자수(만명)", "EMPLOYMENT_RATE": "고용률(%)",
|
||||
"BASE_RATE": "기준금리(%)", "CD_RATE": "CD91일(%)",
|
||||
"GOVT_3Y": "국고3Y(%)", "GOVT_10Y": "국고10Y(%)",
|
||||
"CORP_AA": "회사체AA-(%)", "CORP_BBB": "회사체BBB-(%)",
|
||||
"CPI_GROWTH": "소비자물가상승률(%)", "IMPORT_PRICE": "수입물가지수",
|
||||
"PPI": "생산자물가지수", "USDKRW": "원/달러환율",
|
||||
"M2": "M2광의통화(조원)", "DISHONOR_RATE": "어음부도율(%)",
|
||||
"DISHONOR_AMT": "부도금액(억원)", "HOUSEHOLD_DEBT": "가계부채(조원)",
|
||||
"CONSTRUCTION": "건설수주액(억원)", "CONSTRUCTION_DONE": "건설기성액",
|
||||
"CREDIT_SPREAD": "신용스프레드(BBB-AA)", "TERM_SPREAD": "기간스프레드(10Y-3Y)",
|
||||
"CREDIT_SPREAD_LAG1": "신용스프레드(t-1)",
|
||||
"EXPORT_DIFF": "수출증감액", "IPI_LAG1": "광공업생산(t-1)",
|
||||
"CONSTR_INVEST_GR": "건설투자증가율", "CURRENT_ACCOUNT": "경상수지",
|
||||
}
|
||||
# 변환 변수 한글명 자동 생성
|
||||
TRANSFORM_SUFFIX = {"_LAG2": "(t-2)", "_L": "(log)", "_D": "(차분)",
|
||||
"_R": "(수익률)", "_LR": "(log수익률)"}
|
||||
|
||||
def _kor(varname):
|
||||
if varname in VAR_KOR:
|
||||
return VAR_KOR[varname]
|
||||
for sfx, label in TRANSFORM_SUFFIX.items():
|
||||
if varname.endswith(sfx):
|
||||
base = varname[:-len(sfx)]
|
||||
base_kor = VAR_KOR.get(base, base)
|
||||
return f"{base_kor}{label}"
|
||||
return varname
|
||||
|
||||
def sheet_macro(wb, macro_data, forced_vars):
|
||||
ws = wb.create_sheet("원시데이터_거시변수")
|
||||
display_cols = list(forced_vars) + [c for c in macro_data.columns if c not in forced_vars]
|
||||
display_cols = [c for c in display_cols if c in macro_data.columns]
|
||||
_widths(ws, [3,8]+[14]*len(display_cols))
|
||||
nc = 2+len(display_cols); r=1
|
||||
_title(ws, r, " 원시 데이터: 거시경제변수", nc)
|
||||
r=3
|
||||
r = _section(ws, r, f" ★ 선택 변수: {', '.join([_kor(v) for v in forced_vars])}", nc)
|
||||
r = _headers(ws, r, ["","연도"]+[_kor(c) for c in display_cols])
|
||||
for i,(year,rd) in enumerate(macro_data.iterrows()):
|
||||
vals = [None,int(year)]+[rd[c] if c in rd and pd.notna(rd[c]) else None for c in display_cols]
|
||||
r = _row(ws, r, vals, alt=i%2==1, fmt=NUM2)
|
||||
|
||||
|
||||
def sheet_zt(wb, zt_dict, macro_data, forced_vars, rho):
|
||||
ws = wb.create_sheet("Zt_추정")
|
||||
ncols = 3+len(forced_vars)
|
||||
_widths(ws, [3,10,14]+[14]*len(forced_vars))
|
||||
r=1; _title(ws, r, " Zt 추정 (Belkin & Suchower 1998)", ncols)
|
||||
r=3
|
||||
r = _section(ws, r, " 방법론: 관측 전이행렬 역산 → WLS → Zt", ncols)
|
||||
zv = np.array(list(zt_dict.values()))
|
||||
r = _kv(ws, r, "자산상관계수 (ρ)", rho, fmt=NUM4)
|
||||
r += 1
|
||||
# ρ 근거
|
||||
r = _section(ws, r, " ρ = 0.20 근거", ncols)
|
||||
ws.cell(row=r, column=2, value="[1] Basel III IRB: 기업 ρ = 0.12~0.24 (CRE31.6)").font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value=" R = 0.12×(1-e^(-50×PD))/(1-e^(-50)) + 0.24×(1-(1-e^(-50×PD))/(1-e^(-50)))").font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value="[2] BBB(PD≈0.2%) → R=0.208, A(PD≈0.07%) → R=0.217").font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value="[3] 한국 기업 포트폴리오 평균: ρ ≈ 0.20 (투자/투기 혼합)").font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value="[4] Moody's Analytics CreditEdge: single-factor ρ ≈ 0.15~0.25").font = SMALL_FONT; r+=1
|
||||
r += 1
|
||||
r = _kv(ws, r, "Zt 평균 (μ)", float(zv.mean()), fmt=NUM4)
|
||||
r = _kv(ws, r, "Zt 표준편차 (σ)", float(zv.std()), fmt=NUM4)
|
||||
r = _kv(ws, r, "관측 기간", f"{min(zt_dict.keys())}~{max(zt_dict.keys())} ({len(zt_dict)}개년)")
|
||||
r += 1
|
||||
hdrs = ["","연도","Zt"]+forced_vars
|
||||
r = _headers(ws, r, hdrs)
|
||||
for i,(year,zt) in enumerate(sorted(zt_dict.items())):
|
||||
vals = [None,int(year),float(zt)]
|
||||
for v in forced_vars:
|
||||
if v in macro_data.columns and year in macro_data.index:
|
||||
vals.append(macro_data.loc[year,v] if pd.notna(macro_data.loc[year,v]) else None)
|
||||
else: vals.append(None)
|
||||
r = _row(ws, r, vals, alt=i%2==1, fmt=NUM4)
|
||||
|
||||
|
||||
def sheet_ar1(wb, model, diag):
|
||||
ws = wb.create_sheet("AR1_모형")
|
||||
_widths(ws, [3,22,14,14,14,14,14])
|
||||
r=1; _title(ws, r, " AR(1) + Macro 회귀 모형", 7)
|
||||
r=3
|
||||
r = _section(ws, r, " Z(t) = c + φ·Z(t-1) + Σ βᵢ·Xᵢ_std(t) + ε(t)", 7)
|
||||
ws.cell(row=r, column=2, value="※ 거시변수는 표준화(mean=0, std=1) 후 투입. β = '1σ 충격 → ΔZ'로 해석").font = SMALL_FONT
|
||||
r += 2
|
||||
# 계수
|
||||
r = _section(ws, r, " 회귀 계수", 7)
|
||||
r = _headers(ws, r, ["","변수","계수","표준오차","t값","p값","유의성"])
|
||||
coef_df = diag.get("coefficients", pd.DataFrame())
|
||||
for i,(_,rd) in enumerate(coef_df.iterrows()):
|
||||
pv = rd.get("p값",1)
|
||||
sig = "***" if pv<0.01 else "**" if pv<0.05 else "*" if pv<0.10 else ""
|
||||
vals = [None, rd.get("변수",""), rd.get("계수",0), rd.get("표준오차",0), rd.get("t값",0), pv, sig]
|
||||
rn = _row(ws, r, vals, alt=i%2==1, fmt=NUM4)
|
||||
if pv < 0.05: ws.cell(row=r,column=7).font = PASS_FONT
|
||||
elif pv < 0.10: ws.cell(row=r,column=7).font = Font(name="맑은 고딕",size=9,color="FF8F00")
|
||||
r = rn
|
||||
r += 1
|
||||
# 진단 — 모형 적합도
|
||||
r = _section(ws, r, " 모형 적합도", 7)
|
||||
for k,v,f in [("R²","r_squared",NUM4),("Adj. R²","adj_r_squared",NUM4),
|
||||
("F 통계량","f_stat",NUM4),("F p-value","f_pvalue",NUM4),
|
||||
("AIC","aic",NUM2),("BIC","bic",NUM2)]:
|
||||
r = _kv(ws, r, k, diag.get(v, None), fmt=f)
|
||||
r += 1
|
||||
# 진단 — 잔차 검정 (6개 전항목)
|
||||
r = _section(ws, r, " 잔차 검정 (6개 전항목)", 7)
|
||||
r = _headers(ws, r, ["","검정","통계량","p-value","기준","결과","해석"])
|
||||
tests_data = [
|
||||
("ADF (Zt 정상성)", diag.get("adf_stat"), diag.get("adf_pvalue"),
|
||||
"p < 0.05", diag.get("adf_pvalue",1) < 0.05 if diag.get("adf_pvalue") else False,
|
||||
"BIC lag 선택, H0: 비정상"),
|
||||
("Ljung-Box Q(5)", diag.get("ljung_box_stat"), diag.get("ljung_box_pvalue"),
|
||||
"p > 0.05", diag.get("ljung_box_pvalue",0) > 0.05 if diag.get("ljung_box_pvalue") else False,
|
||||
"H0: 자기상관 없음"),
|
||||
("Durbin-Watson", diag.get("durbin_watson"), None,
|
||||
"1.5~2.5", 1.5 <= diag.get("durbin_watson",0) <= 2.5 if diag.get("durbin_watson") else False,
|
||||
"≈2 이상적"),
|
||||
("Breusch-Pagan", diag.get("bp_stat"), diag.get("bp_pvalue"),
|
||||
"p > 0.05", diag.get("bp_pvalue",0) > 0.05 if diag.get("bp_pvalue") else False,
|
||||
"H0: 등분산"),
|
||||
("ARCH-LM", diag.get("arch_stat"), diag.get("arch_pvalue"),
|
||||
"p > 0.05", diag.get("arch_pvalue",0) > 0.05 if diag.get("arch_pvalue") else False,
|
||||
"H0: ARCH 효과 없음"),
|
||||
("Shapiro-Wilk", diag.get("shapiro_stat"), diag.get("shapiro_pvalue"),
|
||||
"p > 0.05", diag.get("shapiro_pvalue",0) > 0.05 if diag.get("shapiro_pvalue") else False,
|
||||
"H0: 정규분포"),
|
||||
]
|
||||
for tname, stat, pval, crit, passed, note in tests_data:
|
||||
stat_str = f"{stat:.4f}" if stat is not None else "-"
|
||||
pval_str = f"{pval:.4f}" if pval is not None else "-"
|
||||
result_str = "Pass ✅" if passed else "Fail ❌"
|
||||
vals = [None, tname, stat_str, pval_str, crit, result_str, note]
|
||||
r = _row(ws, r, vals)
|
||||
if passed:
|
||||
ws.cell(row=r-1, column=6).font = PASS_FONT
|
||||
else:
|
||||
ws.cell(row=r-1, column=6).font = FAIL_FONT
|
||||
r += 1
|
||||
# 변수 통계
|
||||
r = _section(ws, r, " 거시변수 표본 통계 (표준화 전 원시값)", 7)
|
||||
r = _headers(ws, r, ["","변수","평균","표준편차","최근값","",""])
|
||||
for var,st in model.ar1_macro_stats.items():
|
||||
vals = [None,_kor(var),st["mean"],st["std"],st["last"],None,None]
|
||||
r = _row(ws, r, vals, fmt=NUM2)
|
||||
r += 1
|
||||
# 경제적 해석 섹션
|
||||
r = _section(ws, r, " 변수별 경제적 해석", 7)
|
||||
interp = {
|
||||
"CORP_BBB_LAG2": "2년전 BBB금리↑ → 신용위험 잔존 → 부도↑ → Z↓ (시차효과)",
|
||||
"GFCF_GROWTH_LAG2": "2년전 고정자본투자↑ → 생산능력↑ → 부도↓ → Z↑",
|
||||
"SAVING_RATE_L": "log(저축률)↑ → 경제안정성↑ → 부도↓ → Z↑",
|
||||
"HOUSING_PRICE": "주택가격↑ → 담보가치↑ → 차입여력↑ → 부도↓ → Z↑",
|
||||
"CREDIT_SPREAD_LAG1": "전년 스프레드↑ → 당해 신용위험 전이 → 부도↑ → Z↓ (시차 효과)",
|
||||
"EXPORT_DIFF": "수출증감↑ → 기업매출↑ → 수익성↑ → 부도↓ → Z↑",
|
||||
"CURRENT_ACCOUNT": "경상수지↑(흑자) → 불황기 수출의존↑ → Z↓",
|
||||
"CURRENT_ACCOUNT_R": "경상수지변화율↑ → 대외부문 개선 속도↑ → Z↑ (단기 모멘텀)",
|
||||
"LEADING_INDEX": "경기선행지수↑ → 3~6개월 후 경기확장 → 부도↓ → Z↑",
|
||||
"CONSTR_INVEST_GR": "건설투자↑ → 과잉투자/레버리지 → Z↓ (민스키 가설)",
|
||||
}
|
||||
for var in model.selected_vars:
|
||||
beta = model.ar1_beta.get(var, 0)
|
||||
sign = "+" if beta > 0 else "−"
|
||||
desc = interp.get(var, "")
|
||||
ws.cell(row=r, column=2, value=_kor(var)).font = BODY_BOLD
|
||||
ws.cell(row=r, column=3, value=f"β={beta:+.4f} ({sign})").font = NUM_FONT
|
||||
ws.cell(row=r, column=4, value=desc).font = SMALL_FONT
|
||||
ws.merge_cells(start_row=r, start_column=4, end_row=r, end_column=7)
|
||||
r += 1
|
||||
|
||||
|
||||
def sheet_zpath(wb, z_paths, config):
|
||||
ws = wb.create_sheet("시나리오_Z경로")
|
||||
scenarios = list(z_paths.keys())
|
||||
nc = 2+len(scenarios)
|
||||
_widths(ws, [3,10]+[16]*len(scenarios))
|
||||
r=1; _title(ws, r, " 시나리오별 Z(t) 경로", nc)
|
||||
r=3
|
||||
r = _section(ws, r, " t=1: 거시 충격 적용 | t≥2: AR(1) 감쇠 → TTC 수렴", nc)
|
||||
r += 1
|
||||
names = []
|
||||
for s in scenarios:
|
||||
c = config.get("scenarios",{}).get(s,{})
|
||||
names.append(c.get("name",s))
|
||||
r = _headers(ws, r, ["","연도(t+k)"]+names)
|
||||
horizon = len(list(z_paths.values())[0])
|
||||
key_years = list(range(1,11))+[15,20,25,30,40,50]
|
||||
for t in key_years:
|
||||
if t <= horizon:
|
||||
vals = [None,t]+[float(z_paths[s][t-1]) for s in scenarios]
|
||||
r = _row(ws, r, vals, alt=t%2==0, fmt=NUM4)
|
||||
|
||||
|
||||
def sheet_pd(wb, pd_results, config, grades8):
|
||||
ws = wb.create_sheet("Lifetime_PD")
|
||||
ng = len(grades8)-1 # D 제외
|
||||
_widths(ws, [3,14,8]+[14]*ng)
|
||||
nc = 3+ng
|
||||
r=1; _title(ws, r, " 시나리오별 누적 Lifetime PD (%)", nc)
|
||||
r=3
|
||||
ky = [1,2,3,5,7,10,15,20,30,50]
|
||||
by_sc = pd_results.get("by_scenario", {})
|
||||
for sname, sdata in by_sc.items():
|
||||
c = config.get("scenarios",{}).get(sname,{})
|
||||
dn = c.get("name",sname); w = c.get("weight",0)
|
||||
r = _section(ws, r, f" {dn} (가중치 {w*100:.0f}%)", nc)
|
||||
r = _headers(ws, r, ["","시나리오","연도"]+list(grades8[:-1]))
|
||||
cpd = sdata.get("cumulative_pd", np.zeros((50,ng)))
|
||||
for t in ky:
|
||||
if t <= cpd.shape[0]:
|
||||
vals = [None,dn,t]+[cpd[t-1,g]*100 for g in range(min(ng,cpd.shape[1]))]
|
||||
r = _row(ws, r, vals, alt=ky.index(t)%2==1, fmt=NUM4)
|
||||
r += 1
|
||||
|
||||
|
||||
def sheet_weighted(wb, pd_results, config, grades8):
|
||||
ws = wb.create_sheet("가중평균_PD")
|
||||
ng = len(grades8)-1
|
||||
_widths(ws, [3,8]+[14]*ng)
|
||||
nc = 2+ng
|
||||
r=1; _title(ws, r, " 확률가중 Lifetime PD (%)", nc)
|
||||
r=3
|
||||
# IFRS 9 근거
|
||||
r = _section(ws, r, " IFRS 9 근거: 확률가중 기대신용손실", nc)
|
||||
ws.cell(row=r, column=2, value='IFRS 9 B5.5.42: "기대신용손실은 확률가중 금액이어야 하며,').font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value='가능한 결과의 범위를 반영하여야 한다. 단일 가장 가능성 높은 결과가 아닌,').font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value='신용위험의 벽혹을 변경시키는 일반적 경제 조건에 대한 예측을 포함하여야 한다."').font = SMALL_FONT; r+=1
|
||||
ws.cell(row=r, column=2, value='IFRS 9 B5.5.44: "최소 2개 시나리오(호황/불황)+확률가중치 = ECL 요구사항을 충족할 수 있다."').font = SMALL_FONT; r+=1
|
||||
r += 1
|
||||
r = _section(ws, r, " PD_weighted(t) = Σ w_s × PD_s(t)", nc)
|
||||
wstr = " + ".join([f"{c.get('weight',0)*100:.0f}%×{c.get('name',s)}" for s,c in config.get("scenarios",{}).items()])
|
||||
ws.cell(row=r, column=2, value=f"= {wstr}").font = SMALL_FONT
|
||||
r += 2
|
||||
ky = [1,2,3,5,7,10,15,20,30,50]
|
||||
r = _headers(ws, r, ["","연도"]+list(grades8[:-1]))
|
||||
wcpd = pd_results.get("weighted_cumulative_pd", np.zeros((50, ng)))
|
||||
for t in ky:
|
||||
if t <= wcpd.shape[0]:
|
||||
wpd = wcpd[t-1,:ng] * 100
|
||||
else:
|
||||
wpd = np.zeros(ng)
|
||||
vals = [None,t]+list(wpd)
|
||||
r = _row(ws, r, vals, alt=ky.index(t)%2==1, fmt=NUM4)
|
||||
|
||||
|
||||
def sheet_validation(wb, val_df):
|
||||
ws = wb.create_sheet("검증결과")
|
||||
_widths(ws, [3,30,22,14,14,10,40])
|
||||
r=1; _title(ws, r, " 통계적 검증 결과", 7)
|
||||
r=3
|
||||
cols = list(val_df.columns)
|
||||
r = _headers(ws, r, [""]+cols)
|
||||
for i,(_,rd) in enumerate(val_df.iterrows()):
|
||||
vals = [None]+[rd[c] for c in cols]
|
||||
rn = _row(ws, r, vals, alt=i%2==1)
|
||||
# 결과 색상
|
||||
result_col = cols.index("결과")+2 if "결과" in cols else None
|
||||
if result_col:
|
||||
cell = ws.cell(row=r, column=result_col)
|
||||
if "Pass" in str(cell.value):
|
||||
cell.fill = PASS_FILL; cell.font = PASS_FONT
|
||||
elif "Fail" in str(cell.value):
|
||||
cell.fill = FAIL_FILL; cell.font = FAIL_FONT
|
||||
r = rn
|
||||
|
||||
|
||||
# ================================================================
|
||||
# 메인
|
||||
# ================================================================
|
||||
def generate_report(config_path="config.yaml", output_path="results/lifetime_pd_report.xlsx"):
|
||||
print("=" * 60)
|
||||
print(" Lifetime PD 분석 보고서 생성")
|
||||
print("=" * 60)
|
||||
|
||||
with open(config_path) as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
rho = config.get("model",{}).get("rho", 0.20)
|
||||
grades = config.get("model",{}).get("rating_grades", list(RATING_GRADES))
|
||||
forced_vars = config.get("model",{}).get("macro_vars", [])
|
||||
macro_method = config.get("model",{}).get("macro_method", "ar1_macro")
|
||||
horizon = config.get("convergence",{}).get("total_horizon", 50)
|
||||
|
||||
from data.pd_floor import apply_pd_floor_to_matrices, build_complete_pd_floor_table
|
||||
|
||||
# 1. 데이터
|
||||
print("\n [1/6] 데이터 로딩...")
|
||||
data_config = config.get("data", {})
|
||||
tm_source = data_config.get("transition_source", "real")
|
||||
tm_dir = data_config.get("transition_dir", None)
|
||||
tm_all = load_transition_matrices(tm_source, data_dir=tm_dir)
|
||||
# 2000-2025 필터
|
||||
tm_raw = {y:m for y,m in tm_all.items() if 2000 <= y <= 2025}
|
||||
|
||||
# KAP 채권 YTM 기반 PD Floor 적용
|
||||
pd_floors, _, pd_floors_full = build_complete_pd_floor_table()
|
||||
tm = apply_pd_floor_to_matrices(tm_raw, pd_floors)
|
||||
ttc = compute_ttc_matrix(tm)
|
||||
|
||||
# 거시변수
|
||||
macro_data = _fallback_macro_data()
|
||||
try:
|
||||
ecos = load_ecos_macro()
|
||||
if ecos is not None and not ecos.empty:
|
||||
macro_data = pd.concat([macro_data, ecos], axis=1)
|
||||
macro_data = macro_data.loc[:,~macro_data.columns.duplicated()]
|
||||
except: pass
|
||||
derived = compute_derived_features(macro_data)
|
||||
if not derived.empty:
|
||||
macro_data = pd.concat([macro_data, derived], axis=1)
|
||||
macro_data = macro_data.loc[:,~macro_data.columns.duplicated()]
|
||||
# 확장 변환: LAG2, log, diff, pctchg, log-return
|
||||
base_cols = list(macro_data.columns)
|
||||
for col in base_cols:
|
||||
s = macro_data[col]
|
||||
d = s.diff()
|
||||
if d.std() > 1e-10:
|
||||
macro_data[f"{col}_D"] = d
|
||||
pc = s.pct_change().replace([np.inf, -np.inf], np.nan)
|
||||
if pc.dropna().std() > 1e-10:
|
||||
macro_data[f"{col}_R"] = pc
|
||||
if (s > 0).all():
|
||||
ls = np.log(s)
|
||||
if ls.std() > 1e-10:
|
||||
macro_data[f"{col}_L"] = ls
|
||||
ld = ls.diff()
|
||||
if ld.dropna().std() > 1e-10:
|
||||
macro_data[f"{col}_LR"] = ld
|
||||
l2 = s.shift(1)
|
||||
if l2.dropna().std() > 1e-10:
|
||||
macro_data[f"{col}_LAG2"] = l2
|
||||
macro_data = macro_data.ffill().bfill()
|
||||
macro_data = macro_data.loc[:,~macro_data.columns.duplicated()]
|
||||
print(f" 전이행렬: {len(tm)}개년 [{tm_source}], PD Floor 적용, 거시변수: {len(macro_data.columns)}개")
|
||||
|
||||
# 2. Zt
|
||||
print(" [2/6] Zt 추정...")
|
||||
zt_dict = estimate_zt_series(tm, ttc, rho)
|
||||
|
||||
# 3. AR(1)
|
||||
print(" [3/6] AR(1)+Macro 적합...")
|
||||
model = build_macro_zt_model(zt_dict, macro_data, method=macro_method, forced_vars=forced_vars)
|
||||
diag = model.diagnostics()
|
||||
# 추가 진단 통계 (AR1 시트 6개 검정용)
|
||||
zt_arr = np.array([zt_dict[yr] for yr in sorted(zt_dict.keys())])
|
||||
from statsmodels.tsa.stattools import adfuller as _adfuller
|
||||
_adf = _adfuller(zt_arr, autolag="BIC")
|
||||
diag["adf_stat"] = _adf[0]; diag["adf_pvalue"] = _adf[1]
|
||||
if model.result is not None:
|
||||
_resid = model.result.resid
|
||||
_exog = model.result.model.exog
|
||||
from statsmodels.stats.diagnostic import acorr_ljungbox as _lb, het_breuschpagan as _bp, het_arch as _arch
|
||||
from scipy.stats import shapiro as _shapiro
|
||||
try:
|
||||
_lbr = _lb(_resid, lags=[5], return_df=True)
|
||||
diag["ljung_box_stat"] = float(_lbr["lb_stat"].iloc[0])
|
||||
diag["ljung_box_pvalue"] = float(_lbr["lb_pvalue"].iloc[0])
|
||||
except: pass
|
||||
try:
|
||||
_bpr = _bp(_resid, _exog)
|
||||
diag["bp_stat"] = float(_bpr[0]); diag["bp_pvalue"] = float(_bpr[1])
|
||||
except: pass
|
||||
try:
|
||||
_ar = _arch(_resid, nlags=3)
|
||||
diag["arch_stat"] = float(_ar[0]); diag["arch_pvalue"] = float(_ar[1])
|
||||
except: pass
|
||||
try:
|
||||
_sw = _shapiro(_resid)
|
||||
diag["shapiro_stat"] = float(_sw.statistic); diag["shapiro_pvalue"] = float(_sw.pvalue)
|
||||
except: pass
|
||||
diag["bic"] = float(model.result.bic) if hasattr(model.result, 'bic') else None
|
||||
print(f" φ={model.ar1_phi:.4f}, R²={diag['r_squared']:.4f}, Adj.R²={diag['adj_r_squared']:.4f}")
|
||||
|
||||
# 4. 시나리오
|
||||
print(" [4/6] 시나리오 Z경로...")
|
||||
engine = ScenarioEngine(config)
|
||||
z_paths = engine.generate_z_paths(zt_dict, macro_model=model)
|
||||
weights = engine.get_scenario_weights()
|
||||
|
||||
# 5. Lifetime PD
|
||||
print(" [5/6] Lifetime PD 산출...")
|
||||
ttc_8x8 = expand_to_8x8(ttc) if ttc.shape == (7,7) else ttc
|
||||
pd_engine = LifetimePDEngine(ttc_8x8, rho, rating_grades=RATING_GRADES_8)
|
||||
pd_results = pd_engine.compute_all_scenarios(z_paths, weights, horizon)
|
||||
|
||||
# 6. 검증
|
||||
print(" [6/6] 통계 검증...")
|
||||
zt_series = pd.Series(zt_dict)
|
||||
reg_result = model.result
|
||||
val_df = run_full_validation(zt_series.values, reg_result, pd_results, list(RATING_GRADES[:-1]))
|
||||
|
||||
# ================================================================
|
||||
# Excel 생성
|
||||
# ================================================================
|
||||
print(f"\n Excel 보고서 생성 중...")
|
||||
wb = Workbook()
|
||||
sheet_summary(wb, config, model, zt_dict, diag, z_paths, val_df, pd_engine, pd_results, RATING_GRADES_8)
|
||||
sheet_tm(wb, tm_raw, tm, ttc, pd_floors, config)
|
||||
sheet_macro(wb, macro_data, forced_vars)
|
||||
sheet_zt(wb, zt_dict, macro_data, forced_vars, rho)
|
||||
sheet_ar1(wb, model, diag)
|
||||
sheet_zpath(wb, z_paths, config)
|
||||
sheet_pd(wb, pd_results, config, RATING_GRADES_8)
|
||||
sheet_weighted(wb, pd_results, config, RATING_GRADES_8)
|
||||
sheet_validation(wb, val_df)
|
||||
|
||||
os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
|
||||
wb.save(output_path)
|
||||
print(f"\n ✓ 보고서 저장: {output_path}")
|
||||
print(f" 시트: {len(wb.sheetnames)}개 ({', '.join(wb.sheetnames)})")
|
||||
return output_path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Lifetime PD 보고서 생성")
|
||||
parser.add_argument("--config", default="config.yaml")
|
||||
parser.add_argument("--output", default="results/lifetime_pd_report.xlsx")
|
||||
args = parser.parse_args()
|
||||
generate_report(args.config, args.output)
|
||||
BIN
results/lifetime_pd_report.xlsx
Normal file
BIN
results/lifetime_pd_report.xlsx
Normal file
Binary file not shown.
BIN
results/lifetime_pd_report_v2.xlsx
Normal file
BIN
results/lifetime_pd_report_v2.xlsx
Normal file
Binary file not shown.
BIN
results/lifetime_pd_report_v3.xlsx
Normal file
BIN
results/lifetime_pd_report_v3.xlsx
Normal file
Binary file not shown.
BIN
results/lifetime_pd_report_v4.xlsx
Normal file
BIN
results/lifetime_pd_report_v4.xlsx
Normal file
Binary file not shown.
BIN
results/lifetime_pd_report_v5.xlsx
Normal file
BIN
results/lifetime_pd_report_v5.xlsx
Normal file
Binary file not shown.
BIN
results/lifetime_pd_report_v6.xlsx
Normal file
BIN
results/lifetime_pd_report_v6.xlsx
Normal file
Binary file not shown.
BIN
results/lifetime_pd_report_v7.xlsx
Normal file
BIN
results/lifetime_pd_report_v7.xlsx
Normal file
Binary file not shown.
376
results/report_preview.html
Normal file
376
results/report_preview.html
Normal file
@@ -0,0 +1,376 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko"><head><meta charset="utf-8">
|
||||
<title>Lifetime PD Report Preview</title>
|
||||
<style>
|
||||
body { font-family: 'Malgun Gothic', sans-serif; background: #f5f5f5; margin: 20px; }
|
||||
.sheet { background: white; border-radius: 8px; padding: 20px; margin-bottom: 30px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
||||
.sheet-title { font-size: 18px; font-weight: bold; color: #1F3864; border-bottom: 3px solid #2B5797; padding-bottom: 8px; margin-bottom: 15px; }
|
||||
table { border-collapse: collapse; width: 100%; font-size: 11px; }
|
||||
th { background: #2B5797; color: white; padding: 6px 8px; text-align: center; border: 1px solid #B4C6E7; }
|
||||
td { padding: 5px 8px; border: 1px solid #D6E4F0; }
|
||||
tr:nth-child(even) td { background: #EDF2F9; }
|
||||
.merged { background: #1F3864; color: white; font-size: 14px; font-weight: bold; padding: 10px; }
|
||||
.section { background: #D6E4F0; font-weight: bold; color: #1F3864; padding: 6px; }
|
||||
.num { text-align: right; font-family: Consolas, monospace; }
|
||||
.pass { background: #E2EFDA; color: #2E7D32; font-weight: bold; }
|
||||
.fail { background: #FCE4EC; color: #C62828; font-weight: bold; }
|
||||
.nav { position: sticky; top: 0; background: #1F3864; padding: 10px 20px; border-radius: 8px; margin-bottom: 20px; z-index: 100; }
|
||||
.nav a { color: white; text-decoration: none; margin-right: 15px; font-size: 12px; }
|
||||
.nav a:hover { text-decoration: underline; }
|
||||
</style></head><body>
|
||||
<div class="nav"><a href="#요약">요약</a><a href="#원시데이터_전이행렬">원시데이터_전이행렬</a><a href="#원시데이터_거시변수">원시데이터_거시변수</a><a href="#Zt_추정">Zt_추정</a><a href="#AR1_모형">AR1_모형</a><a href="#시나리오_Z경로">시나리오_Z경로</a><a href="#Lifetime_PD">Lifetime_PD</a><a href="#가중평균_PD">가중평균_PD</a><a href="#검증결과">검증결과</a></div>
|
||||
<div class="sheet" id="요약">
|
||||
<div class="sheet-title">📊 요약</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="10" class="merged"> Lifetime PD 분석 보고서</td></tr>
|
||||
<tr><td></td><td>생성일시: 2026-03-11 20:50</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 1. 모형 개요</td></tr>
|
||||
<tr><td></td><td>모형 구조</td><td>Z(t) = c + φ·Z(t-1) + β₁·X₁_std + β₂·X₂_std + β₃·X₃_std + ε</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>모형 유형</td><td>AR(1) + Macro (Vasicek Single-Factor)</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>적용 기준</td><td>IFRS 9 (2018, 2024 개정)</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>변수 선택</td><td>HOUSING_PRICE, CURRENT_ACCOUNT, CREDIT_SPREAD_LAG1</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 2. AR(1) 모형 파라미터</td></tr>
|
||||
<tr><td></td><td>자기회귀 계수 (φ)</td><td class="num">-0.3352</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>반감기</td><td>0.6년</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>절편 (c)</td><td class="num">-0.1781</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>β(HOUSING_PRICE)</td><td class="num">0.6412</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>β(CURRENT_ACCOUNT)</td><td class="num">-0.1771</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>β(CREDIT_SPREAD_LAG1)</td><td class="num">-0.6439</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>잔차 σ</td><td class="num">0.3989</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>장기 균형 Z</td><td class="num">-0.1334</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 3. 모형 적합도</td></tr>
|
||||
<tr><td></td><td>R²</td><td class="num">0.6667</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>Adj. R²</td><td class="num">0.6000</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>F p-value</td><td class="num">0.000130</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>AIC</td><td class="num">34.9959</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>DW</td><td class="num">2.8731</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 4. 시나리오 설정</td></tr>
|
||||
<tr><td></td><td>시나리오</td><td>가중치</td><td>HOUSING_PRICE (σ)</td><td>CURRENT_ACCOUNT (σ)</td><td>CREDIT_SPREAD_LAG1 (σ)</td><td>Z(t+1)</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">0.2000</td><td class="num">1</td><td class="num">1</td><td class="num">-1</td><td class="num">1.0527</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">0.5000</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">-0.0553</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">0.3000</td><td class="num">-1.5000</td><td class="num">-1.5000</td><td class="num">1.5000</td><td class="num">-1.7174</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 5. 1년차 확률가중 PD (%)</td></tr>
|
||||
<tr><td></td><td></td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td>CCC</td><td></td></tr>
|
||||
<tr><td></td><td>가중PD(1Y)</td><td class="num">0.0788</td><td class="num">0.1085</td><td class="num">0.2817</td><td class="num">1.7066</td><td class="num">6.4614</td><td class="num">13.0089</td><td class="num">31.6688</td><td></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="원시데이터_전이행렬">
|
||||
<div class="sheet-title">📊 원시데이터_전이행렬</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="9" class="merged"> 전이행렬 파이프라인: Original → PD Floor → TTC</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="9" class="section"> PD Floor 기준 (Basel III CRE30.4 + S&P/Moody's)</td></tr>
|
||||
<tr><td></td><td>등급</td><td>Floor PD (bp)</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>AAA</td><td class="num">5</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>AA</td><td class="num">5</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>A</td><td class="num">7</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>BBB</td><td class="num">20</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>BB</td><td class="num">60</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>B</td><td class="num">300</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="9" class="section"> TTC 전이행렬 (PD Floor 적용 후, 1998~2025 평균)</td></tr>
|
||||
<tr><td></td><td>From\To</td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td>D</td></tr>
|
||||
<tr><td></td><td>AAA</td><td class="num">0.9911</td><td class="num">0.008428</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">0.000510</td></tr>
|
||||
<tr><td></td><td>AA</td><td class="num">0.0173</td><td class="num">0.9417</td><td class="num">0.0367</td><td class="num">0.003558</td><td class="num">0.000007</td><td class="num">0.000007</td><td class="num">0.000733</td></tr>
|
||||
<tr><td></td><td>A</td><td class="num">0</td><td class="num">0.0532</td><td class="num">0.8965</td><td class="num">0.0429</td><td class="num">0.001260</td><td class="num">0.004052</td><td class="num">0.002048</td></tr>
|
||||
<tr><td></td><td>BBB</td><td class="num">0</td><td class="num">0.000012</td><td class="num">0.0659</td><td class="num">0.8642</td><td class="num">0.0343</td><td class="num">0.0215</td><td class="num">0.0140</td></tr>
|
||||
<tr><td></td><td>BB</td><td class="num">0</td><td class="num">0</td><td class="num">0.003234</td><td class="num">0.0435</td><td class="num">0.8036</td><td class="num">0.0877</td><td class="num">0.0619</td></tr>
|
||||
<tr><td></td><td>B</td><td class="num">0</td><td class="num">0</td><td class="num">0.000735</td><td class="num">0.003678</td><td class="num">0.0277</td><td class="num">0.8248</td><td class="num">0.1431</td></tr>
|
||||
<tr><td></td><td>D</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">1</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="9" class="section"> 연도별 D열 비교 (Original vs Floor Calibrated)</td></tr>
|
||||
<tr><td></td><td>연도</td><td>AAA(원시)</td><td>AA(원시)</td><td>A(원시)</td><td>BBB(원시)</td><td>BB(원시)</td><td>B(원시)</td><td></td></tr>
|
||||
<tr><td></td><td>연도</td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td></td></tr>
|
||||
<tr><td></td><td>1998(원시)</td><td class="num">0</td><td class="num">0</td><td class="num">16.9400</td><td class="num">105.83</td><td class="num">1436.40</td><td class="num">3944.30</td><td></td></tr>
|
||||
<tr><td></td><td>1998(보정)</td><td class="num">5</td><td class="num">5</td><td class="num">16.9400</td><td class="num">105.83</td><td class="num">1436.40</td><td class="num">3944.30</td><td></td></tr>
|
||||
<tr><td></td><td>1999(원시)</td><td class="num">1.8000</td><td class="num">2.5000</td><td class="num">16.5700</td><td class="num">106.39</td><td class="num">198.15</td><td class="num">1881.36</td><td></td></tr>
|
||||
<tr><td></td><td>1999(보정)</td><td class="num">5</td><td class="num">5</td><td class="num">16.5700</td><td class="num">106.39</td><td class="num">198.15</td><td class="num">1881.36</td><td></td></tr>
|
||||
<tr><td></td><td>2000(원시)</td><td class="num">1.8200</td><td class="num">5.0600</td><td class="num">16.5200</td><td class="num">101.54</td><td class="num">333.23</td><td class="num">983.95</td><td></td></tr>
|
||||
<tr><td></td><td>2000(보정)</td><td class="num">5.0000</td><td class="num">5.0600</td><td class="num">16.5200</td><td class="num">101.54</td><td class="num">333.23</td><td class="num">983.95</td><td></td></tr>
|
||||
<tr><td></td><td>2001(원시)</td><td class="num">4.0600</td><td class="num">7.9900</td><td class="num">17.3600</td><td class="num">149.54</td><td class="num">499.89</td><td class="num">1792.24</td><td></td></tr>
|
||||
<tr><td></td><td>2001(보정)</td><td class="num">5.0000</td><td class="num">7.9900</td><td class="num">17.3600</td><td class="num">149.54</td><td class="num">499.89</td><td class="num">1792.24</td><td></td></tr>
|
||||
<tr><td></td><td>2002(원시)</td><td class="num">1.7800</td><td class="num">8.3100</td><td class="num">17.1900</td><td class="num">129.25</td><td class="num">479.37</td><td class="num">2136.03</td><td></td></tr>
|
||||
<tr><td></td><td>2002(보정)</td><td class="num">5.0000</td><td class="num">8.3100</td><td class="num">17.1900</td><td class="num">129.25</td><td class="num">479.37</td><td class="num">2136.03</td><td></td></tr>
|
||||
<tr><td></td><td>2003(원시)</td><td class="num">3.7700</td><td class="num">7.7500</td><td class="num">16.2300</td><td class="num">108.94</td><td class="num">500.14</td><td class="num">867.74</td><td></td></tr>
|
||||
<tr><td></td><td>2003(보정)</td><td class="num">5</td><td class="num">7.7500</td><td class="num">16.2300</td><td class="num">108.94</td><td class="num">500.14</td><td class="num">867.74</td><td></td></tr>
|
||||
<tr><td></td><td>2004(원시)</td><td class="num">5.7900</td><td class="num">8.4200</td><td class="num">17.7000</td><td class="num">111.19</td><td class="num">2047.86</td><td class="num">2152.92</td><td></td></tr>
|
||||
<tr><td></td><td>2004(보정)</td><td class="num">5.7900</td><td class="num">8.4200</td><td class="num">17.7000</td><td class="num">111.19</td><td class="num">2047.86</td><td class="num">2152.92</td><td></td></tr>
|
||||
<tr><td></td><td>2005(원시)</td><td class="num">5.4300</td><td class="num">8.4900</td><td class="num">17.9100</td><td class="num">100.93</td><td class="num">239.44</td><td class="num">1652.70</td><td></td></tr>
|
||||
<tr><td></td><td>2005(보정)</td><td class="num">5.4300</td><td class="num">8.4900</td><td class="num">17.9100</td><td class="num">100.93</td><td class="num">239.44</td><td class="num">1652.70</td><td></td></tr>
|
||||
<tr><td></td><td>2006(원시)</td><td class="num">3.5600</td><td class="num">7.6400</td><td class="num">15.8000</td><td class="num">124.46</td><td class="num">259.23</td><td class="num">339.46</td><td></td></tr>
|
||||
<tr><td></td><td>2006(보정)</td><td class="num">5.0000</td><td class="num">7.6400</td><td class="num">15.8000</td><td class="num">124.46</td><td class="num">259.23</td><td class="num">339.46</td><td></td></tr>
|
||||
<tr><td></td><td>2007(원시)</td><td class="num">5.1500</td><td class="num">7.7700</td><td class="num">18.9900</td><td class="num">100.13</td><td class="num">317.06</td><td class="num">623.64</td><td></td></tr>
|
||||
<tr><td></td><td>2007(보정)</td><td class="num">5.1500</td><td class="num">7.7700</td><td class="num">18.9900</td><td class="num">100.13</td><td class="num">317.06</td><td class="num">623.64</td><td></td></tr>
|
||||
<tr><td></td><td>2008(원시)</td><td class="num">3.4600</td><td class="num">7.6000</td><td class="num">16.0100</td><td class="num">165.80</td><td class="num">847.65</td><td class="num">739.98</td><td></td></tr>
|
||||
<tr><td></td><td>2008(보정)</td><td class="num">5</td><td class="num">7.6000</td><td class="num">16.0100</td><td class="num">165.80</td><td class="num">847.65</td><td class="num">739.98</td><td></td></tr>
|
||||
<tr><td></td><td>2009(원시)</td><td class="num">5.2000</td><td class="num">7.4800</td><td class="num">15.7600</td><td class="num">122.28</td><td class="num">1444.45</td><td class="num">1310.57</td><td></td></tr>
|
||||
<tr><td></td><td>2009(보정)</td><td class="num">5.2000</td><td class="num">7.4800</td><td class="num">15.7600</td><td class="num">122.28</td><td class="num">1444.45</td><td class="num">1310.57</td><td></td></tr>
|
||||
<tr><td></td><td>2010(원시)</td><td class="num">5.1000</td><td class="num">7.4700</td><td class="num">16.5900</td><td class="num">127.38</td><td class="num">1171.52</td><td class="num">2557.17</td><td></td></tr>
|
||||
<tr><td></td><td>2010(보정)</td><td class="num">5.1000</td><td class="num">7.4700</td><td class="num">16.5900</td><td class="num">127.38</td><td class="num">1171.52</td><td class="num">2557.17</td><td></td></tr>
|
||||
<tr><td></td><td>2011(원시)</td><td class="num">3.4000</td><td class="num">7.4500</td><td class="num">15.8700</td><td class="num">234.21</td><td class="num">530.64</td><td class="num">1499.83</td><td></td></tr>
|
||||
<tr><td></td><td>2011(보정)</td><td class="num">5.0000</td><td class="num">7.4500</td><td class="num">15.8700</td><td class="num">234.21</td><td class="num">530.64</td><td class="num">1499.83</td><td></td></tr>
|
||||
<tr><td></td><td>2012(원시)</td><td class="num">3.4200</td><td class="num">7.5400</td><td class="num">64.5399</td><td class="num">222.52</td><td class="num">811.32</td><td class="num">3796.36</td><td></td></tr>
|
||||
<tr><td></td><td>2012(보정)</td><td class="num">5</td><td class="num">7.5400</td><td class="num">64.5399</td><td class="num">222.52</td><td class="num">811.32</td><td class="num">3796.36</td><td></td></tr>
|
||||
<tr><td></td><td>2013(원시)</td><td class="num">1.7500</td><td class="num">7.4500</td><td class="num">15.7700</td><td class="num">402.43</td><td class="num">790.61</td><td class="num">1011.51</td><td></td></tr>
|
||||
<tr><td></td><td>2013(보정)</td><td class="num">5.0000</td><td class="num">7.4500</td><td class="num">15.7700</td><td class="num">402.43</td><td class="num">790.61</td><td class="num">1011.51</td><td></td></tr>
|
||||
<tr><td></td><td>2014(원시)</td><td class="num">5.1700</td><td class="num">7.4200</td><td class="num">68.6300</td><td class="num">210.13</td><td class="num">669.82</td><td class="num">688.92</td><td></td></tr>
|
||||
<tr><td></td><td>2014(보정)</td><td class="num">5.1700</td><td class="num">7.4200</td><td class="num">68.6300</td><td class="num">210.13</td><td class="num">669.82</td><td class="num">688.92</td><td></td></tr>
|
||||
<tr><td></td><td>2015(원시)</td><td class="num">5.4400</td><td class="num">7.3400</td><td class="num">17.1000</td><td class="num">113.01</td><td class="num">1003.99</td><td class="num">1469.78</td><td></td></tr>
|
||||
<tr><td></td><td>2015(보정)</td><td class="num">5.4400</td><td class="num">7.3400</td><td class="num">17.1000</td><td class="num">113.01</td><td class="num">1003.99</td><td class="num">1469.78</td><td></td></tr>
|
||||
<tr><td></td><td>2016(원시)</td><td class="num">5.1600</td><td class="num">7.3400</td><td class="num">15.9700</td><td class="num">115.10</td><td class="num">526.32</td><td class="num">1094.35</td><td></td></tr>
|
||||
<tr><td></td><td>2016(보정)</td><td class="num">5.1600</td><td class="num">7.3400</td><td class="num">15.9700</td><td class="num">115.10</td><td class="num">526.32</td><td class="num">1094.35</td><td></td></tr>
|
||||
<tr><td></td><td>2017(원시)</td><td class="num">3.3900</td><td class="num">7.3600</td><td class="num">17.8200</td><td class="num">123.67</td><td class="num">232.74</td><td class="num">584.60</td><td></td></tr>
|
||||
<tr><td></td><td>2017(보정)</td><td class="num">5</td><td class="num">7.3600</td><td class="num">17.8200</td><td class="num">123.67</td><td class="num">232.74</td><td class="num">584.60</td><td></td></tr>
|
||||
<tr><td></td><td>2018(원시)</td><td class="num">0</td><td class="num">7.5200</td><td class="num">17.6600</td><td class="num">141.91</td><td class="num">217.93</td><td class="num">485.42</td><td></td></tr>
|
||||
<tr><td></td><td>2018(보정)</td><td class="num">5</td><td class="num">7.5200</td><td class="num">17.6600</td><td class="num">141.91</td><td class="num">217.93</td><td class="num">485.42</td><td></td></tr>
|
||||
<tr><td></td><td>2019(원시)</td><td class="num">1.6900</td><td class="num">7.5100</td><td class="num">16.5700</td><td class="num">108.98</td><td class="num">721.38</td><td class="num">1451.42</td><td></td></tr>
|
||||
<tr><td></td><td>2019(보정)</td><td class="num">5</td><td class="num">7.5100</td><td class="num">16.5700</td><td class="num">108.98</td><td class="num">721.38</td><td class="num">1451.42</td><td></td></tr>
|
||||
<tr><td></td><td>2020(원시)</td><td class="num">5.0800</td><td class="num">7.4300</td><td class="num">15.9200</td><td class="num">104.60</td><td class="num">180.18</td><td class="num">499.99</td><td></td></tr>
|
||||
<tr><td></td><td>2020(보정)</td><td class="num">5.0800</td><td class="num">7.4300</td><td class="num">15.9200</td><td class="num">104.60</td><td class="num">180.18</td><td class="num">499.99</td><td></td></tr>
|
||||
<tr><td></td><td>2021(원시)</td><td class="num">5.1700</td><td class="num">7.2500</td><td class="num">16.5700</td><td class="num">105.93</td><td class="num">216.50</td><td class="num">544.44</td><td></td></tr>
|
||||
<tr><td></td><td>2021(보정)</td><td class="num">5.1700</td><td class="num">7.2500</td><td class="num">16.5700</td><td class="num">105.93</td><td class="num">216.50</td><td class="num">544.44</td><td></td></tr>
|
||||
<tr><td></td><td>2022(원시)</td><td class="num">3.4700</td><td class="num">7.3400</td><td class="num">16.6100</td><td class="num">106.95</td><td class="num">117.13</td><td class="num">622.53</td><td></td></tr>
|
||||
<tr><td></td><td>2022(보정)</td><td class="num">5</td><td class="num">7.3400</td><td class="num">16.6100</td><td class="num">106.95</td><td class="num">117.13</td><td class="num">622.53</td><td></td></tr>
|
||||
<tr><td></td><td>2023(원시)</td><td class="num">1.8000</td><td class="num">7.6400</td><td class="num">20.9400</td><td class="num">160.26</td><td class="num">982.60</td><td class="num">2376.81</td><td></td></tr>
|
||||
<tr><td></td><td>2023(보정)</td><td class="num">5</td><td class="num">7.6400</td><td class="num">20.9400</td><td class="num">160.26</td><td class="num">982.60</td><td class="num">2376.81</td><td></td></tr>
|
||||
<tr><td></td><td>2024(원시)</td><td class="num">5.1300</td><td class="num">7.2700</td><td class="num">17.4800</td><td class="num">118.96</td><td class="num">243.33</td><td class="num">1196.36</td><td></td></tr>
|
||||
<tr><td></td><td>2024(보정)</td><td class="num">5.1300</td><td class="num">7.2700</td><td class="num">17.4800</td><td class="num">118.96</td><td class="num">243.33</td><td class="num">1196.36</td><td></td></tr>
|
||||
<tr><td></td><td>2025(원시)</td><td class="num">5.1200</td><td class="num">7.4100</td><td class="num">16.5500</td><td class="num">107.88</td><td class="num">321.42</td><td class="num">1764.13</td><td></td></tr>
|
||||
<tr><td></td><td>2025(보정)</td><td class="num">5.1200</td><td class="num">7.4100</td><td class="num">16.5500</td><td class="num">107.88</td><td class="num">321.42</td><td class="num">1764.13</td><td></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="원시데이터_거시변수">
|
||||
<div class="sheet-title">📊 원시데이터_거시변수</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="42" class="merged"> 원시 데이터: 거시경제변수</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="42" class="section"> ★ 선택 변수: HOUSING_PRICE, CURRENT_ACCOUNT, CREDIT_SPREAD_LAG1</td></tr>
|
||||
<tr><td></td><td>연도</td><td>HOUSING_PRICE</td><td>CURRENT_ACCOUNT</td><td>CREDIT_SPREAD_LAG1</td><td>GDP_GROWTH</td><td>UNEMPLOYMENT</td><td>BASE_RATE</td><td>CD_RATE</td><td>CPI_GROWTH</td><td>LEADING_INDEX</td><td>GOVT_3Y</td><td>CORP_AA</td><td>CORP_BBB</td><td>IPI</td><td>EXPORT</td><td>GOVT_10Y</td><td>IMPORT_AMT</td><td>USDKRW</td><td>M2</td><td>CSI</td><td>KOSPI</td><td>IMPORT_PRICE</td><td>DISHONOR_RATE</td><td>HOUSEHOLD_DEBT</td><td>FACILITY_INVEST</td><td>RETAIL_SALES</td><td>EMPLOYED</td><td>EMPLOYMENT_RATE</td><td>OIL_PRICE</td><td>COINCIDENT</td><td>BSI_MANUF</td><td>CONSTRUCTION_DONE</td><td>SPI</td><td>CONSTR_INVEST_GR</td><td>GFCF_GROWTH</td><td>SAVING_RATE</td><td>INVEST_RATE</td><td>TRADE_GNI</td><td>MANUF_CAPACITY</td><td>IPI_LAG1</td><td>EXPORT_DIFF</td></tr>
|
||||
<tr><td></td><td class="num">2000</td><td class="num">55.2000</td><td class="num">123.50</td><td></td><td class="num">8.9000</td><td class="num">4.4000</td><td class="num">5.2500</td><td class="num">7.0900</td><td class="num">2.3000</td><td class="num">101.20</td><td class="num">8.3500</td><td class="num">9.3500</td><td class="num">11.9000</td><td class="num">102.50</td><td class="num">172268</td><td class="num">8.5500</td><td class="num">160481</td><td class="num">1131</td><td class="num">651.80</td><td class="num">101</td><td class="num">504</td><td class="num">78.5000</td><td class="num">0.4600</td><td class="num">194</td><td class="num">62.5000</td><td class="num">72</td><td class="num">2115</td><td class="num">58.5000</td><td class="num">26.2000</td><td class="num">99.8000</td><td class="num">90</td><td class="num">56.3000</td><td class="num">58</td><td class="num">-1.4000</td><td class="num">11.4000</td><td class="num">33.7000</td><td class="num">31</td><td class="num">72.5000</td><td class="num">109.50</td><td></td><td></td></tr>
|
||||
<tr><td></td><td class="num">2001</td><td class="num">56.8000</td><td class="num">80.3000</td><td class="num">2.5500</td><td class="num">4.5000</td><td class="num">4</td><td class="num">4</td><td class="num">5.3400</td><td class="num">4.1000</td><td class="num">99.5000</td><td class="num">6.7000</td><td class="num">8.1200</td><td class="num">11.2700</td><td class="num">99.5000</td><td class="num">150439</td><td class="num">7.0500</td><td class="num">141098</td><td class="num">1291</td><td class="num">736.50</td><td class="num">96.5000</td><td class="num">694</td><td class="num">73.6000</td><td class="num">0.2800</td><td class="num">225</td><td class="num">58.5000</td><td class="num">73.5000</td><td class="num">2118</td><td class="num">59</td><td class="num">22.8000</td><td class="num">98</td><td class="num">82</td><td class="num">53.8000</td><td class="num">60.2000</td><td class="num">5.6000</td><td class="num">0.6000</td><td class="num">31.7000</td><td class="num">29.3000</td><td class="num">66.3000</td><td class="num">105.80</td><td class="num">102.50</td><td class="num">-21829</td></tr>
|
||||
<tr><td></td><td class="num">2002</td><td class="num">65.3000</td><td class="num">53.9000</td><td class="num">3.1500</td><td class="num">7.4000</td><td class="num">3.3000</td><td class="num">4.2500</td><td class="num">4.9900</td><td class="num">2.8000</td><td class="num">102.30</td><td class="num">6.0600</td><td class="num">7.0200</td><td class="num">9.7500</td><td class="num">108.50</td><td class="num">162471</td><td class="num">6.5800</td><td class="num">152126</td><td class="num">1251</td><td class="num">816.30</td><td class="num">105</td><td class="num">628</td><td class="num">72.1000</td><td class="num">0.1800</td><td class="num">306</td><td class="num">63.2000</td><td class="num">76</td><td class="num">2217</td><td class="num">60</td><td class="num">23.7000</td><td class="num">101.50</td><td class="num">92</td><td class="num">55.2000</td><td class="num">63.5000</td><td class="num">6.5000</td><td class="num">6.7000</td><td class="num">31.3000</td><td class="num">29.1000</td><td class="num">62.4000</td><td class="num">110.50</td><td class="num">99.5000</td><td class="num">12032</td></tr>
|
||||
<tr><td></td><td class="num">2003</td><td class="num">71.5000</td><td class="num">119.50</td><td class="num">2.7300</td><td class="num">2.9000</td><td class="num">3.6000</td><td class="num">3.7500</td><td class="num">4.2400</td><td class="num">3.5000</td><td class="num">98.8000</td><td class="num">4.9300</td><td class="num">5.7000</td><td class="num">8.9700</td><td class="num">109.80</td><td class="num">193817</td><td class="num">5.4500</td><td class="num">178827</td><td class="num">1192</td><td class="num">879.20</td><td class="num">96</td><td class="num">811</td><td class="num">81.3000</td><td class="num">0.1200</td><td class="num">360</td><td class="num">60.5000</td><td class="num">74</td><td class="num">2212</td><td class="num">59.5000</td><td class="num">26.8000</td><td class="num">99.2000</td><td class="num">85</td><td class="num">58</td><td class="num">64.8000</td><td class="num">10</td><td class="num">4</td><td class="num">32.6000</td><td class="num">30</td><td class="num">65</td><td class="num">108.20</td><td class="num">108.50</td><td class="num">31346</td></tr>
|
||||
<tr><td></td><td class="num">2004</td><td class="num">71</td><td class="num">284.20</td><td class="num">3.2700</td><td class="num">4.9000</td><td class="num">3.7000</td><td class="num">3.2500</td><td class="num">3.7700</td><td class="num">3.6000</td><td class="num">100.50</td><td class="num">4.1100</td><td class="num">4.7200</td><td class="num">7.5300</td><td class="num">119.20</td><td class="num">253845</td><td class="num">4.7300</td><td class="num">224463</td><td class="num">1145</td><td class="num">935.30</td><td class="num">97</td><td class="num">896</td><td class="num">90.5000</td><td class="num">0.0800</td><td class="num">394</td><td class="num">66.5000</td><td class="num">74.5000</td><td class="num">2272</td><td class="num">59.8000</td><td class="num">33.5000</td><td class="num">100.80</td><td class="num">88</td><td class="num">63.5000</td><td class="num">66</td><td class="num">1.8000</td><td class="num">2.1000</td><td class="num">34.8000</td><td class="num">30.3000</td><td class="num">73.5000</td><td class="num">113.80</td><td class="num">109.80</td><td class="num">60028</td></tr>
|
||||
<tr><td></td><td class="num">2005</td><td class="num">73.5000</td><td class="num">149.80</td><td class="num">2.8100</td><td class="num">3.9000</td><td class="num">3.7000</td><td class="num">3.7500</td><td class="num">3.8100</td><td class="num">2.8000</td><td class="num">101.80</td><td class="num">4.2700</td><td class="num">4.6800</td><td class="num">6.5100</td><td class="num">126</td><td class="num">284419</td><td class="num">4.9500</td><td class="num">261238</td><td class="num">1024</td><td class="num">1002.70</td><td class="num">100.50</td><td class="num">1011</td><td class="num">99.2000</td><td class="num">0.0600</td><td class="num">440</td><td class="num">68</td><td class="num">76.5000</td><td class="num">2297</td><td class="num">60.3000</td><td class="num">49.3000</td><td class="num">101.20</td><td class="num">92</td><td class="num">66</td><td class="num">68.5000</td><td class="num">-0.4000</td><td class="num">1.9000</td><td class="num">33.4000</td><td class="num">29.7000</td><td class="num">72.5000</td><td class="num">114.50</td><td class="num">119.20</td><td class="num">30574</td></tr>
|
||||
<tr><td></td><td class="num">2006</td><td class="num">80.2000</td><td class="num">53.9000</td><td class="num">1.8300</td><td class="num">5.2000</td><td class="num">3.5000</td><td class="num">4.5000</td><td class="num">4.7200</td><td class="num">2.2000</td><td class="num">102.50</td><td class="num">4.8300</td><td class="num">5.2500</td><td class="num">7.0800</td><td class="num">136</td><td class="num">325465</td><td class="num">5.1700</td><td class="num">309383</td><td class="num">955</td><td class="num">1089.90</td><td class="num">106</td><td class="num">1434</td><td class="num">107.80</td><td class="num">0.0500</td><td class="num">497</td><td class="num">73.5000</td><td class="num">78.5000</td><td class="num">2334</td><td class="num">60.9000</td><td class="num">61.5000</td><td class="num">102.80</td><td class="num">95</td><td class="num">69.5000</td><td class="num">71.2000</td><td class="num">0.5000</td><td class="num">3.4000</td><td class="num">32.5000</td><td class="num">29.6000</td><td class="num">73.2000</td><td class="num">115.80</td><td class="num">126</td><td class="num">41046</td></tr>
|
||||
<tr><td></td><td class="num">2007</td><td class="num">83.5000</td><td class="num">59.5000</td><td class="num">1.8300</td><td class="num">5.5000</td><td class="num">3.2000</td><td class="num">5</td><td class="num">5.3600</td><td class="num">2.5000</td><td class="num">103.10</td><td class="num">5.2300</td><td class="num">5.7000</td><td class="num">7.4400</td><td class="num">144.50</td><td class="num">371489</td><td class="num">5.4200</td><td class="num">356846</td><td class="num">929</td><td class="num">1181.60</td><td class="num">108.50</td><td class="num">1897</td><td class="num">109.30</td><td class="num">0.0400</td><td class="num">560</td><td class="num">78.5000</td><td class="num">80</td><td class="num">2371</td><td class="num">61.3000</td><td class="num">68.4000</td><td class="num">103.50</td><td class="num">97</td><td class="num">72.8000</td><td class="num">74</td><td class="num">1.4000</td><td class="num">4.2000</td><td class="num">32.4000</td><td class="num">29.4000</td><td class="num">77.8000</td><td class="num">115.20</td><td class="num">136</td><td class="num">46024</td></tr>
|
||||
<tr><td></td><td class="num">2008</td><td class="num">84</td><td class="num">-57.8000</td><td class="num">1.7400</td><td class="num">2.8000</td><td class="num">3.2000</td><td class="num">3</td><td class="num">5.7000</td><td class="num">4.7000</td><td class="num">96.5000</td><td class="num">5.2700</td><td class="num">7.0200</td><td class="num">10.7300</td><td class="num">148.20</td><td class="num">422007</td><td class="num">5.5700</td><td class="num">435275</td><td class="num">1103</td><td class="num">1263.20</td><td class="num">86</td><td class="num">1124</td><td class="num">132.50</td><td class="num">0.1100</td><td class="num">630</td><td class="num">76</td><td class="num">79</td><td class="num">2385</td><td class="num">61.5000</td><td class="num">94.3000</td><td class="num">98.5000</td><td class="num">72</td><td class="num">74.5000</td><td class="num">75.5000</td><td class="num">-2.8000</td><td class="num">-1.9000</td><td class="num">31.5000</td><td class="num">31.2000</td><td class="num">96.5000</td><td class="num">112.80</td><td class="num">144.50</td><td class="num">50518</td></tr>
|
||||
<tr><td></td><td class="num">2009</td><td class="num">84.8000</td><td class="num">328.10</td><td class="num">3.7100</td><td class="num">0.8000</td><td class="num">3.6000</td><td class="num">2</td><td class="num">2.6300</td><td class="num">2.8000</td><td class="num">98.2000</td><td class="num">4.0400</td><td class="num">5.8000</td><td class="num">9.2400</td><td class="num">140</td><td class="num">363534</td><td class="num">4.8500</td><td class="num">323085</td><td class="num">1276</td><td class="num">1404.40</td><td class="num">85</td><td class="num">1683</td><td class="num">104.20</td><td class="num">0.1000</td><td class="num">694</td><td class="num">60.5000</td><td class="num">77.5000</td><td class="num">2355</td><td class="num">60.1000</td><td class="num">61.8000</td><td class="num">96.5000</td><td class="num">68</td><td class="num">68.2000</td><td class="num">76</td><td class="num">0.2000</td><td class="num">-1</td><td class="num">31.4000</td><td class="num">26.3000</td><td class="num">82</td><td class="num">102.50</td><td class="num">148.20</td><td class="num">-58473</td></tr>
|
||||
<tr><td></td><td class="num">2010</td><td class="num">87</td><td class="num">282.10</td><td class="num">3.4400</td><td class="num">6.8000</td><td class="num">3.7000</td><td class="num">2.5000</td><td class="num">2.8000</td><td class="num">2.9000</td><td class="num">103</td><td class="num">3.7200</td><td class="num">4.6600</td><td class="num">7.9800</td><td class="num">161.50</td><td class="num">466384</td><td class="num">4.4900</td><td class="num">425212</td><td class="num">1156</td><td class="num">1504.30</td><td class="num">107</td><td class="num">2051</td><td class="num">115.80</td><td class="num">0.0600</td><td class="num">776</td><td class="num">80.5000</td><td class="num">80.5000</td><td class="num">2397</td><td class="num">60.4000</td><td class="num">78.1000</td><td class="num">103</td><td class="num">95</td><td class="num">72</td><td class="num">78.5000</td><td class="num">-1.4000</td><td class="num">5.8000</td><td class="num">33.5000</td><td class="num">29.5000</td><td class="num">87.9000</td><td class="num">113</td><td class="num">140</td><td class="num">102850</td></tr>
|
||||
<tr><td></td><td class="num">2011</td><td class="num">89.5000</td><td class="num">184.10</td><td class="num">3.3200</td><td class="num">3.7000</td><td class="num">3.4000</td><td class="num">3.2500</td><td class="num">3.5500</td><td class="num">4</td><td class="num">101.20</td><td class="num">3.6200</td><td class="num">4.4100</td><td class="num">7.7500</td><td class="num">168</td><td class="num">555214</td><td class="num">4.0500</td><td class="num">524413</td><td class="num">1108</td><td class="num">1586.50</td><td class="num">100</td><td class="num">1826</td><td class="num">130.20</td><td class="num">0.0500</td><td class="num">857</td><td class="num">82</td><td class="num">82</td><td class="num">2424</td><td class="num">60.7000</td><td class="num">106</td><td class="num">102.50</td><td class="num">90</td><td class="num">73.5000</td><td class="num">80</td><td class="num">-4.9000</td><td class="num">0.8000</td><td class="num">34</td><td class="num">29.4000</td><td class="num">96.7000</td><td class="num">112.50</td><td class="num">161.50</td><td class="num">88830</td></tr>
|
||||
<tr><td></td><td class="num">2012</td><td class="num">89</td><td class="num">508.40</td><td class="num">3.3400</td><td class="num">2.4000</td><td class="num">3.2000</td><td class="num">2.7500</td><td class="num">3.1300</td><td class="num">2.2000</td><td class="num">100.30</td><td class="num">3.1300</td><td class="num">3.7600</td><td class="num">6.5600</td><td class="num">168.20</td><td class="num">547870</td><td class="num">3.3500</td><td class="num">519584</td><td class="num">1127</td><td class="num">1673.50</td><td class="num">100.50</td><td class="num">1997</td><td class="num">123.50</td><td class="num">0.0400</td><td class="num">934</td><td class="num">79</td><td class="num">83.5000</td><td class="num">2468</td><td class="num">61.3000</td><td class="num">109.10</td><td class="num">100.50</td><td class="num">85</td><td class="num">72</td><td class="num">82.5000</td><td class="num">-3.2000</td><td class="num">-0.5000</td><td class="num">33.8000</td><td class="num">28.4000</td><td class="num">96.8000</td><td class="num">110.20</td><td class="num">168</td><td class="num">-7344</td></tr>
|
||||
<tr><td></td><td class="num">2013</td><td class="num">88.8000</td><td class="num">812.10</td><td class="num">2.8000</td><td class="num">3.2000</td><td class="num">3.1000</td><td class="num">2.5000</td><td class="num">2.7200</td><td class="num">1.3000</td><td class="num">100.80</td><td class="num">2.7900</td><td class="num">3.1900</td><td class="num">5.8700</td><td class="num">168.80</td><td class="num">559632</td><td class="num">3.2800</td><td class="num">515586</td><td class="num">1095</td><td class="num">1756.20</td><td class="num">103</td><td class="num">2011</td><td class="num">115</td><td class="num">0.0400</td><td class="num">980</td><td class="num">77.5000</td><td class="num">85</td><td class="num">2503</td><td class="num">61.6000</td><td class="num">105.50</td><td class="num">101</td><td class="num">88</td><td class="num">71.5000</td><td class="num">84</td><td class="num">5.4000</td><td class="num">3.3000</td><td class="num">34</td><td class="num">28.7000</td><td class="num">93.2000</td><td class="num">108</td><td class="num">168.20</td><td class="num">11762</td></tr>
|
||||
<tr><td></td><td class="num">2014</td><td class="num">90.2000</td><td class="num">843.50</td><td class="num">2.6800</td><td class="num">3.2000</td><td class="num">3.5000</td><td class="num">2</td><td class="num">2.3600</td><td class="num">1.3000</td><td class="num">101</td><td class="num">2.5600</td><td class="num">2.9900</td><td class="num">5.2200</td><td class="num">168.50</td><td class="num">572665</td><td class="num">2.9200</td><td class="num">525515</td><td class="num">1053</td><td class="num">1871</td><td class="num">104</td><td class="num">1916</td><td class="num">105.60</td><td class="num">0.0400</td><td class="num">1050</td><td class="num">81</td><td class="num">86.5000</td><td class="num">2546</td><td class="num">62.4000</td><td class="num">96.7000</td><td class="num">101.50</td><td class="num">90</td><td class="num">73.8000</td><td class="num">86</td><td class="num">1.1000</td><td class="num">3.1000</td><td class="num">34.5000</td><td class="num">29</td><td class="num">87.6000</td><td class="num">108.80</td><td class="num">168.80</td><td class="num">13033</td></tr>
|
||||
<tr><td></td><td class="num">2015</td><td class="num">95</td><td class="num">1059.40</td><td class="num">2.2300</td><td class="num">2.8000</td><td class="num">3.6000</td><td class="num">1.5000</td><td class="num">1.7200</td><td class="num">0.7000</td><td class="num">100.50</td><td class="num">1.8000</td><td class="num">2.1800</td><td class="num">4.6100</td><td class="num">168</td><td class="num">526757</td><td class="num">2.2500</td><td class="num">436499</td><td class="num">1131</td><td class="num">2010</td><td class="num">103.50</td><td class="num">1961</td><td class="num">79.5000</td><td class="num">0.0300</td><td class="num">1145</td><td class="num">84.5000</td><td class="num">88</td><td class="num">2567</td><td class="num">62.6000</td><td class="num">51.2000</td><td class="num">101</td><td class="num">86</td><td class="num">77.5000</td><td class="num">88.5000</td><td class="num">9.1000</td><td class="num">5.1000</td><td class="num">36</td><td class="num">28.8000</td><td class="num">79.8000</td><td class="num">107.20</td><td class="num">168.50</td><td class="num">-45908</td></tr>
|
||||
<tr><td></td><td class="num">2016</td><td class="num">97.5000</td><td class="num">992.40</td><td class="num">2.4300</td><td class="num">2.9000</td><td class="num">3.7000</td><td class="num">1.2500</td><td class="num">1.4800</td><td class="num">1</td><td class="num">99.8000</td><td class="num">1.4400</td><td class="num">1.8800</td><td class="num">4.6000</td><td class="num">168.50</td><td class="num">495426</td><td class="num">1.8000</td><td class="num">406193</td><td class="num">1161</td><td class="num">2151.10</td><td class="num">100</td><td class="num">2026</td><td class="num">78</td><td class="num">0.0300</td><td class="num">1250</td><td class="num">82</td><td class="num">89.5000</td><td class="num">2597</td><td class="num">63</td><td class="num">41.3000</td><td class="num">100.20</td><td class="num">85</td><td class="num">89.5000</td><td class="num">90</td><td class="num">10.3000</td><td class="num">5.6000</td><td class="num">36.4000</td><td class="num">29.2000</td><td class="num">74.5000</td><td class="num">106</td><td class="num">168</td><td class="num">-31331</td></tr>
|
||||
<tr><td></td><td class="num">2017</td><td class="num">100</td><td class="num">752.60</td><td class="num">2.7200</td><td class="num">3.2000</td><td class="num">3.7000</td><td class="num">1.5000</td><td class="num">1.5200</td><td class="num">1.9000</td><td class="num">101.50</td><td class="num">1.8000</td><td class="num">2.2800</td><td class="num">4.8300</td><td class="num">174.20</td><td class="num">573694</td><td class="num">2.3300</td><td class="num">478478</td><td class="num">1131</td><td class="num">2347.20</td><td class="num">105</td><td class="num">2467</td><td class="num">90.5000</td><td class="num">0.0200</td><td class="num">1364</td><td class="num">92</td><td class="num">92</td><td class="num">2620</td><td class="num">63.2000</td><td class="num">53.1000</td><td class="num">101.80</td><td class="num">92</td><td class="num">90</td><td class="num">92.5000</td><td class="num">7.3000</td><td class="num">9.8000</td><td class="num">36.6000</td><td class="num">31.1000</td><td class="num">77.3000</td><td class="num">107.50</td><td class="num">168.50</td><td class="num">78268</td></tr>
|
||||
<tr><td></td><td class="num">2018</td><td class="num">102</td><td class="num">774.70</td><td class="num">2.5500</td><td class="num">2.9000</td><td class="num">3.8000</td><td class="num">1.7500</td><td class="num">1.8500</td><td class="num">1.5000</td><td class="num">100.80</td><td class="num">2.1000</td><td class="num">2.6700</td><td class="num">5.4100</td><td class="num">178</td><td class="num">604860</td><td class="num">2.5600</td><td class="num">535202</td><td class="num">1100</td><td class="num">2508.90</td><td class="num">102</td><td class="num">2041</td><td class="num">100</td><td class="num">0.0300</td><td class="num">1497</td><td class="num">94.5000</td><td class="num">94</td><td class="num">2633</td><td class="num">63.1000</td><td class="num">69.5000</td><td class="num">101.50</td><td class="num">88</td><td class="num">85.5000</td><td class="num">94.5000</td><td class="num">-4.6000</td><td class="num">-2.4000</td><td class="num">35.9000</td><td class="num">30.3000</td><td class="num">77.3000</td><td class="num">107</td><td class="num">174.20</td><td class="num">31166</td></tr>
|
||||
<tr><td></td><td class="num">2019</td><td class="num">104.50</td><td class="num">597</td><td class="num">2.7400</td><td class="num">2.2000</td><td class="num">3.8000</td><td class="num">1.2500</td><td class="num">1.6300</td><td class="num">0.4000</td><td class="num">99.3000</td><td class="num">1.5000</td><td class="num">1.9300</td><td class="num">4.5200</td><td class="num">175.50</td><td class="num">542233</td><td class="num">1.7400</td><td class="num">503343</td><td class="num">1166</td><td class="num">2694</td><td class="num">97</td><td class="num">2198</td><td class="num">92.5000</td><td class="num">0.0300</td><td class="num">1573</td><td class="num">89</td><td class="num">96.5000</td><td class="num">2660</td><td class="num">63.5000</td><td class="num">63.4000</td><td class="num">100</td><td class="num">82</td><td class="num">82</td><td class="num">97</td><td class="num">-3.1000</td><td class="num">-2.1000</td><td class="num">34.6000</td><td class="num">30.5000</td><td class="num">72.1000</td><td class="num">102.80</td><td class="num">178</td><td class="num">-62627</td></tr>
|
||||
<tr><td></td><td class="num">2020</td><td class="num">110</td><td class="num">752.80</td><td class="num">2.5900</td><td class="num">-0.7000</td><td class="num">4</td><td class="num">0.5000</td><td class="num">0.7600</td><td class="num">0.5000</td><td class="num">97</td><td class="num">0.9800</td><td class="num">2.0300</td><td class="num">5.2500</td><td class="num">170</td><td class="num">512498</td><td class="num">1.5200</td><td class="num">467633</td><td class="num">1180</td><td class="num">3070.20</td><td class="num">90</td><td class="num">2873</td><td class="num">85</td><td class="num">0.0200</td><td class="num">1723</td><td class="num">100</td><td class="num">100</td><td class="num">2630</td><td class="num">62.5000</td><td class="num">42.3000</td><td class="num">97.5000</td><td class="num">76</td><td class="num">79</td><td class="num">100</td><td class="num">-0.1000</td><td class="num">2.6000</td><td class="num">36.3000</td><td class="num">31.3000</td><td class="num">65.8000</td><td class="num">100</td><td class="num">175.50</td><td class="num">-29735</td></tr>
|
||||
<tr><td></td><td class="num">2021</td><td class="num">122</td><td class="num">883</td><td class="num">3.2200</td><td class="num">4.3000</td><td class="num">3.7000</td><td class="num">1</td><td class="num">1.0900</td><td class="num">2.5000</td><td class="num">102.80</td><td class="num">1.4300</td><td class="num">2.2600</td><td class="num">5.6400</td><td class="num">183</td><td class="num">644400</td><td class="num">2.1200</td><td class="num">615093</td><td class="num">1144</td><td class="num">3415.80</td><td class="num">106</td><td class="num">2978</td><td class="num">110.50</td><td class="num">0.0100</td><td class="num">1853</td><td class="num">108.50</td><td class="num">105</td><td class="num">2672</td><td class="num">63.8000</td><td class="num">69.3000</td><td class="num">103</td><td class="num">96</td><td class="num">77.5000</td><td class="num">104.50</td><td class="num">-1.5000</td><td class="num">3.1000</td><td class="num">35.8000</td><td class="num">31.6000</td><td class="num">74.5000</td><td class="num">105.20</td><td class="num">170</td><td class="num">131902</td></tr>
|
||||
<tr><td></td><td class="num">2022</td><td class="num">128</td><td class="num">258.30</td><td class="num">3.3800</td><td class="num">2.6000</td><td class="num">2.9000</td><td class="num">3.2500</td><td class="num">3.7700</td><td class="num">5.1000</td><td class="num">99.2000</td><td class="num">3.1400</td><td class="num">4.2500</td><td class="num">8.1800</td><td class="num">186.50</td><td class="num">683585</td><td class="num">3.6000</td><td class="num">731370</td><td class="num">1292</td><td class="num">3561</td><td class="num">95</td><td class="num">2237</td><td class="num">140.20</td><td class="num">0.0200</td><td class="num">1903</td><td class="num">105</td><td class="num">107.50</td><td class="num">2726</td><td class="num">64.5000</td><td class="num">97</td><td class="num">100.50</td><td class="num">85</td><td class="num">76</td><td class="num">108</td><td class="num">-3.5000</td><td class="num">-0.7000</td><td class="num">34.5000</td><td class="num">31.8000</td><td class="num">85.2000</td><td class="num">104.50</td><td class="num">183</td><td class="num">39185</td></tr>
|
||||
<tr><td></td><td class="num">2023</td><td class="num">118</td><td class="num">355.20</td><td class="num">3.9300</td><td class="num">1.4000</td><td class="num">2.7000</td><td class="num">3.5000</td><td class="num">3.7500</td><td class="num">3.6000</td><td class="num">98.8000</td><td class="num">3.5500</td><td class="num">4.4000</td><td class="num">8.4000</td><td class="num">183</td><td class="num">632744</td><td class="num">3.7800</td><td class="num">642756</td><td class="num">1305</td><td class="num">3680</td><td class="num">96.5000</td><td class="num">2655</td><td class="num">120</td><td class="num">0.0300</td><td class="num">1920</td><td class="num">102</td><td class="num">106</td><td class="num">2750</td><td class="num">65</td><td class="num">82.5000</td><td class="num">99.2000</td><td class="num">80</td><td class="num">72</td><td class="num">109.50</td><td class="num">-0.5000</td><td class="num">1.5000</td><td class="num">34</td><td class="num">30.8000</td><td class="num">80.5000</td><td class="num">101</td><td class="num">186.50</td><td class="num">-50841</td></tr>
|
||||
<tr><td></td><td class="num">2024</td><td class="num">115</td><td class="num">380</td><td class="num">4</td><td class="num">2.2000</td><td class="num">2.8000</td><td class="num">3</td><td class="num">3.3000</td><td class="num">2.3000</td><td class="num">99.5000</td><td class="num">3.2000</td><td class="num">3.9000</td><td class="num">7.5000</td><td class="num">185</td><td class="num">660000</td><td class="num">3.4200</td><td class="num">650000</td><td class="num">1350</td><td class="num">3800</td><td class="num">98</td><td class="num">2400</td><td class="num">115</td><td class="num">0.0300</td><td class="num">1950</td><td class="num">103.50</td><td class="num">105.50</td><td class="num">2760</td><td class="num">65.2000</td><td class="num">80</td><td class="num">99.5000</td><td class="num">82</td><td class="num">68</td><td class="num">110</td><td class="num">-3.3000</td><td class="num">0.8000</td><td class="num">33.5000</td><td class="num">30</td><td class="num">82</td><td class="num">101.50</td><td class="num">183</td><td class="num">27256</td></tr>
|
||||
<tr><td></td><td class="num">2025</td><td class="num">112</td><td class="num">350</td><td class="num">3.6000</td><td class="num">1.8000</td><td class="num">3</td><td class="num">2.7500</td><td class="num">3</td><td class="num">1.8000</td><td class="num">99.8000</td><td class="num">2.8000</td><td class="num">3.5000</td><td class="num">6.8000</td><td class="num">184</td><td class="num">650000</td><td class="num">3.1000</td><td class="num">640000</td><td class="num">1380</td><td class="num">3900</td><td class="num">99</td><td class="num">2500</td><td class="num">110</td><td class="num">0.0300</td><td class="num">1980</td><td class="num">104</td><td class="num">106</td><td class="num">2770</td><td class="num">65.5000</td><td class="num">75</td><td class="num">100</td><td class="num">84</td><td class="num">65</td><td class="num">111</td><td class="num">-2</td><td class="num">1</td><td class="num">33</td><td class="num">29.5000</td><td class="num">81</td><td class="num">101</td><td class="num">185</td><td class="num">-10000</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="Zt_추정">
|
||||
<div class="sheet-title">📊 Zt_추정</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="6" class="merged"> Zt 추정 (Belkin & Suchower 1998)</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="6" class="section"> 방법론: 관측 전이행렬 역산 → WLS → Zt</td></tr>
|
||||
<tr><td></td><td>자산상관계수 (ρ)</td><td class="num">0.2000</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="6" class="section"> ρ = 0.20 근거</td></tr>
|
||||
<tr><td></td><td>[1] Basel III IRB: 기업 ρ = 0.12~0.24 (CRE31.6)</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td> R = 0.12×(1-e^(-50×PD))/(1-e^(-50)) + 0.24×(1-(1-e^(-50×PD))/(1-e^(-50)))</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>[2] BBB(PD≈0.2%) → R=0.208, A(PD≈0.07%) → R=0.217</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>[3] 한국 기업 포트폴리오 평균: ρ ≈ 0.20 (투자/투기 혼합)</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>[4] Moody's Analytics CreditEdge: single-factor ρ ≈ 0.15~0.25</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>Zt 평균 (μ)</td><td class="num">-0.2001</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>Zt 표준편차 (σ)</td><td class="num">0.7673</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>관측 기간</td><td>1998~2025 (28개년)</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>연도</td><td>Zt</td><td>HOUSING_PRICE</td><td>CURRENT_ACCOUNT</td><td>CREDIT_SPREAD_LAG1</td></tr>
|
||||
<tr><td></td><td class="num">1998</td><td class="num">-2.1505</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td class="num">1999</td><td class="num">-0.8673</td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td class="num">2000</td><td class="num">0.0862</td><td class="num">55.2000</td><td class="num">123.50</td><td></td></tr>
|
||||
<tr><td></td><td class="num">2001</td><td class="num">-0.7190</td><td class="num">56.8000</td><td class="num">80.3000</td><td class="num">2.5500</td></tr>
|
||||
<tr><td></td><td class="num">2002</td><td class="num">-0.8970</td><td class="num">65.3000</td><td class="num">53.9000</td><td class="num">3.1500</td></tr>
|
||||
<tr><td></td><td class="num">2003</td><td class="num">-0.0819</td><td class="num">71.5000</td><td class="num">119.50</td><td class="num">2.7300</td></tr>
|
||||
<tr><td></td><td class="num">2004</td><td class="num">-1.3041</td><td class="num">71</td><td class="num">284.20</td><td class="num">3.2700</td></tr>
|
||||
<tr><td></td><td class="num">2005</td><td class="num">-0.3216</td><td class="num">73.5000</td><td class="num">149.80</td><td class="num">2.8100</td></tr>
|
||||
<tr><td></td><td class="num">2006</td><td class="num">1.4361</td><td class="num">80.2000</td><td class="num">53.9000</td><td class="num">1.8300</td></tr>
|
||||
<tr><td></td><td class="num">2007</td><td class="num">0.4562</td><td class="num">83.5000</td><td class="num">59.5000</td><td class="num">1.8300</td></tr>
|
||||
<tr><td></td><td class="num">2008</td><td class="num">0.4385</td><td class="num">84</td><td class="num">-57.8000</td><td class="num">1.7400</td></tr>
|
||||
<tr><td></td><td class="num">2009</td><td class="num">-0.6293</td><td class="num">84.8000</td><td class="num">328.10</td><td class="num">3.7100</td></tr>
|
||||
<tr><td></td><td class="num">2010</td><td class="num">-1.0651</td><td class="num">87</td><td class="num">282.10</td><td class="num">3.4400</td></tr>
|
||||
<tr><td></td><td class="num">2011</td><td class="num">0.000684</td><td class="num">89.5000</td><td class="num">184.10</td><td class="num">3.3200</td></tr>
|
||||
<tr><td></td><td class="num">2012</td><td class="num">-1.1979</td><td class="num">89</td><td class="num">508.40</td><td class="num">3.3400</td></tr>
|
||||
<tr><td></td><td class="num">2013</td><td class="num">-0.4545</td><td class="num">88.8000</td><td class="num">812.10</td><td class="num">2.8000</td></tr>
|
||||
<tr><td></td><td class="num">2014</td><td class="num">0.9161</td><td class="num">90.2000</td><td class="num">843.50</td><td class="num">2.6800</td></tr>
|
||||
<tr><td></td><td class="num">2015</td><td class="num">-0.4494</td><td class="num">95</td><td class="num">1059.40</td><td class="num">2.2300</td></tr>
|
||||
<tr><td></td><td class="num">2016</td><td class="num">-0.0127</td><td class="num">97.5000</td><td class="num">992.40</td><td class="num">2.4300</td></tr>
|
||||
<tr><td></td><td class="num">2017</td><td class="num">0.4831</td><td class="num">100</td><td class="num">752.60</td><td class="num">2.7200</td></tr>
|
||||
<tr><td></td><td class="num">2018</td><td class="num">0.6004</td><td class="num">102</td><td class="num">774.70</td><td class="num">2.5500</td></tr>
|
||||
<tr><td></td><td class="num">2019</td><td class="num">-0.2213</td><td class="num">104.50</td><td class="num">597</td><td class="num">2.7400</td></tr>
|
||||
<tr><td></td><td class="num">2020</td><td class="num">0.5464</td><td class="num">110</td><td class="num">752.80</td><td class="num">2.5900</td></tr>
|
||||
<tr><td></td><td class="num">2021</td><td class="num">0.4557</td><td class="num">122</td><td class="num">883</td><td class="num">3.2200</td></tr>
|
||||
<tr><td></td><td class="num">2022</td><td class="num">0.5197</td><td class="num">128</td><td class="num">258.30</td><td class="num">3.3800</td></tr>
|
||||
<tr><td></td><td class="num">2023</td><td class="num">-0.9198</td><td class="num">118</td><td class="num">355.20</td><td class="num">3.9300</td></tr>
|
||||
<tr><td></td><td class="num">2024</td><td class="num">0.1165</td><td class="num">115</td><td class="num">380</td><td class="num">4</td></tr>
|
||||
<tr><td></td><td class="num">2025</td><td class="num">-0.3662</td><td class="num">112</td><td class="num">350</td><td class="num">3.6000</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="AR1_모형">
|
||||
<div class="sheet-title">📊 AR1_모형</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="7" class="merged"> AR(1) + Macro 회귀 모형</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="7" class="section"> Z(t) = c + φ·Z(t-1) + Σ βᵢ·Xᵢ_std(t) + ε(t)</td></tr>
|
||||
<tr><td></td><td>※ 거시변수는 표준화(mean=0, std=1) 후 투입. β = '1σ 충격 → ΔZ'로 해석</td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="7" class="section"> 회귀 계수</td></tr>
|
||||
<tr><td></td><td>변수</td><td>계수</td><td>표준오차</td><td>t값</td><td>p값</td><td>유의성</td></tr>
|
||||
<tr><td></td><td>const</td><td class="num">-0.1781</td><td class="num">0.0911</td><td class="num">-1.9541</td><td class="num">0.0648</td><td>*</td></tr>
|
||||
<tr><td></td><td>Z_lag1</td><td class="num">-0.3352</td><td class="num">0.1488</td><td class="num">-2.2529</td><td class="num">0.0357</td><td>**</td></tr>
|
||||
<tr><td></td><td>HOUSING_PRICE</td><td class="num">0.6412</td><td class="num">0.1341</td><td class="num">4.7800</td><td class="num">0.000114</td><td>***</td></tr>
|
||||
<tr><td></td><td>CURRENT_ACCOUNT</td><td class="num">-0.1771</td><td class="num">0.1069</td><td class="num">-1.6570</td><td class="num">0.1131</td><td></td></tr>
|
||||
<tr><td></td><td>CREDIT_SPREAD_LAG1</td><td class="num">-0.6439</td><td class="num">0.1099</td><td class="num">-5.8605</td><td class="num">0.000010</td><td>***</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="7" class="section"> 모형 진단 통계</td></tr>
|
||||
<tr><td></td><td>R²</td><td class="num">0.6667</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>Adj. R²</td><td class="num">0.6000</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>F 통계량</td><td class="num">10.0014</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>F p-value</td><td class="num">0.000130</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>AIC</td><td class="num">34.9959</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>Durbin-Watson</td><td class="num">2.8731</td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="7" class="section"> 거시변수 표본 통계 (표준화 전 원시값)</td></tr>
|
||||
<tr><td></td><td>변수</td><td>평균</td><td>표준편차</td><td>최근값</td><td></td><td></td></tr>
|
||||
<tr><td></td><td>HOUSING_PRICE</td><td class="num">91.3192</td><td class="num">19.3527</td><td class="num">112</td><td></td><td></td></tr>
|
||||
<tr><td></td><td>CURRENT_ACCOUNT</td><td class="num">422.33</td><td class="num">334.75</td><td class="num">350</td><td></td><td></td></tr>
|
||||
<tr><td></td><td>CREDIT_SPREAD_LAG1</td><td class="num">2.9036</td><td class="num">0.6262</td><td class="num">3.6000</td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="7" class="section"> 변수별 경제적 해석</td></tr>
|
||||
<tr><td></td><td>HOUSING_PRICE</td><td>β=+0.6412 (+)</td><td rowspan="1" colspan="4">주택가격↑ → 담보가치↑ → 차입여력↑ → 부도↓ → Z↑</td></tr>
|
||||
<tr><td></td><td>CURRENT_ACCOUNT</td><td>β=-0.1771 (−)</td><td rowspan="1" colspan="4">경상수지↑(흑자) → 불황기 수출의존↑ → Z↓ (한국 특수 패턴)</td></tr>
|
||||
<tr><td></td><td>CREDIT_SPREAD_LAG1</td><td>β=-0.6439 (−)</td><td rowspan="1" colspan="4">전년 스프레드↑ → 당해 신용위험 전이 → 부도↑ → Z↓ (시차 효과)</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="시나리오_Z경로">
|
||||
<div class="sheet-title">📊 시나리오_Z경로</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="5" class="merged"> 시나리오별 Z(t) 경로</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="5" class="section"> t=1: 거시 충격 적용 | t≥2: AR(1) 감쇠 → TTC 수렴</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>연도(t+k)</td><td>호황 (Upside)</td><td>중립 (Base)</td><td>불황 (Downside)</td></tr>
|
||||
<tr><td></td><td class="num">1</td><td class="num">1.0527</td><td class="num">-0.0553</td><td class="num">-1.7174</td></tr>
|
||||
<tr><td></td><td class="num">2</td><td class="num">-0.5309</td><td class="num">-0.1595</td><td class="num">0.3975</td></tr>
|
||||
<tr><td></td><td class="num">3</td><td class="num">-0.000133</td><td class="num">-0.1246</td><td class="num">-0.3113</td></tr>
|
||||
<tr><td></td><td class="num">4</td><td class="num">-0.1781</td><td class="num">-0.1363</td><td class="num">-0.0737</td></tr>
|
||||
<tr><td></td><td class="num">5</td><td class="num">-0.1184</td><td class="num">-0.1324</td><td class="num">-0.1534</td></tr>
|
||||
<tr><td></td><td class="num">6</td><td class="num">-0.1384</td><td class="num">-0.1337</td><td class="num">-0.1267</td></tr>
|
||||
<tr><td></td><td class="num">7</td><td class="num">-0.1317</td><td class="num">-0.1333</td><td class="num">-0.1356</td></tr>
|
||||
<tr><td></td><td class="num">8</td><td class="num">-0.1340</td><td class="num">-0.1334</td><td class="num">-0.1326</td></tr>
|
||||
<tr><td></td><td class="num">9</td><td class="num">-0.1332</td><td class="num">-0.1334</td><td class="num">-0.1336</td></tr>
|
||||
<tr><td></td><td class="num">10</td><td class="num">-0.1335</td><td class="num">-0.1334</td><td class="num">-0.1333</td></tr>
|
||||
<tr><td></td><td class="num">15</td><td class="num">-0.1334</td><td class="num">-0.1334</td><td class="num">-0.1334</td></tr>
|
||||
<tr><td></td><td class="num">20</td><td class="num">-0.1334</td><td class="num">-0.1334</td><td class="num">-0.1334</td></tr>
|
||||
<tr><td></td><td class="num">25</td><td class="num">-0.1334</td><td class="num">-0.1334</td><td class="num">-0.1334</td></tr>
|
||||
<tr><td></td><td class="num">30</td><td class="num">-0.1334</td><td class="num">-0.1334</td><td class="num">-0.1334</td></tr>
|
||||
<tr><td></td><td class="num">40</td><td class="num">-0.1334</td><td class="num">-0.1334</td><td class="num">-0.1334</td></tr>
|
||||
<tr><td></td><td class="num">50</td><td class="num">-0.1334</td><td class="num">-0.1334</td><td class="num">-0.1334</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="Lifetime_PD">
|
||||
<div class="sheet-title">📊 Lifetime_PD</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="10" class="merged"> 시나리오별 누적 Lifetime PD (%)</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 호황 (Upside) (가중치 20%)</td></tr>
|
||||
<tr><td></td><td>시나리오</td><td>연도</td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td>CCC</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">1</td><td class="num">0.001305</td><td class="num">0.002038</td><td class="num">0.007820</td><td class="num">0.1078</td><td class="num">0.8244</td><td class="num">2.5111</td><td class="num">11.0291</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">2</td><td class="num">0.0334</td><td class="num">0.0489</td><td class="num">0.1523</td><td class="num">1.2209</td><td class="num">6.1478</td><td class="num">14.3028</td><td class="num">30.5701</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">3</td><td class="num">0.0454</td><td class="num">0.0712</td><td class="num">0.2728</td><td class="num">2.0723</td><td class="num">9.7869</td><td class="num">21.5086</td><td class="num">39.4917</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">5</td><td class="num">0.0774</td><td class="num">0.1449</td><td class="num">0.7004</td><td class="num">4.5770</td><td class="num">18.5657</td><td class="num">35.8879</td><td class="num">52.8646</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">7</td><td class="num">0.1092</td><td class="num">0.2499</td><td class="num">1.3212</td><td class="num">7.5916</td><td class="num">27.1785</td><td class="num">47.4539</td><td class="num">62.0428</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">10</td><td class="num">0.1584</td><td class="num">0.4910</td><td class="num">2.6113</td><td class="num">12.7192</td><td class="num">39.0549</td><td class="num">60.7263</td><td class="num">72.0198</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">15</td><td class="num">0.2491</td><td class="num">1.1786</td><td class="num">5.5874</td><td class="num">21.7816</td><td class="num">55.0271</td><td class="num">75.2884</td><td class="num">82.7164</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">20</td><td class="num">0.3584</td><td class="num">2.2744</td><td class="num">9.2994</td><td class="num">30.3966</td><td class="num">66.4407</td><td class="num">83.9785</td><td class="num">89.0086</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">30</td><td class="num">0.6701</td><td class="num">5.6406</td><td class="num">17.6391</td><td class="num">44.4615</td><td class="num">79.7891</td><td class="num">92.5201</td><td class="num">95.0957</td></tr>
|
||||
<tr><td></td><td>호황 (Upside)</td><td class="num">50</td><td class="num">1.8493</td><td class="num">15.2686</td><td class="num">33.0938</td><td class="num">61.2940</td><td class="num">89.5458</td><td class="num">97.3332</td><td class="num">98.4190</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 중립 (Base) (가중치 50%)</td></tr>
|
||||
<tr><td></td><td>시나리오</td><td>연도</td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td>CCC</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">1</td><td class="num">0.0131</td><td class="num">0.0193</td><td class="num">0.0626</td><td class="num">0.5973</td><td class="num">3.2599</td><td class="num">8.0151</td><td class="num">25.1118</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">2</td><td class="num">0.0291</td><td class="num">0.0456</td><td class="num">0.1786</td><td class="num">1.5138</td><td class="num">7.4367</td><td class="num">16.7838</td><td class="num">40.3810</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">3</td><td class="num">0.0443</td><td class="num">0.0746</td><td class="num">0.3382</td><td class="num">2.6152</td><td class="num">11.7983</td><td class="num">24.7793</td><td class="num">49.7632</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">5</td><td class="num">0.0755</td><td class="num">0.1523</td><td class="num">0.8153</td><td class="num">5.3791</td><td class="num">20.9219</td><td class="num">38.8003</td><td class="num">61.4840</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">7</td><td class="num">0.1073</td><td class="num">0.2648</td><td class="num">1.5026</td><td class="num">8.6767</td><td class="num">29.8387</td><td class="num">50.1129</td><td class="num">69.1802</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">10</td><td class="num">0.1567</td><td class="num">0.5216</td><td class="num">2.9118</td><td class="num">14.2067</td><td class="num">41.9948</td><td class="num">63.0198</td><td class="num">77.3817</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">15</td><td class="num">0.2480</td><td class="num">1.2463</td><td class="num">6.1132</td><td class="num">23.8192</td><td class="num">58.1023</td><td class="num">77.0581</td><td class="num">86.1044</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">20</td><td class="num">0.3583</td><td class="num">2.3902</td><td class="num">10.0534</td><td class="num">32.8144</td><td class="num">69.4296</td><td class="num">85.3543</td><td class="num">91.2138</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">30</td><td class="num">0.6727</td><td class="num">5.8734</td><td class="num">18.7773</td><td class="num">47.2255</td><td class="num">82.3953</td><td class="num">93.4112</td><td class="num">96.1343</td></tr>
|
||||
<tr><td></td><td>중립 (Base)</td><td class="num">50</td><td class="num">1.8588</td><td class="num">15.7497</td><td class="num">34.6026</td><td class="num">63.9444</td><td class="num">91.4614</td><td class="num">97.8386</td><td class="num">98.7959</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="10" class="section"> 불황 (Downside) (가중치 30%)</td></tr>
|
||||
<tr><td></td><td>시나리오</td><td>연도</td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td>CCC</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">1</td><td class="num">0.2401</td><td class="num">0.3283</td><td class="num">0.8295</td><td class="num">4.6213</td><td class="num">15.5554</td><td class="num">28.3305</td><td class="num">56.3571</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">2</td><td class="num">0.2463</td><td class="num">0.3471</td><td class="num">1.0083</td><td class="num">5.5513</td><td class="num">18.4195</td><td class="num">33.1617</td><td class="num">63.4497</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">3</td><td class="num">0.2689</td><td class="num">0.4089</td><td class="num">1.4462</td><td class="num">7.5656</td><td class="num">23.7982</td><td class="num">40.9283</td><td class="num">70.4479</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">5</td><td class="num">0.3005</td><td class="num">0.5393</td><td class="num">2.3388</td><td class="num">11.2799</td><td class="num">32.6050</td><td class="num">52.0021</td><td class="num">77.5624</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">7</td><td class="num">0.3341</td><td class="num">0.7284</td><td class="num">3.4487</td><td class="num">15.3342</td><td class="num">40.9153</td><td class="num">60.9306</td><td class="num">82.1168</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">10</td><td class="num">0.3884</td><td class="num">1.1346</td><td class="num">5.4544</td><td class="num">21.6124</td><td class="num">51.8620</td><td class="num">71.0807</td><td class="num">86.9009</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">15</td><td class="num">0.4951</td><td class="num">2.1663</td><td class="num">9.4855</td><td class="num">31.7065</td><td class="num">65.8925</td><td class="num">82.0951</td><td class="num">91.9642</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">20</td><td class="num">0.6316</td><td class="num">3.6412</td><td class="num">14.0208</td><td class="num">40.6129</td><td class="num">75.5089</td><td class="num">88.5935</td><td class="num">94.9255</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">30</td><td class="num">1.0301</td><td class="num">7.7128</td><td class="num">23.3321</td><td class="num">54.2014</td><td class="num">86.2731</td><td class="num">94.8930</td><td class="num">97.7739</td></tr>
|
||||
<tr><td></td><td>불황 (Downside)</td><td class="num">50</td><td class="num">2.4554</td><td class="num">18.1998</td><td class="num">39.0619</td><td class="num">69.2061</td><td class="num">93.5729</td><td class="num">98.3429</td><td class="num">99.3113</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="가중평균_PD">
|
||||
<div class="sheet-title">📊 가중평균_PD</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="9" class="merged"> 확률가중 Lifetime PD (%)</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td rowspan="1" colspan="9" class="section"> PD_weighted(t) = Σ w_s × PD_s(t)</td></tr>
|
||||
<tr><td></td><td>= 20%×호황 (Upside) + 50%×중립 (Base) + 30%×불황 (Downside)</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>연도</td><td>AAA</td><td>AA</td><td>A</td><td>BBB</td><td>BB</td><td>B</td><td>CCC</td></tr>
|
||||
<tr><td></td><td class="num">1</td><td class="num">0.0788</td><td class="num">0.1085</td><td class="num">0.2817</td><td class="num">1.7066</td><td class="num">6.4614</td><td class="num">13.0089</td><td class="num">31.6688</td></tr>
|
||||
<tr><td></td><td class="num">2</td><td class="num">0.0952</td><td class="num">0.1367</td><td class="num">0.4223</td><td class="num">2.6665</td><td class="num">10.4737</td><td class="num">21.2010</td><td class="num">45.3394</td></tr>
|
||||
<tr><td></td><td class="num">3</td><td class="num">0.1119</td><td class="num">0.1742</td><td class="num">0.6575</td><td class="num">3.9917</td><td class="num">14.9960</td><td class="num">28.9699</td><td class="num">53.9143</td></tr>
|
||||
<tr><td></td><td class="num">5</td><td class="num">0.1434</td><td class="num">0.2669</td><td class="num">1.2494</td><td class="num">6.9889</td><td class="num">23.9556</td><td class="num">42.1784</td><td class="num">64.5837</td></tr>
|
||||
<tr><td></td><td class="num">7</td><td class="num">0.1757</td><td class="num">0.4009</td><td class="num">2.0501</td><td class="num">10.4570</td><td class="num">32.6296</td><td class="num">52.8264</td><td class="num">71.6337</td></tr>
|
||||
<tr><td></td><td class="num">10</td><td class="num">0.2265</td><td class="num">0.6994</td><td class="num">3.6145</td><td class="num">16.1309</td><td class="num">44.3670</td><td class="num">64.9794</td><td class="num">79.1651</td></tr>
|
||||
<tr><td></td><td class="num">15</td><td class="num">0.3224</td><td class="num">1.5088</td><td class="num">7.0197</td><td class="num">25.7778</td><td class="num">59.8243</td><td class="num">78.2153</td><td class="num">87.1847</td></tr>
|
||||
<tr><td></td><td class="num">20</td><td class="num">0.4403</td><td class="num">2.7423</td><td class="num">11.0928</td><td class="num">34.6704</td><td class="num">70.6556</td><td class="num">86.0509</td><td class="num">91.8863</td></tr>
|
||||
<tr><td></td><td class="num">30</td><td class="num">0.7794</td><td class="num">6.3787</td><td class="num">19.9161</td><td class="num">48.7655</td><td class="num">83.0374</td><td class="num">93.6775</td><td class="num">96.4184</td></tr>
|
||||
<tr><td></td><td class="num">50</td><td class="num">2.0359</td><td class="num">16.3885</td><td class="num">35.6386</td><td class="num">64.9928</td><td class="num">91.7117</td><td class="num">97.8888</td><td class="num">98.8751</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sheet" id="검증결과">
|
||||
<div class="sheet-title">📊 검증결과</div>
|
||||
<table>
|
||||
<tr><td rowspan="1" colspan="7" class="merged"> 통계적 검증 결과</td></tr>
|
||||
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td></td><td>검정</td><td>대상</td><td>통계량</td><td>p-value</td><td>결과</td><td>해석</td></tr>
|
||||
<tr><td></td><td>ADF (Augmented Dickey-Fuller)</td><td>Zt 시계열</td><td>-4.8749</td><td>0.0000</td><td class="pass">Pass O</td><td>정상 시계열 (p=0.0000, α=0.05)</td></tr>
|
||||
<tr><td></td><td>Shapiro-Wilk Normality Test</td><td>Zt 시계열</td><td>0.9818</td><td>0.8921</td><td class="pass">Pass O</td><td>정규분포 (p=0.8921, α=0.05)</td></tr>
|
||||
<tr><td></td><td>Ljung-Box Q-test</td><td>잔차 자기상관</td><td>12.2306</td><td>0.0318</td><td class="fail">Fail X</td><td>자기상관 존재 (DW=2.873, LB p=0.0318)</td></tr>
|
||||
<tr><td></td><td>Breusch-Pagan / ARCH-LM</td><td>잔차 이분산</td><td>BP=3.3425</td><td>0.5022</td><td class="pass">Pass O</td><td>등분산 (BP p=0.5022, ARCH p=0.9545)</td></tr>
|
||||
<tr><td></td><td>R² / F-test</td><td>모형 설명력</td><td>R²=0.6667</td><td>0.0001</td><td class="pass">Pass O</td><td>R²=0.667, Adj.R²=0.600</td></tr>
|
||||
<tr><td></td><td>PD Properties</td><td>Cumulative PD (upside)</td><td>-</td><td>-</td><td class="pass">Pass O</td><td>모든 검증 통과</td></tr>
|
||||
<tr><td></td><td>PD Properties</td><td>Cumulative PD (base)</td><td>-</td><td>-</td><td class="pass">Pass O</td><td>모든 검증 통과</td></tr>
|
||||
<tr><td></td><td>PD Properties</td><td>Cumulative PD (downside)</td><td>-</td><td>-</td><td class="pass">Pass O</td><td>모든 검증 통과</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</body></html>
|
||||
@@ -58,14 +58,17 @@ class ScenarioEngine:
|
||||
"""
|
||||
시나리오별 Zt 경로 생성 (50년)
|
||||
|
||||
AR(1) 모형이 있으면: forecast_z_path() 사용 (macro_shocks 기반)
|
||||
없으면: Z-직접 방식 (μ±kσ) fallback
|
||||
|
||||
Parameters
|
||||
----------
|
||||
zt_history : Dict[int, float]
|
||||
과거 Zt 시계열
|
||||
macro_model : MacroZtModel, optional
|
||||
거시→Zt 회귀모형
|
||||
거시→Zt 회귀모형 (AR(1) 또는 OLS)
|
||||
macro_scenarios : Dict[str, pd.DataFrame], optional
|
||||
시나리오별 거시변수 경로
|
||||
(OLS 모형용) 시나리오별 거시변수 경로
|
||||
base_year : int
|
||||
기준 연도
|
||||
|
||||
@@ -77,18 +80,47 @@ class ScenarioEngine:
|
||||
zt_values = np.array(list(zt_history.values()))
|
||||
z_mean = zt_values.mean()
|
||||
z_std = zt_values.std()
|
||||
z_last = zt_values[-1] # 마지막 관측값
|
||||
|
||||
logger.info(f"Zt 통계: μ={z_mean:.4f}, σ={z_std:.4f}")
|
||||
logger.info(f"Zt 통계: μ={z_mean:.4f}, σ={z_std:.4f}, Z_last={z_last:.4f}")
|
||||
|
||||
z_paths = {}
|
||||
|
||||
# AR(1) 모형 사용 경로
|
||||
use_ar1 = (macro_model is not None and
|
||||
hasattr(macro_model, 'is_ar1') and
|
||||
macro_model.is_ar1)
|
||||
|
||||
if use_ar1:
|
||||
logger.info("AR(1)+Macro 모형으로 시나리오 경로 생성")
|
||||
|
||||
for scenario_name, scenario_cfg in self.scenario_config.items():
|
||||
# config에서 macro_shocks 가져오기
|
||||
macro_shocks = scenario_cfg.get("macro_shocks", {})
|
||||
|
||||
# forecast_z_path로 50년 경로 생성
|
||||
z_path = macro_model.forecast_z_path(
|
||||
z_last=z_last,
|
||||
macro_shocks=macro_shocks,
|
||||
horizon=self.total_horizon
|
||||
)
|
||||
|
||||
z_paths[scenario_name] = z_path
|
||||
|
||||
logger.info(
|
||||
f" {scenario_name}: Z[1]={z_path[0]:+.3f}, "
|
||||
f"Z[5]={z_path[4]:+.3f}, Z[10]={z_path[9]:+.3f}, "
|
||||
f"Z[50]={z_path[-1]:+.3f}"
|
||||
)
|
||||
else:
|
||||
# Fallback: Z-직접 방식
|
||||
logger.info("Z-직접 방식으로 시나리오 경로 생성 (AR(1) 없음)")
|
||||
|
||||
for scenario_name, scenario_cfg in self.scenario_config.items():
|
||||
z_multiplier = scenario_cfg.get("z_multiplier", 0.0)
|
||||
|
||||
# 시나리오별 초기 Z 수준
|
||||
z_scenario = z_mean + z_multiplier * z_std
|
||||
|
||||
# 거시 모형이 있으면 단기(1-5년) 거시 기반 Zt 예측
|
||||
# OLS 거시 모형이 있으면 단기 거시 기반
|
||||
if macro_model is not None and macro_scenarios is not None:
|
||||
scenario_key = scenario_name
|
||||
if scenario_key in macro_scenarios:
|
||||
@@ -102,22 +134,21 @@ class ScenarioEngine:
|
||||
z_short = np.full(self.pit_horizon, z_scenario)
|
||||
n_short = self.pit_horizon
|
||||
|
||||
# 전체 50년 Zt 경로 구성
|
||||
z_path = np.zeros(self.total_horizon)
|
||||
|
||||
# Phase 1: PIT 기간 (1~pit_horizon년)
|
||||
# Phase 1: PIT 기간
|
||||
for t in range(min(n_short, self.total_horizon)):
|
||||
val = z_short[t] if t < len(z_short) else z_scenario
|
||||
z_path[t] = val if np.isfinite(val) else z_scenario
|
||||
|
||||
# Phase 2: Mean-reversion 기간 (pit_horizon+1 ~ transition_horizon년)
|
||||
# Phase 2: Mean-reversion
|
||||
for t in range(self.pit_horizon, min(self.transition_horizon, self.total_horizon)):
|
||||
decay = np.exp(-self.mean_reversion_lambda * (t - self.pit_horizon + 1))
|
||||
z_path[t] = z_path[self.pit_horizon - 1] * decay
|
||||
|
||||
# Phase 3: TTC 기간 (transition_horizon+1 ~ total_horizon년)
|
||||
# Phase 3: TTC
|
||||
for t in range(self.transition_horizon, self.total_horizon):
|
||||
z_path[t] = 0.0 # TTC (Z=0)
|
||||
z_path[t] = 0.0
|
||||
|
||||
z_paths[scenario_name] = z_path
|
||||
|
||||
|
||||
@@ -44,7 +44,11 @@ def test_stationarity(
|
||||
-------
|
||||
dict with test_statistic, p_value, critical_values, is_stationary
|
||||
"""
|
||||
result = adfuller(series, autolag="AIC")
|
||||
# BIC를 사용하는 이유:
|
||||
# - AIC는 소표본(N<50)에서 과다 lag 선택 경향 (Hamilton 1994, Ch.17)
|
||||
# - N=26에서 AIC → lag=8 → 유효관측치=17 → 검정력 상실
|
||||
# - BIC는 보수적 lag 선택 → 소표본에서 적절 (Schwarz 1978)
|
||||
result = adfuller(series, autolag="BIC")
|
||||
|
||||
is_stationary = result[1] < significance
|
||||
|
||||
|
||||
Reference in New Issue
Block a user