VaR如何計算?三種方法比較
以固定SPY報酬序列示範歷史、參數與Monte Carlo VaR計算,並說明VaR未涵蓋的尾端損失,協助比較三種方法的差異。
風險值(value at risk,或 VaR)是先設定持有期間與信心水準,再從報酬率序列中讀取相應的百分位數。1日、99%信心水準的 VaR 為 2%,表示每100個交易日中,有99日的損失低於2%,另有1日會超過2%。對相同輸入資料,三種標準方法會給出不同答案,差異往往超出多數人的預期。
VaR 實際衡量的內容
VaR 是損失分布的分位數。將樣本中的每個日報酬率由最差到最好排序,從最差的一端往內數 1%,所落在的報酬率以正數損失表示,即為 1 日 99% 歷史 VaR。這種建構方式並未保證涵蓋最壞情境。它標示的是估計值不再描述的區域邊界。
讀者最常誤解的,正是這項特性。99% VaR 表示最差的 1% 交易日會落在該門檻之外,但不表示超出的幅度。最大回撤回答的是另一個問題:投資組合實際經歷的高點至低點損失。兩者可能以相反順序排列同一對投資組合。
以下每個數值都以同一組資料為基礎:SPY 自2010年初至2025年底的每日收盤價,並換算為收盤至收盤的百分比變化。這段期間固定不變,而非採用滾動視窗;每個圖表都以相同日期重新建立資料序列,因此各次執行的數值不會漂移。
每個數據背後的精確 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這組資料涵蓋 16 個曆年,僅 2010 就有 252 個交易時段。接下來的分析有兩項特徵十分重要。以這個期間而言,單日平均交易時段報酬幾乎無關緊要:2010 為 0.054%;相較之下,每日標準差 sigma 為 1.13%。而且 sigma 並非常數。2020 的每日波動率為 2.11%,2017 則為 0.43%。單一 sigma 無法同時描述這兩段期間。
風險值的計算方式:三種方法
歷史 VaR:從實際發生的結果讀取百分位數
將實際報酬排序,再取出相應的百分位數。這種方法不假設任何分配,這正是它的優點。但它假設樣本中已經包含估計值所要涵蓋的那類交易日。當信賴水準拉得足夠高時,整個觀察期間中最差的幾個交易日,便會決定結果。
參數 VaR:平均值減去 z 乘以 sigma
先以平均值和 sigma 概括報酬序列,再假設報酬遵循常態分配。VaR 等於 z 乘以 sigma 再減去平均值;其中 z 是標準常態分配的分位數:95% 為 1.645、99% 為 2.326、99.9% 為 3.090。計算速度很快,但其假設會以特定方向失效。每日股票報酬在分配中段的集中程度高於常態曲線,極端值的延伸距離也遠得多。這裡的 sigma 是 Sharpe ratio 的分母,因此面對厚尾資料時,也會產生相同的盲點。
Monte Carlo VaR:在固定種子的情況下進行模擬
從假設的過程中抽取大量合成樣本,再從這些抽樣結果讀取百分位數。下方面板使用 40,000 個標準常態抽樣值。這些數值由 Box-Muller transform 根據以雜湊值設定種子的均勻序列產生,再依該序列的平均值與 sigma 進行調整。種子設定在 SQL 中,因此每次重新執行時,抽樣結果都完全相同。模擬可以納入彈性、路徑相依性及多資產相關性,但不會因此增加真實性:如果輸入的是常態分配,輸出的就是加上抽樣雜訊的參數 VaR。若改為重新抽樣實際報酬,也就是 bootstrap 信賴區間 背後採用的技術,便能保留真實尾端風險。
每個數據背後的精確 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)在 95.0% 水準下,三種方法的結果彼此相差不到一個百分點:歷史法為 1.66%、參數法為 1.74%、模擬法為 1.71%。到了 99.9%,三者開始分歧:歷史法為 5.85%,參數法為 3.31%;在相同資料下,兩者相差 2.54 個百分點。模擬法在每個水準都與參數法並列,這正是重點,而不是缺陷。模擬只會重現輸入給它的分配。
沿著欄位向下閱讀面板時,信賴水準每提高一級,門檻就會擴大。沿著列向右比較時,在分配中段,方法選擇幾乎不影響結果;但到了尾端,方法選擇便成為主導因素。未說明方法與觀察期間的 VaR 限額,其他人無法重現,也就不能算是一個完整的數字。
VaR 未能告訴你的事:預期短缺
預期短缺(expected shortfall)也稱為條件 VaR,會計算超過 VaR 的交易日損失平均值。VaR 標示尾端風險的起點;預期短缺則衡量尾端風險範圍內的損失程度。
每個數據背後的精確 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)在 99.0%,這組序列的 VaR 為 3.09%,預期短缺為 4.43%,相當於門檻的 1.44 倍。在相同水準下,若呈常態分布,該比率應接近 1.15。即使在 99.9%,當門檻已達 5.85% 時,超過門檻的交易日平均損失仍為 8.14%。只根據 VaR 設定的限額,會將每次超標都視為相同事件;比率欄則顯示這種假設與實際情況相差多遠。
1日99% VaR 對機構與交易員而言,意思相同嗎?
不相同,差異主要在持有期間。隔夜不持倉的交易員只承擔數小時的風險,因此單一交易時段的門檻大致符合持有期間;但盤中價格路徑可能遠超過收盤至收盤的變動幅度。為長期負債提供資金的機構則持有部位多年,曝險持續數季;其1日99%數值更像是資本與監控工具,而不是對其所承擔風險的完整描述。銀行資本規則多年來以1日99% VaR為基礎,後來《巴塞爾市場風險架構》將衡量方式改為97.5% expected shortfall。
不同持有期間之間的標準換算方式,是以時間平方根進行調整:將單一交易時段的數值乘以持有期間交易時段數的平方根。這個步驟假設報酬彼此獨立,且 sigma 維持不變;上方逐年列出的 sigma 欄位已顯示,後者在現實中並不成立。
每個數據背後的精確 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)第一列是同值檢查:在 1 session,調整後的數值等於實際衡量值,比例為 1。到了 20 sessions,兩者開始分離,而且方向並非通常對這項捷徑的警告方向:以單一交易時段數值進行調整得到 13.8%,而該持有期間實際衡量出的報酬則為 10.7%,比例為 0.78。在這段期間,調整後的數值高於實際衡量的多時段尾端風險。這裡有兩種模式相互拉扯。單一交易時段的厚尾分布在累加報酬後會變薄,因此彙總後的99%分位數擴大速度低於持有期間平方根。波動度群聚則朝相反方向發展,會在同一段期間集中多個劇烈交易時段。在這組序列中,前一種模式影響較大;但在另一段期間或另一項資產中,結果不保證朝任何一個方向,這正是重點:乘數是項假設,而非衡量結果。波動度群聚也是 波動度目標策略 進行部位調整所依據的特性。
還有一項限制需要明確說明。以上全部是針對廣泛指數基金的單一資產 VaR。由十檔具相關性股票構成的投資組合,會承擔 集中風險;投資組合 VaR 會透過相關性估計處理這項風險,而相關性往往在估計最重要的時刻出現最大變動。
方法說明與慣例
- 百分位數採用精確分位數,而非抽樣估計;Monte Carlo 抽樣則來自寫入 SQL 的雜湊種子均勻序列,因此本文每個數值重新計算後都會相同。
- 報酬採用收盤至收盤的價格變動,不進行股息再投資,這是單一交易時段 VaR 的常見慣例。
- 持有期間面板採用重疊視窗:連續20個交易時段的報酬共用19天,因此其尾端風險實際上依據的獨立觀測值遠少於列數所顯示的數量。
常見問題
什麼是1日99% VaR?
這是在單一交易日衡量的損失水準,代表最差的100個交易日中,有1天的損失會超過該水準。在上述序列中,歷史估計值為3.09%。這個數值只界定門檻,並未說明超過門檻後的損失幅度。
哪種 VaR 計算方法最準確?
若不考慮具體假設,三種方法都不能說最準確,因為每種方法回答的問題不同。歷史 VaR 忠實反映所使用的樣本,但不會涵蓋樣本未呈現的情況。參數法 VaR 成本低,但會低估股票報酬分布尾端的風險。Monte Carlo 模擬的準確度,取決於輸入的分布。
VaR 與預期短缺有何不同?
VaR 是特定信心水準下的損失門檻。預期短缺則計算超過該門檻的交易日損失平均值。在這組序列中,99%信心水準下的預期短缺是 VaR 的1.44倍;若採用常態分布,則約為1.15倍。
可以將1日 VaR換算為10日 VaR嗎?
將1日 VaR乘以10的平方根,是標準的簡化算法。這項算法假設報酬彼此獨立,且波動率維持不變。期間面板顯示了兩者的差距:在20個交易日的期間內,按比例換算的數值為13.8%,實際測得的數值則為10.7%。
上述每個面板都附有產生該結果的 SQL。若要在 Strasmore terminal 上,針對其他 ticker 或期間執行相同的三項計算,請以自然語言提出要求。