Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor · · Updated 2026-08-21

위험가치(VaR) 산출 방법 3가지와 특징

SPY 수익률 시계열을 활용한 역사적 시뮬레이션, 파라메트릭, 몬테카를로 방식의 VaR 산출법을 설명합니다. VaR이 포착하지 못하는 꼬리 위험인 기대 손실(Expected Shortfall)의 개념과 한계점을 함께 다룹니다.

위험가치(VaR)의 정의와 산출 방식

위험가치(Value at Risk, VaR)는 특정 기간과 신뢰 수준을 설정한 뒤, 수익률 시계열에서 백분위수를 도출하여 산출한다. 예를 들어 1일 99% VaR이 2%라는 것은, 100거래일 중 99일은 손실이 2% 이내로 유지되고 나머지 하루만 이를 초과한다는 의미이다. 동일한 입력값을 사용하더라도 세 가지 표준 산출 방법론에 따라 결과값이 도출되는데, 이들 간의 차이는 일반적인 예상보다 훨씬 크다.

VaR(위험가치)가 실제로 측정하는 것

VaR은 손실 분포의 분위수(quantile)입니다. 표본 내 모든 일간 수익률을 최악에서 최선 순으로 나열한 뒤, 최악의 지점에서 1%만큼 이동했을 때 도달하는 수익률을 양의 손실 값으로 표기한 것이 1일 99% 역사적 VaR입니다. 이 산출 방식은 그 어떤 최악의 상황도 보장하지 않습니다. 이는 추정치가 설명하는 영역의 경계선을 표시할 뿐입니다.

이는 독자들이 가장 자주 오해하는 속성입니다. 99% VaR은 최악의 1%에 해당하는 날들이 해당 임계값을 넘어선다는 점을 명시할 뿐, 그 너머로 얼마나 큰 손실이 발생하는지에 대해서는 침묵합니다. 최대 낙폭(Maximum drawdown)은 포트폴리오가 실제로 겪었던 고점 대비 저점까지의 손실이라는 다른 질문에 답하며, 이 두 지표는 동일한 포트폴리오 쌍에 대해서도 서로 상반된 순위를 매길 수 있습니다.

아래의 모든 수치는 2010년 초부터 2025년 말까지의 SPY 일간 종가를 기초로 하며, 이를 종가 대비 종가 수익률(percent change)로 변환한 단일 시계열에 근거합니다. 분석 기간은 이동하지 않고 고정되어 있으며, 각 패널은 동일한 날짜의 데이터를 사용하여 시리즈를 재구성하므로 실행 간 수치 변화는 발생하지 않습니다.

조회고정 수익률 시계열: 2010년부터 2025년까지 SPY(S&P 500 ETF) 연도별 일간 수익률
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-12-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
rets AS
(
    SELECT date, 100 * (close_px / prev_close - 1) AS ret_pct
    FROM
    (
        SELECT date, close_px,
               lagInFrame(close_px, 1) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
        FROM px
    )
    WHERE date >= '2010-01-01' AND prev_close > 0
)
SELECT
    toString(toYear(date))        AS year,
    count()                       AS sessions,
    round(avg(ret_pct), 3)        AS mean_return_pct,
    round(stddevSamp(ret_pct), 2) AS daily_sigma_pct
FROM rets
GROUP BY year
ORDER BY year
Run this yourself

해당 시계열은 16년의 달력 기간을 포함하며, 2010년 한 해에만 252번의 거래 세션이 존재합니다. 이어지는 모든 분석에서 다음 두 가지 특징이 중요합니다. 해당 기간의 평균 일간 수익률은 0.054%로, 일간 표준편차(sigma)인 2010와 비교할 때 1.13% 수준에 불과하여 사실상 무의미합니다. 또한 시그마는 상수가 아닙니다. 2020년에는 일간 2.11%의 변동성을 보였으나, 2017년에는 0.43%를 기록했습니다. 단 하나의 시그마 값으로는 이 두 기간을 모두 설명할 수 없습니다.

위험가치(VaR) 산출의 세 가지 방법

과거 시뮬레이션법(Historical VaR): 과거 실적에서 백분위수 도출

실제 수익률을 정렬하여 백분위수를 산출합니다. 분포를 가정하지 않는다는 점이 이 방법의 장점입니다. 다만 이 방법은 표본 데이터에 추정 대상이 되는 유형의 시장 상황이 이미 포함되어 있다고 가정합니다. 신뢰 수준을 충분히 높게 설정하면 전체 기간 중 가장 저조했던 몇 번의 거래일만이 결과값에 반영됩니다.

