σμ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 9 · Données de marché et microstructure›Sujet 9.3·~1 hAvancé

Code asynchrone avec asyncio

Position dans le parcoursModule 9 sur 11 · Sujet 3 sur 5
9.3 Code asynchrone avec asyncio
—Score du lab— / 100XP total—Série—

Attendre sans rester immobile

La plupart du temps, un programme de données ne calcule pas, il attend : la réponse d'un serveur de prix, l'accusé d'un ordre, la fin d'un téléchargement. S'il demande dix prix un par un, il attend dix fois de suite. Le code asynchrone permet à un programme de lancer une requête, de la mettre de côté pendant qu'elle attend et de passer à une autre, le tout dans un seul fil. L'outil Python pour cela est asyncio, construit sur deux mots-clés, async def et await.

# A coroutine: calling it does nothing until it is awaited
import asyncio

async def get_price(symbol):
    await asyncio.sleep(0.01)                  # stands in for waiting on a network answer
    return {"AAPL": 184.25, "MSFT": 410.5}[symbol]

coro = get_price("AAPL")
print(type(coro).__name__)                     # coroutine  nothing has run yet
print(asyncio.run(coro))                       # 184.25     the event loop runs it

async def, await et la boucle d'événements

Une fonction définie avec async def est une fonction coroutine : l'appeler renvoie un objet coroutine et n'exécute rien de son corps. À l'intérieur, await quelquechose met la coroutine en pause jusqu'à ce que quelquechose soit prêt et rend la main à la boucle d'événements, qui peut exécuter d'autres coroutines entre-temps. asyncio.run(coro) démarre une boucle et exécute une coroutine jusqu'au bout. await ne s'emploie que dans une async def ; l'oublier est l'erreur classique, car l'appel renvoie alors une coroutine au lieu de la réponse.

# Awaiting ten prices one after another waits ten times; gather overlaps the waits
import asyncio
import time

async def get_price(symbol):
    await asyncio.sleep(0.05)
    return len(symbol) * 10.0

async def one_by_one(symbols):
    return [await get_price(s) for s in symbols]

async def together(symbols):
    return await asyncio.gather(*(get_price(s) for s in symbols))

symbols = ["AAPL", "MSFT", "NVDA", "TSLA", "AMZN", "META", "GOOG", "NFLX", "ORCL", "INTC"]
t0 = time.perf_counter()
asyncio.run(one_by_one(symbols))
slow = time.perf_counter() - t0
t0 = time.perf_counter()
asyncio.run(together(symbols))
fast = time.perf_counter() - t0
print(f"one by one: {slow:.2f} s, gathered: {fast:.2f} s")      # about 0.50 s against 0.05 s
print(slow > 4 * fast)                                           # True

Erreurs, délais et faire à la main

L'attente peut échouer. Une requête qui ne répond jamais doit être coupée avec asyncio.wait_for(coro, timeout), qui lève TimeoutError ; un await dans un try / except gère les erreurs exactement comme du code ordinaire, donc une nouvelle tentative est une boucle autour d'un appel attendu. Sous le capot, une coroutine est simplement quelque chose qu'on fait avancer : coro.send(None) l'exécute jusqu'à sa prochaine pause et, quand elle se termine, lève StopIteration portant le résultat. Une boucle d'événements est le code qui répète cela pour de nombreuses coroutines à la fois, et c'est aussi ainsi que les tests de ce labo exécutent les vôtres.

# Stepping a coroutine by hand: this is what an event loop does for many of them
class Pause:
    def __await__(self):
        yield

async def job():
    await Pause()
    await Pause()
    return 184.25

coro = job()
steps = 0
while True:
    try:
        coro.send(None)
    except StopIteration as stop:
        print("finished with", stop.value)                  # finished with 184.25
        break
    steps += 1
print("paused", steps, "times")                              # paused 2 times
# A timeout turns a slow answer into an error you can handle, then retry
import asyncio

async def slow_feed():
    await asyncio.sleep(1.0)
    return 184.25

async def main():
    for attempt in range(1, 4):
        try:
            return await asyncio.wait_for(slow_feed(), timeout=0.01)
        except TimeoutError:
            print(f"attempt {attempt} timed out")
    return None

print(asyncio.run(main()))                                   # three timeouts, then None
Concurrence n'est pas parallélismeasyncio n'exécute qu'une coroutine à la fois et ne change qu'à un await. Il accélère l'attente du réseau, pas l'arithmétique lourde : un long calcul dans une coroutine bloque la boucle et toutes les autres coroutines avec. Faites le calcul dans des fonctions ordinaires et gardez les coroutines pour l'attente.
  • async def définit une fonction coroutine ; l'appeler crée seulement une coroutine, rien ne s'exécute encore.
  • await met en pause jusqu'à ce que le résultat soit prêt et laisse la boucle d'événements en exécuter d'autres ; il ne marche que dans async def.
  • asyncio.run démarre la boucle, gather chevauche les attentes, wait_for ajoute un délai ; les erreurs se gèrent avec un try / except ordinaire.

À suivre : le flux de données avec les websockets, recevoir un flux de ticks en direct au fur et à mesure.

Lab : ce qui est noté

  • Attendre une cotation avec get_price25 pts
  • Récupérer une liste de suivi avec get_prices25 pts
  • Survivre à un délai dépassé avec retry_price25 pts
  • Jouer la boucle d'événements avec drive25 pts
Explorateur · lab-114-1
▾ lab-114-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-114-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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