σμ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.1·~1 時間上級

指値注文板

ロードマップ上の位置モジュール 9/11 · トピック 1/5
9.1 指値注文板
—ラボスコア— / 100合計XP—連続日数—

価格が形成される場所

画面上の価格は行列の結果です。取引所では、買い手と売り手が「300株を99.50で買う」「200株を100.00で売る」のような指値注文を出します。約定しなかったものは指値注文板(リミット・オーダーブック)に残ります。買い注文(ビッド)は最高価格が最良、売り注文(アスク)は最低価格が最良です。取引は、新しく来た注文が待っている注文とぶつかったときに成立します。これまでのトピックで価格、スプレッド、スリッページ、インパクトと呼んだものはすべてこの構造から生まれます。

# A side of the book as a dict {price: total size}
bids = {99.5: 300, 99.4: 500, 99.0: 1200}
asks = {100.0: 200, 100.1: 400, 100.5: 800}

best_bid, best_ask = max(bids), min(asks)
print(best_bid, best_ask)                                 # 99.5 100.0
print(round(best_ask - best_bid, 2), (best_bid + best_ask) / 2)   # the spread 0.5 and the mid 99.75

スプレッド、仲値、板の厚み

最良買い気配(ベストビッド)と最良売り気配(ベストアスク)が板の頂点です。その差がスプレッドで、すぐに取引するための価格です。アスクで買ってビッドで売り戻す買い手はスプレッド分を失います。2つの中間が仲値で、公正価格の標準的な目安です。板の厚みは各価格水準とその先に並ぶ数量で、価格が動くまでにどれだけ取引できるかを示します。片側が空なら、その側には価格がまったくないので、スプレッドと仲値は未定義で、ゼロではなく None です。

def best_bid_ask(bids, asks):
    return (max(bids) if bids else None, min(asks) if asks else None)

def spread_and_mid(bids, asks):
    bid, ask = best_bid_ask(bids, asks)
    if bid is None or ask is None:
        return None, None
    return ask - bid, (bid + ask) / 2

print(spread_and_mid({99.5: 300, 99.4: 500}, {100.0: 200, 100.1: 400}))      # (0.5, 99.75)
print(spread_and_mid({}, {100.0: 200}))                                       # (None, None): one side is empty

過去を変えずに注文を追加する

新しい指値注文は、既存の価格水準に加わってその合計数量を増やすか、新しい水準を作ります。これを古い辞書を書き換えるのではなく新しい辞書を返す関数として書くと、注文前の板のスナップショットが残るという実用上の利点があります。テストや再生に役立ちます。dict(side) でコピーし、コピーを変更して返します。引数を黙って書き換える関数は、同じ板を2か所で使うとバグの典型的な原因になります。

def add_limit_order(side, price, size):
    new = dict(side)                                   # a copy: the original book is not touched
    new[price] = new.get(price, 0) + size
    return new

bids = {99.5: 300, 99.4: 500}
after = add_limit_order(bids, 99.5, 100)               # joins the existing level
after = add_limit_order(after, 99.3, 50)               # opens a new level
print(after)                                           # {99.5: 400, 99.4: 500, 99.3: 50}
print(bids)                                            # {99.5: 300, 99.4: 500}  unchanged

成行注文を約定させる

成行の買いは、売り板を最安値から順に上へ取っていきます。最初の水準を全部、次に2番目と、注文が埋まるか板が空になるまで進みます。ソートした価格をループし、各水準で min(left, size_at_price) を取り、約定を記録し、完全に取ったら水準を削除、一部なら数量を減らします。約定リストは注文の約定のトピックで見た平均価格を与え、残った板はあなたの取引後の市場、つまり上が薄くなり最良売り気配が悪化した板です。

def match_market_buy(asks, size):
    remaining = dict(asks)
    fills = []
    left = size
    for price in sorted(asks):                          # cheapest first
        if left <= 0:
            break
        take = min(left, asks[price])
        fills.append((price, take))
        left -= take
        if take == asks[price]:
            del remaining[price]                        # the level is used up
        else:
            remaining[price] = asks[price] - take
    return fills, remaining

asks = {100.0: 200, 100.1: 400, 100.5: 800}
fills, remaining = match_market_buy(asks, 500)
print(fills)                                            # [(100.0, 200), (100.1, 300)]
print(remaining)                                        # {100.1: 100, 100.5: 800}
実際の板は行列である価格ごとの合計数量の辞書は行列を隠しています。各水準では注文が到着順(価格・時間優先)に約定し、その行列の中で自分の注文がどこにあるかで約定するかどうかが決まります。実際のフィードでは注文の取消や変更もあり、これがメッセージ量の大半を占めます。ここでの集約された板は、厚み・スプレッド・インパクトを理解するための適切なモデルで、次のトピックの注文フロー不均衡の出発点です。
  • 各サイドは辞書 {price: size}。最良買いは max(bids)、最良売りは min(asks)。スプレッドは ask - bid、仲値はその平均で、片側が空ならどちらも None
  • 注文の追加は、辞書をコピーしてその価格に加え、コピーを返す。渡された板は変更されない
  • 成行買いはソートした売り板をたどり、水準ごとに min(left, size) を取り、使い切った水準は削除、一部なら減らす。実際の板には各水準に行列がある

次は注文フロー不均衡です。板の圧力を読んで次の値動きを先取りします。

ラボ:採点される内容

  • best_bid_ask で板の最良気配を見つける25点
  • spread_and_mid でスプレッドと仲値を測る25点
  • add_limit_order で指値注文を板に置く25点
  • match_market_buy で売り板を食っていく25点
エクスプローラー · lab-112-1
▾ lab-112-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-112-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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