σμ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 Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 9 · マーケットデータとマイクロストラクチャー›トピック 9.3·~1 時間上級

asyncioによる非同期処理

ロードマップ上の位置モジュール 9/11 · トピック 3/5
9.3 asyncioによる非同期処理
—ラボスコア— / 100合計XP—連続日数—

止まらずに待つ

データを扱うプログラムは、多くの時間を計算ではなく待機に費やします。価格サーバーの応答、注文の受付確認、ダウンロードの完了などです。10個の価格を1つずつ求めれば、10回続けて待つことになります。非同期コードでは、1つのスレッドの中で、リクエストを出して待っている間は脇に置き、別の処理を進められます。そのための Python の道具が asyncio で、async def と await の2つのキーワードでできています。

# 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、イベントループ

async def で定義した関数はコルーチン関数で、呼び出すとコルーチンオブジェクトが返り、本体は何も実行されません。中で await 何か と書くと、その何かが用意できるまでコルーチンを一時停止し、制御をイベントループに返します。その間にループは別のコルーチンを実行できます。asyncio.run(coro) はループを起動してコルーチンを最後まで実行します。await は async def の中でしか使えません。書き忘れが典型的な誤りで、答えの代わりにコルーチンが返ってきます。

# 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

エラー、タイムアウト、手で動かす

待機は失敗することがあります。応答しないリクエストは asyncio.wait_for(coro, timeout) で打ち切り、TimeoutError を送出させます。try / except の中の await は通常のコードと同じようにエラーを処理するので、リトライは待機する呼び出しを囲むループになります。内部では、コルーチンは1歩ずつ進められるものにすぎません。coro.send(None) は次の一時停止まで実行し、終了すると結果を持つ StopIteration を送出します。イベントループは、これを多数のコルーチンについて繰り返し続けるコードで、このラボのテストがあなたのコードを動かす方法でもあります。

# 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
並行性は並列性ではないasyncio は一度に1つのコルーチンしか動かさず、切り替わるのは await のところだけです。速くなるのはネットワーク待ちであって、重い計算ではありません。コルーチンの中の長い計算はループと他のすべてのコルーチンを止めます。計算は通常の関数で行い、コルーチンは待つ場面のために取っておきましょう。
  • async def はコルーチン関数を定義する。呼び出してもコルーチンが作られるだけで、まだ何も実行されない
  • await は結果が用意できるまで一時停止し、その間イベントループが他を実行する。async def の中でのみ使える
  • asyncio.run がループを起動し、gather が待ちを重ね、wait_for がタイムアウトを加える。エラーは通常の try / except で扱う

次はWebSocket によるストリーミングデータです。ティックのライブフィードをリアルタイムで受け取ります。

ラボ:採点される内容

  • get_price で1つの気配を待つ25点
  • get_prices でウォッチリストを取得する25点
  • retry_price でタイムアウトを乗り切る25点
  • drive でイベントループになる25点
エクスプローラー · lab-114-1
▾ lab-114-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-114-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-114-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける