第一階段:全局認識

模組 03:設計哲學與品質屬性理解每個設計決策背後的「為什麼」— 這決定了系統的靈魂

上一個模組我們看了系統「長什麼樣」。這個模組我們要深入理解它「為什麼」要這樣做。NautilusTrader 的每一個設計選擇都不是隨便做的,都有明確的交易場景做依據。

一、五大架構設計模式

⚠️ 進階內容 (選讀)

本節涉及較深的軟體工程與系統架構概念。如果你是量化交易新手,可以先粗略瀏覽或直接跳過本節,這完全不會影響你後續學習如何撰寫策略。

NautilusTrader 使用了五種經過驗證的軟件工程設計模式。不用擔心,我們會用交易場景來解釋每一個。

flowchart LR
                        root(("🐚 NautilusTrader<br/>設計哲學"))

                        subgraph ddd["💎 領域驅動 (DDD)"]
                        direction TB
                        ddd1["用交易語言寫代碼"]
                        ddd2["Price, Order, Position"]
                        end

                        subgraph eda["⚡ 事件驅動 (EDA)"]
                        direction TB
                        eda1["一切皆事件"]
                        eda2["即時回應變化"]
                        end

                        subgraph hexa["🔌 六角形架構"]
                        direction TB
                        hexa1["核心與外部隔離"]
                        hexa2["可替換的適配器"]
                        end

                        subgraph crash["💥 Crash-Only"]
                        direction TB
                        crash1["崩潰即恢復"]
                        crash2["快速重啟"]
                        end

                        subgraph fail["🚫 Fail-Fast"]
                        direction TB
                        fail1["錯誤數據立即拒絕"]
                        fail2["不讓壞數據擴散"]
                        end

                        root ==> ddd
                        root ==> eda
                        root ==> hexa
                        root ==> crash
                        root ==> fail

                        style root fill:#0f1419,stroke:#4fc3f7,stroke-width:2px
                        style ddd fill:#0f1419,stroke:#ab47bc,stroke-width:1px
                        style eda fill:#0f1419,stroke:#ab47bc,stroke-width:1px
                        style hexa fill:#0f1419,stroke:#ab47bc,stroke-width:1px
                        style crash fill:#0f1419,stroke:#ab47bc,stroke-width:1px
                        style fail fill:#0f1419,stroke:#ab47bc,stroke-width:1px
圖 1:NautilusTrader 的五大設計哲學

1.1 領域驅動設計 (DDD)

📖 用交易的語言寫代碼

核心思想:代碼應該用交易員能理解的「語言」來寫,而不是用程式設計師的抽象概念。

在 NautilusTrader 中,你會看到這些名字:

交易概念代碼類型說明
價格Price不是普通的浮點數,而是帶精度的定點數,避免 0.1 + 0.2 ≠ 0.3 的問題
數量Quantity同樣是定點數,保證計算精確
訂單Order完整的訂單對象,追蹤從建立到完成的每個狀態
持倉Position由多個事件聚合而成,即時反映盈虧
金融工具Instrument股票、期貨、期權等,每種都有專門的子類
帳戶Account現金帳戶、保證金帳戶,各有不同的結算邏輯
💡 為什麼這很重要?

因為交易員看代碼時,能直接看懂。你不需要猜「float price」代表什麼精度。Price("1.23450", precision=5) 一目了然:這是一個 5 位小數精度的價格。

1.2 事件驅動架構

⚡ 一切皆事件

核心思想:系統不是按順序跑的,而是「有事件發生時才行動」。

想像一下兩種不同的交易方式:

❌

傳統方式(輪詢)

每秒查一次:「價格變了嗎?」「有新 K 線嗎?」「訂單成交了嗎?」

→ 浪費大量資源在「什麼都沒發生」的時候

✅

事件驅動方式

系統告訴你:「新 K 線來了!」「你的訂單成交了!」

→ 只在有事件時才行動,高效且即時

在 NautilusTrader 中,你的策略會「訂閱」感興趣的事件,然後等系統推送:

