VaR được tính thế nào: ba phương pháp
Tìm hiểu cách tính VaR bằng phương pháp lịch sử, tham số và Monte Carlo trên cùng chuỗi lợi suất SPY, cùng expected shortfall mà VaR bỏ qua.
Giá trị chịu rủi ro, hay VaR, được tính bằng cách xác định kỳ hạn, xác định mức độ tin cậy, rồi lấy một phân vị từ chuỗi lợi suất. VaR một ngày ở mức 99% là 2% có nghĩa là khoản lỗ không vượt quá 2% trong 99 trên mỗi 100 phiên và vượt mức này ở phiên thứ 100. Ba phương pháp tiêu chuẩn trả lời câu hỏi đó từ cùng một bộ dữ liệu đầu vào, nhưng kết quả có thể chênh lệch nhiều hơn hầu hết mọi người dự đoán.
VaR thực sự đo lường giá trị nào
VaR là một phân vị của phân phối thua lỗ. Sắp xếp toàn bộ lợi suất hằng ngày trong một mẫu từ tệ nhất đến tốt nhất, đi vào 1% khoảng cách từ đầu bất lợi, rồi lấy lợi suất tại vị trí đó và biểu thị dưới dạng một khoản lỗ dương. Kết quả là VaR lịch sử trong một ngày với độ tin cậy 99%. Cách tính này không hứa hẹn về kịch bản tệ nhất. Nó chỉ đánh dấu ranh giới của vùng mà ước tính còn mô tả được.
Đây là đặc điểm thường bị người đọc hiểu sai nhất. VaR 99% cho biết 1% số ngày có kết quả tệ nhất nằm vượt qua ngưỡng đó, nhưng không cho biết chúng vượt bao xa. Mức sụt giảm tối đa trả lời một câu hỏi khác: mức lỗ từ đỉnh xuống đáy mà một danh mục thực tế đã trải qua. Hai thước đo này có thể xếp hạng cùng một cặp danh mục theo hai thứ tự ngược nhau.
Mọi số liệu dưới đây đều dựa trên một chuỗi duy nhất: giá đóng cửa hằng ngày của SPY từ đầu năm 2010 đến cuối năm 2025, được chuyển đổi thành phần trăm thay đổi từ giá đóng cửa này đến giá đóng cửa kế tiếp. Khoảng thời gian được cố định thay vì áp dụng cửa sổ trượt. Mỗi biểu đồ đều tái dựng chuỗi từ cùng các ngày đó, nên các con số không thay đổi giữa các lần chạy.
Câu lệnh SQL chính xác đằng sau mỗi con số
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 yearChuỗi này bao phủ 16 năm dương lịch, trong đó riêng 252 phiên thuộc 2010. Hai đặc điểm của chuỗi có ý nghĩa đối với toàn bộ phần phân tích tiếp theo. Lợi suất trung bình mỗi phiên gần như không đáng kể trong khung thời gian này: 0.054% trong 2010, so với độ lệch chuẩn hằng ngày, sigma, ở mức 1.13%. Và sigma không phải một hằng số. 2020 ở mức 2.11% mỗi ngày, còn 2017 ở mức 0.43%. Một sigma không thể mô tả cả hai giai đoạn.
VaR được tính như thế nào: ba phương pháp
VaR lịch sử: đọc phân vị từ dữ liệu đã xảy ra
Sắp xếp các mức lợi nhuận thực tế rồi lấy phân vị tương ứng. Phương pháp này không giả định một phân phối cụ thể, và đó là ưu điểm của nó. Tuy nhiên, phương pháp vẫn giả định mẫu dữ liệu đã có những ngày giao dịch tương tự với giai đoạn mà ước tính cần bao quát. Khi đẩy mức độ tin cậy ra đủ xa, chỉ vài phiên tồi tệ nhất trong toàn bộ khoảng thời gian mới quyết định kết quả.
VaR tham số: trung bình trừ z nhân sigma
Tóm tắt chuỗi dữ liệu bằng giá trị trung bình và sigma, sau đó giả định lợi nhuận tuân theo phân phối chuẩn. VaR bằng z nhân sigma trừ giá trị trung bình, trong đó z là phân vị của phân phối chuẩn tắc: 1.645 ở mức 95%, 2.326 ở mức 99% và 3.090 ở mức 99,9%. Phép tính này gần như tức thời, nhưng giả định của nó sai theo một hướng cụ thể. Lợi nhuận cổ phiếu hằng ngày tập trung chặt hơn đường cong chuẩn ở vùng trung tâm và vươn xa hơn nhiều ở các cực. Sigma ở đây là mẫu số cũng được dùng trong tỷ lệ Sharpe, đồng thời mang theo cùng điểm mù khi xử lý dữ liệu có đuôi dày.
VaR Monte Carlo: mô phỏng với seed cố định
Tạo một mẫu tổng hợp lớn từ một quy trình giả định, rồi đọc phân vị từ các kết quả mô phỏng. Bảng dưới đây sử dụng 40.000 mẫu phân phối chuẩn tắc được tạo bằng phép biến đổi Box-Muller từ một chuỗi đồng đều có seed lấy từ hash, sau đó điều chỉnh theo giá trị trung bình và sigma của chuỗi dữ liệu. Seed được đặt trong SQL, nên các mẫu mô phỏng giống hệt nhau ở mọi lần chạy lại. Mô phỏng đem lại tính linh hoạt, khả năng xử lý phụ thuộc đường đi và tương quan đa tài sản, nhưng không tự tạo ra tính thực tế: nếu đưa vào một phân phối chuẩn, mô hình sẽ trả về đáp án của VaR tham số, cộng thêm nhiễu lấy mẫu. Nếu thay vào đó tái lấy mẫu các mức lợi nhuận quan sát được — kỹ thuật đứng sau khoảng tin cậy bootstrap — phần đuôi thực tế vẫn được giữ lại.
Câu lệnh SQL chính xác đằng sau mỗi con số
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)Ở mức 95.0%, ba phương pháp cho kết quả chênh nhau chưa đến một điểm: 1.66% theo phương pháp lịch sử, 1.74% theo phương pháp tham số và 1.71% theo mô phỏng. Ở mức 99.9%, chúng tách xa nhau: 5.85% theo phương pháp lịch sử so với 3.31% theo phương pháp tham số, chênh lệch 2.54 điểm phần trăm trên cùng một bộ dữ liệu. Cột mô phỏng nằm cạnh cột tham số ở mọi mức, và đó mới là thông điệp chính chứ không phải một khiếm khuyết. Mô phỏng sẽ tái tạo bất kỳ phân phối nào được đưa vào.
Đọc theo chiều dọc của một cột, mỗi bước tăng mức độ tin cậy đều làm ngưỡng mở rộng. Đọc theo chiều ngang của một hàng, lựa chọn phương pháp hầu như không tạo khác biệt ở vùng giữa của phân phối nhưng lại chi phối kết quả ở phần đuôi. Một hạn mức VaR được công bố mà không nêu phương pháp và khoảng thời gian quan sát thì không phải là con số mà người khác có thể tái lập.
VaR không cho bạn biết điều gì: expected shortfall
Expected shortfall, còn gọi là VaR có điều kiện, lấy bình quân các khoản lỗ trong những ngày vượt ngưỡng VaR. VaR xác định điểm bắt đầu của phần đuôi phân phối. Expected shortfall đo lường mức lỗ nằm bên trong phần đuôi đó.
Câu lệnh SQL chính xác đằng sau mỗi con số
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)Tại 99.0%, VaR của chuỗi này là 3.09% và expected shortfall là 4.43%, tương đương 1.44 lần ngưỡng. Với phân phối chuẩn, tỷ lệ này ở cùng mức xác suất sẽ ở gần 1.15. Ngay cả tại 99.9%, khi ngưỡng đã đạt 5.85%, mức lỗ bình quân trong ngày vượt ngưỡng vẫn là 8.14%. Một hạn mức chỉ dựa trên VaR sẽ xem mọi lần vượt ngưỡng là cùng một sự kiện. Cột tỷ lệ cho thấy giả định đó sai lệch đến mức nào.
VaR một ngày ở mức 99% có cùng ý nghĩa với tổ chức và trader không?
Không. Khác biệt chủ yếu nằm ở khung thời gian. Trader không giữ vị thế qua đêm chỉ chịu rủi ro trong vài giờ, nên ngưỡng một phiên tương đối phù hợp với thời gian nắm giữ. Tuy nhiên, diễn biến trong ngày vẫn có thể vượt xa mức biến động từ giá đóng cửa phiên này đến giá đóng cửa phiên kế tiếp. Một tổ chức tài trợ cho các nghĩa vụ dài hạn có thể nắm giữ vị thế trong nhiều năm, với mức độ phơi nhiễm kéo dài qua nhiều quý. Vì vậy, con số VaR một ngày ở mức 99% của tổ chức chủ yếu là công cụ phục vụ vốn và giám sát, thay vì mô tả đầy đủ rủi ro mà tổ chức đang gánh chịu. Trong nhiều năm, quy định vốn ngân hàng sử dụng VaR một ngày ở mức 99%. Sau đó, khung rủi ro thị trường Basel chuyển sang sử dụng expected shortfall ở mức 97,5%.
Cách quy đổi chuẩn giữa các khung thời gian là nhân theo căn bậc hai của thời gian: lấy con số của một phiên nhân với căn bậc hai của số phiên trong khung thời gian cần xét. Cách làm này giả định lợi suất độc lập và sigma không đổi. Cột sigma theo từng năm ở trên cho thấy giả định thứ hai đã không đúng như thế nào.
Câu lệnh SQL chính xác đằng sau mỗi con số
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)Hàng đầu tiên là phép kiểm tra đồng nhất thức: tại 1 session, con số sau khi quy đổi bằng đúng con số đo được, với tỷ lệ 1. Đến 20 sessions, hai con số bắt đầu tách rời. Kết quả cũng không đi theo hướng mà người ta thường cảnh báo khi sử dụng công thức rút gọn: quy đổi con số của một phiên cho ra 13.8%, trong khi lợi suất đo được trong khung thời gian đó cho ra 10.7%, với tỷ lệ 0.78. Trong giai đoạn này, con số sau quy đổi cao hơn phần đuôi phân phối đo được trong nhiều phiên. Hai yếu tố đang tác động theo các hướng ngược nhau. Phân phối lợi suất một phiên có đuôi dày sẽ mỏng hơn khi các lợi suất được cộng dồn. Vì vậy, quantile 99% của lợi suất tổng hợp sẽ mở rộng chậm hơn căn bậc hai của thời gian. Ngược lại, hiện tượng volatility clustering tập trung các phiên biến động mạnh vào cùng một khung thời gian. Trong chuỗi dữ liệu này, yếu tố thứ nhất có tác động lớn hơn. Tuy nhiên, không hướng nào được đảm bảo trong một khung thời gian khác hoặc với một tài sản khác. Đây chính là điểm cần lưu ý: hệ số nhân là một giả định, không phải phép đo. Hiện tượng clustering này cũng là đặc tính mà chiến lược mục tiêu hóa biến động dựa vào khi điều chỉnh quy mô vị thế.
Cần nêu rõ thêm một giới hạn. Toàn bộ phân tích trên là VaR của một tài sản, áp dụng cho một quỹ chỉ số đa dạng. Một danh mục gồm mười mã có tương quan với nhau chịu rủi ro tập trung; portfolio VaR xử lý rủi ro này thông qua ước tính tương quan. Tuy nhiên, tương quan thường biến động mạnh nhất đúng vào thời điểm ước tính đó trở nên quan trọng.
Ghi chú về phương pháp và quy ước
- Các percentile được tính từ quantile chính xác thay vì ước tính từ mẫu. Các lượt mô phỏng Monte Carlo sử dụng chuỗi đồng nhất được khởi tạo bằng hash và ghi trực tiếp trong SQL, nên mọi con số ở đây đều được tính lại thành cùng một giá trị.
- Lợi suất được tính từ thay đổi giá đóng cửa đến giá đóng cửa, không tái đầu tư cổ tức. Đây là quy ước thông thường đối với VaR một phiên.
- Phần phân tích theo khung thời gian sử dụng các cửa sổ chồng lấn: các lợi suất của 20 phiên liên tiếp chia sẻ 19 ngày. Vì vậy, phần đuôi phân phối dựa trên số quan sát độc lập ít hơn nhiều so với con số hàng dữ liệu thể hiện.
Câu hỏi thường gặp
VaR 99% trong một ngày là gì?
Đó là mức thua lỗ mà chỉ có 1 trong 100 ngày giao dịch tệ hơn, được đo trong một phiên duy nhất. Trên chuỗi dữ liệu ở trên, ước tính lịch sử là 3.09%. Con số này chỉ xác định một ngưỡng, không cho biết quy mô thua lỗ vượt quá ngưỡng đó.
Phương pháp tính VaR nào chính xác nhất?
Không phương pháp nào trong ba phương pháp chính xác một cách tuyệt đối, vì mỗi phương pháp trả lời câu hỏi dựa trên các giả định khác nhau. VaR lịch sử phản ánh đúng mẫu dữ liệu được cung cấp nhưng không cho biết những gì mẫu dữ liệu đã bỏ sót. VaR tham số có chi phí thấp nhưng thường đánh giá thấp rủi ro đuôi của cổ phiếu. Monte Carlo chỉ chính xác khi phân phối dữ liệu đầu vào phù hợp.
VaR khác expected shortfall như thế nào?
VaR là ngưỡng thua lỗ tại một mức độ tin cậy được chọn. Expected shortfall là mức thua lỗ trung bình trong những ngày vượt qua ngưỡng đó. Trên chuỗi dữ liệu này, expected shortfall ở mức 99% bằng 1.44 lần VaR, so với khoảng 1,15 lần theo phân phối chuẩn.
Có thể chuyển VaR một ngày thành VaR mười ngày không?
Nhân VaR với căn bậc hai của 10 là cách tính nhanh thường dùng. Cách này giả định lợi suất độc lập và biến động không đổi. Bảng thời hạn đo lường khoảng chênh lệch: trong 20 phiên, con số sau khi nhân theo cách này là 13.8%, trong khi mức đo được là 10.7%.
Mỗi bảng ở trên đều có câu lệnh SQL đã tạo ra kết quả tương ứng. Để chạy ba phép tính tương tự cho một ticker hoặc khoảng thời gian khác, hãy yêu cầu bằng tiếng Anh thông thường trên terminal Strasmore.