모수적 방법(Parametric VaR): 평균에서 z 곱하기 시그마를 차감

수익률 시계열을 평균과 시그마(표준편차)로 요약한 뒤, 수익률이 정규분포를 따른다고 가정합니다. VaR는 'z 곱하기 시그마'에서 평균을 뺀 값이며, 여기서 z는 표준정규분포의 분위수입니다(95% 신뢰 수준에서 1.645, 99%에서 2.326, 99.9%에서 3.090). 계산은 즉각적이지만, 이 가정은 특정 방향으로 오류를 범합니다. 일간 주식 수익률은 정규분포 곡선보다 중앙에 더 밀집되어 있고 양 끝단(꼬리)은 훨씬 더 길게 뻗어 있습니다. 여기서 사용되는 시그마는 샤프 지수(Sharpe ratio)의 분모이기도 하며, 팻 테일(fat-tailed) 데이터를 다룰 때 동일한 사각지대를 노출합니다.

몬테카를로 시뮬레이션(Monte Carlo VaR): 시드 값을 고정한 시뮬레이션

가정된 프로세스에서 대규모 합성 표본을 추출하여 백분위수를 도출합니다. 아래 패널은 해시 시드(hash seeded) 균등 분포 시퀀스에서 박스-뮬러 변환(Box-Muller transform)을 통해 생성된 4만 개의 표준정규분포 난수를 사용하며, 이를 해당 시계열의 평균과 시그마에 맞춰 조정합니다. SQL 내에 시드 값이 고정되어 있어 재실행 시에도 동일한 난수가 생성됩니다. 시뮬레이션은 유연성, 경로 의존성, 다중 자산 간 상관관계를 반영할 수 있게 해주지만, 현실성을 보장하지는 않습니다. 정규분포를 입력하면 샘플링 노이즈가 추가된 모수적 방법의 결과값이 출력될 뿐입니다. 반면 부트스트랩 신뢰구간(bootstrapped confidence intervals) 기법처럼 관측된 수익률을 재표본 추출하면 실제 데이터의 꼬리 분포를 유지할 수 있습니다.

조회단일 시계열, 세 가지 방법론: 6개 신뢰 수준에서의 1일 VaR(Value at Risk, 위험가치)
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-12-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
rets AS
(
    SELECT date, 100 * (close_px / prev_close - 1) AS ret_pct
    FROM
    (
        SELECT date, close_px,
               lagInFrame(close_px, 1) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
        FROM px
    )
    WHERE date >= '2010-01-01' AND prev_close > 0
),
emp AS
(
    SELECT
        avg(ret_pct)                  AS mu,
        stddevSamp(ret_pct)           AS sd,
        quantileExact(0.100)(ret_pct) AS h90,
        quantileExact(0.050)(ret_pct) AS h95,
        quantileExact(0.025)(ret_pct) AS h975,
        quantileExact(0.010)(ret_pct) AS h99,
        quantileExact(0.005)(ret_pct) AS h995,
        quantileExact(0.001)(ret_pct) AS h999
    FROM rets
),
draws AS
(
    SELECT
        quantileExact(0.100)(z) AS z90,
        quantileExact(0.050)(z) AS z95,
        quantileExact(0.025)(z) AS z975,
        quantileExact(0.010)(z) AS z99,
        quantileExact(0.005)(z) AS z995,
        quantileExact(0.001)(z) AS z999
    FROM
    (
        SELECT sqrt(-2 * log(u1)) * cos(2 * pi() * u2) AS z
        FROM
        (
            SELECT
                (cityHash64('var-seed-u1', i) % 999999937 + 1) / 999999938.0 AS u1,
                (cityHash64('var-seed-u2', i) % 999999937 + 1) / 999999938.0 AS u2
            FROM (SELECT arrayJoin(range(40000)) AS i)
        )
    )
)
SELECT
    tupleElement(lvl, 1)                                                  AS confidence,
    round(-1 * tupleElement(lvl, 2), 2)                                   AS historical_var_pct,
    round(tupleElement(lvl, 3) * sd - mu, 2)                              AS parametric_var_pct,
    round(-1 * (mu + sd * tupleElement(lvl, 4)), 2)                       AS monte_carlo_var_pct,
    round(-1 * tupleElement(lvl, 2) - (tupleElement(lvl, 3) * sd - mu), 2) AS method_spread
