σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 2 · NumPy et pandas pour les données de marché›Sujet 2.10·~35 minIntermédiaire

Rendements simples

Position dans le parcoursModule 2 sur 11 · Sujet 10 sur 20
2.10 Rendements simples
—Score du lab— / 100XP total—Série—

La variation en pourcentage

Les prix sont difficiles à comparer : un mouvement de 5 $ n'a pas le même sens sur une action à 20 $ que sur une à 500 $. Un rendement simple supprime l'échelle. C'est la variation de prix rapportée au prix précédent, si bien que +2 % signifie la même chose sur n'importe quel actif.

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

Avec pandas, prices.shift(1) décale chaque valeur d'un cran vers le bas, si bien que chaque ligne voit le prix précédent. Diviser par ce résultat calcule tous les rendements d'un coup, et le premier est NaN faute de prix précédent. prices.pct_change() fait la même chose en un appel.

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]

Les rendements se composent, ils ne s'additionnent pas

Une journée à +2 % suivie d'une journée à -1 % ne donne pas +1 % sur deux jours. Chaque rendement s'applique à un prix de départ différent, donc les facteurs de croissance 1 + r se multiplient : le rendement total est (1 + r1) * (1 + r2) * ... - 1. Ignorez le NaN de la première ligne (prod l'ignore) et le résultat est égal au prix final divisé par le prix initial, moins un. Additionner les rendements s'en approche mais c'est faux, et l'écart grandit avec la volatilité.

# 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

Entre actifs et entre années

Les rendements simples s'additionnent entre actifs : le rendement d'un portefeuille sur une période est la somme de poids * rendement de ses lignes. Avec des Series, weights * returns associe les entrées par étiquette, donc l'ordre des symboles importe peu (avec .values, vous perdez cette protection). Dans le temps, ils ne s'additionnent pas mais se composent : pour annoncer un taux annuel à partir d'une période plus courte, on élève le facteur de croissance à la puissance jours_de_bourse / jours.

# 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
Un rendement dépend du prix de départPrécisez toujours la période (quotidien, mensuel, annuel) à côté d'un rendement et ne mélangez pas les périodes : 1 % par jour et 1 % par an sont des nombres très différents. Mettez la période dans le nom de la variable, par exemple daily_returns.
  • Un rendement simple vaut prix / prix précédent - 1 : prices / prices.shift(1) - 1 ou prices.pct_change() ; la première entrée est NaN.
  • Dans le temps, les rendements se composent : (1 + returns).prod() - 1, jamais returns.sum().
  • Entre actifs, ils s'additionnent avec des poids : (weights * returns).sum(), associés par étiquette.

À suivre : les rendements logarithmiques, une alternative qui s'additionne dans le temps et qui est le choix habituel en statistique.

Lab : ce qui est noté

  • Transformer des prix en rendements par période avec simple_returns25 pts
  • Composer les rendements en un total avec cumulative_return25 pts
  • Pondérer les rendements des actifs en un seul nombre avec portfolio_return25 pts
  • Exprimer un rendement en taux annuel avec annualized_return25 pts
Explorateur · lab-43-1
▾ lab-43-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-43-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-43-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub