Python × asyncio/仮想通貨 Bot 開発向け

最近はMMbot作りに多くの時間を割いています。その中で「非同期プログラミング」を活用しようと試みているのですが、これが本当に失敗・エラーの連続です。それでも、失敗とその原因を特定し解決することを通して、知見が貯まってきました。本記事では、その経験を通して得た学びをまとめました。
1.なぜ非同期は“事故り”やすいのか
非同期処理は 速度とスケーラビリティ をもたらす一方、
タスクの状態が見えにくくなる “ブラックボックス化” が避けられません。
仮想通貨 Bot では ─
- API レート制限 でタスクが詰まりやすい
- 価格急変 でミリ秒遅延が即 PnL へ直撃
結果として 発注遅延 → 価格乖離 → 損失拡大 の連鎖を起こします。
「転ばぬ先の計測」と「転んでも壊れない設計」が肝要です。
2.典型的な罠3選
# | 症状 | 原因イメージ | 回避ワザ |
---|---|---|---|
① 暗黙キャンセル | 1 つのタスクが例外で落ちた途端、他のタスクも静かに停止 | 旧式 asyncio.gather(..., return_exceptions=False) が例外を握り潰す | TaskGroup で例外を即バブルアップ |
② 競合条件 | REST が先に走り板在庫がズレる | 並列タスクに同期点が無い | 順序バリア or キュー化 |
③ イベントループ飢餓 | CPU 0 %なのに板更新が 10 秒止まる | I/O 待ちタスクが雪だるま化 → スケジューラ飢餓 | Back-Pressure を設計し列制限 |
Log Tips
task_id
と 経過時間(ms) を毎イベントで記録するだけで
原因特定に掛かる時間は体感 1/2 になります。
3.抜け道と実装ヒント
3-1.TaskGroup で「生死」を束ねる
import asyncio async def main(): async with asyncio.TaskGroup() as tg: tg.create_task(worker_A()) tg.create_task(worker_B()) # ...
どれかが例外 ⇒ 全キャンセル ⇒ まとめて raise。
“片肺運転” を防ぎ、アラート集約 も容易。
3-2.Shield + Timeout で二重防御
try: await asyncio.wait_for( asyncio.shield(critical_io()), # ← キャンセル耐性 timeout=<YELLOW-MASKED>) except asyncio.TimeoutError: logger.warning("critical_io timed-out")
「暴走させず、放置もしない」 を両立。
3-3.Back-Pressure を前提に設計する
queue = asyncio.Queue(maxsize=100) # 上限を必ず設定
溢れたら 古いデータ破棄 または スロットル。
「全部処理する」幻想を捨て、遅延スパイラル を断ち切る。
3-4.Kubernetes 連携 Tips(GREEN 抜粋)
Probe | 目的 | 実装ポイント |
---|---|---|
readinessProbe | ループ健全性 | bot が応答しても 動けている か確認 |
livenessProbe | ハング検出 | 応答停止 ⇒ 自動再起動 |
PDB | ノード再起動対策 | minAvailable: 1 で穴あきを防止 |
4.今日から出来るチェックリスト
- ログ に task_id / latency を付与
- 重要処理を TaskGroup + timeout に移行
- キューの maxsize を明示し、溢れ対策を決める
readinessProbe
で「生きてるふり」を禁止
5.まとめ & 次のステップ
速度 × 安全 を両立したいなら、
「失敗を前提に設計」→「失敗を観測しやすくする」 が最短ルート。
次回は CI パイプライン 上で pytest-asyncio
を回し、
“手動デバッグの沼” から抜け出す工程を解説予定です。
👇ラジオで話したこと
「はじめての非同期プログラミング ─ つまずきポイントと安全運転のコツ」
【オープニング】
こんにちは、よだかです。
きょうは「非同期(ひどうき)プログラミングって何? そして初心者がハマりやすい落とし穴は?」を、なるべく専門用語をかみ砕いてお話しします。Python で仮想通貨 Bot を組みはじめたばかりの方に向けて、基本の“転ばぬ先の杖”をまとめました。
1️⃣ そもそも非同期って?
- イメージ:
キッチンで「煮込み」「炒め」「オーブン」を同時進行する感じ。
同じコンロを 1 つしか持たないより、作業が重ならない分スピードアップ。 - Python での道具
asyncio
の イベントループ が“キッチンの指揮官”。
料理(=タスク)に「次ここまでやったら声かけて!」と指示し、空いた時間で別の料理を進めます。
2️⃣ 初心者がつまずく三大ポイント
つまずき | どうなる? | 原因イメージ |
---|---|---|
① 途中で止まる | プログラムが固まって返事なし | 片方の鍋 が焦げて火災報知器が鳴ったのに、指揮官が気づかない |
② 順番がぐちゃぐちゃ | データが最新じゃないまま発注 → ミス取引 | 材料(板情報)を入れ替える前に味付け(注文)しちゃう |
③ 渋滞して遅い | CPU はヒマなのに反応が 10 秒遅れる | 注文票が山積み。レジ 1 台でさばき切れず行列 |
✔ チェック方法
画面に「いつ・どの料理が動いたか」を 時刻つき でプリントするだけでも、原因の八割は見えます。
3️⃣ 安全運転のコツ
- 「まとめて面倒を見る箱」を作る
- Python3.11 以降は
TaskGroup
という“お弁当箱”が標準装備。 - どれか 1 品が焦げたら、箱ごとコンロから下ろす → 火を止めて知らせる。
- Python3.11 以降は
- タイマーを必ずセット
- 「○秒たっても戻って来なければ強制終了」のキッチンタイマーを、
ぜんぶの作業に付ける。放置しない・暴走させない。
- 「○秒たっても戻って来なければ強制終了」のキッチンタイマーを、
- 皿洗い待ち行列を制限
- 受信データを貯める “キュー” に上限を決める。
- 溢れたら古い皿を捨てる or 料理スピードを自動で落とす。
- 健康診断を入れる(Kubernetes を使う人向け)
- 生存チェック → プログラムが 応答しているか を外から確認。
- 健康チェック → 応答はあるけど中身が壊れていないか?を追加で確認。
🛑 注意: ここで紹介していない具体的な YAML やパラメータ設定は、 公開ポリシー(YELLOW)に当たる部分なので公開資料ではマスクしています。ご理解を!
4️⃣ 今日からできる3ステップ
- print でもいいから すべてのタスク開始・終了を時刻つきでログに残す
- 重要そうな
await
の前後に タイマー を置く - 受信データをためる
queue
に 最大サイズ を決める(例:100 件)
これだけで「知らぬ間に固まっていた」「いつのまにか遅かった」を激減できます。
【エンディング】
非同期は便利ですが、“並行する=失敗も増える”という裏面があります。
だからこそ 「転ぶ前提でガードを付ける」 のが本当の時短。
次回は、自動テストで「徹夜デバッグ」を卒業する話を予定しています。
それではまた!