FROM
(
    SELECT
        mu,
        sd,
        arrayJoin([
            ('90.0%', h90,  1.281552, z90,  1),
            ('95.0%', h95,  1.644854, z95,  2),
            ('97.5%', h975, 1.959964, z975, 3),
            ('99.0%', h99,  2.326348, z99,  4),
            ('99.5%', h995, 2.575829, z995, 5),
            ('99.9%', h999, 3.090232, z999, 6)
        ]) AS lvl
    FROM emp
    CROSS JOIN draws
)
ORDER BY tupleElement(lvl, 5)
Run this yourself

95.0% 수준에서 세 가지 방법의 결과값은 1포인트 이내로 수렴합니다(1.66% 과거 시뮬레이션법, 1.74% 모수적 방법, 1.71% 시뮬레이션). 그러나 99.9% 수준으로 넘어가면 차이가 발생합니다. 동일한 데이터를 사용했음에도 5.85% 과거 시뮬레이션법과 3.31% 모수적 방법 사이에는 2.54 퍼센트포인트의 격차가 나타납니다. 시뮬레이션 열이 모든 수준에서 모수적 방법 열과 나란히 배치되는 것은 결함이 아니라 중요한 교훈을 시사합니다. 시뮬레이션은 입력된 분포를 그대로 재현할 뿐입니다.

패널의 열을 아래로 읽어 내려가면 신뢰 수준이 높아질수록 임계값이 확대됩니다. 행을 가로질러 읽으면 분포의 중앙부에서는 방법론 선택이 결과에 거의 영향을 미치지 않지만, 꼬리 부분에서는 방법론이 결과를 지배합니다. 산출 방법과 데이터 기간이 명시되지 않은 VaR 한도는 타인이 재현할 수 없는 무의미한 수치에 불과합니다.

VaR이 알려주지 않는 것: 기대손실(Expected Shortfall)

조건부 VaR(conditional VaR)이라고도 불리는 기대손실은 VaR을 초과하는 날의 손실 평균을 산출합니다. VaR은 꼬리 위험(tail risk)이 시작되는 지점을 표시하며, 기대손실은 그 꼬리 안쪽의 내용을 측정합니다.

조회VaR 대비 ES(Expected Shortfall, 기대손실): 임계값을 초과하는 평균 손실
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-12-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
rets AS
(
    SELECT date, 100 * (close_px / prev_close - 1) AS ret_pct
    FROM
    (
        SELECT date, close_px,
               lagInFrame(close_px, 1) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
        FROM px
    )
    WHERE date >= '2010-01-01' AND prev_close > 0
),
qs AS
(
    SELECT
        quantileExact(0.100)(ret_pct) AS q90,
        quantileExact(0.050)(ret_pct) AS q95,
        quantileExact(0.025)(ret_pct) AS q975,
        quantileExact(0.010)(ret_pct) AS q99,
        quantileExact(0.005)(ret_pct) AS q995,
        quantileExact(0.001)(ret_pct) AS q999
    FROM rets
),
tails AS
(
    SELECT
        any(q90)                        AS var90,
        any(q95)                        AS var95,
        any(q975)                       AS var975,
        any(q99)                        AS var99,
        any(q995)                       AS var995,
        any(q999)                       AS var999,
        avgIf(ret_pct, ret_pct <= q90)  AS es90,
        avgIf(ret_pct, ret_pct <= q95)  AS es95,
        avgIf(ret_pct, ret_pct <= q975) AS es975,
        avgIf(ret_pct, ret_pct <= q99)  AS es99,
        avgIf(ret_pct, ret_pct <= q995) AS es995,
        avgIf(ret_pct, ret_pct <= q999) AS es999
    FROM rets
    CROSS JOIN qs
    HAVING countIf(ret_pct <= q999) > 0
)
SELECT
    tupleElement(lvl, 1)                                  AS confidence,
    round(-1 * tupleElement(lvl, 2), 2)                   AS historical_var_pct,
    round(-1 * tupleElement(lvl, 3), 2)                   AS expected_shortfall_pct,
    round(tupleElement(lvl, 3) / tupleElement(lvl, 2), 2) AS es_to_var_ratio
FROM
(
    SELECT
        arrayJoin([
            ('90.0%', var90,  es90,  1),
            ('95.0%', var95,  es95,  2),
            ('97.5%', var975, es975, 3),
            ('99.0%', var99,  es99,  4),
            ('99.5%', var995, es995, 5),
            ('99.9%', var999, es999, 6)
        ]) AS lvl
    FROM tails
)
ORDER BY tupleElement(lvl, 4)
Run this yourself

