σμ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›الوحدة 8 · النماذج العشوائية والتعلم الآلي›الموضوع 8.10·~1 سمتقدم

التحقق المتدرج (Walk-forward)

الموضع في المسارالوحدة 8 من 11 · الموضوع 10 من 10
8.10 التحقق المتدرج (Walk-forward)
—درجة المختبر— / 100إجمالي XP—التتابع—

اختبر كما كنت ستتداول

التقسيم العشوائي إلى صفوف تدريب واختبار كما في التعلم الآلي العادي يفشل مع السلاسل الزمنية: فمجموعة التدريب تحوي أيامًا بعد أيام الاختبار، والأيام المتجاورة مترابطة، فيكون الاختبار نسخة جزئية من التدريب. والتحقق المتدرج (walk-forward) يحترم الزمن: تدريب على كتلة من الماضي واختبار على الكتلة التالية لها مباشرة ثم إزاحة النافذة والتكرار. وكل تنبؤ يصدر عن نموذج لم ير المستقبل قط، وهذا هو التداول الحي بالضبط.

import numpy as np

n, train_size, test_size = 10, 4, 2
start = 0
while start + train_size + test_size <= n:
    train = np.arange(start, start + train_size)
    test = np.arange(start + train_size, start + train_size + test_size)
    print("train", train.tolist(), "test", test.tolist())
    start += test_size                                   # slide forward by one test block

متحرك أو متوسع، والحظر

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

def walk_forward_splits(n, train_size, test_size, gap=0):
    splits, start = [], 0
    while start + train_size + gap + test_size <= n:
        train = list(range(start, start + train_size))
        test = list(range(start + train_size + gap, start + train_size + gap + test_size))
        splits.append((train, test))
        start += test_size
    return splits

for train, test in walk_forward_splits(12, 5, 2, gap=1):
    print(train, "->", test)                         # the row between them is skipped

تقييم كل طيّة

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

import numpy as np

def walk_forward_splits(n, train_size, test_size):
    out, start = [], 0
    while start + train_size + test_size <= n:
        out.append((np.arange(start, start + train_size), np.arange(start + train_size, start + train_size + test_size)))
        start += test_size
    return out

rng = np.random.default_rng(2)
returns = rng.normal(0.0004, 0.01, size=300)
errors = []
for train, test in walk_forward_splits(len(returns), 100, 50):
    errors.append(np.mean((returns[test] - returns[train].mean()) ** 2))
print(np.round(np.array(errors) * 1e4, 3))                      # squared error in basis points squared, fold by fold

اكتشاف تسرب

التقسيم نظيف فقط إذا سبق كل صف تدريب بصرامة كل صف اختبار. وفحص train.max() < test.min() سطر واحد ويصطاد أشيع الأخطاء: فهرس غير مرتب أو مخلوط، أو شريحة مزاحة بواحد، أو تقسيم من دالة مكتبة تخلط افتراضيًا. ضع هذا الفحص داخل الشيفرة التي تبني الطيّات ليوقف التسرب التنفيذ بدل أن ينتج نتيجة مجاملة.

import numpy as np

def leaks_future(train_idx, test_idx):
    return bool(np.max(train_idx) >= np.min(test_idx))

train = np.arange(0, 100)
print(leaks_future(train, np.arange(100, 120)))                     # False: clean
shuffled = np.random.default_rng(1).permutation(120)                # what a shuffling splitter does
print(leaks_future(shuffled[:100], shuffled[100:]))                 # True: training rows after test rows
طيّات أقل لا تعني دليلًا أكثرتعيد كتل الاختبار المتداخلة استخدام الأيام نفسها، وكل طيّة تأتي من سوق واحد. واستراتيجية جيدة في 7 من 8 طيّات قد تدين بذلك إلى حدث مشترك. أبلغ عن تشتت نتائج الطيّات لا عن المتوسط فقط، واعتبر نتيجة walk-forward أفضل تقدير أمين لديك، وهو سيتقلص في الواقع أيضًا. ولا تضبط النموذج على الطيّات ثم تستشهد بالطيّات نفسها دليلًا.
  • Walk-forward: تدريب على كتلة من الماضي واختبار على التالية ثم إزاحة؛ كل صف اختبار يأتي بعد كل صفوف تدريبه.
  • النوافذ المتحركة تثبّت حجم التدريب؛ والمتوسعة تبدأ من الصف 0؛ وفجوة (حظر) تفصل التدريب عن الاختبار حين تمتد السمات على عدة أيام.
  • قيّم كل طيّة على حدة وانظر إلى التشتت؛ وافحص train.max() < test.min()؛ ولا تضبط أبدًا على الطيّات التي تبلغ عنها.

التالي: دفتر الأوامر المحددة، بنية البيانات التي تتكون فيها الأسعار فعلًا.

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

  • حرّك نافذة عبر الزمن باستخدام walk_forward_splits25 نقطة
  • احتفظ بكل التاريخ باستخدام expanding_splits25 نقطة
  • اختبر تنبؤًا طيّة بطيّة باستخدام forecast_mse25 نقطة
  • اكتشف التسرب باستخدام leaks_future25 نقطة
المستكشف · lab-111-1
▾ lab-111-1
…
bash · داخل المتصفح
learner@quantcraft:~/lab-111-1$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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