Bot

開発記録#205(2025/5/2)MMbot開発ログ13「Docker-MMBotが動くまでにハマった7つの落とし穴と抜け道」

2025年5月2日

前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。

**この記事は “自宅 PC × Docker Compose × Python‐bot” を初めて回し始めたときに私が **“ハマって → 抜け出した” トラブル集 です。
同じ所ではまった未来の自分&読者さんのために、

  • 何が起きた?(症状)
  • なぜ?(原因)
  • どう直した?(応急処置)
  • 次は迷わないコツ(再発防止)
    をまとめました。
    ※ API キーや Slack URL など “秘密情報” は すべて伏せ字 にしてあります。

1. config_mainnet.json が「フォルダ扱い」になって読めない

内容
症状コンテナ内で cat /app/config_mainnet.jsonIs a directory と怒られる
原因ホスト側に 同じ名前のディレクトリ を作ってしまい、
Docker が “ディレクトリ over ファイル” でマウントした
応急処置bash mv config_mainnet.json config_mainnet_backup_dir
→ 改めて JSON ファイルを作成
もう迷わないコマンドを習慣に:
ls -ld config_mainnet.json → 先頭が -rw ならファイル、drw ならフォルダ

2. --profile オプションが効かない

内容
症状unknown flag: --profile
原因Docker Compose v2 は
docker compose --profile live up -d順番じゃないと読んでくれない
応急処置コマンドを並び替え
もう迷わないbash alias dcu='docker compose --profile live up -d'

3. ソースを書き換えても古いコードが動く(.pyc の罠)

内容
症状直したはずのバグがコンテナ内で再現…
原因Python は先に .pyc を読む。
タイムスタンプが古いと新ファイルを無視することも
応急処置bash find . -name '*.pyc' -delete
touch *.py で更新時間を前に出す
もう迷わない開発中は環境変数 PYTHONDONTWRITEBYTECODE=1 に。
.dockerignore__pycache__/ を追加

4. from __future__ import annotations なのに SyntaxError

内容
症状「future import はファイル冒頭に置け」と怒られる
原因#!/usr/bin/env… と docstring の さらに後ろ に書いていた
応急処置docstring の直後 に移動
もう迷わないVS Code / PyCharm の Linter を有効化(位置ズレで即警告)

5. envsubst で作るはずの設定ファイルが空

内容
症状/app/config_mainnet.json が 0 byte
原因同じパスを volume マウントすると、
生成された直後にホストの空ファイルで上書き
応急処置テンプレートは /tmp/config.tpl として 読み取り専用 でマウントし、
entrypoint で /app/config_mainnet.json を生成
黄金パターン```yaml
volumes:./config.tpl:/run/config.tpl:ro
entrypoint: >
/bin/sh -c "
envsubst < /run/config.tpl > /app/config_mainnet.json &&
exec python …
"``` |

6. Bybit エラー retCode 110007 (InsufficientAB)

内容
症状注文が弾かれ “証拠金不足” メッセージ
原因MAX_NOTIONAL_USD を超えるロット/レバが低すぎ
応急処置ロットを半分 (0.0005) にし、環境変数 DEFAULT_LEVERAGE=10 を設定
もう迷わない注文前に “余力 API” を叩いてログ出力 → 門前払い するコードを仕込む

7. Slack 通知が毎回 “Cancel” になる

内容
症状取引所では約定しているのに Slack では全部 Cancel
原因約定チェックを Order List REST でポーリング。
実際の Fill は Execution WebSocket で流れてくる
応急処置現在 WS 版に書き換え中(TODO)
もう迷わない原則:
「口座残高が動くイベント=WebSocketで即時検知」

迷ったら使う“5 ステップ”デバッグ

  • 1.症状の種類をラベル付け(ビルド?ランタイム?API?)
  • 2.最小再現を 1 コマンドに↓
docker compose exec mmbot python - <<'PY'
# 20 行以下で症状を再現
PY
  • 3.ログを絞る
