課程「建立自己的套利機器人」第 16 課,也是最後一課。機器人會掃描、決定交易大小、模擬成交、以模擬方式運作,並在觸及限制時停止。今天你會把它變成其他人可以閱讀、執行和判斷的東西。
直接的方法
展示機器人的直白方式,是發一張終端機截圖,上面有一個綠色數字,或發一則訊息:「它這週賺了 40 美元」。截圖無法核驗、無法重現,還會隱藏一切重要資訊:交易有多少筆,最差的一筆有多大,設定是什麼,是否用了真錢。
發布會在別人提問前回答這些問題。它有四部分:日誌、報告、README 和儲存庫。每一部分都不到一小時。
第 1 部分:記錄每個決策的日誌
機器人會為每一筆模擬交易向檔案追加一行。一行是一個小型 JSON 物件。這種格式稱為 JSON Lines:每行一個物件,容易追加,也容易讀回。記錄的不只是利潤:時間、交易對、大小、價格和當時啟用的設定。沒有記錄的內容,以後就無法解釋。
第 2 部分:讀取日誌的報告
一個小腳本讀取日誌,並先印出訪客最想看到的數字:交易數、獲利交易數、總計和最差的單筆交易。最差交易和總計一樣重要。接著同一個腳本會寫出一個網頁。
import json, random, html
random.seed(21)
# 1. the bot appends one line per decision to trades.jsonl (here we fake 12 paper trades)
with open("trades.jsonl", "w") as f:
for i in range(12):
size = round(random.choice([0.5, 1.0]), 1)
profit = round(random.gauss(0.03, 0.12) * size, 4)
f.write(json.dumps({"n": i + 1, "pair": "BTC/USDT", "size": size, "profit": profit}) + "\n")
# 2. the report reads the log and prints the numbers a visitor wants first
trades = [json.loads(line) for line in open("trades.jsonl")]
total = sum(t["profit"] for t in trades)
wins = sum(t["profit"] > 0 for t in trades)
worst = min(t["profit"] for t in trades)
print(f"trades {len(trades)} wins {wins} win rate {wins/len(trades):.0%}")
print(f"total profit {total:+.4f} worst trade {worst:+.4f}")
# 3. the same numbers as a page you can open in a browser or publish
rows = "".join(f"<tr><td>{t['n']}</td><td>{html.escape(t['pair'])}</td><td>{t['size']}</td><td>{t['profit']:+.4f}</td></tr>" for t in trades)
page = f"""<!doctype html><meta charset="utf-8"><title>Paper bot report</title>
<h1>Paper bot report</h1>
<p>Trades: {len(trades)}. Win rate: {wins/len(trades):.0%}. Total profit: {total:+.4f}. Worst trade: {worst:+.4f}.</p>
<p><b>Paper mode: no real money.</b></p>
<table border="1" cellpadding="4"><tr><th>#</th><th>pair</th><th>size</th><th>profit</th></tr>{rows}</table>"""
open("report.html", "w").write(page)
print("wrote report.html,", len(page), "bytes")
輸出:
trades 12 wins 6 win rate 50%
total profit +0.0432 worst trade -0.2725
wrote report.html, 1088 bytes
在瀏覽器中開啟 report.html。把這兩個數字放在一起看。一半交易獲利,總計略為正數,但最差的一筆交易(−0.2725)是總利潤(0.0432)的六倍。一筆壞交易就會抹掉整個執行。一條像「profit +0.0432」這樣的單獨數字會掩蓋這一點。這就是報告將最差交易放在總計旁邊印出的原因。
這裡的虛構交易是隨機數,只是為了給腳本一些可讀取的內容。在你的機器人中,日誌來自第 12 課的模擬帳戶。
第 3 部分:README
README 是儲存庫的第一頁。訪客讀它十秒,就會決定繼續還是離開。一個好的 README 應按這個順序包含以下部分:
- 一句話:機器人做什麼。「一個在兩家交易所之間尋找價格缺口的模擬交易機器人。」
- 狀態行:「模擬模式。沒有真錢。不是財務建議。」
- 如何執行:三條可在乾淨機器上執行的命令,安裝、複製範例設定、啟動。
- 你會看到什麼:一次執行的輸出,或報告頁面的連結。
- 它如何運作:第 3 課的各個階段,每個階段一行。
- 已知限制:模擬帳戶沒有建模什麼,延遲、其他機器人、第 11 課的交易腿失敗。寫下這些比隱藏它們更能贏得信任。
- 結果:報告中的數字,附上日期和設定。
像陌生人那樣測試 README:新建一個空資料夾,複製儲存庫,按自己的步驟操作,修正每一個你卡住的地方。
第 4 部分:乾淨的公開儲存庫
在公開儲存庫前,檢查以下事項。外洩的金鑰無法收回。
- 沒有機密資訊。 金鑰、權杖和密碼放在環境變數中,或放在列入 .gitignore 的檔案中,絕不放在程式碼或你提交的設定中。應提交帶有空值或假值的 config.example.json。
- 搜尋歷史。 如果金鑰曾被提交過,在新提交中刪除它並不能從歷史裡抹掉它。在交易所建立新金鑰,撤銷舊金鑰,並從一段全新的歷史開始公開儲存庫。
- 一個 .gitignore,排除日誌檔、虛擬環境和包含真實值的設定。
- 一個授權檔,讓人們知道能否重複使用程式碼。
- 小而易讀的提交,提交訊息說明改了什麼。
資料夾準備好後,執行這些命令:
git init
git add .
git status # read this list: no keys, no logs
git commit -m "Paper-trading arbitrage bot: first release"
在你使用的程式碼代管網站上建立一個空儲存庫,GitHub 或 GitLab,然後把它的位址新增為遠端並推送。你可以讓代理人提供該網站精確的兩條命令,但在首次推送前,自己閱讀 git status。
如何談論結果
- 每次都說「模擬」。模擬利潤是上限,因為真實市場的成交更差,第 11 課已經說明。
- 展示最差交易和交易數量,並同時展示總計。
- 給出日期和設定。沒有它們的結果無法重現。
- 不要把一次短暫的好結果稱為證明。十二筆交易說明不了多少,不同市場日的一百筆交易能說明更多。
- 用具體方式徵求回饋:「模擬帳戶裡的哪個假設看起來有問題?」比「你怎麼看?」得到的回答更好。
讓 AI 代理人寫它並測試
一個模糊的要求:
為我的機器人準備發布。
一個內建檢查方式的要求:
完成這些步驟,並展示每一步的輸出。1. 搜尋整個儲存庫,包括 git 歷史,查找看起來像 API 金鑰或權杖的字串,列出每個相符項目,暫時不要修正任何內容。2. 撰寫 report.py,它讀取 trades.jsonl,印出交易數、獲利數、總利潤和最差交易,並寫入 report.html;在第 16 課範例上,它必須印出「trades 12 wins 6」和「worst trade -0.2725」。3. 撰寫一個 README,其中包含第 16 課的七個部分和一個乾淨機器上的執行部分;在一個新空資料夾中執行命令,並報告它們在哪裡失敗。4. 撰寫 .gitignore 和 config.example.json。不要推送,不要建立任何遠端儲存庫。
親自閱讀第 1 步的清單。只有你決定什麼算作機密資訊。
練習
- 執行報告程式碼並開啟 report.html。將印出的各行與本課比較。
- 將 seed 改為 22,再改為 23。勝率、總利潤和最差交易變化多少?這說明從十二筆交易中展示一個數字有什麼問題?
- 連接你自己的機器人:讓它的模擬帳戶寫入 trades.jsonl,並針對真實的模擬輸出執行報告。
- 寫 README 並交給一個從未見過該專案的朋友。觀察他們在哪裡卡住,修 README,不要修朋友。
- 發布儲存庫。把連結發給三個人,並問每個人同一個具體問題。
課程結束,接下來是什麼
你從一個需要一小時的掃描器開始,最後得到一個能尋找價差、沿訂單簿計算、考慮費用和大小、預期失敗、在限制內以模擬方式運作、交易第二種策略,並能讓其他人閱讀和執行的機器人。真錢是另一個決定:它需要長期的模擬執行、小規模,以及你信任的風險限制。慢慢來。
如果你在任何地方卡住,前面的課程都遵循同一個模式:先是一個直白版本,再給出修正它的想法,最後是一個帶內建檢查的代理人要求。回到讓你難受的那一課,重新做它的練習。