σμ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.10·~35 دمتوسط

العوائد البسيطة

الموضع في المسارالوحدة 2 من 11 · الموضوع 10 من 20
2.10 العوائد البسيطة
—درجة المختبر— / 100إجمالي XP—التتابع—

نسبة التغير

يصعب مقارنة الأسعار: حركة بخمسة دولارات تعني شيئًا في سهم بعشرين دولارًا وشيئًا آخر في سهم بخمسمئة. العائد البسيط يزيل المقياس. هو تغير السعر كنسبة من السعر السابق، فيعني +2% الشيء نفسه في أي أصل.

r_t = (P_t - P_{t-1}) / P_{t-1} = P_t / P_{t-1} - 1

في pandas ينقل prices.shift(1) كل قيمة خطوة للأسفل، فيرى كل صف السعر السابق. والقسمة عليه تحسب كل العوائد دفعة واحدة، والأول NaN لأنه لا يوجد سعر سابق. ويفعل prices.pct_change() الشيء نفسه باستدعاء واحد.

import pandas as pd

prices = pd.Series([100.0, 102.0, 100.98, 103.0], index=["Mon", "Tue", "Wed", "Thu"])

returns = prices / prices.shift(1) - 1
print(returns.round(4))
# Mon     NaN
# Tue    0.02
# Wed   -0.01
# Thu    0.02
# dtype: float64

print(prices.pct_change().round(4).tolist())   # [nan, 0.02, -0.01, 0.02]

العوائد تتراكب ولا تُجمع

يوم بنسبة +2% يليه يوم بنسبة -1% لا يعطي +1% خلال يومين. فكل عائد يُطبَّق على سعر بداية مختلف، لذلك تُضرب عوامل النمو 1 + r: العائد الكلي هو (1 + r1) * (1 + r2) * ... - 1. تجاهل NaN في الصف الأول (يتجاهله prod) فتساوي النتيجة سعر النهاية مقسومًا على سعر البداية ناقص واحد. وجمع العوائد يقترب من ذلك لكنه خاطئ، والفجوة تكبر مع التقلب.

# Compounding the returns gives the same total as the end price over the start price
returns = prices.pct_change().dropna()

print(round((1 + returns).prod() - 1, 6))            # 0.03
print(round(prices.iloc[-1] / prices.iloc[0] - 1, 6))   # 0.03
print(round(returns.sum(), 6))                       # 0.030004   close, but not the same

print((1 + returns).cumprod().round(4).tolist())     # [1.02, 1.0098, 1.03]   the growth of 1 dollar
# Gains and losses are not symmetric: +10% then -10% does not get you back
up, down = 0.10, -0.10
print(round((1 + up) * (1 + down) - 1, 4))   # -0.01

# After a -50% loss you need +100% to recover
print(1 / (1 - 0.5) - 1)   # 1.0

عبر الأصول وعبر السنوات

تُجمع العوائد البسيطة عبر الأصول: عائد المحفظة في فترة واحدة هو مجموع الوزن * العائد لمكوناتها. ومع Series يطابق weights * returns العناصر بالتسمية فلا يهم ترتيب الرموز (استخدام .values يفقدك هذه الحماية). أما عبر الزمن فلا تُجمع بل تتراكب، ولذلك تُرفع عامل النمو إلى القوة أيام_التداول / الأيام لذكر معدل سنوي انطلاقًا من فترة أقصر.

# Weights times returns, matched by label (the order of the symbols differs on purpose)
weights = pd.Series({"AAPL": 0.5, "MSFT": 0.3, "TSLA": 0.2})
day = pd.Series({"TSLA": -0.03, "AAPL": 0.012, "MSFT": 0.004})

print(round((weights * day).sum(), 4))               # 0.0012   correct: AAPL with AAPL, TSLA with TSLA
print(round((weights.values * day.values).sum(), 4))   # -0.0106  wrong: position by position, TSLA met AAPL's weight
# Annualising: 5% in 60 trading days, if it kept compounding at that pace for a year of 252 days
total, days = 0.05, 60
print(round((1 + total) ** (252 / days) - 1, 4))   # 0.2274
العائد نسبي إلى سعر البدايةاذكر دائمًا الفترة (يومي أو شهري أو سنوي) بجانب العائد ولا تخلط الفترات: 1% يوميًا و1% سنويًا رقمان مختلفان جدًا. ضع الفترة في اسم المتغير، مثل daily_returns.
  • العائد البسيط هو السعر / السعر السابق - 1: prices / prices.shift(1) - 1 أو prices.pct_change()؛ والعنصر الأول NaN.
  • عبر الزمن تتراكب العوائد: (1 + returns).prod() - 1 وليس returns.sum() أبدًا.
  • عبر الأصول تُجمع بالأوزان: (weights * returns).sum() مع المطابقة بالتسمية.

التالي: العوائد اللوغاريتمية، بديل يُجمع عبر الزمن وهو الخيار المعتاد في الإحصاء.

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

  • حوّل الأسعار إلى عوائد الفترات باستخدام simple_returns25 نقطة
  • اجمع العوائد تركيبيًا في إجمالي باستخدام cumulative_return25 نقطة
  • رجّح عوائد الأصول في رقم واحد باستخدام portfolio_return25 نقطة
  • عبّر عن العائد كمعدل سنوي باستخدام annualized_return25 نقطة
المستكشف · lab-43-1
▾ lab-43-1
…
bash · داخل المتصفح
learner@quantcraft:~/lab-43-1$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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