docker compose logs -f mmbot | grep -E 'ERROR|retCode'
  • 4.ホストとコンテナの差分を疑う
docker compose exec mmbot env | sort | head
docker inspect mmbot-mmbot-1 --format '{{ json .Mounts }}'
  • 5.1 問題 = 1 コミット / 1 メモ
    Notion や Git の PR に “現象→原因→Fix” を残すと未来の自分が助かる

そもそもトラブルを減らす仕掛け

仕組み何が嬉しい?
CI で docker build && pytest変な import や空 JSON をプッシュ前に検知
.dockerignore を整理古い .pyc.env がイメージに混入しない
Secrets Manager or .env ファイルAPI キーをシェル履歴に残さない
Makefile / bash aliasmake up, make logs でタイプ量 1/3
Runbook「110007 が出たら余力チェック」など 判断を手順化

次の To Do

  1. Execution WS で約定検知 → Slack 完全同期
  2. DB に正しく保存 → PnL 計算ロジックをテスト
  3. パラメータ (lot / s_entry) チューニング
  4. GitHub Actions で CI 設定 → “壊しても怖くない” フローへ

このサイクルを回せば、ほとんどのトラブルは 「仕組みで未然防止 or 1 コマンドで再現&修正」 できるようになります。
それでは楽しい自動売買ライフを!🚀

👇ラジオで話したこと

MMBot開発ログ13|Docker MMBotが動くまでにハマった7つの落とし穴と抜け道


こんにちは、よだかです。
今回は、自分がDocker上でMMBotを初めて回したときにぶつかった
**「7つのトラブルと、それをどう乗り越えたか」**をまとめてお届けします。

どれも初心者が最初の一歩で引っかかりやすい落とし穴ばかり。
将来の自分や、これからBot開発に入る方へのメモとしても残しておきたい内容です。


1️⃣ config_mainnet.json が「フォルダ扱い」になった

症状
Dockerの中で cat config_mainnet.json を実行したら
「Is a directory」って怒られる

原因
ホスト側で config_mainnet.json/ というフォルダを作ってしまっていた
Dockerはファイルとディレクトリが同じ名前なら、ディレクトリを優先してマウントしてしまう仕様です。

抜け道
ディレクトリを退避して、ちゃんとした .json ファイルを作り直す。

再発防止のコツ

ls -ld config_mainnet.json

で事前に確認:先頭が -rw ならファイル、drw ならフォルダ。


2️⃣ --profile オプションが効かない

症状
docker compose --profile live up -d を打ったのに「unknown flag」と出る。

原因
Compose v2では --profile の位置がコマンドの途中にあると無効
正解はコレ:

docker compose --profile live up -d

抜け道
間違えないようにエイリアスを登録:

alias dcu='docker compose --profile live up -d'

3️⃣ ソースを書き換えても反映されない(.pycの罠)

症状
修正したはずのバグが、Dockerコンテナ内で再現され続ける。

原因
Pythonは .pyc という中間ファイルを自動で読み込む。
古い.pycファイルが残っていると、ソース変更が無視されることがある。

対処法

find . -name '*.pyc' -delete
touch *.py

予防策

  • .dockerignore__pycache__/ を追加
  • 環境変数で .pyc を書かせない:
PYTHONDONTWRITEBYTECODE=1

4️⃣ future import の位置エラー

症状
from __future__ import annotations を書いたら、SyntaxError が出た

原因
この行はファイルの先頭じゃないとダメ。
私は #!/usr/bin/env python や docstring の後ろに書いていた。

抜け道
"""モジュール説明""" の直後、すぐに future import を書く。

コツ
VSCodeやPyCharmの**Linter(構文チェッカー)**をONにしておくと、
この位置ズレも即座に警告してくれる。


5️⃣ envsubst で作る設定ファイルが 0 バイト

症状
Bot起動時に /app/config_mainnet.json を作るはずが、空っぽ(0バイト)

