【訂正・2026-08-29】 この記事の初出版には、実装していない内容が含まれていました。「シグナル発生から発注完了までを区間ごとに計測した(42ms / 310ms / 180ms)」「発注直前に価格を再取得し乖離0.3%で見送る層を入れた」「BOTの評価順序を毎サイクル ローテーションさせた」およびそれらに紐づく数値(平均スリッページ0.18%→0.09%、約定83件→61件、指値で0.02%・非約定27%)は、いずれも AutoTrader に存在しない実装・計測です。記事は自動生成パイプラインで作っており、実コードを参照せずに書かれたことが原因です。生成側は材料(実コード)を渡さないと書けない仕組みに直しました。以下は実装を確認して書き直した本文です。
約定スリッページを「記録するところまで」やった話
自動売買BOTでロジックを改善する前に、そもそも発注価格と約定価格がどれだけずれているかを知りたくなる。AutoTrader ではスリッページを1約定ごとに記録するところまでは入れてある。この記事では、その記録の仕組みと、記録しているだけで分析まで到達していない現状を書く。
スリッページの定義と記録場所
シグナルが持っていた価格と、実際に約定した価格の差を百分率で出している。
# スリッページ計算
slippage = 0.0
if signal.price and signal.price > 0 and filled_price:
slippage = abs(filled_price - signal.price) / signal.price * 100
絶対値を取っているので、有利方向にずれた場合も同じ値になる。方向を区別したいなら符号を残すべきだが、今は「どれだけずれたか」だけを見ている。
保存先は trades テーブルの列で、既定値は 0.0。
slippage: Mapped[float] = mapped_column(Float, default=0.0) # スリッページ(%)
既定 0.0 なので、signal.price が無い経路(成行で参考価格を持たない場合など)の行は 0 のまま入る。集計するときはここを除外しないと、平均が実際より小さく出る。この点は実際に集計を始めるときの落とし穴になるはずで、まだ踏んでいない。
計測しているレイテンシは1種類だけ
時間の計測は、ティッカー取得の応答時間だけ入れてある。
async def timed_get_ticker(self, symbol: str) -> tuple[dict, float]:
"""ティッカー取得 + レスポンス時間計測"""
start = time.time()
try:
ticker = await self.get_ticker(symbol)
elapsed_ms = (time.time() - start) * 1000
return ticker, elapsed_ms
except Exception as e:
elapsed_ms = (time.time() - start) * 1000
logger.error(f"ティッカー取得エラー ({elapsed_ms:.0f}ms): {e}")
raise
この値は異常検知に渡していて、遅いときに警告を出す。
is_slow, slow_msg = self.anomaly_detector.check_api_latency(
latency_ms, exchange_id=exchange_client.exchange_id
)
つまり測っているのは「取引所のティッカーAPIが遅いかどうか」であって、シグナル発生から発注完了までの内訳ではない。判定ロジックのループ時間も、発注APIの往復時間も、個別には測っていない。
やっていないこと(正直に)
-
区間ごとのレイテンシ計測: シグナル→指標計算→判定→発注 のような分解はしていない。コード中に
perf_counter/time.monotonicはテスト以外に存在しない - 発注直前の価格再取得と乖離チェック: そういう層は入っていない
- BOTの評価順序のローテーション: していない
- スリッページの集計・分析: 列に貯めているだけで、平均や分布を出す仕組みはまだ無い
書いていて気づいたが、「記録はしているが集計していない」というのが今の正確な状態で、これは分析の一歩手前で止まっているということでもある。列にデータは入っているので、集計自体はSQL一本でできる。次にやるならそこからで、そのときは既定 0.0 の行の扱いを最初に決める必要がある。
まとめ
スリッページは1約定ごとに trades.slippage に入っている。レイテンシはティッカー取得の応答時間だけを測って異常検知に使っている。区間ごとの分解や、スリッページの傾向分析はまだやっていない。自分のBOTで同じことをするなら、まず「発注時の参考価格を残す」ところから始めると、あとから集計できる形になる。