1. VaR의 정의와 위험요인(Risk Factor)

VaR는 회사가 보유한 포지션의 위험에 대한 구체적인 수치로서, 시장이 불리한 방향으로 움직일 경우 보유한 포트폴리오에서 일정기간 동안에 발생하는 최대손실 가능액을 의미한다. VaR를 측정하기 위해 중요한 것은 포트폴리오 가치변화(\(\Delta V\))의 분포를 그려내는 것이다. 그러나 포지션의 가치변화의 분포를 알아내는 일은 쉽지 않다. 이에 따라 VaR의 측정은 포지션의 가치변화 분포를 어떻게 추정할 것인가의 문제가 된다.

포트폴리오의 가치변화를 파악하기 위해서는 우선 그 가치에 영향을 주는 위험요인(Risk Factor)이 무엇인지 파악해야 한다. 주식의 경우 주가, 채권의 경우 채권 수익률이 위험요인이 될 수 있을 것이다. 그러나 스왑이나 옵션과 같은 파생상품 등이 포지션에 포함된 경우에는 포지션의 가치변화를 평가하는 것이 쉽지 않기에 더욱 복잡한 방식이 요구된다. 또한 포지션의 위험요인이 많아질수록 각 위험요인간의 상관관계를 추가적으로 추정해야 하기 때문에 실제 VaR계산이 매우 어려워진다.

1.1 Delta Normal method

델타노말 분석 방법은 포지션의 가치변동을 위험요인의 선형결합으로 나타내고, 포지션 가치변화의 분포를 정규분포가 되도록 하여 VaR를 측정한다. 대략적인 분석 방법은 다음과 같다.

\[ VaR_{(1-\alpha)} = z_{\alpha} \times \sigma(\Delta x) \times \frac{\delta V}{\delta x} \]

  1. 포지션 가치(\(V\))에 영향을 주는 위험요인(\(X\)) 식별
  2. 위험요인(\(X\))을 정규분포로 가정하고, \(V\)\(\Delta x\)의 선형함수로 표현
  3. \(\Delta x\)의 선형결합인 \(V\) 역시 정규분포라고 가정하고 이로부터 VaR를 측정!!!

1.2 금융자산별 VaR 계산 과정

1.2.1 개별주식

포지션의 가치(\(S\))이고 위험요인으로서 주가수익률(\(\Delta S / S\))인 경우

\[ \begin{equation} \begin{split} \sigma(\Delta S) \cdot z & = \sigma(S \cdot \Delta S / S) \cdot z \\ & = S \cdot \sigma(\Delta S / S) \cdot z \end{split} \end{equation} \]


만약 위험요인으로 주가지수(\(K\))의 수익률을 사용한다면 주가지수에 대한 주가의 민감도를 나타내는 \(\beta\)를 델타로 사용하여 VaR 계산

\[ \begin{equation} \begin{split} \sigma(\Delta S) \cdot z & = \sigma(S \cdot \Delta S / S) \cdot z \\ & = \sigma(S \cdot \beta \cdot \Delta K / K) \cdot z \\ & = S \cdot \sigma(\Delta K / K) \cdot z \cdot \beta \end{split} \end{equation} \]

1.2.2 채권

위험요인으로는 채권의 유통수익률(YTM, \(\Delta y\))을 사용하며 채권가치의 변화와 수익률을 연계시키는 민감도는 수정듀레이션(modified duration)이다. 즉, 수정듀레이션이 델타로 사용된다.

\[ \begin{equation} MD = \frac{\Delta P/P}{\Delta y} \\ \begin{split} \sigma(\Delta P) \cdot z & = \sigma(P \cdot MD \cdot \Delta y) \cdot z \\ & = P \cdot \sigma(\Delta y) \cdot z \cdot MD \end{split} \end{equation} \]

2. 현금흐름 분해(Cash Flow Shredding)

자산의 위험을 측정하는 시발점은 그 자산의 현금흐름을 파악하는 것이다. 특정 자산을 그 자산의 현금흐름에 따라 여러 개의 무이표채로 나누는 것을 현금흐름분해라 한다. 현금흐름분해는 네 개의 요소로 구성되는데 거래상대방, 자산, 금액, 만기가 그것이다.

