كيف تُحسب قيمة المخاطر VaR: ثلاث طرق
تعرّف إلى حساب قيمة المخاطر VaR التاريخية والبارامترية ومحاكاة مونت كارلو باستخدام سلسلة عوائد SPY نفسها، مع العجز المتوقع الذي يغفله VaR.
تُحسب قيمة المخاطر، أو VaR، بتحديد أفق زمني ومستوى ثقة، ثم استخراج قيمة مئوية من سلسلة العوائد. وتعني قيمة VaR ليوم واحد عند مستوى ثقة يبلغ 99%، والبالغة 2%، أن الخسارة تبقى دون 2% في 99 جلسة من كل 100 جلسة، وتتجاوزها في الجلسة المئة. وتجيب ثلاث طرق معيارية عن هذا السؤال باستخدام المدخلات نفسها، لكنها تختلف في نتائجها بأكثر مما يتوقعه معظم الناس.
ما الذي يقيسه VaR فعلياً
VaR هو كَمٌّ من توزيع الخسائر. رتّب كل عائد يومي في عينة من الأسوأ إلى الأفضل، ثم تحرّك مسافة تعادل 1% من الطرف السيئ. ويكون العائد الذي تصل إليه، بعد كتابته كخسارة موجبة، هو VaR التاريخي ليوم واحد عند مستوى 99%. ولا يضمن هذا البناء شيئاً بشأن أسوأ نتيجة ممكنة. فهو يحدد حافة المنطقة التي يتوقف التقدير عن وصفها.
وهذه هي الخاصية التي يسيء القراء فهمها أكثر من غيرها. يشير VaR عند مستوى 99% إلى أن أسوأ 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 سنة تقويمية، وتشمل 252 جلسة في 2010 وحده. وهناك سمتان فيها تؤثران في كل ما يلي. فمتوسط الجلسة يكاد يكون غير مهم في هذا الأفق: 0.054% في 2010، مقابل انحراف معياري يومي، سيغما، قدره 1.13%. كما أن سيغما ليست ثابتة. فقد بلغ معدلها في 2020 نحو 2.11% يومياً، بينما بلغ في 2017 نحو 0.43%. ولا يمكن لسيغما واحدة أن تصف الفترتين معاً.
كيفية حساب القيمة المعرضة للخطر بثلاث طرق
VaR التاريخية: استخرج النسبة المئوية من النتائج الفعلية
رتّب العوائد الفعلية واستخرج النسبة المئوية المطلوبة. لا يُفترض أي توزيع إحصائي، وهذه هي جاذبية الطريقة. لكن الطريقة تفترض أن العينة تتضمن بالفعل نوع اليوم الذي يُفترض أن يغطيه التقدير. وكلما رفعت مستوى الثقة، أصبحت الإجابة معتمدة على عدد قليل فقط من أسوأ الجلسات في كامل الفترة.
VaR البارامترية: المتوسط ناقص z مضروباً في سيغما
لخّص السلسلة بمتوسطها وسيغما، ثم افترض أن العوائد تتبع توزيعاً طبيعياً. تساوي VaR حاصل ضرب z في سيغما ناقص المتوسط، حيث تمثل z كمية التوزيع الطبيعي المعياري: 1.645 عند 95%، و2.326 عند 99%، و3.090 عند 99.9%. الحساب فوري، لكن الافتراض يفشل في اتجاه محدد. فعوائد الأسهم اليومية تتجمع حول الوسط بصورة أكثر إحكاماً من المنحنى الطبيعي، وتمتد بدرجة أكبر بكثير عند الأطراف. وسيغما هنا هي المقام المستخدم أيضاً في نسبة Sharpe، ولذلك تحمل معها نقطة الضعف نفسها عند التعامل مع البيانات ذات الذيول السميكة.
VaR بمحاكاة Monte Carlo: حاكِ البيانات مع تثبيت البذرة
ولّد عينة اصطناعية كبيرة من عملية مفترضة، ثم استخرج النسبة المئوية من القيم الناتجة. يستخدم الرسم أدناه 40,000 سحبة من التوزيع الطبيعي المعياري، أنشأها تحويل Box-Muller انطلاقاً من تسلسل منتظم عشوائي مولّد من تجزئة، ثم جرى تحجيمها وفق متوسط السلسلة وسيغما الخاصة بها. وتوجد البذرة في SQL، لذلك تكون السحبات متطابقة في كل إعادة تشغيل. تمنح المحاكاة مرونة، واعتماداً على المسار، وارتباطاً بين الأصول المتعددة، لكنها لا تمنح واقعية تلقائياً. فإذا زوّدتها بتوزيع طبيعي، أعادت لك إجابة الطريقة البارامترية مع ضوضاء المعاينة فوقها. أما إعادة أخذ عينات من العوائد المرصودة، وهي التقنية التي تقوم عليها فواصل الثقة المعتمدة على إعادة المعاينة، فتبقي الذيل الفعلي ضمن الحساب.
استعلام 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: الخسارة المتوقعة
تُعرف الخسارة المتوقعة أيضاً باسم 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 وحده يعامل كل تجاوز على أنه الحدث نفسه، ويقيس عمود النسبة مدى عدم صحة ذلك.
هل يعني VaR ليوم واحد عند مستوى 99% الشيء نفسه للمؤسسة والمتداول؟
لا. ويرجع الاختلاف في معظمه إلى الأفق الزمني. يحتفظ المتداول الذي لا يملك مراكز عند الإغلاق بمخاطر تمتد لساعات، لذلك يتوافق حد جلسة واحدة مع فترة الاحتفاظ، مع أن المسار خلال اليوم قد يتجاوز كثيراً الرقم المحسوب من إغلاق إلى إغلاق. أما المؤسسة التي تموّل التزامات طويلة الأجل، فتحتفظ بمراكز لسنوات، وتمتد مخاطرها على مدى أرباع سنوية. لذلك يعمل رقم VaR ليوم واحد عند مستوى 99% لديها كأداة لرأس المال والمراقبة، لا كوصف للمخاطر التي تتحملها فعلياً. اعتمدت قواعد رأس المال المصرفي لسنوات على VaR ليوم واحد عند مستوى 99%، ثم نقل إطار بازل لمخاطر السوق المقياس لاحقاً إلى الخسارة المتوقعة عند مستوى 97.5%.
والطريقة القياسية للربط بين الآفاق الزمنية هي القياس وفق الجذر التربيعي للزمن: تضرب رقم الجلسة الواحدة في الجذر التربيعي لعدد الجلسات في الأفق. وتفترض هذه الخطوة أن العوائد مستقلة وأن 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 تأتي من تسلسل موحّد مهيأ ببذرة hash ومُدرج في SQL، لذلك تعيد كل قيمة هنا الحساب إلى النتيجة نفسها.
- العوائد هي تغيرات الأسعار من إغلاق إلى إغلاق، من دون إعادة استثمار التوزيعات، وهو الاصطلاح المعتاد لحساب VaR لجلسة واحدة.
- تستخدم لوحة الأفق نوافذ متداخلة: تشترك عوائد 20 جلسة متتالية في 19 يوماً، لذلك يستند ذيلها إلى عدد أقل بكثير من الملاحظات المستقلة مما يوحي به عدد الصفوف.
الأسئلة الشائعة
ما مستوى VaR ليوم واحد عند مستوى ثقة 99%؟
هو مستوى الخسارة الذي تتجاوزه أسوأ جلسة من بين كل 100 جلسة تداول، مقاساً على مدى جلسة واحدة. في السلسلة أعلاه، يبلغ التقدير التاريخي 3.09%. يحدد هذا الرقم عتبة الخسارة، ولا يوضح شيئاً عن حجم الخسائر التي تتجاوزها.
ما طريقة حساب VaR الأكثر دقة؟
لا توجد طريقة هي الأدق بصورة مطلقة، لأن كل طريقة تجيب عن السؤال وفق افتراضات مختلفة. يلتزم VaR التاريخي بالعينة المتاحة له، لكنه لا يقدم معلومات عن أي شيء لم ترصده العينة. أما VaR البارامتري فتكلفته منخفضة، لكنه يقلل من تقدير مخاطر الذيل في الأسهم. ولا تكون محاكاة Monte Carlo أفضل من التوزيع الإحصائي المُدخل فيها.
ما الفرق بين VaR والخسارة المتوقعة؟
VaR هو عتبة الخسارة عند مستوى ثقة محدد. أما الخسارة المتوقعة فتحسب متوسط الخسائر في الأيام التي تتجاوز هذه العتبة. في هذه السلسلة، تبلغ الخسارة المتوقعة عند مستوى 99% 1.44 ضعف VaR، مقابل نحو 1.15 ضعفاً وفق التوزيع الطبيعي.
هل يمكنك تحويل VaR ليوم واحد إلى VaR لعشرة أيام؟
يُعد الضرب في الجذر التربيعي للعدد 10 الاختصار المعتمد، ويفترض ذلك استقلال العوائد وثبات التقلب. تقيس لوحة الآفاق الزمنية الفارق؛ فعلى مدى 20 جلسة، بلغ الرقم بعد التحجيم 13.8%، مقابل 10.7% وفق القياس الفعلي.
تتضمن كل لوحة أعلاه استعلام SQL الذي أنتج بياناتها. ولإجراء الحسابات الثلاثة نفسها على رمز أو فترة مختلفين، اطلب ذلك بلغة إنجليزية عادية عبر منصة Strasmore.