σμ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.6·~1 h 30 minExpert

Tenue de marché

Position dans le parcoursModule 11 sur 11 · Sujet 6 sur 13
11.6 Tenue de marché
—Score du lab— / 100XP total—Série—

Gagner l'écart, porter le risque

Un teneur de marché affiche un bid et un ask et attend. Quand un vendeur frappe le bid et un acheteur prend l'ask, le teneur achète bas et vend haut et garde l'écart. C'est le revenu. Le coût est le risque d'inventaire : entre les deux transactions, le teneur détient une position dont la valeur varie avec le prix, et il est plus souvent exécuté par quelqu'un qui en sait plus (sélection adverse) que par quelqu'un qui n'en sait pas. Bien tenir un marché, c'est équilibrer des cotations assez serrées pour être touchées souvent et assez larges pour rémunérer le risque, tout en gardant l'inventaire proche de zéro.

# The income: buy at the bid, sell at the ask, keep the spread
bid, ask, size = 99.95, 100.05, 100
cash = -bid * size + ask * size
print(round(cash, 2))                                # 10.0    a round trip of 100 shares earned the 0.10 spread

# The risk: between the two trades the price moves against the inventory
inventory, mid_after = 100, 99.80
print(round(inventory * (mid_after - bid), 2))       # -15.0   the position lost more than the spread would have earned

Avellaneda-Stoikov : où coter

Le modèle d'Avellaneda-Stoikov donne une réponse en forme close. D'abord, le prix de réservation r = mid - q * gamma * sigma^2 * tau est l'endroit où le teneur serait content de négocier étant donné l'inventaire q : un teneur long (q > 0) doit valoriser l'actif sous le mid et coter plus bas pour attirer des acheteurs, un teneur short doit coter plus haut. L'aversion au risque gamma, la volatilité sigma et le temps restant tau fixent l'intensité de l'inclinaison. Ensuite, l'écart optimal gamma * sigma^2 * tau + (2 / gamma) * ln(1 + gamma / k) a deux parties : une charge pour le risque de garder la position jusqu'à l'horizon, et un terme pour le flux d'ordres, où k dit à quelle vitesse les exécutions se tarissent quand on cote plus loin du mid. Les cotations sont r - spread/2 et r + spread/2.

# The reservation price leans against inventory: long -> quote lower, short -> quote higher
def reservation_price(mid, inventory, gamma, sigma, tau):
    return mid - inventory * gamma * sigma ** 2 * tau

for inventory in (-50, 0, 50):
    print(inventory, round(reservation_price(100.0, inventory, 0.1, 0.2, 1.0), 4))
# -50 100.2   short: quote higher to buy back
#   0 100.0   flat: centred on the mid
#  50  99.8   long: quote lower to sell
# The optimal spread and the two quotes around the reservation price
import math

def optimal_spread(gamma, sigma, tau, k):
    return gamma * sigma ** 2 * tau + (2 / gamma) * math.log(1 + gamma / k)

def make_quotes(mid, inventory, gamma, sigma, tau, k):
    centre = mid - inventory * gamma * sigma ** 2 * tau
    half = optimal_spread(gamma, sigma, tau, k) / 2
    return centre - half, centre + half

print(round(optimal_spread(0.1, 0.2, 1.0, 1.5), 4))                       # 1.2948
bid, ask = make_quotes(100.0, 0, 0.1, 0.2, 1.0, 1.5)
print(round(bid, 4), round(ask, 4))                  # 99.3526 100.6474   symmetric around the mid when flat
bid, ask = make_quotes(100.0, 50, 0.1, 0.2, 1.0, 1.5)
print(round(bid, 4), round(ask, 4))                  # 99.1526 100.4474   long 50: both quotes move down by 0.2

Inventaire, exécutions et P&L

Suivez l'état par deux nombres, l'inventaire et la trésorerie. Quand le bid est touché, vous avez acheté : l'inventaire monte et la trésorerie baisse de price * quantity ; quand l'ask est touché, vous avez vendu : l'inverse. Votre richesse à tout instant est la trésorerie plus l'inventaire valorisé au mid, cash + inventory * mid, et sa variation est votre P&L. Ce P&L vient de l'écart capté et des mouvements de prix pendant la détention d'inventaire ; coter autour du prix de réservation ramène l'inventaire vers zéro car le côté qui le réduit devient plus attractif. Les limites comptent en pratique : un plafond strict d'inventaire, une pause dans les marchés rapides, et la vérification à chaque cotation que le bid est sous l'ask.

# Booking fills and marking the book to market
def apply_fill(state, side, price, quantity):
    inventory, cash = state
    if side == "bid":
        return inventory + quantity, cash - price * quantity
    return inventory - quantity, cash + price * quantity

state = (0, 0.0)
state = apply_fill(state, "bid", 99.95, 100)         # our bid is hit: we buy 100
print(state)                                         # (100, -9995.0)
state = apply_fill(state, "ask", 100.05, 100)        # our ask is hit: we sell 100
print(state)                                         # (0, 10.0)   flat again, and the spread is the profit
inventory, cash = apply_fill(state, "bid", 99.9, 50)
print(round(cash + inventory * 99.9, 2))             # 10.0   marked at the price we just paid, the equity is unchanged
Un modèle est un point de départLe modèle suppose un prix mid en marche aléatoire, une volatilité constante et une courbe d'arrivée d'ordres exponentielle lisse. Les vrais marchés ont des files d'attente, des pas de cotation, des frais et rabais, des sauts, et des traders informés qui prennent les cotations périmées. Voyez les formules comme une façon de raisonner sur le sens de chaque effet (inventaire long : coter plus bas ; plus de volatilité : plus large), puis calibrez et testez sur des données enregistrées dans un simulateur avant de risquer de l'argent, avec des limites strictes qu'aucun modèle ne peut contourner.
  • Un teneur de marché gagne l'écart mais porte un risque d'inventaire et de sélection adverse ; le but : assez serré pour négocier, assez large pour être payé, inventaire proche de zéro.
  • Le prix de réservation mid - q * gamma * sigma^2 * tau s'appuie contre l'inventaire ; l'écart gamma * sigma^2 * tau + (2/gamma) ln(1 + gamma/k) croît avec la volatilité pondérée par l'aversion au risque et le temps.
  • Suivez inventaire et trésorerie, valorisez au mid, imposez des limites d'inventaire strictes, et testez la stratégie dans un simulateur sur des données enregistrées avant de l'utiliser en réel.

À suivre : le modèle de Heston, une volatilité elle-même aléatoire.

Lab : ce qui est noté

  • S'appuyer contre l'inventaire avec reservation_price25 pts
  • Fixer la largeur avec optimal_spread25 pts
  • Coter les deux côtés avec make_quotes25 pts
  • Enregistrer une exécution avec apply_fill25 pts
Explorateur · lab-135-1
▾ lab-135-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-135-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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