原因
ホスト側と同じパスをvolumeでマウント → マウント元が空ファイルだと、生成した内容が上書きされて消える

抜け道
テンプレートは /run/config.tpl にしてreadonlyでマウントし、
起動時に envsubst/app/config_mainnet.json を生成。

volumes:
  ./config.tpl:/run/config.tpl:ro

entrypoint: >
  /bin/sh -c "
    envsubst < /run/config.tpl > /app/config_mainnet.json &&
    exec python …
  "

6️⃣ retCode 110007:証拠金不足エラー

症状
注文が通らず「InsufficientAB」というエラーが返る。

原因

  • レバレッジが低すぎた
  • ロットが MAX_NOTIONAL_USD を超えていた

抜け道

  • ロットを0.0005 BTCに下げる
  • 環境変数でレバレッジを10倍に:
DEFAULT_LEVERAGE=10

再発防止
発注前に「余力チェックAPI」を叩くようにコードを修正。


7️⃣ Slack 通知が全部 Cancel 扱い

症状
Slack通知が全部「Cancel」になるのに、実際は約定していた。

原因
Botは /order/list をポーリングして約定を見ていたが、
本物のFillはExecution WebSocket(WS)からしか来なかった

対応中
現在、約定検知をREST → WebSocketに切り替え中。

原則として覚えること

「口座残高が変わるような重要イベントは、WebSocketでリアルタイム検知するのが基本」


💡 迷ったら使う「5ステップ」デバッグ術

  1. 症状にラベルをつける(ビルド?ランタイム?API?)
  2. 最小再現コードを1コマンドにする
  3. ログを絞るdocker compose logs -f mmbot | grep 'ERROR|retCode'
  4. ホスト/コンテナの差分をチェックenvdocker inspect
  5. 1問題=1コミット/1メモ(NotionやPRに記録)

🛡️ トラブルを減らす“仕掛け”たち

仕組み効果
CIで docker build && pytestプッシュ前に構文&動作チェック
.dockerignore の整理ゴミファイル混入を防止
.envやSecrets Managerの使い分け鍵の流出防止
Makefile/aliasコマンド入力を1/3に短縮
Runbook「110007が出たらどうする?」を手順化

✅ 次のTo Do

  • Execution WebSocketでfill検知 → Slack連携完成
  • DBにfill保存 → PnL計算ロジックをテスト
  • パラメータ(lot / s_entry)を調整
  • GitHub ActionsにCIフロー導入
    壊しても怖くない開発体制へ!

🎙️おまけコーナー:

「今回出てきたけど、ちょっと気になった言葉たちをまとめて解説します」


🎤さて、ここからはおまけコーナー。
今回の開発ログで出てきた用語の中から、初心者の方にとって “ここちょっとわかりにくそうだな” というものを、ひとつずつ解説していきますね。


✅ ホスト側(ほすとがわ)

これは「Botを動かすマシン側のこと」。
Dockerの場合、コンテナの外にあるPCやMacのことを“ホスト”って呼びます

つまり「自分のパソコン上で動いてるDockerの親」です。


ls -ld config_mainnet.json

これは config_mainnet.json が「ファイルなのか、フォルダなのか」を調べるコマンドです。

  • ls:ファイルを見る
  • -l:詳細表示(パーミッションとか)
  • -d:ディレクトリをそのまま表示(中に入らず)

-rwdrwの違い

  • -rw は「普通のファイル」
    • -:これはファイル
    • rw:読み書き可能
  • drw は「ディレクトリ(フォルダ)」
    • d:これはディレクトリ
    • rw:読み書き可能

最初の文字だけ見れば、
ファイルかフォルダか一発で分かります。


docker compose --profile live up -d の「-d

これは **--detach の略で「バックグラウンドで起動」**という意味です。

つまり、
画面にログを出しっぱなしにせず、
後ろでこっそりBotを動かしてくれるオプションです。


✅ エイリアス(alias)

長いコマンドに、短いニックネームをつける仕組みです。

