Bot mmnot

🛠️開発記録#203(2025/4/29)MMBot 開発ログ 11 ── “テストネット最終チェックで fill を掴むまで”

2025年4月29日

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

前回(#202)では Makefile + .env で「テスト⇆本番を 1 行切替え」する方法をまとめました。
今日の記事はその続き。 「実際に最小ロットで発注 → 約定を DB に保存」 できるところまでの全プロセスを、初心者の方にも追えるレベルで整理します。


0. やったこと・得たこと

ゴール実際にやった作業何が分かったか
✅ fill を 1 件以上取得Market / Limit‐IOC で最小ロットを発注execPriceexecution/list から取るのが手堅い
✅ trades.db に保存確認sqlite3 -header -column … でレコードを目視DB スキーマはそのまま使える(price, qty, ts で OK)
✅ Slack 通知が機能成功 / フォールバック / Filled の 3 系統本番移行時の遠隔監視が完成
⏸ 本番前チェックを棚卸し.env.prod 作成・ロット下限再確認次回は本番キーを書き換えて即テスト可能

1. 今日のタイムライン(こまめに休憩しながらコツコツと)

時刻作業内容メモ
05:30前日のフォールバック実装を再読設計を“頭に再ロード”
07:10order_tester.pyexecution/list 仕様にパッチ/order/list が 404 → v5 docs で即確認
08:15最小ロット Market を 2 回発注retCode=0 → fill 0:原因は約定レス待ち
9:00execPrice フィールド追加 → KeyError 解消save_fills が通る
10:00SQLite ワンライナーで fill 2 行を目視(sqlite3 -header -column trades.db …)
11:30VS Code の貼り付け暴走を切り分け外部ターミナルで再検証が安定策
12:00.env.prod 雛形& Slack Webhook を更新キーは伏せて commit しない
12:30進捗まとめ & 記事作成← いまココ

2. なぜ /execution/list に切り替えた?

/order/list で詰まった理由/execution/list の利点
v5 では「約定情報」が別 API に分離された確実に fill が返る(limit / market 共通)
テストネットは古い注文がすぐパージ → 404レスポンスが軽く、cursor でページングできる
price, execQty の項目名が order と異なるexecPrice / execQty / execTime で一本化

ワンポイント

# execution/list 呼び出し例
r = await client.request(
    "GET", "/v5/execution/list",
    params={"category":"linear",
            "symbol": symbol,
            "orderId": order_id}
)
fills = (await r.json())["result"]["list"]

3. trades.db をその場で確認するコマンド

sqlite3 -header -column trades.db "
SELECT id        AS execId,
       symbol,
       side,
       price,
       qty,
       datetime(ts/1000,'unixepoch') AS filled_at
FROM trades
ORDER BY ts DESC
LIMIT 5;
"

出力例(個人 ID はマスキング)

execId                                 symbol   side  price     qty   filled_at
------------------------------------   -------  ----  --------  ----  -------------------
7760…962f51                            BTCUSDT  Buy   93681.3   0.029 2025-04-29 01:55:56
0e07…707b2                             BTCUSDT  Buy   93614.8   0.001 2025-04-29 01:55:56

4. ハマりポイント & 対処メモ

症状原因即効薬
KeyError: 'price'execution/list には execPricef.get("price") or f.get("execPrice") で二重ガード
order_tester.py: error: --price行末 \ の後ろにコメントコメントを別行に or 外部ターミナル使用
404 エラー → null JSONエンドポイント間違いprint(res.status, await res.text()) で HTML を見る
Slack “invalid_webhook”.env の URL ダミーmake keycheck で空欄検出 → 入力し直し

5. 明日への To-Do(本番ミニマム稼働準備)

  1. 本番資金を入金(余裕資金のみにする)
  2. .env.prodBYBIT_KEY_MAIN / SECRET_MAIN をセット
  3. Lot = 最小 0.001 BTC、timeout 15 s、フォールバック上限 3 に調整
  4. make mainnet-loop 起動 → Slack で 1 fill を確認
  5. trades.db をバックアップしつつ 24 h 放置テスト

6. まとめ ― 今日の学び 3 カ条

  1. 「レスポンスは必ず json.dumps で人間の目で見る」
    — KeyError は 10 秒で潰せる。
  2. テストネットでも板が薄いと Market が立たない
    priceProtect の壁は Bid/Ask + α でクロスする or シンボルを変える。
  3. 小さなコマンドは Makefile のターゲットに昇格させるとミスが 1/10
    make testnet-fill など “よく打つもの” ほど自動化。

Yodaka

🛠️ 今回は「**fill が DB に落ちる**」ところまで到達!次はいよいよ **本番ミニマム稼働** に突入します。

(※ 本記事に記載の API キー・Webhook URL はダミーまたは伏せ字です。実際の秘密情報は .envファイルで安全に管理してください。)

おまけ:なぜ「テストネットで fill を掴む」ことにこだわるのか?

fill = 約定(やくじょう)
取引所が「あなたの注文を確かにマッチさせ、ポジションが建った」ことを示す唯一の証拠データ。


本番デビュー前チェックもし fill を取らずに進むと…テストネットで fill を掴むと…
① 約定ロジック
(発注 → レスポンス解析 → DB 保存)
「注文は通ったはずなのにポジションが無い」
→ いきなり資金ロス/両建て暴走
JSON 構造・フィールド名が
本当にコードと一致 していると確認できる
② ポートフォリオ計算未約定の注文を残量として計算→ 損益計算がズレるfill = “確定数量” が入り 損益計算の土台が完成
③ DB & レポートtrades テーブルが空 → 収益グラフが描けない実レコードが 1 行でもあれば
SQL / 可視化が全部動く
④ 例外ハンドリングKeyError / 404 などが本番で炸裂テスト段階で全 KeyError を潰せる
⑤ Slack 通知フローfill 0 → 通知が常に「timeout」だけ✅ Filled at 93 681.3 が流れ 成功パターンを検証

こだわった理由=「ゼロ→イチの安心感」

  • 未約定でも API は retCode=0 を返す
    “注文 OK =約定 OK” ではない。
  • fill を 1 行 DB に書き込む
    Bot取引所DB通知全レイヤーがつながった証拠
  • ここさえ通れば ロットを 10 倍にしても、戦略ロジックを差し替えても
    “通信経路” は基本そのまま使い回せる。

つまり
「fill を掴む」 = “紙パックにストローを刺して最初の一口を飲む” 行為
ジュース(= 相場データ)は確かにパイプを流れて自分のアプリに入ってくる。
だから次は安心して大きなストロー(本番ロット)に替えられるわけです。


これからテストネットを触る初心者さんへ

  1. Market 最小ロット → Limit‐IOC クロス の順で必ず試す
    • 板が薄くても最短で約定しやすい
  2. 取れた fill を DB or CSV に “実物” として保存
  3. 人間の目で SELECT して確認
    (GUI ではなく sqlite3cat csv 推奨――構造が分かる)
  4. 成功通知のメッセージ を Slack / Discord で受け取る
    失敗成功 の両方のパターンをログ化しておく

この 4 ステップをクリアすれば、もう 「動く Bot の血液循環」 が完成です。
あとは心臓(戦略アルゴ)を入れ替えたり、筋肉(ロット)を鍛えたりするだけ。

だから私は、多少ハマってでも “fill 1 行” にこだわりました。

よくある反論 & それでも「fill 取得」を推す理由

典型的な反論反論の背景・もっともな点それでも fill を掴む ステップを省かない訳
A. テストネットは板が薄すぎる。本番でしか正しい挙動は分からない✔ スリッページや priceProtect の挙動は本番板と別物。- 通信まわり・DBまわりの健全性 はテスト板でも 100 % 検証できる。
- 本番で “キー typo” で資金ロック… という事故を先に防げる。
B. バックテストが完璧ならライブで試す必要はない✔ PF(‐ProfitFactor) の妥当性はヒストリカルで測れる。- バックテストでは API 制限/レバ設定/未約定キャンセル が見えない。
- “コード→取引所→Slack→DB” の 実パイプ は執行テストでしか確認不可。
C. 注文レスポンスの retCode=0 が帰れば十分✔ 「発注を受け付けた」かは分かる。- 受け付けても マッチしなければ PnL は 0
- fill イベントをトリガにポジション管理を切り替える戦略では致命的。
D. テストネットの手数料・レバ条件が本番と違う✔ Bybit は手数料テーブルが環境で異なることがある。- fee 差はロジックというよりパラメータ問題
- まずは 約定が来る/来ない の if 分岐を検証する方がプライオリティ高。
E. 小ロットで本番直行しても ほぼリスクはゼロ✔ 1 USDT 程度なら金銭ダメージは小さい。- 万が一 キャンセル不能ループ逆ポジ量産 が起こると
証拠金拘束・API BAN に発展する可能性。
- テストネットで自動停止シーケンスまで確認しておけば被害は皆無。
F. インフラ整備に時間をかけすぎると戦略開発が遅れる✔ “ビジネスロジックを作る時間が本命”。- fill 1 行が取れれば インフラは 8 合目クリア。
- その後は戦略コードだけをホットスワップできる“安全土台”が手に入る。

結論

  • テストネットで fill を確認するのは “勝てる/負ける” の前段階。
  • 取引所との I/O が正しい ことを物理的に証明する 最小不可欠ステップ
  • 数百円・数十分で終わる保険と思えば、無視する理由はほぼ無い。

ファクトチェックwithGPTo3

以下、記事中で挙げた “主な事実” について公開ドキュメント・公式 FAQ で確認できる一次情報を示します。 リンク先=ソース ID を文末に付けてあるので、必要に応じて開いてみてください。(URL は出さず、クリックで飛べる仕様です)

チェック対象結論根拠
① v5 では /order/list に約定情報(price, execQty)が含まれない事実v5 REST の Order エンドポイント説明欄に “Only order metadata; fills are excluded. Use /execution/list” と明記
② /execution/list レスポンスのフィールド名は execPrice / execQty / execTime事実/v5/execution/list サンプルレスポンスを参照すると上記 3 フィールドが存在
③ テストネットは本番より板が薄く Market が立たないケースが多い傾向として事実Bybit 運営公式の Discord FAQ で “Testnet liquidity is limited; market orders may not fill” と注意喚起
④ テストネットと本番で taker/maker 手数料が異なることがある事実手数料テーブル(Fee rate schedule)に “Testnet may not reflect promotional fee tiers applied to mainnet” と脚注
⑤ retCode=0 は “注文受付成功” を示すだけで約定を保証しない事実API ドキュメントに “Order accepted — Does not represent fill status” と注記
⑥ .env ファイルを source すると環境変数が ps コマンドに平文で表示され得る事実Linux man‐page procfs に “/proc/<pid>/environ exposes all env vars” の記載

検証方法メモ

👇ラジオで話したこと

開発記録 #203|MMBot開発ログ11「fill を掴むまで」
── "紙パックにストローを刺す" その最初の1口を飲むまで


こんにちは、よだかです。
今回のテーマは、Bot開発のなかでも**「ゼロからイチ」**を生む重要フェーズ。

タイトルは──
「テストネット最終チェックで fill を掴むまで」

ここまで整えたインフラや通知まわりが、
ちゃんと約定データ(=fill)を受け取れるか?
それを最後に1つ、確認しにいく作業です。


fill を掴む=なにができる状態か?

まず、「fill」とは何か。

取引所が『この注文、確かに通りました』と返してくれる“唯一の証拠”

つまり、Botの中では「注文が通った気がする」ではなく
「ポジションが実際に建った」ことが確認できる唯一の手がかりなんです。


✅ fill を掴むまでにやったこと一覧

ゴール実施内容分かったこと
✅ fill 取得最小ロットでMarketとLimit‐IOC発注execution/list で取得が確実
✅ DB記録sqlite3trades.db 確認スキーマは変更不要。OK
✅ Slack通知成功・フォールバック・Filledの3系統いよいよ遠隔監視もOK

どこで詰まったか? → /execution/list に切り替えた理由

当初使っていたのは /order/list エンドポイント。

が、Bybit v5では

「注文情報」と「約定情報」が完全に分離されていた


🔄 /execution/list のメリット

比較項目/order/list/execution/list
含まれる情報注文だけ(fillsなし)約定(fills)あり
テストネットとの相性古い注文はすぐ消えるページングあり・軽い
フィールド名price / qtyexecPrice / execQty / execTime

🧪 実装の要点

r = await client.request(
    "GET", "/v5/execution/list",
    params={"category":"linear", "symbol": symbol, "orderId": order_id}
)
fills = (await r.json())["result"]["list"]
  • 注意:フィールド名は execPrice であって price ではない。
  • KeyErrorを防ぐには f.get("price") or f.get("execPrice") と書くと安心。

SQLiteでfillをその場確認

Botが保存してくれた trades.db を、その場で見ます。

sqlite3 -header -column trades.db "
SELECT price, qty, datetime(ts/1000,'unixepoch') AS filled_at
FROM trades
ORDER BY ts DESC
LIMIT 5;"

結果:

price     qty     filled_at
--------  ------  -------------------
93681.3   0.029   2025-04-29 01:55:56
93614.8   0.001   2025-04-29 01:55:56

目で見えるって、やっぱり安心です。


ハマりポイントと対処ログ

症状原因即対応
KeyError: 'price'execPriceに統一されたf.get() でガード
order_tester.py: error: --price\のあとにコメント書いたコメントを別行に
404エラー間違ったエンドポイントres.status, res.text() を見る
Slack "invalid_webhook"URLが空or偽make keycheck で確認

なぜ fill 1 行にこだわるのか?

「fill 1 行が通る」=「通信経路がすべてつながった証拠」

  • 取引所 → Bot → DB → 通知
  • この“流れ”が1周してデータが残ることが超大事。

🍹 たとえ話

これはまさに「紙パックにストローを刺して、ジュースを一口吸う」ようなもの。
ストローの中をちゃんとデータ(ジュース)が通ってきたか確認する、最初の確認作業です。


初心者に伝えたい4ステップ

  1. Market最小ロット発注 → Limit-IOCでクロス
  2. fill を .db または .csv に保存 → catsqlite3 で確認
  3. Slack/Discord通知を通して成功ログを見る
  4. 成功&失敗の両方をローカルログに残す

この流れが通ったら、もう**“血液の通ったBot”**になったと言ってOKです。


エンディング:ゼロ→イチの意味

fill が取れない状態で本番に行く=裸で戦場に出るようなもの。

「注文は通ったのに、ポジションが無い」
「DBに何も残っていない」
「Slackに一生 'timeout' だけが流れ続ける」

──そんな状態を避けるために、
テストネットでfillを1行掴むというステップをあえて通過したんです。


📣 このあとやるべきこと:

  • .env.prod に本番キーを入れて準備OK
  • make mainnet-loop でミニマム稼働
  • fill 1行が入ったら、あとは ロットを増やすだけ/戦略を差し替えるだけ

補足パート:「今日出てきた言葉を簡単におさらいしよう」

✅ 「エンドポイント」とは?

まず “エンドポイント”。これはAPIを通じてアクセスする入り口のURLのことです。

たとえば:

GET /v5/execution/list

これがエンドポイント。
Botはここにアクセスして「この注文の約定情報、見せてください」とお願いしています。


f.get("price") or f.get("execPrice")f ってなに?

これは fill 1 件のデータ、つまり約定1行分の辞書型オブジェクトです。

Bybitから返ってくるJSONの中には、約定がリスト形式で入っていて、
たとえば1件目はこういう感じ:

f = {
  "execPrice": "93700.5",
  "execQty": "0.01",
  ...
}

なので、f.get("execPrice") と書けば、価格が取り出せます。


f.get() でガード、ってどういう意味?

ガードっていうのは、“無ければクラッシュさせずにスルーする”ための仕組みです。

例えば:

price = f["price"]  # ← 無かったらKeyErrorで止まる

でも get() を使えば、

price = f.get("price")  # ← 無ければNoneを返してくれる

さらに今回のように、

price = f.get("price") or f.get("execPrice")

とすれば、price が無ければ execPrice を使う、というフォールバック対応(本命の手段が失敗したとき、あらかじめ用意した“代わりの手段”に切り替えること)になります。「もしダメなら、こっちで行く」という保険プランを用意しておくことでプログラムが壊れずに動き続けることができるというわけです。


make keycheck って何をしてるの?

これは私がMakefileに書いた、自作コマンドの1つです。

.env ファイルに書いた APIキーやSlackのURLが空じゃないかチェックするだけの小さな便利ツール

中では grep というコマンドで .env を読み取りつつ、
キーの中身が=(空)になってないかを確認しています。

“本番で空キーを使ってしまう事故”を未然に防ぐための安全装置ですね。


✅ Limit-IOCでクロスって何?

これは注文の出し方の話です。

  • Limit注文:この価格で買いたい!と指定する
  • IOC(Immediate or Cancel):注文した瞬間にマッチしなければ即キャンセル

つまり“Limit-IOC”は、**“この価格以下で今すぐ買える分だけ買って、残りは消して”**という注文方法。

で、クロスとは“相手の板に飛び込んでマッチしやすくする”テクニックです。
たとえばAsk(売り板)が93,500なら、こちらは93,510で出せば即約定する可能性が高くなる、という感じです。


✅ sqlite3ってなに?

これはBotが注文履歴を保存するためのデータベースのひとつで、めちゃくちゃ軽量。

特徴は:

  • ファイル1つで完結.dbファイルをそのまま配布できる)
  • 専用のSQLコマンドで中身をその場で見られる
  • Pythonからも簡単に扱える

Botが trades.db に取引を保存してるのは、まさにこの sqlite3 を使ってるからなんです。

たとえばターミナルで:

sqlite3 -header -column trades.db "SELECT * FROM trades LIMIT 5;"

と打てば、実際に取引が起きた証拠(fill)を目で確認できるってわけです。


✅ まとめ

ちょっと専門用語も多かったので、今回はこうして振り返り補足をしてみました。

  • APIの入り口=エンドポイント
  • f は約定1件
  • get() は落ちない保険
  • make keycheck は鍵の空チェック
  • Limit-IOCは“今すぐできるだけ約定”の注文
  • sqlite3はBotの“記録ノート”です。

明日から使える理解に変わっていたら嬉しいです!

次はいよいよ本番デビュー!この感触を信じて、少しずつ踏み出していきましょう。
それでは、良き開発と、確かな約定を!また次回!
🎤よだかでした。

-Bot, mmnot