sequenceDiagram
                        participant S as 🎯 你的策略
                        participant MB as 📡 消息總線

                        Note over S: on_start() 啟動時
                        S->>MB: 訂閱 BTC/USDT 的 1 分鐘 K 線
                        S->>MB: 訂閱 BTC/USDT 的報價
                        Note over S: 等待事件...
                        MB->>S: on_bar(bar) — 新 K 線到了!
                        S->>S: 計算指標、判斷信號
                        MB->>S: on_quote_tick(tick) — 新報價到了!
                        S->>S: 更新即時數據
                        MB->>S: on_order_filled(event) — 訂單成交了!
                        S->>S: 處理成交邏輯
圖 2:事件驅動的策略運作方式

1.3 六角形架構(Ports and Adapters)

🔌 策略不知道自己連接的是誰

核心思想:核心業務邏輯和外部世界完全隔離。透過「端口」(Port) 和「適配器」(Adapter) 連接。

flowchart LR
                        subgraph 外部_左["外部數據"]
                        binance_data["Binance WS"]
                        ib_data["IB API"]
                        file_data["歷史文件"]
                        end

                        subgraph 端口_左["📥 數據端口"]
                        dc["DataClient<br/>(介面)"]
                        end

                        subgraph 核心["🧠 核心業務邏輯"]
                        de["DataEngine"]
                        strategy["你的策略"]
                        ee["ExecutionEngine"]
                        end

                        subgraph 端口_右["📤 執行端口"]
                        ec["ExecutionClient<br/>(介面)"]
                        end

                        subgraph 外部_右["外部交易所"]
                        binance_exec["Binance API"]
                        ib_exec["IB API"]
                        sim_exec["模擬器"]
                        end

                        binance_data --> dc
                        ib_data --> dc
                        file_data --> dc
                        dc --> de --> strategy --> ee --> ec
                        ec --> binance_exec
                        ec --> ib_exec
                        ec --> sim_exec
圖 3:六角形架構 — 核心不依賴任何特定交易所
✅ 對你的直接好處

這就是為什麼你的策略代碼在回測和實盤中完全一樣:策略只和「端口」互動,不知道背後是模擬器還是 Binance。換交易所只需要換一個適配器。

1.4 Crash-Only 設計

💥 崩潰 = 重啟的一種方式

核心思想:與其花大量精力寫「優雅關閉」的代碼,不如確保系統能從崩潰中快速恢復。

🔄

統一恢復路徑

啟動和崩潰恢復走同一條代碼路徑。因為每次啟動都在「恢復」,所以這條路徑被充分測試。

💾

外部化狀態

關鍵狀態(訂單、持倉)持久化到外部存儲(如 Redis)。崩潰也不會丟失狀態。

⚡

快速重啟

設計為能在崩潰後毫秒級重啟,最小化停機時間。

🔁

冪等操作

操作設計為可以安全重試。重啟後重新執行不會產生副作用。

⚠️ 交易場景

想像凌晨 3 點你的交易系統崩潰了。Crash-Only 設計意味著系統會自動重啟,從 Redis 恢復所有訂單和持倉狀態,然後繼續運行。你可能連睡夢中都不知道發生過崩潰。

1.5 Fail-Fast 數據完整性策略

🚫 壞數據?立即拒絕!

核心思想:在交易系統中,腐壞的數據比沒有數據更危險。發現錯誤數據時立即停止,絕不讓它擴散。

