σμ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 11 · Systèmes de production et modèles de pointe›Sujet 11.4·~1 hExpert

Exécution TWAP

Position dans le parcoursModule 11 sur 11 · Sujet 4 sur 13
11.4 Exécution TWAP
—Score du lab— / 100XP total—Série—

Pourquoi ne pas tout envoyer d'un coup

Achetez 200 000 actions avec un seul ordre au marché et vous allez dévorer le carnet d'ordres : les premières actions s'exécutent au meilleur ask, les suivantes à un prix moins bon, et ainsi de suite, et tous ceux qui vous voient acheter relèvent leurs prix. Cet impact de marché est un vrai coût, souvent plus grand que la commission. Un algorithme d'exécution découpe l'ordre en ordres enfants plus petits envoyés dans le temps, chacun étant absorbé avec moins d'impact. Le plus simple est le TWAP, l'algorithme de prix moyen pondéré par le temps : on divise la quantité en parts égales et on en envoie une à chacun des instants régulièrement espacés entre un début et une fin.

# Splitting 10 shares into 3 child orders: whole shares, earlier pieces take the extra
def twap_schedule(total, slices):
    size, extra = divmod(total, slices)
    return [size + (1 if index < extra else 0) for index in range(slices)]

print(twap_schedule(10, 3))                          # [4, 3, 3]
print(twap_schedule(200_000, 8))                     # [25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000]
print(sum(twap_schedule(10_001, 7)))                 # 10001   nothing is lost to rounding

Le calendrier : combien et quand

Un calendrier TWAP a deux parties. Les tailles découpent le total en n parts d'actions entières qui diffèrent d'au plus une action et totalisent exactement l'ordre : divmod(total, n) donne la taille de base et le nombre de parts qui reçoivent une action de plus. Les instants sont n heures de début régulièrement espacées entre le début et la fin : start + i * (end - start) / n. Un ordre d'une journée de 200 000 actions en 40 tranches fait 5 000 actions toutes les dix minutes, envoyées par un ordonnanceur comme celui du sujet précédent. Les vraies implémentations randomisent un peu tailles et instants, car un motif parfaitement régulier est facile à repérer et à exploiter par les autres.

# When each slice starts: a trading day from 09:30 to 16:00 in 13 half-hour slices (seconds since midnight)
def slice_times(start, end, slices):
    step = (end - start) / slices
    return [start + index * step for index in range(slices)]

times = slice_times(9.5 * 3600, 16 * 3600, 13)
print(times[0], times[1] - times[0])                 # 34200.0 1800.0   09:30 and a slice every 30 minutes
print(times[-1] / 3600)                              # 15.5             the last slice starts at 15:30, the end (16:00) is not a start time

La mesurer : prix moyen d'exécution et glissement

Une fois les tranches exécutées, le prix moyen d'exécution est la moyenne des prix pondérée par les quantités, sum(price * quantity) / sum(quantity) : une grosse exécution compte plus qu'une petite, donc une moyenne simple des prix serait fausse. Pour juger le résultat, comparez-le au prix d'arrivée, le prix du marché au moment de la décision. L'écart est le glissement (slippage), exprimé en points de base (1 pb vaut 0,01 %) : pour un achat (moyen - arrivée) / arrivée * 10 000, pour une vente le signe inverse, de sorte qu'un nombre positif est toujours un coût. C'est le cœur du shortfall d'implémentation, la façon standard de noter une exécution.

# Average fill price (quantity-weighted) and slippage against the arrival price, in basis points
def average_fill_price(fills):
    return sum(price * quantity for price, quantity in fills) / sum(quantity for _, quantity in fills)

def slippage_bps(avg_price, arrival_price, side):
    sign = 1 if side == "buy" else -1
    return sign * (avg_price - arrival_price) / arrival_price * 10000

fills = [(100.0, 400), (100.2, 300), (100.4, 300)]
average = average_fill_price(fills)
print(round(average, 2), sum(price for price, _ in fills) / 3)      # 100.18 100.2   the plain mean of the prices is not the answer
print(round(slippage_bps(average, 100.0, "buy"), 2))                # 18.0   a buy that averaged 100.18 against an arrival of 100.00 cost 18 bp
print(round(slippage_bps(average, 100.0, "sell"), 2))               # -18.0  the same prices would have been a gain for a seller
# A toy run: the price drifts up while we buy 10 slices; the slippage is what the drift cost us
prices = [100.00, 100.03, 100.05, 100.04, 100.08, 100.10, 100.09, 100.12, 100.15, 100.14]     # price at each slice time
pieces = [100] * 10                                                                         # TWAP: equal child orders

arrival = prices[0]
fills = list(zip(prices, pieces))
average = sum(p * q for p, q in fills) / sum(pieces)
print(round(average, 4))                             # 100.08
print(round((average - arrival) / arrival * 10000, 1))      # 8.0   bp of slippage for buying into a rising market
Ce qui est prévisible est exploitableLe TWAP ignore le marché : il échange la même quantité aux moments calmes et aux moments actifs, et son empreinte régulière peut être reconnue et devancée. C'est une base raisonnable quand on n'a pas d'avis sur le volume et qu'on veut de la simplicité. Les améliorations sont le sujet suivant : suivre la courbe de volume de la journée (VWAP), plafonner votre part du volume de chaque intervalle, randomiser tailles et instants, et s'arrêter ou ralentir quand le prix vous échappe. Comparez toujours le glissement de l'algorithme à un benchmark simple sur les mêmes jours avant de lui faire confiance.
  • Les gros ordres font bouger le prix ; un algorithme d'exécution envoie de plus petits ordres enfants dans le temps. Le TWAP envoie des parts égales à intervalles réguliers.
  • Tailles : divmod(total, n) avec les actions en plus dans les premières parts ; instants : start + i * (end - start) / n.
  • Notez une exécution par son prix moyen pondéré par les quantités face au prix d'arrivée, en points de base ; un nombre positif est un coût.

À suivre : l'exécution VWAP, suivre le volume de la journée plutôt que l'horloge.

Lab : ce qui est noté

  • Dimensionner chaque tranche avec twap_schedule25 pts
  • Espacer les tranches dans le temps avec slice_times25 pts
  • Moyenner vos exécutions avec average_fill_price25 pts
  • Chiffrer le coût avec slippage_bps25 pts
Explorateur · lab-133-1
▾ lab-133-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-133-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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