σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyالمسارات
Ctrl K
AO
+1.2k متعلّم
تسجيل الدخولانضم ↗
المسارات
AO
+1.2k متعلّم
تسجيل الدخول
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
مسار Python›الوحدة 10 · هندسة Python عالية الأداء›الموضوع 10.8·~1 سمتقدم

استدعاء C++ باستخدام pybind11

الموضع في المسارالوحدة 10 من 11 · الموضوع 8 من 13
10.8 استدعاء C++ باستخدام pybind11
—درجة المختبر— / 100إجمالي XP—التتابع—

حين يحتاج بايثون إلى C++

جزء كبير من الشيفرة الجادة في عالم التداول هو C++: محركات المطابقة ومعالجات بيانات السوق ومكتبات التسعير المضبوطة على مدى خمس عشرة سنة. ونادرًا ما تريد إعادة كتابتها ونادرًا ما تريد التخلي عن بايثون للبحث. وpybind11 هو الجسر: مكتبة C++ رؤوس فقط تكتب فيها بضعة أسطر تقول «اعرض دالة C++ هذه لبايثون بهذا الاسم» ثم تترجمها إلى مكتبة مشتركة وتستوردها من بايثون كأي وحدة. وتُحوَّل الوسائط والنتائج عنك: قائمة بايثون من الأعداد العشرية تصبح std::vector<double> والعكس.

# A C++ function and the pybind11 file that exposes it to Python (shown here as text)
cpp = """\
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>

namespace py = pybind11;

double mean(const std::vector<double>& x) {
    double total = 0.0;
    for (double v : x) total += v;
    return x.empty() ? 0.0 : total / x.size();
}

PYBIND11_MODULE(fastmath, m) {
    m.def("mean", &mean, "Mean of a list of floats");
}
"""
lines = cpp.splitlines()
print(lines[-2].strip())                             # m.def("mean", &mean, "Mean of a list of floats");
print(len(lines))                                    # 15

الأنواع عبر الجسر

لكل نوع في بايثون نظير في C++: int هو int64_t وfloat هو double وstr هو std::string وقائمة الأعداد العشرية هي std::vector<double>؛ وكل ما عدا ذلك يمكن حمله كـ py::object عام. وتضمين <pybind11/stl.h> يجعل تحويلات الحاويات القياسية تلقائية. ومرّر القيم الكبيرة بمرجع ثابت (const std::vector<double>& x) كي لا تنسخها دالة C++ والصغيرة كالأعداد بالقيمة. وتحويل قائمة بايثون إلى متجه ينسخ مرة لكل نداء، فللمصفوفات الكبيرة استعمل py::array_t<double> الذي يرى ذاكرة مصفوفة NumPy مباشرة دون نسخ.

# Choosing the C++ type of each argument; big values are passed by const reference
CPP_TYPES = {"int": "int64_t", "float": "double", "str": "std::string", "list[float]": "std::vector<double>"}

def cpp_type(py_type):
    return CPP_TYPES.get(py_type, "py::object")

def cpp_signature(name, params, returns):
    result = "void" if returns is None else cpp_type(returns)
    arguments = []
    for param, py_type in params:
        ctype = cpp_type(py_type)
        arguments.append(f"const {ctype}& {param}" if ctype.startswith("std::") else f"{ctype} {param}")
    return f"{result} {name}({', '.join(arguments)})"

print(cpp_signature("mean", [("x", "list[float]"), ("n", "int")], "float"))      # double mean(const std::vector<double>& x, int64_t n)
print(cpp_signature("label", [("symbol", "str")], "str"))                       # std::string label(const std::string& symbol)

البناء وGIL والملكية

يُبنى بـ CMake (pybind11_add_module) أو بـ pybind11.setup_helpers في setup.py والناتج ملف .so أو .pyd تستورده. أمران يجب معرفتهما. الأول أن قفل المفسّر العام (GIL) يبقى ممسوكًا أثناء تنفيذ شيفرة C++ ما لم تحرره بـ py::gil_scoped_release؛ والحساب الطويل في C++ الذي لا يلمس كائنات بايثون ينبغي أن يحرره لتعمل خيوط بايثون الأخرى. والثاني الملكية: إن أرجعت C++ مرجعًا أو مؤشرًا إلى ذاكرتها فلا يجوز أن يعيش بايثون أكثر منها (يتحكم py::return_value_policy في ذلك)، والانهيار بلا تتبع بايثون يدل عادة على مؤشر متدلٍّ. واختبر الدوال المربوطة من بايثون مقابل نسخة بايثون خالصة تمامًا كما في المختبرات السابقة.