2.1 현금흐름분해 예시

  • 채권 종류: 금융채Ⅱ(금융기관채)/무보증/A+
  • 평가기준일: 2025-05-12
  • 잔존만기: 2년
  • 쿠폰: 9%
  • 이자지급: 연 1회
  • 액면가: 100만원

2.1.1 현금흐름분해

library(purrr)
package <- c("tidyverse", "xts", "lubridate", "pander")
pack_map <- map(package, ~ library(.x, character.only = TRUE))

options(scipen = 999)

1) Spot rate 데이터 불러오기

spot_yield <- readxl::read_excel("spot_rate.xls")

spot_yield <- spot_yield %>% 
  mutate(일자 = as.Date(일자, format = "%Y/%m/%d"),
         
         across(-일자, as.numeric)) %>% 
  select(일자, `1년`, `2년`)

spot_yield_xts <- xts(spot_yield[, 2:3], order.by = spot_yield$일자) / 100
tail(spot_yield_xts)
##                1년     2년
## 2025-04-30 0.03411 0.03455
## 2025-05-02 0.03406 0.03462
## 2025-05-07 0.03396 0.03440
## 2025-05-08 0.03386 0.03455
## 2025-05-09 0.03386 0.03485
## 2025-05-12 0.03386 0.03485

2. Cash Flow Mapping Matrix 생성

  • 2025-05-12 기준 1년과 2년 spot rate은 각각 3.386%, 3.485%이다.
  • 95% VaR의 z = 1.645
z <- 1.645

CF_matrix <- tibble(
  Year = c(1, 2),
  Yield = c(0.03386, 0.03485),
  Yield_vol = apply(diff(spot_yield_xts), 2, sd, na.rm = TRUE),
  CF = c(90000, 1090000)
)
CF_matrix <- CF_matrix %>% 
  mutate(PV = CF / (1 + Yield) ^ Year,
         MD = Year / (1 + Yield) ^ Year,
         VaR = PV * MD * Yield_vol * z)
pander(CF_matrix)
Year Yield Yield_vol CF PV MD VaR
1 0.03386 0.0003228 90000 87052 0.9672 44.71
2 0.03485 0.0005198 1090000 1017822 1.868 1625

3. 상관관계를 반영한 포트폴리오 VaR 계산

cor <- cor(spot_yield_xts)
pander(cor)
  1년 2년
1년 1 0.957
2년 0.957 1

\(VaR_p = \sqrt{VaR^T \cdot R \cdot VaR}\)

Portfolio_VaR <- sqrt(t(CF_matrix$VaR) %*% cor %*% CF_matrix$VaR)
  • 포트폴리오 \(1 Day~VaR_{95 \%}\) = 1,668.1

3. 현금흐름 매핑(Cash Flow Mapping)

현금흐름 매핑은 현금흐름분해를 통해 모든 개별현금흐름의 시점과 금액을 파악한 후 이를 현재가치로 전환하여 표준만기(standard time vertex)에 할당하는 일련의 과정을 말한다. <2. 현금흐름 분해>의 예시에서는 개별 현금흐름의 시점이 표준만기와 동일하므로 계산이 보다 간단하다.

하지만, 개별현금흐름의 시점이 표준만기와 다를 경우에는 이를 표준만기에 할당하는 별도의 계산과정이 필요하다. 일반적으로 spot rate의 변동성과 이들 사이의 상관계수의 추정이 가능한 잔존만기는 3M, 6M, 1Y, 2Y, 3Y, 5Y, 7Y, 10Y 등이다. 즉, VaR는 표준만기의 현금흐름에 대해서만 계산할 수 있다!!! 현금흐름을 분해한 후 이들의 잔존만기가 표준만기와 일치하지 않을 경우 이들을 인접한 두개의 표준만기에 할당하는 현금흐름 매핑을 실시한다.

3.1 표준만기 할당 함수

