Judgment, Oversight & Execution
一個五角色的 AI 交易台,用真實行情跑一個紙面帳戶。這個月它第一次算出自己的 alpha,發現自己落後 SPY 129 個基點,並為這個數字暴露出的問題交付了四十項修復——回測引擎、帶外急停開關、雜湊鏈日誌、按證據淘汰訊號,以及給自己研究迴圈加上的多重檢驗校正。24/7 跑在一台 Synology NAS 上。
分享這篇案例 →難點在於:硬性風控寫在模型改不了的程式碼裡,還有一個自我提升迴圈——同一個錯誤反覆出現之後,會從一條教訓升級成寫死在程式碼裡的硬性攔截。
APScheduler-based; wakes each role on a market-driven schedule. This is the scheduler that actually runs the desk in production — distinct from the bounded ReAct orchestrator, which is built but has never been promoted past shadow mode.
Python, Postgres, Alpaca, DeepSeek, Qwen, Video Factory LLM gateway
一個基於 APScheduler 的編排器按行情節奏喚醒每個 agent。分析師在美東 08:30 用品質檔運行——重推理、每天一次——讀隔夜新聞、總體經濟日曆和自己過去的教訓,寫出盤前簡報。交易員由一套逐分鐘的頻率解析器喚醒:開盤後約每 5 分鐘一次,盤中每 25 分鐘,尾盤每 12 分鐘,另有一條獨立的、每 8 分鐘檢查一次持倉風險的線——它用 fast 檔決定開平哪些標的、多大倉位、停損設在哪。評估者在中午重新檢視每個持倉是否還符合當初的邏輯。覆盤者在收盤時運行,驅動下面的自我提升迴圈。只有交易員能真正下單。
挑戰者角色坐在任何有實質影響的開倉之前,作為對抗性的第二意見:它是獨立的一次呼叫、獨立的一套提示詞,用的還是更重的檔位,而不是同一次執行裡順手帶出來的「第二意見」。這個月的審計發現它一次都沒觸發過。它的「是否重要」判斷從 action 裡讀訂單數量——而交易員自己的提示詞明確禁止模型輸出這個欄位——所以這個判斷每次都看到「不重要」,與此同時設定開關顯示安全層已經啟用。這類缺陷只有在你去找「什麼東西沒有發生」時才會浮出來,這也正是角色執行次數要放進公開決策單的原因:它今天有沒有觸發,你可以自己看,不用聽任何人說。
每一個交易決策函式都是純函式——零 I/O、零副作用、可完整單測。52 週新高動能訊號(引 George & Hwang 2004)無論候選是否通過,都回傳全部診斷欄位,所以不存在「黑箱式拒絕」。核心倉再平衡器同樣是純的:沒有 I/O、沒有 LLM,只輸出一張訂單清單交給薄薄的外殼去執行。實際效果是:大約九成的交易邏輯在不接券商、不用 mock 的情況下就被單測覆蓋,而且測試過程中不存在誤下真實訂單的風險。
交易員是五個 agent 裡工具集最豐富的一個,但無論模型怎麼判斷,都要受幾道確定性關卡約束。一套冷卻機制防止它在連續幾輪裡反覆交易同一檔股票——開倉後 120 分鐘,評估後未行動 40 分鐘,而且冷卻只會延長、不會縮短。一個出場管理器給每筆平倉打上標準化原因,並把持倉超過約 30 個交易日的標記為「逾期」——這是 Triple Barrier 方法裡「時間屏障」的純函式實作,門檻從 48 小時上調過,因為實測下來它要等的漂移就是這麼長。這兩道之上還有一個每日熔斷:組合當天回撤達到或超過 2%,當天徹底停止開新倉,沒有例外。如果分析師的盤前簡報缺失或過期,交易員會硬性攔截新開倉,而不是矇著眼交易。
其中四道關卡是這個月新增的,每一道都替掉了一個「看起來像關卡」的東西。帶外急停開關可以從 agent 迴圈之外叫停交易台——在這之前,唯一的剎車是 LLM 自己寫下的風險姿態,那根本不算剎車。數字論斷接地檢查現在會記錄一條決策的理由裡的數字是否真的追溯到證據,而不是只檢查「有沒有填證據欄位」——它已經建好、在影子模式下量過,但還不允許它拒單。倉位計算被重建,因為一次線上實測顯示 Kelly 乘數塌縮到 0.0057,把每一筆都壓成同一個約 500 美元的倉位:最高信心和最低信心下出的單完全一樣。
組合被拆成兩個機械上獨立的倉。核心倉(SPY 70% / QQQ 30%,佔淨值 65%)跑零 LLM 的漂移帶再平衡——純數學,偏離超過 8% 才下單。衛星倉是剩下的約 35%,完全由 LLM 驅動,走閘道的 fast 檔,而這一檔優先使用自託管模型。這保證了就算衛星 agent 的推理徹底跑偏,核心倉也紋絲不動、依然跟著市場。資本保全是被架構進去的,不是寫在制度裡的。
每個交易日結束時,覆盤者計算盈虧歸因、在交易之間找規律,把帶日期和 ID 的教訓寫進日誌——提交並推到 Gitea,沒有向量庫、沒有 RAG;git 歷史就是記憶系統。這本日誌現在是雜湊鏈的,所以它的不可竄改來自結構,而不是來自有沒有認真讀 diff。
這個迴圈以前就到此為止,而那正是它的弱點:一條教訓能影響明天的簡報,卻沒有路徑變成一條被檢驗過的論斷。現在它閉合了。有了回測引擎,一個風險參數可以從一次運行裡論證出來,而不是從一次對話裡——在此之前,風險設定裡的每一個值都是「講道理講進去的」,從沒被量過。訊號評估迴圈會同時搜尋大量假設,現在帶上了多重檢驗校正,因為這麼寬的搜尋總會找到點什麼。訊號可以退役:以前有十一個訊號不管證據如何都能永生,現在它們的衰減由實際預測結果驅動。
迴圈也仍然把教訓升級成程式碼。在若干次「在 20 日均線下方開倉然後虧損」被記錄之後,這條門檻不再是「要記住的注意事項」,而成了風險模組裡的一條強制規則,並附帶一處有文件的例外:如果某個策略的入場條件本身就是這條均線,它仍可以在下方開倉,但倉位減半。從「我們注意到這裡出過錯」變成「程式碼把這件事算進去了」。
Python、DeepSeek API(品質檔與 fast 檔,經由共享 LLM 閘道)、Alpaca、Finnhub、EDGAR、pgvector、APScheduler、Docker、自建 Gitea Actions CI/CD。