# Binding lines and the module file, generated from a table of functions
BACKSLASH = chr(92)

def binding_line(py_name, cpp_name, doc):
    escaped = doc.replace(BACKSLASH, BACKSLASH + BACKSLASH).replace('"', BACKSLASH + '"')
    return f'm.def("{py_name}", &{cpp_name}, "{escaped}");'

def module_source(module, functions):
    lines = ["#include <pybind11/pybind11.h>", "#include <pybind11/stl.h>", "", "namespace py = pybind11;", ""]
    lines.append(f"PYBIND11_MODULE({module}, m) {{")
    for py_name, cpp_name, doc in functions:
        lines.append("    " + binding_line(py_name, cpp_name, doc))
    lines.append("}")
    return "\n".join(lines) + "\n"

source = module_source("pricing", [("mean", "mean_impl", "Mean of x"), ("price", "bs_price", 'Black-Scholes "call" price')])
print(source.splitlines()[-3])                       #     m.def("mean", &mean_impl, "Mean of x");
print(source.splitlines()[-2])                       #     m.def("price", &bs_price, "Black-Scholes \"call\" price");
# From Python the built module is used like any other; this stand-in has the same interface
import types

def mean(x):
    """Mean of a list of floats"""
    return sum(x) / len(x) if x else 0.0

fastmath = types.SimpleNamespace(mean=mean)          # stands in for `import fastmath` after the build
print(fastmath.mean([100.0, 102.0, 101.0]))          # 101.0
print(fastmath.mean.__doc__)                         # Mean of a list of floats   the doc string given to m.def
print(fastmath.mean([]))                             # 0.0   what the C++ code returns for an empty vector
الانهيار ليس استثناء بايثونقد يُسقط خطأ في شيفرة C++ عملية بايثون كلها بخطأ تجزئة وبلا تتبع، وفي نظام تداول يعني هذا أن العملية ذهبت. تحقق عند الحدود من الأحجام والقيم المستلمة وارمِ استثناء C++ (يحوّله pybind11 إلى استثناء بايثون) وابنِ مع التحذيرات والـ sanitizers أثناء التطوير وأبقِ سطح C++ صغيرًا: بضع دوال مختبرة جيدًا تؤدي العمل الثقيل وبايثون تتولى التنسيق.
  • يعرض pybind11 دوال C++ لبايثون بـ PYBIND11_MODULE وأسطر m.def(...)؛ والناتج وحدة .so/.pyd قابلة للاستيراد.
  • تتقابل الأنواع: int←int64_t وfloat←double وstr←std::string والقوائم←std::vector؛ ومرّر القيم الكبيرة بمرجع ثابت ومصفوفات NumPy بـ py::array_t.
  • حرّر GIL في الأعمال الطويلة بـ C++ خالصة، وانتبه لمن يملك الذاكرة المُرجَعة، وتحقق من المدخلات عند الحدود، واختبر مقابل نسخة بايثون خالصة.

التالي: الخيوط وGIL، ما تستطيع خيوط بايثون فعله بالتوازي وما لا تستطيع.

المختبر: ما الذي يُقيَّم

  • اربط أنواع بايثون بأنواع C++ باستخدام cpp_type25 نقطة
  • صرّح بدالة باستخدام cpp_signature25 نقطة
  • اعرض دالة باستخدام binding_line25 نقطة
  • اجمع الوحدة باستخدام module_source25 نقطة
المستكشف · lab-124-1
▾ lab-124-1
…
bash · داخل المتصفح
learner@quantcraft:~/lab-124-1$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

هل تريد إعادة ضبط هذا الموضوع؟

سيعود كل ما في lab-124-1 إلى الملفات الابتدائية وستُمسح الطرفية. ستفقد حلّك الحالي ولا يمكن التراجع عن ذلك.

سيُعاد ضبطه
  • main.py ← الشيفرة الابتدائية
  • سجل الطرفية ومخرجات الاختبارات
  • التغييرات غير المُرسلة
سيُحتفظ به
  • أفضل درجة في المختبر
  • نقاط XP والتتابع
  • تقدّم المحاضرة والملاحظات

سجّل الدخول لتشغيل الكود الخاص بك.

قراءة كل المحاضرات مجانية. أنشئ حسابًا أو سجّل الدخول لتشغيل الكود وتنفيذ الاختبارات وتسليم هذا المختبر.

  • بيئة تطوير داخل المتصفح مع طرفية bash — لا حاجة لتثبيت أي شيء
  • تصحيح فوري ونقاط خبرة ومكانك في المسار
إنشاء حسابتسجيل الدخول
أو
المتابعة عبر GitHub