Strasmore Research
تعلَّم Matt Connorبقلم Matt Connor · data as of August 12, 2026 · refreshed weekly

استخدام مكتبة libitofin لتسعير المشتقات المالية بلغة Rust

تعرف على مكتبة libitofin كبديل لـ QuantLib بلغة Rust وكيفية الوصول إليها عبر Python. اكتشف لماذا تتفوق المكتبات البرمجية على جداول البيانات في إدارة منحنيات الفائدة.

ما الذي تقدمه مكتبة التسعير ولا تقدمه معادلات جداول البيانات

تأخذ معادلة Black-Scholes في جدول البيانات خمسة مدخلات وتعيد سعراً. المعادلة هي الجزء السهل، بينما تتكون المكتبة من أربع طبقات تحيط بها.

هياكل الفوائد (Term structures). سعر الخصم هو منحنى يمتد عبر آجال الاستحقاق، مع قاعدة استيفاء (interpolation) للفجوات بين النقاط التي يتم تسعيرها فعلياً. اللوحة أدناه تمثل المادة الخام: آجال استحقاق سندات الخزانة المسعرة في تاريخ واحد.

الاستعلاممنحنى الخزانة في آخر تاريخ تسعير
استعلام SQL الدقيق وراء كل رقم
SELECT
    arrayElement(tenors, i)                  AS tenor,
    round(arrayElement(rates, i), 2)         AS yield_pct,
    formatDateTime(curve_date, '%b %e, %Y')  AS as_of
FROM
(
    SELECT
        date AS curve_date,
        ['1 month', '3 months', '6 months', '1 year', '2 years', '3 years',
         '5 years', '7 years', '10 years', '20 years', '30 years']         AS tenors,
        [toFloat64(yield_1_month), toFloat64(yield_3_month), toFloat64(yield_6_month),
         toFloat64(yield_1_year),  toFloat64(yield_2_year),  toFloat64(yield_3_year),
         toFloat64(yield_5_year),  toFloat64(yield_7_year),  toFloat64(yield_10_year),
         toFloat64(yield_20_year), toFloat64(yield_30_year)]                AS rates,
        arrayJoin(range(1, 12))                                            AS i
    FROM global_markets.treasury_yields
    WHERE date = (SELECT max(date) FROM global_markets.treasury_yields)
)
WHERE yield_pct > 0
ORDER BY i
Run this yourself

اعتباراً من Aug 10, 2026، احتوى المنحنى المسعر على 7 من آجال الاستحقاق، تمتد من 3.79% عند 1 month إلى 5.25% عند 30 years. في جدول البيانات، يتم التعامل مع هذا عبر عملية بحث وقيمة ثابتة بنسبة أربعة بالمائة. أما المكتبة فتتعامل معه عبر كائن منحنى (curve object) يسعر كل أداة بناءً عليه، وفقاً لقاعدة استيفاء محددة (خطي لمعدلات الصفر، لوغاريتمي-خطي لعوامل الخصم، أو منحنيات الربط الرتيبة) وسياسة استقراء (extrapolation) محددة لما بعد النقطة الأخيرة. إذا قمت بإزاحة هذا الكائن بمقدار نقطة أساس واحدة، فإن كل مؤشرات الحساسية في المحفظة تتحرك بشكل متسق معه.

اتفاقيات حساب الأيام (Day-count conventions). تتراكم الفائدة على مدار جزء من السنة، وتعريف هذا الجزء هو اتفاقية مرتبطة بالأداة المالية. تقسم طريقة Actual/360 الأيام المنقضية على ثلاثمائة وستين، بينما تقسم Actual/365 على ثلاثمائة وخمسة وستين. وتفترض عائلة 30/360 أن كل شهر يتكون من ثلاثين يوماً. أما طريقة Business/252 فتحسب جلسات التداول مقابل سنة من مائتين واثنين وخمسين يوماً، وهو ما يتطلب تقويم بورصة حقيقياً محملاً بالعطلات. لنفترض اقتراض مليون دولار بفائدة خمسة بالمائة لمدة تسعين يوماً: ستتراكم فائدة قدرها اثنا عشر ألفاً وخمسمائة دولار بطريقة Actual/360، واثنا عشر ألفاً وثلاثمائة وتسعة وعشرون دولاراً بطريقة Actual/365 لنفس العملية. توضح اللوحة أدناه لماذا تحتاج أساس أيام العمل إلى تقويم بدلاً من مجرد قاسم عددي.

الاستعلامجلسات التداول مقابل الأيام التقويمية، حسب الشهر
استعلام SQL الدقيق وراء كل رقم
SELECT
    formatDateTime(toStartOfMonth(date), '%Y-%m')      AS month,
    countDistinct(date)                                AS trading_days,
    toUInt8(toDayOfMonth(toLastDayOfMonth(max(date)))) AS calendar_days
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
  AND date >= toStartOfMonth(today() - 365)
  AND date <  toStartOfMonth(today())
