Value at Risk कसे मोजतात: 3 पद्धती
Historical, parametric आणि Monte Carlo VaR एका SPY return seriesवर मोजा; VaRमधून सुटणारा expected shortfall समजून तीन निकालांची तुलना करा.
Value at risk, किंवा VaR, मोजण्यासाठी कालावधी आणि confidence level निश्चित केला जातो. त्यानंतर returnsच्या मालिकेतून संबंधित percentile घेतला जातो. 1-day 99% VaR 2% असल्यास, प्रत्येक 100 trading sessionsपैकी 99 सत्रांत तोटा 2%च्या आत राहतो आणि एका सत्रात तो 2%पेक्षा जास्त होतो, असा त्याचा अर्थ असतो. समान inputs वापरून या प्रश्नाचे उत्तर देणाऱ्या तीन standard पद्धती आहेत. त्यांच्या निकालांमध्ये बहुतेकांना अपेक्षा असते त्यापेक्षा अधिक फरक आढळतो.
जोखीममापन प्रत्यक्षात काय मोजते
VaR हा तोटा-वितरणातील एक quantile असतो. नमुन्यातील प्रत्येक दैनिक परतावा सर्वात वाईट ते सर्वात चांगला अशा क्रमाने लावा. खराब टोकापासून 1% अंतर आत या. ज्या परताव्यावर तुम्ही पोहोचता, तो सकारात्मक तोटा म्हणून लिहिल्यास 1-दिवसाचा 99% ऐतिहासिक VaR मिळतो. या रचनेत worst caseचे कोणतेही आश्वासन नसते. अंदाज ज्या प्रदेशाचे वर्णन करणे थांबवतो, त्याची सीमा तो दर्शवतो.
वाचकांकडून सर्वाधिक वेळा चुकीच्या ठिकाणी लावला जाणारा गुणधर्म हाच आहे. 99% VaR सांगतो की सर्वात वाईट 1% दिवस या thresholdच्या पलीकडे आहेत. मात्र ते किती पुढे आहेत, हे तो सांगत नाही. कमाल drawdown वेगळ्या प्रश्नाचे उत्तर देते: एखाद्या पोर्टफोलिओने प्रत्यक्ष अनुभवलेला peak-to-trough तोटा. त्यामुळे समान दोन पोर्टफोलिओंची क्रमवारी या दोन्ही मापांनुसार परस्परविरोधी असू शकते.
खालील प्रत्येक आकडा एका seriesवर आधारित आहे: 2010च्या सुरुवातीपासून 2025च्या अखेरपर्यंतचे SPY दैनिक closing prices, close-to-close टक्केवारीतील बदलांमध्ये रूपांतरित केलेले. हा कालावधी rolling नसून निश्चित आहे. प्रत्येक panelमध्ये त्याच तारखांपासून series पुन्हा तयार केली जाते. त्यामुळे वेगवेगळ्या runsमध्ये आकडे बदलत नाहीत.
प्रत्येक आकड्यामागील अचूक 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या seriesमध्ये 16 calendar years आणि केवळ 2010मध्ये 252 sessions आहेत. पुढील सर्व विश्लेषणासाठी तिची दोन वैशिष्ट्ये महत्त्वाची आहेत. या कालावधीवर सरासरी sessionचा परिणाम जवळपास नगण्य आहे: 2010मध्ये 0.054%, तर दैनिक standard deviation, sigma, 1.13% आहे. तसेच sigma स्थिर नसतो. 2020चा दैनिक स्तर 2.11% होता, तर 2017चा 0.43%. एकच sigma या दोन्हींचे वर्णन करू शकत नाही.
जोखीममूल्याची गणना कशी केली जाते: तीन पद्धती
ऐतिहासिक VaR: प्रत्यक्ष घडलेल्या घटनांमधून percentile काढणे
प्रत्यक्ष returns क्रमाने लावा आणि percentile घ्या. कोणतेही distribution गृहीत धरावे लागत नाही, हीच या पद्धतीची उपयुक्तता आहे. मात्र, अंदाज ज्या प्रकारच्या दिवसासाठी आहे, तसा दिवस त्या नमुन्यात आधीपासून आहे, असे ही पद्धत गृहीत धरते. Confidence level पुरेसा दूर नेल्यास संपूर्ण कालावधीतील सर्वांत खराब काही सत्रेच उत्तर ठरवतात.
Parametric VaR: mean वजा z गुणिले sigma
Seriesचा mean आणि sigma यांच्या मदतीने तिचा सारांश तयार करा आणि returns normal distributionचे अनुसरण करतात, असे गृहीत धरा. VaR म्हणजे z गुणिले sigma वजा mean. येथे z हा standard normal quantile आहे: 95%वर 1.645, 99%वर 2.326 आणि 99.9%वर 3.090. गणना त्वरित होते; मात्र गृहीतक एका विशिष्ट दिशेने चुकते. Daily equity returns normal curveच्या मध्यभागी तिच्यापेक्षा अधिक घट्ट गटबद्ध होतात आणि टोकाच्या पातळ्यांवर तिच्यापेक्षा खूप पुढे जातात. येथे sigma हा Sharpe ratioमध्येही वापरला जाणारा denominator आहे. त्यामुळे fat-tailed dataबाबत त्याच मर्यादा येथेही राहतात.
Monte Carlo VaR: seed निश्चित ठेवून simulation
गृहीत धरलेल्या processमधून मोठा synthetic sample तयार करा आणि त्या drawsमधून percentile काढा. खालील panelमध्ये hash-seeded uniform sequenceपासून Box-Muller transformने तयार केलेले 40,000 standard normal draws वापरले आहेत. हे draws seriesच्या mean आणि sigmaनुसार scale केले आहेत. Seed SQLमध्ये ठेवलेला आहे, त्यामुळे प्रत्येक rerunमध्ये draws समान राहतात. Simulationमुळे flexibility, path dependence आणि multi-asset correlation मिळते; मात्र realism मिळत नाही. त्याला normal distribution दिल्यास sampling noise जोडलेले parametric उत्तरच परत मिळते. त्याऐवजी observed returnsचे resampling केल्यास, bootstrapped confidence intervalsमागील तंत्राप्रमाणे, प्रत्यक्ष tail विचारात राहतो.
प्रत्येक आकड्यामागील अचूक 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% पातळीवर तीनही पद्धतींचे निकाल एकमेकांपासून एका percentage pointच्या आत आहेत: 1.66% historical, 1.74% parametric आणि 1.71% simulated. 99.9%वर मात्र त्यांच्यात फरक पडतो: 5.85% historicalच्या तुलनेत 3.31% parametric, म्हणजे समान dataवर 2.54 percentage pointsची तफावत. प्रत्येक पातळीवर simulated column हा parametric columnच्या शेजारीच राहतो. हाच मुद्दा आहे; ही त्रुटी नाही. Simulationला जे distribution दिले जाते, तेच ती पुन्हा तयार करते.
एका columnमध्ये panel वाचल्यास confidenceमधील प्रत्येक वाढ threshold रुंदावते. एका rowमध्ये आडवे वाचल्यास distributionच्या मध्यभागी पद्धतीची निवड फारशी परिणामकारक ठरत नाही; tailमध्ये मात्र तिचा प्रभाव निर्णायक असतो. पद्धत आणि window नमूद न करता दिलेली VaR limit अशी संख्या नाही की ती दुसरा कोणी पुन्हा तयार करू शकेल.
VaR काय सांगत नाही: expected shortfall
Expected shortfallला conditional VaR असेही म्हणतात. VaRचे उल्लंघन करणाऱ्या दिवसांतील तोटा यामध्ये सरासरीने मोजला जातो. VaR tail कुठून सुरू होते हे दर्शवते. Expected shortfall त्या tailच्या आत किती तोटा आहे हे मोजते.
प्रत्येक आकड्यामागील अचूक 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% आहे आणि expected shortfall 4.43%, म्हणजे thresholdच्या 1.44 पट आहे. त्याच पातळीवर normal distribution असल्यास हे गुणोत्तर सुमारे 1.15 असते. threshold आधीच 5.85% वर पोहोचलेले असताना, 99.9% येथेही breach झालेल्या दिवसांतील सरासरी तोटा 8.14% इतका आहे. केवळ VaRवर आधारित मर्यादा प्रत्येक breachला समान घटना मानते. हे गृहितक किती चुकीचे आहे, हे ratio column दर्शवतो.
1-दिवसीय 99% VaRचा संस्थेसाठी आणि traderसाठी अर्थ एकच असतो का?
नाही. हा फरक प्रामुख्याने कालावधीचा असतो. Overnight position न ठेवणारा trader काही तासांसाठी जोखीम धरतो. त्यामुळे एक-session threshold त्याच्या holding periodशी साधारण जुळतो. मात्र intraday हालचाल close-to-close आकड्यापेक्षा बरीच मोठी असू शकते. दीर्घ मुदतीच्या liabilitiesसाठी निधी उभारणारी संस्था positions अनेक वर्षे ठेवते. तिची exposure अनेक quartersपर्यंत असते. त्यामुळे तिचा 1-day 99% आकडा ती वहन करत असलेल्या जोखमीचे वर्णन नसून capital आणि monitoringसाठी वापरले जाणारे मोजमाप ठरते. Bank capital rulesमध्ये अनेक वर्षे 1-day 99% VaR वापरले गेले. त्यानंतर Basel market risk frameworkने हे मोजमाप 97.5% expected shortfallकडे वळवले.
वेगवेगळ्या कालावधींमधील सामान्य दुवा म्हणजे timeच्या square rootनुसार scaling. एक-session आकडा sessionsमध्ये मोजलेल्या horizonच्या square rootने गुणला जातो. या पद्धतीत returns स्वतंत्र आहेत आणि sigma स्थिर आहे, असे गृहीत धरले जाते. मात्र वरचा year-by-year sigma column हे गृहीतक का अपयशी ठरते, ते आधीच दाखवतो.
प्रत्येक आकड्यामागील अचूक 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)पहिली row identity check आहे. 1 session येथे scaled figure हा measured figureइतकाच आहे आणि ratio 1 आहे. 20 sessions येथे हे दोन्ही वेगळे होतात. तसेच shortcutबाबत सामान्यतः दिल्या जाणाऱ्या इशाऱ्याच्या विरुद्ध दिशेने ते वेगळे होतात: one-session आकडा scale केल्यावर 13.8% मिळतो, तर त्या horizonवरील प्रत्यक्ष मोजलेले returns 10.7% दाखवतात; ratio 0.78 आहे. या windowमध्ये scaled figure हा measured multi-session tailपेक्षा वर आहे. येथे दोन नमुने परस्परविरोधी दिशेने काम करतात. एका sessionमधील fat-tailed distributionमध्ये returns एकत्र केल्यावर tail कमी जड होतो. त्यामुळे aggregated 99% quantile हा horizonच्या square rootपेक्षा हळू वाढतो. Volatility clustering याच्या उलट परिणाम करते. एका windowमध्ये ती अतिशय अस्थिर sessions एकत्र आणते. या seriesमध्ये पहिला नमुना अधिक प्रभावी आहे. मात्र दुसऱ्या windowमध्ये किंवा दुसऱ्या assetमध्ये कोणतीही दिशा हमखास असेलच असे नाही. हाच मुख्य मुद्दा आहे: multiplier हे गृहीतक आहे, मोजमाप नाही. Positionsचा आकार बदलताना volatility targeting याच clusteringच्या गुणधर्मावर अवलंबून असते.
आणखी एक मर्यादा स्पष्टपणे सांगणे आवश्यक आहे. वरील सर्व मोजमाप broad index fundवरील single-asset VaRचे आहे. परस्पर-सहसंबंधित दहा namesचा book concentration risk निर्माण करतो. Portfolio VaR हा धोका correlation estimateद्वारे हाताळतो. मात्र estimate सर्वाधिक महत्त्वाचा ठरतो त्याच वेळी correlationsमध्ये सर्वाधिक बदल होतात.
पद्धतीच्या नोंदी आणि प्रचलित पद्धती
- Percentiles sampled estimateऐवजी exact quantileमधून काढले आहेत. Monte Carlo draws हे SQLमध्ये लिहिलेल्या hash-seeded uniform sequenceमधून घेतले आहेत. त्यामुळे येथे दिलेला प्रत्येक आकडा पुन्हा मोजल्यावर त्याच valueवर येतो.
- Returns हे dividend reinvestment न धरता close-to-close price changesवर आधारित आहेत. One-session VaRसाठी ही नेहमीची पद्धत आहे.
- Horizon panelमध्ये overlapping windows वापरल्या आहेत. सलग 20-session returnsमध्ये 19 दिवस समान असतात. त्यामुळे row countवरून दिसते त्यापेक्षा त्याच्या tailमागे स्वतंत्र observationsची संख्या खूप कमी आहे.
वारंवार विचारले जाणारे प्रश्न
1-day 99% VaR म्हणजे काय?
एका सत्रासाठी मोजलेली ही अशी तोट्याची पातळी आहे, जी सर्वाधिक तोटा झालेल्या 100 trading daysपैकी 1 दिवशी ओलांडली जाते. वरील मालिकेवरील ऐतिहासिक अंदाज 3.09% आहे. हा आकडा केवळ एक threshold दर्शवतो; त्यापुढील तोट्यांचा आकार सांगत नाही.
कोणती VaR गणना पद्धत सर्वाधिक अचूक आहे?
सैद्धांतिकदृष्ट्या यापैकी कोणतीही पद्धत सर्वाधिक अचूक नाही, कारण प्रत्येक पद्धत वेगवेगळ्या गृहीतकांवर आधारित प्रश्नाचे उत्तर देते. Historical VaR दिलेल्या sampleशी सुसंगत असतो; मात्र sampleमध्ये न आलेल्या घटनांबाबत तो काही सांगत नाही. Parametric VaR कमी खर्चिक असतो, पण equity tail risk कमी दाखवतो. Monte Carloची अचूकता त्यात वापरलेल्या distributionइतकीच असते.
VaR आणि expected shortfallमध्ये काय फरक आहे?
VaR ही निवडलेल्या confidence levelवरील threshold असते. Expected shortfall त्या thresholdपेक्षा अधिक तोटा झालेल्या दिवसांवरील तोट्याची सरासरी काढतो. या मालिकेत 99% पातळीवरील expected shortfall हा VaRच्या 1.44 पट आहे. Normal distributionमध्ये हे प्रमाण साधारण 1.15 पट असते.
1-day VaRचे 10-day VaRमध्ये रूपांतर करता येते का?
10च्या square rootने गुणाकार करणे हा standard shortcut आहे. या पद्धतीत returns स्वतंत्र असून volatility स्थिर आहे असे गृहीत धरले जाते. Horizon panel हा फरक मोजतो: 20 sessionsमध्ये scaled figure 13.8% होता, तर मोजलेला आकडा 10.7% होता.
वरील प्रत्येक panelमध्ये ते तयार करणारी SQL query दिली आहे. वेगळ्या ticker किंवा कालावधीवर हीच तीन गणना चालवण्यासाठी Strasmore terminalवर plain Englishमध्ये विनंती करा.