σμ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 6 · Backtesting et coûts de trading›Sujet 6.4·~45 minAvancé

Coûts de transaction

Position dans le parcoursModule 6 sur 11 · Sujet 4 sur 6
6.4 Coûts de transaction
—Score du lab— / 100XP total—Série—

Négocier n'est pas gratuit

Un backtest qui négocie gratuitement surestime toute stratégie, et plus elle négocie, plus il ment. Les coûts réels sont la commission du courtier, les frais de bourse et de régulation, et surtout le écart acheteur-vendeur franchi à chaque fois. On les cote généralement en points de base de la valeur négociée : 1 pb vaut 0,01 %. Un aller-retour à 5 pb dans chaque sens coûte 10 pb, qu'une stratégie gagnant 3 pb par jour ne survit pas si elle bascule chaque jour.

def trade_cost(notional, bps):
    return abs(notional) * bps / 10000

print(trade_cost(25000, 5))         # 12.5   buying 25,000 at 5 bps
print(trade_cost(-25000, 5))        # 12.5   selling costs the same
print(trade_cost(1_000_000, 1))     # 100.0  one basis point on a million

Rotation : combien vous négociez

Le coût dépend de la variation de la position, pas de sa taille. Avec la position en fraction du compte, la rotation d'un jour est la variation absolue par rapport à la position précédente : abs(position[t] - position[t - 1]), à partir d'une position nulle au jour 0. Rester acheteur ne coûte rien après le premier achat ; passer d'acheteur à vendeur négocie deux fois le compte. Additionnée sur une année, la rotation est le premier chiffre à regarder dans toute stratégie.

import numpy as np

positions = np.array([1.0, 1.0, -1.0, -1.0, 0.0])        # long, long, flip to short, short, flat
turnover = np.abs(np.diff(positions, prepend=0.0))       # day 0 starts from a flat position
print(turnover)                                          # [1. 0. 2. 0. 1.]
print(turnover.sum())                                    # 4.0  accounts traded over the five days

Rendements nets et coût d'équilibre

Le coût du jour, en fraction du compte, vaut turnover * bps / 10000, et le rendement net est le rendement brut moins ce coût. Un nombre utile est le coût d'équilibre : le coût par unité négociée pour lequel le profit total est exactement nul, sum(gross) / sum(turnover) * 10000. Comparez-le à ce que vous payez vraiment. Une stratégie à 2 pb d'équilibre est morte en pratique, une à 40 pb a de la marge.

# Gross and net returns of a rule that flips often, at 5 bps per unit traded
import numpy as np

rng = np.random.default_rng(11)
positions = np.where(rng.random(250) > 0.5, 1.0, -1.0)      # a coin-flip position, flipping often
gross = rng.normal(0.0015, 0.005, size=250)                   # a strategy with a real daily edge before costs

turnover = np.abs(np.diff(positions, prepend=0.0))
net = gross - turnover * 5 / 10000
print(round(float(gross.sum()), 4), round(float(net.sum()), 4))
print(round(float(turnover.sum()), 1), "accounts traded;", round(float(gross.sum() / turnover.sum() * 10000), 2), "bps break-even")
# The break-even cost of a small example
import numpy as np

gross = np.array([0.004, 0.002, 0.003, 0.001, 0.002])
positions = np.array([1.0, 1.0, -1.0, -1.0, 0.0])
turnover = np.abs(np.diff(positions, prepend=0.0))
breakeven = gross.sum() / turnover.sum() * 10000
print(round(float(breakeven), 2))                                           # 30.0 bps per unit traded
print(abs(round(float((gross - turnover * breakeven / 10000).sum()), 10)))       # 0.0: the whole profit is gone
Les coûts ne sont pas que la commissionUn coût fixe en pb est le modèle le plus simple. Les coûts réels dépendent aussi de l'instrument, de l'heure et de la taille de l'ordre : l'écart est plus large sur les petites capitalisations et à l'ouverture, et les gros ordres déplacent le prix contre vous, sujet des prochains thèmes. Quel que soit le modèle, un backtest qui ignore les coûts est optimiste, et un qui les facture trop bas est dangereux.
  • Les coûts se cotent en points de base de la valeur négociée : abs(notional) * bps / 10000, identique à l'achat et à la vente.
  • La rotation est abs(variation de position) (un retournement vaut 2) ; rendement net = brut - rotation * pb / 10000.
  • Le coût d'équilibre sum(gross) / sum(turnover) * 10000 dit combien de coût une stratégie supporte ; comparez-le au coût réel.

À suivre : le glissement, l'écart entre le prix attendu et le prix obtenu.

Lab : ce qui est noté

  • Chiffrer une transaction avec trade_cost25 pts
  • Compter ce qui est négocié avec turnover25 pts
  • Déduire la facture avec net_returns25 pts
  • Trouver le coût qui l'annule avec breakeven_bps25 pts
Explorateur · lab-90-1
▾ lab-90-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-90-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-90-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