GROUP BY month
ORDER BY month
Run this yourself

على مدار 12 شهراً الموضحة، احتوى 2026-07 على 22 جلسة مقابل 31 يوماً تقويمياً. لا توجد قاعدة حسابية تنتج هذا الرقم الأول؛ فهو يأتي من تقويم العطلات، ولكل شهر نتيجته الخاصة. توفر المكتبة هذه التقاويم لكل سوق ولكل بلد، بينما يطلب منك جدول البيانات إدارتها يدوياً.

آليات المعايرة (Calibration machinery). لا يتم تسعير معايير النموذج في أي مكان. أنت تختارها من خلال مطابقة أسعار النموذج مع أسعار السوق المرصودة عبر سطح كامل من الأسعار، ثم تعيد المطابقة مع تحرك هذا السطح. هذه مسألة مربعات صغرى مقيدة، وتوفر المكتبة الحلقة البرمجية المحيطة بها: مُحسّن Levenberg-Marquardt، وتحويلات للمعايير تحافظ على التباين موجباً دون قيود صارمة، ودالة تكلفة عبر مجموعة الأسعار، ومعايير تقارب تفشل بوضوح بدلاً من إعادة التخمين الأولي بصمت.

طبقة الحسابات العددية. يكمن تحت كل شيء الجبر الخطي والتكامل. تحل عمليات تفكيك QR وSVD الأنظمة التي تنتجها عملية المطابقة، بينما تقوم التربيعات وتكاملات فورير بتسعير النماذج التي تكون معادلاتها تكاملات، وتستخرج خوارزميات إيجاد الجذور التقلب الضمني. هذه الطبقة مملة، وهي بالضبط الطبقة التي يُعاد تنفيذها بشكل سيئ. النسخة التقليدية هي مُحلل نيوتن يدوي يتقارب مع الأسعار السائلة عند مستوى السعر الحالي (at-the-money) ويضل الطريق عند الأسعار البعيدة جداً (out-of-the-money). ويليها في السوء معكوس المصفوفة الذي يفقد دقته في المطابقة شبه المفردة ويعيد معايير تبدو منطقية تماماً. إذا كنت تبني نموذجاً ذهنياً من الصفر، فإن كتاب تداول كمي مفتوح المصدر هو نقطة انطلاق أفضل من أي مرجع لواجهة برمجة تطبيقات (API) لأي مكتبة.

ما هي libitofin، وهل هي QuantLib بلغة Rust؟

نعم، بالمعنى المهم. إنها نقل لتصميم QuantLib إلى لغة Rust، ويذكر المشروع أنه تم اختباره مقابل مجموعة اختبارات QuantLib الخاصة. هذه هي الطريقة النزيهة لنقل مكتبة حسابات عددية: يتم التحقق من النتائج مقابل التنفيذ المرجعي بدلاً من التوقعات المكتوبة يدوياً لما يجب أن تكون عليه الإجابة. مسار Python هو حزمة تسمى itofin، بحيث يمكن لعملية Python الوصول إلى محرك Rust دون الحاجة إلى سلسلة أدوات C++ في الحلقة.

التصنيف الأكثر أهمية هو أنها ما قبل الإصدار 1.0. بموجب اتفاقية إصدارات Rust، لا يحمل إصدار 0.x أي وعد بالتوافق بين الإصدارات الفرعية: فمن المسموح للإصدار 0.4 إلى 0.5 إعادة تسمية أو نقل أو حذف أي شيء يريده. تعامل مع واجهة البرمجة كهدف متحرك، واتبع العادات التالية:

  • ثبّت إصداراً محدداً بدقة في ملف القفل (lockfile) الخاص بك وقم بالترقية عن قصد، مع استخدام مجموعة الاختبارات الخاصة بك كبوابة.
  • احتفظ بغلاف رقيق خاص بك حول أنواع المكتبة، بحيث يتركز أي تغيير في الاسم في ملف واحد بدلاً من أربعين.
  • سجل إصدار المكتبة بجانب الأرقام التي أنتجتها، بحيث تشير أي إعادة تشغيل تعطي نتائج مختلفة إلى الترقية بدلاً من السوق.
  • احتفظ بأرقام هوامش الإنتاج والضمانات ومخاطر التنظيم على شيء يحمل وعداً بالتوافق حتى صدور الإصدار 1.0.

