前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。
前回(#202)では Makefile + .env で「テスト⇆本番を 1 行切替え」する方法をまとめました。
今日の記事はその続き。 「実際に最小ロットで発注 → 約定を DB に保存」 できるところまでの全プロセスを、初心者の方にも追えるレベルで整理します。
0. やったこと・得たこと
ゴール | 実際にやった作業 | 何が分かったか |
---|---|---|
✅ fill を 1 件以上取得 | Market / Limit‐IOC で最小ロットを発注 | execPrice は execution/list から取るのが手堅い |
✅ trades.db に保存確認 | sqlite3 -header -column … でレコードを目視 | DB スキーマはそのまま使える(price, qty, ts で OK) |
✅ Slack 通知が機能 | 成功 / フォールバック / Filled の 3 系統 | 本番移行時の遠隔監視が完成 |
⏸ 本番前チェックを棚卸し | .env.prod 作成・ロット下限再確認 | 次回は本番キーを書き換えて即テスト可能 |
1. 今日のタイムライン(こまめに休憩しながらコツコツと)
時刻 | 作業内容 | メモ |
---|---|---|
05:30 | 前日のフォールバック実装を再読 | 設計を“頭に再ロード” |
07:10 | order_tester.py を execution/list 仕様にパッチ | /order/list が 404 → v5 docs で即確認 |
08:15 | 最小ロット Market を 2 回発注 | retCode=0 → fill 0:原因は約定レス待ち |
9:00 | execPrice フィールド追加 → KeyError 解消 | save_fills が通る |
10:00 | SQLite ワンライナーで fill 2 行を目視 | (sqlite3 -header -column trades.db … ) |
11:30 | VS 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 には execPrice | f.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(本番ミニマム稼働準備)
- 本番資金を入金(余裕資金のみにする)
.env.prod
に BYBIT_KEY_MAIN / SECRET_MAIN をセット- Lot = 最小 0.001 BTC、timeout 15 s、フォールバック上限 3 に調整
make mainnet-loop
起動 → Slack で 1 fill を確認- trades.db をバックアップしつつ 24 h 放置テスト
6. まとめ ― 今日の学び 3 カ条
- 「レスポンスは必ず
json.dumps
で人間の目で見る」
— KeyError は 10 秒で潰せる。 - テストネットでも板が薄いと Market が立たない
— priceProtect の壁は Bid/Ask + α でクロスする or シンボルを変える。 - 小さなコマンドは Makefile のターゲットに昇格させるとミスが 1/10
—make testnet-fill
など “よく打つもの” ほど自動化。

🛠️ 今回は「**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 を掴む」 = “紙パックにストローを刺して最初の一口を飲む” 行為
ジュース(= 相場データ)は確かにパイプを流れて自分のアプリに入ってくる。
だから次は安心して大きなストロー(本番ロット)に替えられるわけです。
これからテストネットを触る初心者さんへ
- Market 最小ロット → Limit‐IOC クロス の順で必ず試す
- 板が薄くても最短で約定しやすい
- 取れた fill を DB or CSV に “実物” として保存
- 人間の目で SELECT して確認
(GUI ではなくsqlite3
やcat csv
推奨――構造が分かる) - 成功通知のメッセージ を 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” の記載 |
検証方法メモ
- ①②⑤︰Bybit v5 Official REST API reference
- ③︰Bybit Discord #testnet チャンネル固定メッセージ
- ④︰Fee Schedule ページ下部 “Testnet” セクション
- ⑥︰Unix /proc ドキュメント
👇ラジオで話したこと
開発記録 #203|MMBot開発ログ11「fill を掴むまで」
── "紙パックにストローを刺す" その最初の1口を飲むまで
こんにちは、よだかです。
今回のテーマは、Bot開発のなかでも**「ゼロからイチ」**を生む重要フェーズ。
タイトルは──
「テストネット最終チェックで fill を掴むまで」
ここまで整えたインフラや通知まわりが、
ちゃんと約定データ(=fill)を受け取れるか?
それを最後に1つ、確認しにいく作業です。
fill を掴む=なにができる状態か?
まず、「fill」とは何か。
取引所が『この注文、確かに通りました』と返してくれる“唯一の証拠”
つまり、Botの中では「注文が通った気がする」ではなく
「ポジションが実際に建った」ことが確認できる唯一の手がかりなんです。
✅ fill を掴むまでにやったこと一覧
ゴール | 実施内容 | 分かったこと |
---|---|---|
✅ fill 取得 | 最小ロットでMarketとLimit‐IOC発注 | execution/list で取得が確実 |
✅ DB記録 | sqlite3 で trades.db 確認 | スキーマは変更不要。OK |
✅ Slack通知 | 成功・フォールバック・Filledの3系統 | いよいよ遠隔監視もOK |
どこで詰まったか? → /execution/list
に切り替えた理由
当初使っていたのは /order/list
エンドポイント。
が、Bybit v5では
「注文情報」と「約定情報」が完全に分離されていた
🔄 /execution/list
のメリット
比較項目 | /order/list | /execution/list |
---|---|---|
含まれる情報 | 注文だけ(fillsなし) | 約定(fills)あり |
テストネットとの相性 | 古い注文はすぐ消える | ページングあり・軽い |
フィールド名 | price / qty | execPrice / 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ステップ
- Market最小ロット発注 → Limit-IOCでクロス
- fill を
.db
または.csv
に保存 →cat
やsqlite3
で確認 - Slack/Discord通知を通して成功ログを見る
- 成功&失敗の両方をローカルログに残す
この流れが通ったら、もう**“血液の通ったBot”**になったと言ってOKです。
エンディング:ゼロ→イチの意味
fill が取れない状態で本番に行く=裸で戦場に出るようなもの。
「注文は通ったのに、ポジションが無い」
「DBに何も残っていない」
「Slackに一生 'timeout' だけが流れ続ける」
──そんな状態を避けるために、
テストネットでfillを1行掴むというステップをあえて通過したんです。
📣 このあとやるべきこと:
.env.prod
に本番キーを入れて準備OKmake 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の“記録ノート”です。
明日から使える理解に変わっていたら嬉しいです!
次はいよいよ本番デビュー!この感触を信じて、少しずつ踏み出していきましょう。
それでは、良き開発と、確かな約定を!また次回!
🎤よだかでした。