工程 · 2026.10.06 · 4 min · 更新於 2026.10.26
對帳告警響了兩次:Bitfinex 放貸部位換邊造成的漏算與重複計息
我用實際付息除以帳面應計的比值監控收益有沒有算錯,這個告警響過兩次,一次漏算、一次重複計息,根因都是 Bitfinex 把同一筆部位分在 credits 與 loans 兩側。
閱讀設定
對帳告警響過兩次,一次比值 0.970、分母漏算,一次 0.488、同一段利息算了兩次。
這個比值是「實際付息 ÷ 帳面應計」的 7 天比值,我的 Bitfinex 自動放貸平台用它檢查使用者看到的收益有沒有算錯。
兩次方向相反,根因都一樣:Bitfinex 把同一筆放貸部位分在 credits 與 loans 兩側,換邊時換一個新 id、卻保留原本的開倉時間。
兩次自動分診的推論都不對,真正找到原因的是把數字對到收斂。
對帳比值怎麼算
「放貸中」的真值是 Bitfinex 回來的部位快照,不是 bot 自己的訂單表。
訂單表是 bot 的動作紀錄,取消、部分成交、手動操作都可能留下殘留的 row,修補每一種殘留只是在維護一份次級投影。
Bitfinex 每天付一次利息,付進來的已經扣掉 15% 手續費。我這邊根據部位快照,把每個部位的 金額 × 利率 × 秒數 按秒攤到每一天,算出帳面應計(照利率和時間算出、扣手續費前的利息),兩者相除:
funding_recon_ratio = 7 天內實際付息總和 / 7 天內帳面應計總和
付息扣過手續費,所以乾淨的帳戶應該落在 0.85 附近。
比值連續 3 天跑出帶狀區間就告警:太低是應計算多了,太高是有部位漏掉。
這個比值跟金額大小無關,每個帳戶、每個幣別各自一條,哪個帳戶開始偏就知道從哪查。
同一筆部位有兩個家
Bitfinex 的放貸部位分 /funding/credits 和 /funding/loans 兩種 endpoint。借方還沒動用的錢在 loans 那一側,開始動用之後移到 credits 那一側。
兩邊的 id 是不同的命名空間,換邊時拿到新 id,但保留開倉時間 MTS_OPENING,金額也不變。
已經結束的部位會進各自的 hist(歷史紀錄)。credits 的 hist 會寫進我的 credit 歷史表,loans 的不會,因為 id 命名空間不同,混進去會污染帳本。
Bitfinex 120 天放貸的 bid-take lane上線後,它的成交大多落在 loans 那一側。
接下來兩個 bug 都跟這一側有關。

第一次 0.970:平倉部位從帳上消失
bid-take 上線約十天後,告警對正式站的一個帳戶響了三天,比值 0.970。
Claude Code 唯讀自動分診指向「每日物化 job 對新 lane 的 pro-rata 算錯」,方向對了一半,機制是錯的。物化是把算好的結果先存成表,pro-rata 是按比例分攤。
部位快照是活部位的鏡像,部位離開交易所就刪列。credits 那一側之後會從 hist 進歷史表,loans 那一側不會。
所以一筆 bid-take 部位平倉的瞬間,它整段生命週期的應計就從帳面上消失,分母被低估,比值被撐高。
視窗內剛好有五筆這樣的部位。我把它們按秒補回去,比值從 0.970 變成 0.851。
差額剛好是這五筆,所以不用再找第二個原因。對帳類的 bug,先把數字對到收斂再動手。
修法是加一張平倉快照表:刪掉離開的部位之前,先把金額、利率、開倉與平倉時間留下來,每日物化 job 把它當第三個來源讀。
唯一要想清楚的是換邊不能算平倉。
部位從 loans 移到 credits,舊 id 消失、新 id 出現,看起來像一筆平倉加一筆新開倉,寫進平倉表就會跟新的那筆重疊。所以同一輪裡看到一去一來、開倉時間與金額相同,就認成換邊,不寫平倉紀錄。
我也考慮過讓物化 job 記住算過的應計、只增不減,但那會把之後真正的修正也擋掉,等於把看得到的錯換成看不到的錯。
第二次 0.488:換邊那段算了兩次
兩週後告警又響了,這次是另一個帳戶,比值 0.488,分母多算了。
分診的推論是「使用者暫停了放貸,快照凍住了」,但兩個 config 都是 active,推論不成立。
我把那個帳戶的每日應計逐日攤開。從某一天起,開著的部位數從 1 變 2,應計剛好翻倍。
再把歷史表與平倉快照裡開倉時間、金額相同的列對起來。一筆 120 天的部位從 credits 換到 loans,舊 credit 的 hist 涵蓋開倉到換邊這段,新的 loan 又從開倉時間算起,重疊的那幾天每天都算了兩次。
換邊也不罕見,近 60 天的 6,528 筆 hist credit 裡,有 1,452 筆是換邊來的。
反方向也有問題。上一次加的換邊偵測,會讓一個新 id 把所有同 key 的舊 id 都吃掉。同一批成交的等額部位常常一起換邊,結果只有第一筆被認成換邊,其餘被記成平倉。
Bitfinex 本來就告訴我們部位是從哪個 endpoint 來的,只是我沒存。
修法是在兩張快照表加一個 from_loans 欄位。物化 job 對 loans 那一側的列,去找開倉時間與金額相同的 hist credit,只從那筆結束之後才開始計息;有一筆還沒結束,整段就不算。
這一條規則兩個方向都對。換邊偵測也改成一個新 id 只配一個舊 id。

重算之後,三個受影響帳戶的 7 天比值:
| 帳戶 | 重算前 | 重算後 |
|---|---|---|
| A | 0.734 | 0.855 |
| B | 0.488 | 0.914 |
| C | 修前沒量 | 0.877 |
正式站回填出的 loans 平倉列,沒有一筆跟 hist credit 撞 key,所以正式站沒有中這個 bug。
分診只能當線索
第一次分診猜對了哪條 lane、猜錯了哪裡算錯,第二次連哪裡壞都猜錯,照著改都會改到沒壞的地方。
MTS_OPENING 是經濟上的開倉時間,不是這個 id 的出生時間。
用開倉時間當計息起點、又把多個 id 加總的地方,都要想同一筆部位會不會以不同 id 出現兩次。
交易所已經告訴你的資訊,第一次就存下來。之後要回推,只能靠時間與金額猜。

工程
Claude Code 自動分診 Sentry 告警:用 --allowedTools 限定唯讀的設計與踩坑
告警響了先讓只有唯讀工具的 Claude session 查根因、把結論貼到 Discord,這篇講權限邊界怎麼畫,以及模型改寫指令路徑、Sentry 噪音這兩個踩過的坑。
留言
載入留言中…