99.0%에서 이 시리즈의 VaR은 3.09%이며, 기대손실은 4.43%, 즉 임계값의 1.44배입니다. 정규분포 하에서는 동일한 수준에서 해당 비율이 1.15 근처에 위치하게 됩니다. 임계값이 이미 5.85%에 도달한 99.9%에서도 평균적인 초과 손실일은 8.14%로 나타납니다. VaR만을 기준으로 작성된 한도는 모든 초과 손실을 동일한 사건으로 취급하며, 비율 열은 그것이 얼마나 사실과 다른지를 측정합니다.

1일 99% VaR(Value at Risk, 위험가치)는 기관과 트레이더에게 동일한 의미인가?

아니며, 그 차이는 주로 기간(horizon)에서 발생합니다. 장 마감 후 포지션을 보유하지 않는(flat) 트레이더는 몇 시간 동안만 위험을 부담하므로, 1회 세션 기준의 임계값이 보유 기간과 일치합니다. 다만 장중 변동 경로는 종가 기준 수치를 크게 상회할 수 있습니다. 장기 부채를 조달하는 기관은 수년간 포지션을 보유하며, 그 익스포저(exposure)는 수 분기에 걸쳐 지속됩니다. 따라서 기관에게 1일 99% 수치는 그들이 부담하는 위험을 설명하는 지표라기보다 자본 관리 및 모니터링을 위한 도구로 기능합니다. 은행 자본 규제는 수년간 1일 99% VaR을 기반으로 설계되었으며, 이후 바젤(Basel) 시장 위험 프레임워크는 해당 측정치를 97.5% 기대손실(expected shortfall)로 변경했습니다.

기간 간의 차이를 연결하는 표준적인 방법은 시간의 제곱근(square root of time)을 이용한 스케일링입니다. 1회 세션 수치에 기간(세션 단위)의 제곱근을 곱하는 방식입니다. 이 단계는 수익률이 독립적이며 시그마(sigma, 표준편차)가 일정하다는 가정을 전제로 하지만, 위 연도별 시그마 열은 이미 그 가정이 성립하지 않음을 보여줍니다.

조회시간의 제곱근 스케일링과 측정된 다회차 세션 손실 비교, 99% 신뢰 수준
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
px AS
(
    SELECT date, max(toFloat64(close)) AS close_px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
      AND date >= '2009-11-01'
      AND date <  '2026-01-01'
    GROUP BY date
),
multi AS
(
    SELECT
        date,
        100 * (close_px / p1  - 1) AS r1,
        100 * (close_px / p5  - 1) AS r5,
        100 * (close_px / p10 - 1) AS r10,
        100 * (close_px / p20 - 1) AS r20
    FROM
    (
        SELECT
            date,
            close_px,
            lagInFrame(close_px, 1)  OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p1,
            lagInFrame(close_px, 5)  OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p5,
            lagInFrame(close_px, 10) OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p10,
            lagInFrame(close_px, 20) OVER (ORDER BY date ROWS BETWEEN 20 PRECEDING AND CURRENT ROW) AS p20
        FROM px
    )
    WHERE date >= '2010-01-01' AND p20 > 0
),
q AS
(
    SELECT
        quantileExact(0.01)(r1)  AS q1,
        quantileExact(0.01)(r5)  AS q5,
        quantileExact(0.01)(r10) AS q10,
        quantileExact(0.01)(r20) AS q20
    FROM multi
)
SELECT
    tupleElement(h, 1)                                             AS horizon,
    round(-1 * tupleElement(h, 3), 2)                              AS actual_var_pct,
    round(-1 * q1 * sqrt(tupleElement(h, 2)), 2)                   AS sqrt_scaled_var_pct,
    round(tupleElement(h, 3) / (q1 * sqrt(tupleElement(h, 2))), 2) AS actual_to_scaled_ratio
FROM
(
    SELECT
        q1,
        arrayJoin([
            ('1 session',   1.0,  q1,  1),
            ('5 sessions',  5.0,  q5,  2),
            ('10 sessions', 10.0, q10, 3),
            ('20 sessions', 20.0, q20, 4)
        ]) AS h
    FROM q
)
ORDER BY tupleElement(h, 4)
Run this yourself