현금흐름의 현재가치를 표준만기 1과 2에 k, 1-k의 비율로 할당한다고 하면 포트폴리오의 분산은 다음과 같다.

\(k^2 \sigma^2_1 + (1-k)^2 \sigma^2_2 + 2k(1-k) \rho \sigma_1 \sigma_2\)

이것은 원래 포트폴리오의 분산인 \(\sigma^2_0\)과 같아야 한다.

\[ k^2 \sigma^2_1 + (1-k)^2 \sigma^2_2 + 2k(1-k) \rho \sigma_1 \sigma_2 = \sigma^2_0 \\ (\sigma^2_1 + \sigma^2_2-2\rho \sigma_1 \sigma_2)k^2 + 2(\rho\sigma_1 \sigma_2 - \sigma^2_2) + (\sigma^2_2-\sigma^2_1) = 0 \]

위 방정식의 해 중에서 0과 1 사이의 값을 택하면 이것이 표준만기 1에 할당하는 비율이 된다.

# 표준만기 할당 함수
weight_k <- function(s1, s2, s0, r) {
  
  a <- s1 ^ 2 + s2 ^ 2 - 2 * r * s1 * s2
  b <- -(s2 ^ 2) + r * s1 * s2
  c <- s2 ^ 2 - s0 ^ 2
  
  if (a == 0) {
    stop("이차항이 0이면 안됩니다.")
  } else if(b ^ 2 - a * c < 0) {
    stop("주어진 2차방정식은 실근이 없습니다.")
  }
  
  x1 <- (-b + sqrt(b ^ 2 - a * c)) / a
  x2 <- (-b - sqrt(b ^ 2 - a * c)) / a
  
  if (x1 > 0 & x1 < 1) {
    return(x1)
  } else if(x2 > 0 & x2 < 1) {
    return(x2)
  } else {
    message("해가 0~1 범위에 없습니다.")
    return(NULL)
  }
}

# 선형보간 함수 정의
interpolation <- function(r1, r2, t1, t2, t0) {
  val <- r1 + (r2 - r1) * (t0 - t1) / (t2 - t1)
  return(as.vector(val))
}

3.2 현금흐름매핑 예시

  • <2.1> 예시와 조건이 동일하지만 발행일로부터 3개월이 경과되어 표준만기가 불일치하는 경우
  • 발행일: 2025-02-12
  • 쿠폰지급일
    • 1차: 2026-02-12
    • 2차: 2027-02-12
  • 평가기준일: 2025-05-12
  • 잔존만기: 2년
  • 쿠폰: 9%
  • 이자지급: 연 1회
  • 액면가: 100만원

1) Spot rate 데이터 불러오기

spot_yield2 <- readxl::read_excel("spot_rate2.xls")

spot_yield2 <- spot_yield2 %>% 
  mutate(일자 = as.Date(일자, format = "%Y/%m/%d"),
         
         across(-일자, as.numeric)) %>% 
  select(일자, `6월`, `1년`, `2년`, `3년`)

spot_yield2_xts <- xts(spot_yield2[, 2:5], order.by = spot_yield2$일자) / 100
tail(spot_yield2_xts)
##                6월     1년     2년     3년
## 2025-04-30 0.03408 0.03411 0.03455 0.03724
## 2025-05-02 0.03398 0.03406 0.03462 0.03734
## 2025-05-07 0.03398 0.03396 0.03440 0.03709
## 2025-05-08 0.03393 0.03386 0.03455 0.03737
## 2025-05-09 0.03388 0.03386 0.03485 0.03782
## 2025-05-12 0.03388 0.03386 0.03485 0.03779

2) 표준만기 VaR matrix 생성

표준만기별 상관관계

# 95% 신뢰수준 z 값
z <- 1.645
# 평가기준일
EVAL_Date <- ymd(20250512)
# 표준만기별 상관관계
cor2 <- cor(spot_yield2_xts)
pander(cor2)
  6월 1년 2년 3년
6월 1 0.9418 0.8342 0.8496
1년 0.9418 1 0.957 0.9566
2년 0.8342 0.957 1 0.9961
3년 0.8496 0.9566 0.9961 1

