σμ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.6·~55 minAvancé

Exécutions d'ordres réalistes

Position dans le parcoursModule 6 sur 11 · Sujet 6 sur 6
6.6 Exécutions d'ordres réalistes
—Score du lab— / 100XP total—Série—

Une exécution n'est pas une certitude

Un backtest naïf suppose qu'un ordre de n'importe quelle taille est exécuté au prix des données. Les ordres réels posent trois questions. Quelle est la profondeur du carnet : un ordre au marché prend d'abord la meilleure offre, puis la suivante, donc un gros ordre paie un prix moyen moins bon. Le marché a-t-il atteint ma limite : un ordre à cours limité obtient le prix demandé ou mieux, mais seulement si les échanges l'atteignent. Et combien puis-je prendre : un gros ordre ne peut être qu'une fraction du volume de la barre, donc il est exécuté par morceaux dans le temps.

def fill_market_order(book, quantity):
    remaining = quantity
    cost = 0.0
    filled = 0
    for price, size in book:                      # cheapest offer first
        if remaining <= 0:
            break
        take = min(size, remaining)
        cost += take * price
        filled += take
        remaining -= take
    if filled == 0:
        return 0.0, 0
    return cost / filled, filled

book = [(50.00, 300), (50.02, 500), (50.05, 1000)]
print(fill_market_order(book, 200))        # (50.0, 200)           inside the first level
print(fill_market_order(book, 600))        # (50.01, 600)          300 at 50.00 and 300 at 50.02
print(fill_market_order(book, 5000))       # (50.0333, 1800)      the whole book; 3,200 shares stay unfilled
# The average price and the slippage against the best offer grow with the size
def average_price(book, quantity):
    remaining, cost, filled = quantity, 0.0, 0
    for price, size in book:
        take = min(size, remaining)
        cost += take * price
        filled += take
        remaining -= take
    return cost / filled

book = [(50.00, 300), (50.02, 500), (50.05, 1000)]
for quantity in (100, 300, 800, 1800):
    avg = average_price(book, quantity)
    print(quantity, round(avg, 4), round((avg - 50.00) / 50.00 * 10000, 2), "bps above the best offer")

Ordres à cours limité et écarts

Avec des barres (ouverture, plus bas, plus haut), on ne voit pas le chemin à l'intérieur de la barre, seulement ses extrêmes. Un achat à cours limité L est exécuté si le plus bas de la barre a atteint L ou moins. Le prix est L, sauf si la barre a ouvert sous L : le marché a alors sauté votre limite et vous êtes exécuté à l'ouverture, ce qui est meilleur. Une vente à cours limité est l'image miroir. Un ordre limité simplement touché peut ne pas être exécuté en réalité, car la file devant vous a été servie d'abord ; c'est donc la borne optimiste.

def limit_fill(side, limit, bar_open, bar_low, bar_high):
    if side == "buy" and bar_low <= limit:
        return min(bar_open, limit)             # a gap below the limit fills at the better open
    if side == "sell" and bar_high >= limit:
        return max(bar_open, limit)
    return None

print(limit_fill("buy", 49.95, 50.10, 49.90, 50.20))   # 49.95   traded down through the limit
print(limit_fill("buy", 49.95, 49.90, 49.80, 50.20))   # 49.9    opened below: filled at the open
print(limit_fill("buy", 49.95, 50.10, 50.00, 50.20))   # None    never reached
print(limit_fill("sell", 50.15, 50.00, 49.90, 50.20))  # 50.15

Travailler un gros ordre

Beaucoup de tables d'exécution utilisent une limite de participation : ne pas prendre plus de, disons, 10 % du volume. Dans une barre de 40 000 titres échangés, c'est 4 000, arrondi à l'inférieur. Un ordre de 5 000 titres prend alors deux barres, pas une. Parcourir les barres est une boucle qui porte la quantité restante : chaque barre exécute min(remaining, int(volume * participation)), et les barres suivant la fin de l'ordre n'exécutent rien.

def partial_fill(quantity, bar_volume, participation):
    return min(quantity, int(bar_volume * participation))

def fill_over_bars(quantity, volumes, participation):
    remaining = quantity
    fills = []
    for volume in volumes:
        filled = partial_fill(remaining, volume, participation)
        fills.append(filled)
        remaining -= filled
    return fills

fills = fill_over_bars(5000, [40000, 30000, 80000, 100000], 0.1)
print(fills, sum(fills))                      # [4000, 1000, 0, 0] 5000
Les modèles d'exécution fixent votre honnêtetéChaque simplification ici penche du même côté : supposer qu'un ordre limité est exécuté dès qu'il est touché, que vous ne déplacez jamais le marché, que le volume est là. Plus un résultat dépend des exécutions, plus il faut le tester avec des règles pessimistes (exécution seulement si le prix traverse la limite, plafond de participation plus bas) et vérifier que l'avantage survit.
  • Un ordre au marché parcourt le carnet niveau par niveau ; le prix moyen est pondéré par les volumes et se dégrade avec la taille, et un carnet mince n'exécute qu'une partie.
  • Un ordre limité est exécuté si la barre atteint la limite, à l'ouverture si la barre l'a sautée ; un simple contact peut ne pas être exécuté en réalité.
  • Les plafonds de participation limitent chaque barre à int(volume * participation) ; une boucle porte la quantité restante d'une barre à l'autre.

À suivre : le MEDAF et le bêta, mesurer la part d'un rendement qui vient du marché.

Lab : ce qui est noté

  • Parcourir le carnet avec fill_market_order25 pts
  • Décider si la limite est atteinte avec limit_fill25 pts
  • Respecter le volume avec partial_fill25 pts
  • Travailler l'ordre dans le temps avec fill_over_bars25 pts
Explorateur · lab-92-1
▾ lab-92-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-92-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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