첫 번째 행은 동일성 검증입니다. 1 session 시점에서는 스케일링된 수치가 측정된 수치와 같으며, 그 비율은 1입니다. 20 sessions 시점에서는 두 수치가 분리되는데, 이는 일반적으로 경고하는 지름길의 방향과는 다릅니다. 1회 세션 수치를 스케일링하면 13.8%이 나오지만, 해당 기간 동안 측정된 수익률은 10.7%를 기록하며, 그 비율은 0.78입니다. 이 구간에서 스케일링된 수치는 측정된 다중 세션 꼬리(tail)보다 높게 나타납니다. 여기에는 두 가지 패턴이 서로 상충합니다. 팻 테일(fat-tailed)을 가진 단일 세션 분포는 수익률이 합산됨에 따라 얇아지며, 이에 따라 집계된 99% 분위수는 기간의 제곱근보다 더 느리게 확대됩니다. 반면 변동성 군집(volatility clustering)은 이와 반대로 작용하여, 하나의 기간 내에 격렬한 세션들을 쌓아 올립니다. 이 시계열에서는 첫 번째 패턴이 더 크게 작용하지만, 다른 구간이나 다른 자산에서 어느 방향이 나타날지는 보장할 수 없습니다. 핵심은 승수(multiplier)가 측정값이 아닌 가정이라는 점입니다. 이러한 변동성 군집은 변동성 타겟팅(volatility targeting)이 포지션 규모를 조정할 때 의존하는 속성이기도 합니다.

한 가지 더 분명한 한계가 있습니다. 위에서 언급한 모든 내용은 광범위한 인덱스 펀드에 대한 단일 자산 VaR입니다. 상관관계가 있는 10개 종목으로 구성된 북(book)은 집중 위험(concentration risk)을 수반하며, 포트폴리오 VaR은 상관관계 추정을 통해 이를 처리합니다. 그러나 상관관계는 그 추정치가 가장 중요해지는 순간에 가장 크게 변동합니다.

방법론적 참고 및 관례
  • 백분위수는 표본 추정치가 아닌 정확한 분위수(exact quantile)에서 산출되었으며, 몬테카를로 시뮬레이션의 추출값은 SQL에 기록된 해시 시드 균등 분포 시퀀스를 따르므로 모든 수치는 동일한 값으로 재계산됩니다.
  • 수익률은 배당 재투자를 제외한 종가 기준 가격 변동으로, 1회 세션 VaR의 일반적인 관례를 따릅니다.
  • 기간 패널은 중첩 윈도우(overlapping windows)를 사용합니다. 연속적인 20세션 수익률은 19일의 데이터를 공유하므로, 그 꼬리 부분은 행의 개수가 암시하는 것보다 훨씬 적은 독립적 관측치에 기반합니다.

자주 묻는 질문(FAQ)

1일 99% VaR이란 무엇입니까?

이는 단일 거래 세션 동안 최악의 100일 중 하루에 해당하는 손실 수준을 초과하는 값을 의미합니다. 위 시계열 데이터에서 역사적 추정치는 3.09%입니다. 이 수치는 임계값을 나타낼 뿐, 그 임계값을 초과하는 손실의 규모에 대해서는 설명하지 않습니다.

어떤 VaR 계산 방식이 가장 정확합니까?

세 가지 방식 모두 추상적인 관점에서는 정확하다고 할 수 없습니다. 각 방식은 서로 다른 가정하에 질문에 답하기 때문입니다. 역사적 VaR(Historical VaR)은 주어진 표본에 충실하지만 표본에서 누락된 정보에 대해서는 알 수 없습니다. 파라메트릭 VaR(Parametric VaR)은 비용이 저렴하지만 주식의 꼬리 위험(equity tails)을 과소평가하는 경향이 있습니다. 몬테카를로(Monte Carlo) 시뮬레이션은 입력된 분포의 품질만큼만 정확합니다.

VaR과 기대손실(Expected Shortfall)의 차이는 무엇입니까?

VaR은 선택된 신뢰 수준에서의 임계값입니다. 기대손실은 해당 임계값을 초과하는 날들의 손실 평균을 구합니다. 이 시계열 데이터에서 99% 수준의 기대손실은 VaR의 1.44배 수준이며, 이는 정규분포 가정하의 약 1.15배와 대비됩니다.

1일 VaR을 10일 VaR로 변환할 수 있습니까?

10의 제곱근을 곱하는 것이 표준적인 약식 계산법이며, 이는 수익률이 독립적이고 변동성이 일정하다는 가정을 전제로 합니다. 기간별 패널은 그 차이를 측정합니다. 20개 세션에 걸쳐 환산된 수치는 13.8%이었으나, 실제 측정값은 10.7%였습니다.


위의 모든 패널에는 해당 데이터를 산출한 SQL이 포함되어 있습니다. 다른 티커(ticker)나 기간에 대해 동일한 세 가지 계산을 수행하려면 Strasmore 터미널에서 평이한 영어로 요청하십시오.

#risk#value at risk#expected shortfall#quant#position sizing