flowchart LR
                        subgraph 壞數據場景["😱 如果沒有 Fail-Fast"]
                        bad_price["收到 NaN 價格"] --> calc["計算指標"] --> signal["產生錯誤信號"] --> order["下錯誤的訂單"] --> loss["💸
                        虧損"]
                        end
flowchart LR
                        subgraph 好的場景["✅ Fail-Fast 保護"]
                        bad_price2["收到 NaN 價格"] --> reject["❌ 立即拒絕並報錯"] --> log["記錄錯誤日誌"] --> safe["系統安全"]
                        end
圖 4:Fail-Fast 防止錯誤數據擴散

以下情況系統會立即停止(Panic):

情況例子為什麼要停止
算術溢出時間戳相加超過 u64 上限結果會是錯誤的數值
無效數據價格是 NaN 或 Infinity會導致後續所有計算錯誤
類型轉換失敗負數量、負時間戳這些在物理上不可能存在
格式錯誤無法解析的價格字串說明數據源有問題

二、品質屬性優先級

在設計系統時,不同的需求會互相衝突。NautilusTrader 按以下優先級做取捨:

flowchart LR
                        r["🥇 可靠性 (可靠第一)"] ==> p["🥈 性能 (速度其次)"]
                        p ==> m["🥉 模組化 (結構優雅)"]
                        m ==> t["4️⃣ 其它品質屬性 (測試、維護、部署)"]
圖 5:品質屬性優先級排序
⚠️ 優先級取捨的實際意義

這個排序解釋了很多設計選擇。比如:系統在遇到可疑數據時會選擇崩潰(可靠性優先)而不是嘗試修復繼續運行(可用性優先)。因為在交易中,一個錯誤的決策比短暫的停機更危險。

三、組件狀態機

NautilusTrader 中的每個組件(引擎、策略、適配器)都遵循相同的狀態機模式。這意味著它們有明確定義的「生命階段」:

stateDiagram-v2
                        [*] --> PRE_INITIALIZED : 剛建立

                        PRE_INITIALIZED --> READY : register() 註冊

                        READY --> STARTING : start() 啟動中
                        STARTING --> RUNNING : 啟動完成

                        RUNNING --> STOPPING : stop() 停止中
                        STOPPING --> STOPPED : 停止完成

                        STOPPED --> STARTING : start() 重新啟動
                        STOPPED --> RESETTING : reset() 重置中
                        RESETTING --> READY : 重置完成

                        RUNNING --> DEGRADING : degrade() 降級中
                        DEGRADING --> DEGRADED : 進入降級狀態

                        RUNNING --> FAULTING : fault() 故障中
                        FAULTING --> FAULTED : 進入故障狀態

                        DEGRADED --> STOPPING : stop()
                        DEGRADED --> FAULTING : fault()

                        STOPPED --> DISPOSING : dispose() 釋放中
                        FAULTED --> DISPOSING : dispose()
                        DISPOSING --> DISPOSED : 已釋放

                        DISPOSED --> [*]
圖 6:組件生命週期狀態機 — 每個方框都是一個明確的狀態

穩定狀態 vs 過渡狀態

狀態類型說明
PRE_INITIALIZED穩定剛建立,還沒配置好
READY穩定已配置,可以啟動
RUNNING穩定正常運行中
STOPPED穩定已停止
DEGRADED穩定降級運行(部分功能受限)
FAULTED穩定故障狀態
DISPOSED穩定已釋放所有資源
STARTING, STOPPING, …過渡正在轉換中,不會長時間停留
💡 交易場景

當交易所的 WebSocket 斷線時,對應的適配器會從 RUNNING 轉為 DEGRADED(降級)。系統繼續用緩存中的數據運行,同時嘗試重新連接。如果重連成功,回到 RUNNING。如果重連失敗,可能轉為 FAULTED。

四、Actor vs Component 特性

在 Rust 的底層實現中,系統區分了兩種「角色」:

🎭

Actor(演員)

功能:接收和處理消息
方法:handle(message)
特點:輕量、專注於消息處理
例子:節流器 (Throttler)

🔧

Component(組件)

功能:管理生命週期
方法:start(), stop(), reset()
特點:有狀態機、有生命週期
例子:DataEngine, ExecutionEngine

有些東西同時具備兩種特性:

flowchart LR
                        subgraph actors["🎭 Actor 特性 (消息處理)"]
                        direction TB
                        throttler["Throttler (節流器)"]
                        end

                        subgraph hybrid["🧩 雙重特性"]
                        direction TB
                        strategy["Strategy (你的策略)"]
                        end

                        subgraph components["🔧 Component 特性 (生命週期)"]
                        direction TB
                        de["DataEngine (數據引擎)"]
                        ee["ExecutionEngine (執行引擎)"]
                        end

                        actors --- hybrid --- components
圖 7:Actor 和 Component 特性的分離與組合
💡 為什麼要分開?

分離的好處是靈活組合。你的策略需要接收消息(Actor)也需要被啟動/停止(Component),所以它兩者都是。但節流器只是一個消息轉發器,它不需要複雜的生命週期管理,所以只做 Actor 就夠了。

五、線程模型

這是一個非常重要的技術決策,直接影響系統的性能和行為:

🧵 核心定律:一個線程統治一切

NautilusTrader 的核心處理邏輯在單一線程上運行。這包括消息總線上的所有消息調度、策略邏輯、風控檢查、訂單管理。

flowchart LR
                        subgraph single["🧵 單線程核心 (確定性執行)"]
                        direction TB
                        logic["消息調度 / 策略邏輯<br/>風控檢查 / 持倉更新"]
                        end

                        subgraph multi["🔀 多線程 IO (高併發服務)"]
                        direction TB
                        io["WebSocket / API / DB<br/>數據持久化"]
                        end

                        multi ==>|"事件推送"| single
                        single ==>|"指令下發"| multi

                        style single fill:#0f1419,stroke:#4fc3f7,stroke-width:2px
                        style multi fill:#0f1419,stroke:#ab47bc,stroke-width:1px
圖 8:單線程核心 + 多線程 IO 的線程模型
✅

單線程核心的好處

• 事件順序確定性保證
• 不需要加鎖(無鎖競爭)
• 回測和實盤行為一致
• 更容易調試

⚡

多線程 IO 的好處

• 網絡操作不阻塞核心邏輯
• WebSocket 連接在獨立線程
• 數據庫操作不影響策略速度
• 充分利用多核 CPU

💡 LMAX 交易所的啟發

這個設計靈感來自 LMAX 交易所的 Disruptor 架構。LMAX 是一家真實的外匯交易所,它用單線程處理每秒600 萬筆交易,證明了單線程架構在交易場景中的卓越性能。

六、流程限制:一個進程一個節點

🚨 重要限制

不能在同一個進程中同時運行多個 TradingNode 或 BacktestNode。原因是系統內部有全局共享的單例資源(日誌系統、全局運行時等)。

正確做法:

  • 在一個 TradingNode 中增加多個策略 ✅
  • 用多個進程分別運行不同的節點 ✅
  • 依次運行多個回測(先結束再開始下一個)✅

七、互動練習

🧠 練習 1:設計決策推理

以下場景中,NautilusTrader 會怎麼做?請選擇並解釋原因:

  1. 收到一個價格為 -5.00 的報價 → 系統會 ____
    A) 忽略這筆數據繼續運行
    B) 立即崩潰報錯
  2. WebSocket 連接斷開 3 秒 → 適配器狀態變為 ____
    A) FAULTED
    B) DEGRADED
  3. 策略代碼要從 Binance 切到 OKX → 需要修改 ____
    A) 只改配置文件
    B) 重寫策略代碼
