σμ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 ロードマップ›モジュール 6 · バックテストと取引コスト›トピック 6.6·~55 分上級

現実的な注文約定

ロードマップ上の位置モジュール 6/11 · トピック 6/6
6.6 現実的な注文約定
—ラボスコア— / 100合計XP—連続日数—

約定は確実ではない

素朴なバックテストは、どんなサイズの注文もデータ上の価格で約定すると仮定します。実際の注文には3つの問いがあります。板の厚さ:成行注文はまず最良の売り気配を取り、次の気配へと進むので、大きな注文ほど悪い平均価格を払います。指値に届いたか:指値注文は指定した価格かそれより有利な価格で約定しますが、取引がそこに届いた場合に限ります。どれだけ取れるか:大きな注文はバーの出来高の一部しか占められず、時間をかけて分割して約定します。

def fill_market_order(book, quantity):
    remaining = quantity
    cost = 0.0
    filled = 0
    for price, size in book:                      # cheapest offer first
        if remaining <= 0:
            break
        take = min(size, remaining)
        cost += take * price
        filled += take
        remaining -= take
    if filled == 0:
        return 0.0, 0
    return cost / filled, filled

book = [(50.00, 300), (50.02, 500), (50.05, 1000)]
print(fill_market_order(book, 200))        # (50.0, 200)           inside the first level
print(fill_market_order(book, 600))        # (50.01, 600)          300 at 50.00 and 300 at 50.02
print(fill_market_order(book, 5000))       # (50.0333, 1800)      the whole book; 3,200 shares stay unfilled
# The average price and the slippage against the best offer grow with the size
def average_price(book, quantity):
    remaining, cost, filled = quantity, 0.0, 0
    for price, size in book:
        take = min(size, remaining)
        cost += take * price
        filled += take
        remaining -= take
    return cost / filled

book = [(50.00, 300), (50.02, 500), (50.05, 1000)]
for quantity in (100, 300, 800, 1800):
    avg = average_price(book, quantity)
    print(quantity, round(avg, 4), round((avg - 50.00) / 50.00 * 10000, 2), "bps above the best offer")

指値注文とギャップ

バーのデータ(始値・安値・高値)では、バー内の値動きの経路は分からず、極値だけが見えます。L の買い指値は、バーの安値が L 以下に届けば約定します。価格は L ですが、バーが L より下で始まった場合は、市場があなたの指値を飛び越えたので、より有利な始値で約定します。売り指値はその鏡像です。ただ触れただけの指値は、手前の待ち行列が先に処理されるため、現実には約定しないこともあり、これは楽観的な上限です。

def limit_fill(side, limit, bar_open, bar_low, bar_high):
    if side == "buy" and bar_low <= limit:
        return min(bar_open, limit)             # a gap below the limit fills at the better open
    if side == "sell" and bar_high >= limit:
        return max(bar_open, limit)
    return None

print(limit_fill("buy", 49.95, 50.10, 49.90, 50.20))   # 49.95   traded down through the limit
print(limit_fill("buy", 49.95, 49.90, 49.80, 50.20))   # 49.9    opened below: filled at the open
print(limit_fill("buy", 49.95, 50.10, 50.00, 50.20))   # None    never reached
print(limit_fill("sell", 50.15, 50.00, 49.90, 50.20))  # 50.15

大きな注文をこなす

多くの執行デスクは参加率の上限を使います。たとえば出来高の10%までしか取らないというものです。4万株が取引されたバーなら4,000株(切り捨て)です。5,000株の注文は1本ではなく2本のバーが必要になります。バーをたどる処理は残りの数量を持ち回すループで、各バーで min(remaining, int(volume * participation)) を約定し、注文が終わった後のバーでは何も約定しません。

def partial_fill(quantity, bar_volume, participation):
    return min(quantity, int(bar_volume * participation))

def fill_over_bars(quantity, volumes, participation):
    remaining = quantity
    fills = []
    for volume in volumes:
        filled = partial_fill(remaining, volume, participation)
        fills.append(filled)
        remaining -= filled
    return fills

fills = fill_over_bars(5000, [40000, 30000, 80000, 100000], 0.1)
print(fills, sum(fills))                      # [4000, 1000, 0, 0] 5000
約定モデルが誠実さを決めるここでの単純化はどれも同じ方向に偏っています。指値は触れれば約定する、自分は市場を動かさない、出来高は十分ある、という仮定です。結果が約定に依存するほど、悲観的なルール(価格が指値を通過した場合のみ約定、参加率の上限を下げる)で試し、優位性が残るか確かめるべきです。
  • 成行注文は板を気配ごとにたどる。平均価格は出来高加重で、サイズが大きいほど悪化し、板が薄ければ一部しか約定しない
  • 指値はバーが届けば約定し、ギャップで飛び越えた場合は始値で約定する。触れただけでは現実には約定しないこともある
  • 参加率の上限で各バーは int(volume * participation) までに制限される。ループが残りの数量をバーからバーへ持ち回す

次はCAPMとベータです。リターンのうち市場に由来する部分を測ります。

ラボ:採点される内容

  • fill_market_order で板をたどる25点
  • limit_fill で指値に届いたかを判定する25点
  • partial_fill で出来高を守る25点
  • fill_over_bars で注文を時間をかけて執行する25点
エクスプローラー · lab-92-1
▾ lab-92-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-92-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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