σμ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›الوحدة 2 · NumPy وpandas لبيانات السوق›الموضوع 2.3·~45 دمبتدئ

التوجيه المتجهي

الموضع في المسارالوحدة 2 من 11 · الموضوع 3 من 20
2.3 التوجيه المتجهي
—درجة المختبر— / 100إجمالي XP—التتابع—

فكّر بالمصفوفات الكاملة لا بالعناصر

يعني التوجيه (vectorisation) كتابة الحساب كعمليات على مصفوفات كاملة وترك NumPy تشغّل الحلقة بكود مُترجَم سريع. فـa + b تجمع مصفوفتين عنصرًا عنصرًا وa * b تضربهما وa ** 2 تربّع كل عنصر وnp.sqrt(a) تأخذ كل جذر تربيعي. وهذه دوال شاملة (ufuncs). والنتيجة أقصر كتابةً وأسهل قراءةً، وهي في البيانات الكبيرة أسرع غالبًا بخمسين ضعفًا من حلقة for المكافئة في بايثون التي تدفع كلفة المفسّر عند كل عنصر.

import numpy as np

a = np.array([1.0, 2.0, 3.0])
b = np.array([10.0, 20.0, 30.0])

print(a + b)             # [11. 22. 33.]
print(a * b)             # [10. 40. 90.]
print(a ** 2)            # [1. 4. 9.]
print(np.sqrt(a))        # [1.         1.41421356 1.73205081]

# The loop version of a * b, which vectorisation replaces
result = np.empty(3)
for i in range(3):
    result[i] = a[i] * b[i]
print(result)            # [10. 40. 90.]

عوائد بلا حلقة: الإزاحة بالشرائح

يحتاج الحساب الذي يقارن كل عنصر بجاره إلى نسخة ثانية من المصفوفة مزاحة بمقدار واحد، وتعطيها الشرائح مجانًا. فـp[1:] سعر اليوم وp[:-1] سعر الأمس، وطول كل منهما n - 1، فتكون العوائد البسيطة p[1:] / p[:-1] - 1. والزوج نفسه يعطي الفروق (p[1:] - p[:-1] أو np.diff(p)). والمجاميع والجداءات التراكمية هي np.cumsum وnp.cumprod؛ ومسار النمو هو np.cumprod(1 + returns). وتحلّ مقارنات المصفوفات الكاملة (r > 0) وnp.where(شرط, إن_صح, إن_لم) محل if داخل حلقة.

import numpy as np

p = np.array([100.0, 102.0, 101.0, 103.0])

returns = p[1:] / p[:-1] - 1                 # today / yesterday - 1, no loop
print(np.round(returns, 4))                  # [ 0.02   -0.0098  0.0198]
print(np.diff(p))                            # [ 2. -1.  2.]
print(np.cumprod(1 + np.array([0.01, 0.02])))   # [1.01   1.0302]

# np.where replaces if/else inside a loop
r = np.array([0.01, -0.02, 0.03, -0.01])
print(np.where(r > 0, "up", "down"))         # ['up' 'down' 'up' 'down']
print(np.where(r > 0, r, 0.0))               # [0.01 0.   0.03 0.  ]

التجميع على طول محور وضرب المصفوفات

تختزل دوال مثل sum وmean وstd وmin وmax المصفوفة. ومع مصفوفة ثنائية الأبعاد من العوائد (صف لكل يوم وعمود لكل أصل) يختزل axis=0 الصفوف ويعطي قيمة لكل عمود (متوسط كل أصل عبر الزمن)، بينما يعطي axis=1 قيمة لكل صف (متوسط كل يوم عبر الأصول). وحاصل الضرب المصفوفي R @ w لمصفوفة (أيام, أصول) في متجه أوزان طوله أصول يعطي عائد المحفظة لكل يوم في استدعاء واحد: وهو الجداء النقطي من موضوع المتجهات منفّذًا على كل الصفوف دفعة واحدة.

import numpy as np

# 2 days x 3 assets
R = np.array([[0.02, -0.01, 0.03],
              [0.01,  0.00, 0.02]])
w = np.array([0.5, 0.3, 0.2])

print(R.shape)            # (2, 3)
print(R.mean(axis=0))     # [ 0.015 -0.005  0.025]   one value per asset
print(R.sum(axis=1))      # [0.04 0.03]              one value per day
print(R @ w)              # [0.013 0.009]            portfolio return each day

# Standardise: how many standard deviations from the mean?
z = np.array([1.0, 2.0, 3.0, 4.0])
print((z - z.mean()) / z.std())   # [-1.34164079 -0.4472136   0.4472136   1.34164079]

المتوسطات المتحركة بـ cumsum وحدود التوجيه

يبدو المتوسط المتحرك على نافذة w محتاجًا إلى حلقة، لكنه مع مجموع تراكمي لا يحتاج: ضع 0 أمام np.cumsum(x) فيصبح مجموع أي w قيمة متتالية فرقًا بين مجموعين تراكميين، cs[w:] - cs[:-w]؛ ثم اقسم على w. وللنتيجة len(x) - w + 1 قيمة. وجّه الحساب لا المنطق الذي يعتمد على النتيجة السابقة (كمركز يعتمد على تنفيذ الأمس مثلًا): فهذا يبقى في حلقة أو ينتقل لاحقًا إلى أدوات مُترجَمة مثل Numba. وراقب الذاكرة أيضًا لأن كل مصفوفة وسيطة نسخة كاملة.

import numpy as np

x = np.array([1.0, 2.0, 4.0, 8.0, 16.0])
w = 3

cs = np.concatenate(([0.0], np.cumsum(x)))   # [0, 1, 3, 7, 15, 31]
print((cs[w:] - cs[:-w]) / w)                # [2.33333333 4.66666667 9.33333333]

# Check against the plain definition
print([x[i:i + w].mean() for i in range(len(x) - w + 1)])   # three values, the same
وجّه ثم قارن بالنسخة البطيئةالحساب السريع الخاطئ أسوأ من البطيء الصحيح. احتفظ بنسخة الحلقة الواضحة، واكتب النسخة الموجَّهة، وقارنهما بـnp.allclose(السريعة, البطيئة) على بيانات عشوائية قبل الاعتماد عليها. واعنَ بالحواف أيضًا: مصفوفة فارغة أو عنصر واحد أو سلسلة ثابتة (انحراف معياري صفر فتقسم الدرجة المعيارية على صفر) وقيم nan تستحق قرارًا لا انهيارًا ولا رقمًا خاطئًا صامتًا.
  • عمليات المصفوفات الكاملة (a + b وa ** 2 وnp.sqrt(a)) تحل محل الحلقات؛ وقارن بـnp.where.
  • شرائح مزاحة: p[1:] / p[:-1] - 1؛ وcumsum وcumprod للمجاميع التراكمية والنمو.
  • axis=0 يختزل الصفوف (لكل عمود) وaxis=1 يختزل الأعمدة (لكل صف)؛ وR @ w جداء نقطي لكل صف.

التالي: البث (broadcasting)، القاعدة التي تتيح لمصفوفات مختلفة الشكل أن تعمل معًا.

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

  • احسب كل العوائد دفعة واحدة باستخدام simple_returns25 نقطة
  • احصل على عائد المحفظة لكل يوم باستخدام portfolio_returns25 نقطة
  • نعِّم سلسلة بلا حلقة باستخدام moving_average25 نقطة
  • قيِّس القيم معياريًا باستخدام zscore25 نقطة
المستكشف · lab-36-1
▾ lab-36-1
…
bash · داخل المتصفح
learner@quantcraft:~/lab-36-1$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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