例えば:

alias dcu='docker compose --profile live up -d'

って書けば、
次回からは dcu と打つだけで、長いコマンドを一発で呼び出せます。


PYTHONDONTWRITEBYTECODE=1

読み:パイソンドントライトバイトコード=ワン
意味:**「.pyc ファイルを作るな」**という指示です。

開発中に不要なキャッシュが増えないように、
この環境変数をセットしておくと安心です。


.pyc__pycache__

  • .pyc は「Pythonの中間ファイル」。
    → 実行速度を上げるために、自動で作られることがあります。
  • __pycache__/ はその .pyc を保存しておく専用のフォルダです。

開発中は古い .pyc がバグの原因になりやすいので、削除することも多いです。


from __future__ import annotations

これはPythonの「将来の機能を先取りして使いたい」という宣言文です。

ただし、この文だけは
ファイルの一番上に書かないとエラーになる
というルールがあるので注意が必要です。


✅ volumeでマウント

ホストのファイルやフォルダをDockerコンテナの中に共有する方法」のことをボリュームマウントといいます。

たとえば:

./config.tpl:/run/config.tpl:ro

という書き方をすれば、
**自分のパソコンのconfig.tpl**を、Dockerの中の/run/に持ち込むという意味になります。


✅ readonlyでマウント(:ro

これは上のボリュームマウントに付けるオプションで、

  • :ro=read-only=読み取り専用
  • コンテナの中からは、そのファイルを書き換えできなくなります

事故防止にめちゃくちゃ役立ちます。


✅ envsubst(エンブサブ)

環境変数を埋め込んだテンプレートから、実際の設定ファイルを生成するコマンドです。

config.tpl みたいなテンプレートに ${API_KEY} と書いておいて、
それを envsubst に通すと、
今の環境変数の値を埋め込んで .json などに変換してくれます。


✅ 「余力チェックAPI」を叩く

Botが注文を出す前に、
**「いまの口座でちゃんと買える? 証拠金足りてる?」**という情報を取得するAPIを叩くこと。

これをやらないと、
発注エラーが起きたときに理由がわからず迷子になります。


✅ 約定検知を REST → WebSocket に切り替え

  • REST API:定期的に「何か起きた?」って聞きに行く方式(Pull型)
  • WebSocket:相手から「今これ起きたよ!」ってリアルタイム通知が来る方式(Push型)

Botは注文の約定(fill)を即座に検知したいので、
WebSocketを使う方がベストです。


✅ Runbook(ランブック)

「このエラーが出たら、こう直す」っていう手順書です。

  • 例:110007 が出たら → ロットを下げて余力を確認
  • 一度書いておけば、次に同じことが起きたときに即対処できるので便利です。

✅ GitHub Actions

これは GitHubが提供するCI/CD(自動実行)サービス

  • コードを push したら
  • 自動で docker buildpytest を動かして
  • 「壊れてないか?」をチェックしてくれる

まさに “小さなロボットエンジニア” みたいな存在です。


✅ CI(シーアイ)

正式には Continuous Integration(継続的インテグレーション)

「コードを変更したら、すぐに自動でテストして、バグを早期に検出する仕組み」のことです。

GitHub Actionsなどのツールを使って、CIを自分のプロジェクトに導入できます。


ということで、おまけコーナーでした!
“あれ何だったっけ?”を全部ここで消化できていたらうれしいです。
このへん理解しておくと、トラブルにも強くなれるので、時間あるときにちょっとずつ復習してみてくださいね。

🏁 エンディング

今回の開発ログは、Botが動くまでのリアルなつまずきと回復の記録でした。

トラブルは防げないけど、
仕組みで未然に防ぐ or 1コマンドで再現して直せる状態にしておく
これが次のステージに進む鍵になります。



あなたのBotも、最初のつまずきを経て強くなる。
良き開発と、トラブルを恐れない成長を!
また次回お会いしましょう!よだかでした。🎤

-Bot