لا شيء مما سبق يعد انتقاداً للمشروع. "ما قبل 1.0" هو وصف ذاتي دقيق والمكان الصحيح لمكتبة شابة وكبيرة جداً. نمط الفشل هو أن يتعامل القارئ معها كبديل مباشر لـ QuantLib ويواجه تغييراً في التوقيع في إصدار فرعي في منتصف الربع المالي. تعليمات التثبيت تتغير أيضاً مع الإصدار، ويحتاج كل من cargo وpip إلى الوصول للشبكة لحل أي تبعيات، لذا اقرأ ملف README الخاص بالمشروع عند الإصدار الذي تنوي تثبيته بدلاً من نسخ مقتطف من منشور مدونة، بما في ذلك هذا المنشور.

Python، أم محرك مترجم، أم Rust؟

سؤالان يحسمان هذا الأمر، ولا علاقة لأي منهما بالتفضيل الشخصي. كم مرة في الثانية تقوم بإعادة التسعير، وهل يجب أن تتطابق أرقامك عبر عمليات منفصلة؟ حجم السلسلة يحدد مقياس السؤال الأول.

الاستعلامعقود الخيارات المميزة التي تم تداولها في جلسة واحدة
استعلام SQL الدقيق وراء كل رقم
WITH (SELECT max(date) FROM global_markets.options_greeks) AS last_session
SELECT
    underlying_symbol                      AS symbol,
    countDistinct(ticker)                  AS contracts_priced,
    formatDateTime(max(date), '%b %e, %Y') AS as_of
FROM global_markets.options_greeks
WHERE date = last_session
  AND underlying_symbol IN ('SPY', 'AAPL', 'NVDA', 'MSFT', 'KO')
  AND volume > 0
GROUP BY symbol
ORDER BY contracts_priced DESC
Run this yourself

في Aug 11, 2026، تداول SPY ما مجموعه 5336 عقداً مختلفاً في جلسة واحدة، مقابل 360 لـ KO. تسعير السلسلة الواسعة مرة واحدة ليس بالأمر الجلل. أما تسعيرها بخمسة مؤشرات حساسية لكل عقد، عند كل تحديث للسعر، عبر محفظة من الأصول الأساسية، فهو برنامج مختلف بمتطلبات مختلفة.

ابقَ في Python مع مكتبة راسخة عندما تُقاس الحلقة بآلاف التقييمات في الدقيقة ويكون العمل المحيط بها بحثاً أو تحليلاً أو تقييمات نهاية اليوم. روابط Python الخاصة بـ QuantLib هي الخيار الناضج: نفس محرك C++، وأوسع تغطية للأدوات، وسنوات من الخبرة في الإنتاج. نادراً ما تكون السرعة هي القيد الملزم في كود البحث؛ التغطية والدقة هما الأهم.

استدعِ محركاً مترجماً من Python عندما تكون الحلقة مكثفة والكود المحيط بها ليس كذلك. التكلفة التي يجب مراقبتها هي الحدود نفسها. الاستدعاء لكل عقد من Python يدفع تكلفة إضافية عند كل عبور، والحل هو تمرير مصفوفة للمحرك واستعادة مصفوفة. هذا هو المكان الذي تستهدفه itofin، إلى جانب QuantLib-Python.

اكتب بلغة Rust عندما تكون حلقة التسعير هي المنتج نفسه: مسعر داخل خدمة تسعير، أو تشغيل مخاطر وفق جدول زمني لا يمكنك تفويته، أو ملف تنفيذي يتم شحنه إلى جهاز لا يحتوي على Python. السؤال الثاني يصب هنا أيضاً. تعتمد نتائج الفاصلة العائمة على ترتيب العمليات، لذا يمكن لنموذجين تم تنفيذهما مرتين أن يختلفا في الأرقام الأخيرة، ودفتر ملاحظات بحثي يختلف مع خدمة إنتاج يعني أسبوعاً من التحقيقات. المحرك الواحد المستخدم من كلا الجانبين يزيل هذه الفئة بالكامل من التناقض، وهي الحجة الدائمة لنواة مترجمة مع روابط بغض النظر عن اللغة التي كُتبت بها. تنطبق نفس الغريزة على عمل الاستراتيجية، كما يوضح اختبار رجعي قابل للتكرار.

أين يكمن هدف المعايرة فعلياً؟

تحتاج المعايرة إلى شيء للمطابقة معه، وهذا الشيء هو سطح من التقلبات الضمنية في السوق. جانب إيجاد الجذور لذلك مغطى في كيفية حساب التقلب الضمني. الشكل أدناه هو ما يجب أن يطابقه النموذج.