1. B) 立即崩潰報錯 → Fail-Fast 策略。負數價格在物理上不可能,說明數據已損壞。 繼續運行會導致錯誤的交易決策。2. B) DEGRADED(降級) → 短暫斷線進入降級狀態,系統繼續用緩存數據運行, 同時嘗試重連。只有確認無法恢復時才會變為 FAULTED。3. A) 只改配置文件 → 六角形架構的威力。策略只和端口交互, 不知道背後是 Binance 還是 OKX。 換交易所只需要把適配器從 BinanceAdapter 換成 OKXAdapter。
🧠 練習 2:找出設計模式

以下 NautilusTrader 的特性分別體現了哪種設計模式?

  1. 訂單對象叫 Order 而不是 DataPacket → ________
  2. 策略的 on_bar() 回調在有新 K 線時自動觸發 → ________
  3. 系統崩潰後從 Redis 恢復狀態 → ________
  4. 同一策略可以切換不同交易所而不改代碼 → ________
1. 領域驅動設計 (DDD) — 用交易領域的語言命名 2. 事件驅動架構 — 由事件觸發而非輪詢 3. Crash-Only 設計 — 統一恢復路徑、外部化狀態 4. 六角形架構 (Ports and Adapters) — 核心業務與外部解耦

八、本模組重點回顧

📋 你應該記住的關鍵點
  1. 五大設計模式:DDD、事件驅動、六角形架構、Crash-Only、Fail-Fast
  2. 品質優先級:可靠性 > 性能 > 模組化 > 可測試性 > 可維護性 > 可部署性
  3. 組件狀態機:有 7 個穩定狀態和 5 個過渡狀態,所有組件遵循相同規則
  4. 單線程核心:保證事件順序確定性;網絡 IO 在後台線程
  5. Fail-Fast:壞數據立即被拒絕,不會污染系統
  6. 六角形架構:這是實現「回測 = 實盤」的核心技術手段