前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。
本稿は、暗号資産マーケット‐メイカー Bot(以下 MMBot)を Docker 環境で運用する際に遭遇した 「ログが出ない/接続箇所が特定できない」 問題を、最短で切り分けるまでの開発メモを整理したものです。
売買ロジックや実運用パラメータは伏せ、公開してもエッジを損なわない範囲の“開発ノウハウ”だけを共有します。
環境依存の落とし穴と対策
症状 | 原因 | 一発解決テクニック |
---|---|---|
DEBUG が標準出力に出ず、Docker logs が空 | loguru のデフォルトレベル=INFO | logger.add(lambda m: print(m, end="", flush=True), level="DEBUG") ✓ レベル昇格 ✓ バッファ即 flush |
grep では見えるはずの文字列がヒットしない | JSON シリアライズで改行が崩れる | まず 生ログ を見る → 必要なら jq -r '.message' で整形 |
WS ストリームが沈黙 | Subscribe 失敗 or 心拍欠如 | before/after ws_connect マーカー → 詰まり位置を 0.5 秒で特定 |
段階的な接続ヘルスチェック
① REST : ping instruments-info → OK? ② get_minQty: DBG before / after → 数値取得? ③ WS connect: DBG before / after → ハンドシェイク通過? ④ orderbook : HDLR ERR 例外ログ → パース成功?
REST → WS → パーサ の順に「パイプの詰まり」を潰すと、再現性のあるデバッグ手順になります。
計測用 METRIC 行の設計
10 秒間隔で 運用に直結しない抽象値 だけを残すことで、安全に統計が取れます。
METRIC spr_pct=<%> spr_ticks=<tick差> notional=<USD> loop_ms=<ms>
- パラメータ (
<%>
,<USD>
) は範囲化 or 正規化して保存 loop_ms
でボトルネック(DB I/O や API レイテンシ)を即検知
開発ログから得た学び — 2 時間のタイムライン
時刻 | アクション | 学び |
---|---|---|
T+0 min | Docker イメージ再構築 → mainnet へデプロイ | まずは現象再現 |
+10 min | REST 疎通テスト(aiohttp 5 s timeout) | 外形監視を最小実装しておくと安心 |
+15 min | DBG マーカーを 2 か所挿入 | レイヤごとに “光を当てる” |
+25 min | HDLR 例外捕捉 → メッセージ構造を確認 | 仕様変更耐性アップ |
+30 min | stdout シンクを DEBUG + flush | ログが「出ない」原因は大抵これ |
+35 min | METRIC 行でループ遅延を可視化 | 調整に必要なデータ構造が完成 |
今後の改善 TODO
項目 | 狙い |
---|---|
24 h ログ収集 → ヒートマップ | spread × fill の実測から 適切なしきい値 を導出 |
max_cycles = 0 | 長期計測モードへの切替え |
Watchdog + Slack 死活通知 | 稼働中断を 1 分以内に検知 |
CI に log-lint | level="DEBUG" や flush=True の抜けを自動チェック |
おわりに
今回のアップデートで 実行パイプラインの見える化 と 計測フレーム が整備されました。これにより、次フェーズの パラメータ最適化 を 「勘」ではなくデータドリブン で進められます。
同じ問題に悩む開発者の参考になれば幸いです。
👇ラジオで話したこと
きょうは 「MMBotを“静かに”24 時間まわすためのデバッグ&リアルタイム計測テク」 がテーマです。ポイントは ①.env
→tpl
→json
の安全な設定パイプライン、②Makefile 1行で Mainnet/Testnet を切り替える運用フロー、③Loguru で WARN だけ を抽出して tail 監視するログ設計、④スプレッドが狭い市場では「Fill 0 = 成功」と腹をくくるリスク管理、の 4 本柱です。
1. なぜ「静かな 24 h 監視」が必要?
- Mainnet 最小ロット(0.0001 BTC)でまず 1 bps だけ差し込むと、板が詰まっている日は 注文が一度も約定しない ことがあります。
- しかしこの “Fill 0” は 損しない という意味で成功シナリオ。— 市場が開くまで待てる「我慢強さ」を Bot 側で担保しておくわけです。 SQL Easy
2. .env
→ tpl
→ json
の安全パイプライン
.env.prod
に API キーやロットサイズを保存。config_mainnet.tpl
に${BYBIT_KEY_MAIN}
のようなプレースホルダを書く。- Makefile で
envsubst < config_mainnet.tpl > config_mainnet.json
— envsubst
はシェル組込みではなく GNU gettext
に付属するコマンドで、環境変数を一括展開します。 Stack Overflow
4. 生成後はすぐ .gitignore
に入っているか確認し、rm -f
で掃除。
3. Makefile ワンライナー運用
ENV ?= .env.test # “?=” は「未定義なら代入」の意 run: $(CONFIG) set -a && source $(ENV) && set +a && \ docker compose run --rm --entrypoint "" $(SERVICE) \ python -m MMbotbybit.MMbotbybit \ --lot $(LOT) --max_loss_usd $(LOSS)
set -a
(セット・ハイフン・エー) は 「読み込んだ変数をすべて export」 の意味。 Stack OverflowENV=.env.prod make run
と打てば 後勝ち ルールで.env.prod
が優先されます。
4. Loguru で WARN だけを追う
logger.add("logs/warn.log", level="WARNING", rotation="10 MB", enqueue=True) logger.add(lambda m: print(m, end="", flush=True), level="DEBUG")
flush=True
を付けないと DEBUG が バッファに溜まって Docker logs に流れません。 LoguruReal Python- 端末では
tail -F logs/warn.log
。-F
は “ファイル名の切替えも追従” の略なので、ローテーション後も自動で追い続けます。 prefetch.net
5. リアルタイム計測用 METRIC 行
METRIC spr_pct=0.12 spr_ticks=3 notional=24 loop_ms=87
- 10 秒おきに “抽象値” だけを残し、個別約定情報は書かないので流出リスクが低い。
loop_ms
が 500 ms を超えたら DB I/O やネット遅延を疑う。ログ設計の基本は「まず可視化,後で最適化」 です。 Better Stack
6. 24 h ランで詰まったポイント & 直し方
つまずき | 原因 | 即 fix |
---|---|---|
${…} が残る | .env.prod 未定義 → envsubst 空展開 | 変数を追加して再生成 |
JSONDecodeError | 数値を "50" とクオートしていた | 数値は裸の 50 にする Stack Overflow |
TypeError: '>' not supported | env が文字列で渡る | float(cfg["max_notional"]) でキャスト |
Fill が来ない | スプレッドが 0.1 bps と狭い | curl で板を取得し S_ENTRY を再調整 Bybit Exchange |
7. 今日の学び
- “何もしない” もテスト — スプレッドが狭い日はマッチしないのが正解。
- ログレベル分離 は tail × flush で 90 % 片付く。
- 環境変数を数字にする と JSON 生成エラーが減る。
- 小さいコミットを刻む と
git revert
で 10 分ロールバックが可能。 Stack Overflow
一言用語解説
用語 | 読み⽅ | ひと言で何? | ざっくり解説 |
---|---|---|---|
GNU | 「グヌー」 | “自由ソフト”の旗印 | GNU = “GNU’s Not Unix” の再帰略語。Linux カーネルと組み合せて OS が完成。 |
プレースホルダ | – | 「あとで値を入れる空席」 | 例:%s や ${S_ENTRY} 。実行時に実データと置換される。 |
後勝ちルール | 「あとがち」 | 同じ変数を後で再定義すると後が優先 | Makefile・シェルとも共通。.env → コマンド行上書きが代表例。 |
flush=True | 「フラッシュ トゥルー」 | ログを“即書き出し”モード | バッファを溜めずに標準出力へ強制送信。Docker logs が欠ける問題を解消。 |
METRIC 行 | 「メトリック」 | 統計用の専用ログ行 | METRIC spr=0.05 lot=0.001 のような計測データだけを 10 秒ごとに吐く仕組み。 |
抽象値 | 「ちゅうしょうち」 | “直接バレても困らない加工値” | 例:実残高でなく 正規化 した 0 〜 1 の値をログへ。安全に共有・統計できる。 |
I/O / DB I/O | 「アイオー」 | 入出⼒/データベース操作 | CPU ではなくディスク・ネットの遅延がボトルネックか判断するときに計測。 |
${VAR} | 「ダラー ブイエーアール」 | bash の変数展開 | envsubst でテンプレを実値に差し替える記法。 |
クオートする | 「クオート」 | 文字列を ' ' や " で囲む | 数値をクオートすると JSONDecodeError の原因になるので要注意。 |
curl | 「カール」 | HTTP を叩くコマンド | `curl -s |
tail -F | 「テイル エフ」 | “ファイルを追尾+切替え検知” | ログローテでファイル名が変わってもストリームが途切れない。 |
tail × flush | – | tail -F + flush=True の合わせ技 | 出し忘れ&読めないを同時に無くす王道セット。 |
JSON 生成エラー | – | JSONDecodeError / ValueError | ほぼ「数値をクオートした」「カンマ抜け」の 2 大原因。 |
git revert | 「ギット リバート」 | 特定コミット を“打ち消す”新コミット | 安全な巻き戻し。git revert HEAD~1 で 1 つ前を取り消し ⇒ 10 分ロールバック など即時復旧に使う。 |
最後まで聞いてくださってありがとうございました。
それではまた次回の放送でお会いしましょう。よだかでした。