الاستعلامالتقلب الضمني لـ AAPL عند سعر السوق حسب وقت الاستحقاق
استعلام SQL الدقيق وراء كل رقم
SELECT
    multiIf(days_to_expiry <=   7, '0 to 7 days',
            days_to_expiry <=  30, '8 to 30 days',
            days_to_expiry <=  60, '31 to 60 days',
            days_to_expiry <= 120, '61 to 120 days',
            days_to_expiry <= 240, '121 to 240 days',
                                   '241 days or more') AS dte_bucket,
    round(avg(implied_volatility) * 100, 1)            AS iv_pct,
    countDistinct(ticker)                              AS contracts
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
  AND date >= today() - 10
  AND days_to_expiry >= 0
  AND iv_converged = 1
  AND volume > 0
  AND abs(toFloat64(strike_price) / toFloat64(underlying_close) - 1) < 0.05
GROUP BY dte_bucket
ORDER BY min(days_to_expiry)
Run this yourself

بالقرب من مستوى السعر الحالي، بلغ متوسط عقود AAPL في سلة 0 to 7 days ما نسبته 29.6% من التقلب الضمني عبر الجلسات الموضحة، مقابل 29.3% عند 241 days or more. لا يمكن لنموذج يحمل معيار تقلب واحداً أن يغطي كلتا النقطتين في وقت واحد، وهذا هو السبب الكامل لوجود نماذج ذات هيكل زمني للتقلب. مطابقة نموذج مع سطح كهذا هي خطوة المعايرة، ومؤشرات الحساسية التي تنتج عن النموذج المطابق هي "اليونانيات" (Greeks)، المغطاة في شرح يونانيات الخيارات.

كيف تم بناء هذه اللوحات

تقرأ لوحة المنحنى أحدث تاريخ مسعر في سلسلة الخزانة وتفك أعمدة آجال الاستحقاق الأحد عشر الخاصة بها إلى صفوف، مع إسقاط أي أجل استحقاق ليس له سعر في ذلك اليوم. تحسب لوحة الجلسات التواريخ المميزة في شريط SPY لكل شهر، وهو مؤشر نظيف لعدد جلسات البورصة الكاملة. تحسب لوحة السلسلة رموز العقود المميزة ذات الحجم غير الصفري في أحدث جلسة متاحة، مجمعة حسب الأصل الأساسي. تحتفظ لوحة التقلب فقط بالحلول المتقاربة ذات الحجم غير الصفري وأسعار التنفيذ ضمن خمسة بالمائة من سعر إغلاق الأصل الأساسي، وهو النطاق القياسي القريب من السعر الحالي؛ تشمل السلة الأمامية عقود انتهاء الصلاحية في نفس اليوم.

الأسئلة الشائعة

هل libitofin بديل مباشر لـ QuantLib؟

لا. اعتباراً من أغسطس 2026، هو إصدار ما قبل 1.0 يغطي جزءاً من سطح QuantLib ويتحقق من نتائجه مقابل مجموعة اختبارات QuantLib الخاصة. تظل QuantLib نفسها، التي يتم الوصول إليها عبر روابط Python، الخيار الأوسع والأكثر استقراراً لأعمال الإنتاج.

ماذا يعني "ما قبل 1.0" بالنسبة لمكتبة تسعير؟

بموجب اتفاقية إصدارات Rust، لا يقدم إصدار 0.x أي وعد بالتوافق: الإصدار الفرعي التالي حر في إعادة تسمية أو حذف أي شيء. عملياً، يعني هذا تثبيت إصدار محدد بدقة وإعادة تشغيل مجموعة الاختبارات الخاصة بك عند كل ترقية.

هل أحتاج إلى كتابة Rust لاستخدام libitofin؟

لا. ينشر المشروع حزمة Python تسمى itofin، لذا يمكن استدعاء المحرك من عملية Python عادية. تصبح كتابة Rust ذات صلة عندما تكون حلقة التسعير نفسها هي المنتج الذي تشحنه.

ما الذي تقدمه لي مكتبة التسعير ولا تقدمه معادلة جدول البيانات؟

منحنيات بدلاً من معدلات فردية، واتفاقيات حساب أيام مع تقاويم بورصة حقيقية خلفها، وحلقة معايرة تطابق معايير النموذج مع الأسعار المسعرة، وطبقة حسابات عددية مختبرة تحت كل ذلك. المعادلة المغلقة هي الجزء الصغير من المهمة.

هل تغير لغة البرمجة سعر الخيار؟

ليس رياضياً. إنها تغير القابلية للتكرار: تعتمد نتائج الفاصلة العائمة على ترتيب العمليات، لذا يمكن لتنفيذين لنموذج واحد أن يختلفا في الأرقام النهائية. تشغيل البحث والإنتاج من نفس المحرك يزيل هذه الفجوة.


تحمل كل لوحة هنا كود SQL الخاص بها، لذا قم بتوسيع إحداها لترى كيف تم الحساب. يمكن طرح نفس أسئلة المنحنى والتقويم والسلسلة باللغة الإنجليزية البسيطة على محطة Strasmore.