표준만기 VaR matrix

VaR_matrix <- tibble(
  Year = c(0.5, 1, 2, 3),
  Yield = c(0.03388, 0.03386, 0.03485, 0.03779),
  Yield_vol = apply(diff(spot_yield2_xts), 2, sd, na.rm = TRUE),
  Price_vol = (Year / (1 + Yield) ^ Year) * Yield_vol * z,
  cor = c(cor2[1,2], cor2[2,3], cor2[3, 4], NA)
)

pander(VaR_matrix)
Year Yield Yield_vol Price_vol cor
0.5 0.03388 0.0002432 0.0001968 0.9418
1 0.03386 0.0003228 0.0005136 0.957
2 0.03485 0.0005198 0.001597 0.9961
3 0.03779 0.0005638 0.002489 NA

3) 현금흐름 matrix 생성

CF_matrix <- tibble(
  Year = c(1, 2),
  CF = c(90000, 1090000),
  CF_Date = c(ymd(20260212), ymd(20270212))
  ) %>% 
    mutate(Maturity = as.vector((CF_Date - EVAL_Date) / 365))

CF1_yield <- interpolation(VaR_matrix$Yield[1], VaR_matrix$Yield[2], VaR_matrix$Year[1], VaR_matrix$Year[2], CF_matrix$Maturity[1])
CF2_yield <- interpolation(VaR_matrix$Yield[2], VaR_matrix$Yield[3], VaR_matrix$Year[2], VaR_matrix$Year[3], CF_matrix$Maturity[2])
CF1_yield_vol <- interpolation(VaR_matrix$Yield_vol[1], VaR_matrix$Yield_vol[2], VaR_matrix$Year[1], VaR_matrix$Year[2], CF_matrix$Maturity[1])
CF2_yield_vol <- interpolation(VaR_matrix$Yield_vol[2], VaR_matrix$Yield_vol[3], VaR_matrix$Year[2], VaR_matrix$Year[3], CF_matrix$Maturity[2])

CF_matrix <- CF_matrix %>% 
  mutate(Yield = c(CF1_yield, CF2_yield),
         Yield_vol = c(CF1_yield_vol, CF2_yield_vol),
         PV = CF / (1 + Yield) ^ Maturity,
         MD = Maturity / (1 + Yield) ^ Maturity,
         Price_vol = MD * Yield_vol * z
         )

CF1_w <- weight_k(VaR_matrix$Price_vol[1], VaR_matrix$Price_vol[2], CF_matrix$Price_vol[1], VaR_matrix$cor[1])
CF2_w <- weight_k(VaR_matrix$Price_vol[2], VaR_matrix$Price_vol[3], CF_matrix$Price_vol[2], VaR_matrix$cor[2])

CF_matrix <- CF_matrix %>% 
  mutate(weight = c(CF1_w, CF2_w),
         VaR_1 = PV * weight,
         VaR_2 = PV * (1 - weight))

pander(CF_matrix[, c("Year", "CF", "VaR_1", "VaR_2")])
Year CF VaR_1 VaR_2
1 90000 45672 42089
2 1090000 291463 735317
  • 각 표준만기에 할당되는 VaR는 다음과 같다.
    • 0.5 -> 45,672.36
    • 1 -> 333,551.8
    • 2 -> 735,317

테이블로 정리하면 다음과 같다.

Final_matrix <- tibble(
  Year = c(0.5, 1, 2),
  CF = c(CF_matrix$VaR_1[1], CF_matrix$VaR_1[2] + CF_matrix$VaR_2[1], CF_matrix$VaR_2[2]),
  Price_vol = VaR_matrix$Price_vol[1:3]
) %>% 
  mutate(
    VaR = CF * Price_vol
  )

cor <- cor2[1:3, 1:3]

PF_VaR <- sqrt(t(Final_matrix$VaR) %*% cor %*% Final_matrix$VaR)
  • 포트폴리오 \(1 Day~VaR_{95 \%}\) = 1,346.69