第三階段:實戰應用

模組 11:策略開發完全指南從零開始寫你的第一個交易策略

NautilusTrader 的策略開發遵循一個簡單的模式: 繼承基類 → 實現回調 → 填入邏輯。這個模組會教你從零開始寫出完整的交易策略。

💡 為什麼要用 Strategy 基類,而不是自己寫?

❌ 如果自己從零寫交易邏輯:你需要自己處理數據訂閱、訂單管理、持倉追蹤、日誌記錄、風控對接… 至少 2000 行代碼才能做到 Strategy 基類免費給你的功能。

✅ 用 Strategy 基類:你只需要關心「什麼時候買、什麼時候賣」。其餘的基礎設施(緩存、消息總線、訂單工廠、Portfolio 查詢)全部自動注入。

一、Strategy 基類 — 你的策略框架

所有策略都繼承自 Strategy 基類。它提供了完整的「腳手架」,你只需要填入交易邏輯:

flowchart LR
                        subgraph lifecycle["🔄 策略生命週期"]
                        direction TB
                        init["__init__<br/>初始化配置和指標"]
                        start["on_start()<br/>訂閱數據、初始化邏輯"]
                        running["運行中"]
                        stop["on_stop()<br/>清理資源、平倉"]
                        init --> start --> running --> stop
                        end

                        subgraph callbacks["📡 事件回調 (運行中)"]
                        direction TB
                        on_bar["on_bar(bar)"]
                        on_quote["on_quote_tick(tick)"]
                        on_trade["on_trade_tick(tick)"]
                        on_filled["on_order_filled(event)"]
                        end

                        running ==> callbacks

                        style lifecycle fill:#0f1419,stroke:#4fc3f7,stroke-width:1px
                        style callbacks fill:#0f1419,stroke:#ab47bc,stroke-width:1px
圖 1:策略的生命週期和回調方法

二、策略模板 — 最小可運行策略

from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.config import StrategyConfig
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.model.data import Bar, BarType


class MyStrategyConfig(StrategyConfig, frozen=True):
    """策略配置 — 所有可調參數放這裡"""
    instrument_id: InstrumentId
    bar_type: BarType
    fast_ema_period: int = 10
    slow_ema_period: int = 20


class MyStrategy(Strategy):
    """你的策略 — 填入交易邏輯"""

    def __init__(self, config: MyStrategyConfig):
        super().__init__(config)
        # 保存配置
        self.instrument_id = config.instrument_id
        self.bar_type = config.bar_type

        # 初始化指標
        self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)

    def on_start(self):
        """策略啟動時調用 — 訂閱數據"""
        # 訂閱 K 線數據
        self.subscribe_bars(self.bar_type)
        # 記錄日誌
        self.log.info("策略已啟動")

    def on_bar(self, bar: Bar):
        """收到新 K 線時調用 — 核心交易邏輯"""
        # 更新指標
        self.fast_ema.update_raw(bar.close.as_double())
        self.slow_ema.update_raw(bar.close.as_double())

        # 等指標準備好
        if not self.fast_ema.initialized:
            return

        # ===== 交易邏輯 =====
        # 快線上穿慢線 → 買入
        if self.fast_ema.value > self.slow_ema.value:
            if self.portfolio.is_flat(self.instrument_id):
                self._buy()

        # 快線下穿慢線 → 賣出
        elif self.fast_ema.value < self.slow_ema.value:
            if self.portfolio.is_net_long(self.instrument_id):
                self._sell()

    def _buy(self):
        """建立多頭持倉"""
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            quantity=Quantity.from_str("0.01"),
        )
        self.submit_order(order)

    def _sell(self):
        """平掉多頭持倉"""
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=Quantity.from_str("0.01"),
        )
        self.submit_order(order)

    def on_order_filled(self, event):
        """訂單成交回調"""
        self.log.info(f"訂單成交: {event}")

    def on_stop(self):
        """策略停止時調用 — 清理"""
        self.cancel_all_orders(self.instrument_id)
        self.close_all_positions(self.instrument_id)
        self.log.info("策略已停止")
✅ 這就是完整的策略了!

不到 80 行代碼,你就擁有了一個完整的 EMA 交叉策略:有配置、有指標、有交易邏輯、有風控(透過全局 RiskEngine)。同樣的代碼可以直接用在回測和實盤中。

三、核心回調方法一覽

回調方法觸發時機常用操作
on_start()策略啟動訂閱數據、載入歷史指標值
on_stop()策略停止取消訂單、平倉
on_bar(bar)收到新 K 線更新指標、判斷開平倉
on_quote_tick(tick)收到新報價觀察買賣價差
on_trade_tick(tick)收到新成交觀察成交量和方向
on_order_filled(event)訂單成交記錄日誌、調整持倉
on_order_rejected(event)訂單被拒記錄錯誤、重試
on_order_canceled(event)訂單被取消清理掛單記錄
on_position_opened(event)新持倉建立設置止損
on_position_closed(event)持倉關閉記錄盈虧
on_event(event)任何事件通用事件處理

四、指標系統

NautilusTrader 內建了豐富的技術指標:

類別指標
趨勢EMA, SMA, WMA, DEMA, TEMA, KAMA, HMA
動量RSI, MACD, Stochastic, CCI, ROC, Williams %R
波動率ATR, Bollinger Bands, Keltner Channel, Donchian
成交量OBV, VWAP, MFI, AD
統計Standard Deviation, Variance, Linear Regression
# 使用指標的三步曲
# 1. __init__ 中建立
self.rsi = RelativeStrengthIndex(period=14)

# 2. on_bar 中更新
self.rsi.update_raw(bar.close.as_double())

# 3. 讀取值
if self.rsi.initialized and self.rsi.value > 70:
    # RSI 超買 → 考慮賣出
    pass

四B、Actor vs Strategy — 什麼時候該用哪個?

💡 為什麼要有 Actor?Strategy 不夠嗎?

❌ 如果把所有邏輯都塞進 Strategy:你的策略會變成「超級大類」,既做數據處理又做交易決策。難以測試、難以複用。

✅ 用 Actor + Strategy:Actor 負責「觀察和計算」(如生成信號),Strategy 負責「交易」。它們之間用 MessageBus 通訊,完全解耦。

特性ActorStrategy
能訂閱數據✅✅
能下單❌✅
有 order_factory❌✅
有 portfolio 訪問✅(唯讀)✅
典型用途信號生成、數據處理、監控交易邏輯、訂單管理
"""Actor → Strategy 協作範例:信號生成器 + 交易執行器"""
from nautilus_trader.common.actor import Actor
from nautilus_trader.common.signal import Signal

class SignalGenerator(Actor):
    """Actor:只生成信號,不做交易"""
    def on_bar(self, bar: Bar):
        rsi = self._calculate_rsi(bar)
        if rsi < 30:
            # 發布「建議買入」信號到 MessageBus
            self.publish_signal(name="BUY", value=rsi)
        elif rsi > 70:
            self.publish_signal(name="SELL", value=rsi)

class TradeExecutor(Strategy):
    """Strategy:接收信號並執行交易"""
    def on_start(self):
        self.subscribe_signal(name="BUY")
        self.subscribe_signal(name="SELL")

    def on_signal(self, signal: Signal):
        if signal.name == "BUY" and self.portfolio.is_flat(self.instrument_id):
            self._buy()
        elif signal.name == "SELL" and self.portfolio.is_net_long(self.instrument_id):
            self._sell()

四C、OrderFactory — 進階下單模式

self.order_factory 是你在策略中下單的唯一工具。它能創建所有 9 種訂單,這裡展示最實用的幾個模式:

模式 1:Bracket Order(括號訂單 = 入場 + 止損 + 止盈)

"""一次下三個訂單:市價入場 + 止損 + 止盈"""
from nautilus_trader.model.orders import OrderList

def _open_position_with_bracket(self):
    # OrderList 把三個訂單綁在一起
    bracket = self.order_factory.bracket(
        instrument_id=self.instrument_id,
        order_side=OrderSide.BUY,
        quantity=Quantity.from_str("0.01"),
        # 入場用市價單
        entry_trigger_price=None,  # 市價入場
        # 止損設在入場價下方 2%
        sl_trigger_price=Price.from_str("49000.00"),
        # 止盈設在入場價上方 4%
        tp_price=Price.from_str("52000.00"),
    )
    # 一次提交整個 bracket
    self.submit_order_list(bracket)
    # 當入場單成交後,止損和止盈自動啟動
    # 當止損或止盈任一觸發,另一個自動取消(OCO)

模式 2:Trailing Stop(移動止損)

"""用 ATR 動態計算止損距離"""
def _set_trailing_stop(self):
    atr_value = self.atr.value  # ATR 指標的當前值

    order = self.order_factory.trailing_stop_market(
        instrument_id=self.instrument_id,
        order_side=OrderSide.SELL,  # 賣出止損
        quantity=Quantity.from_str("0.01"),
        # 止損距離 = 2 倍 ATR
        trailing_offset=Decimal(str(atr_value * 2)),
        trailing_offset_type=TrailingOffsetType.PRICE,
        trigger_type=TriggerType.LAST_TRADE,
    )
    self.submit_order(order)
    # 價格上漲時,止損價跟著上移
    # 價格下跌超過 2×ATR 時,觸發止損

五、官方範例策略解讀

NautilusTrader 附帶了 17 個官方範例策略,涵蓋各種交易模式。以下是精選的 5 個最有學習價值的:

📐

ema_cross.py

EMA 交叉策略
最基礎的趨勢跟蹤策略。快 EMA 上穿慢 EMA 做多,反之做空。學習策略基本結構的最佳起點。

📊

ema_cross_bracket.py

EMA 交叉 + 括號訂單
在 EMA 交叉基礎上加入止損止盈(Bracket Order),展示條件單組合的使用。

📈

ema_cross_trailing_stop.py

移動止損策略
使用 ATR 指標動態計算止損距離,展示如何用移動止損鎖定利潤。

📖

orderbook_imbalance.py

訂單簿失衡策略
基於訂單簿買賣壓力失衡做交易。展示如何使用 Level 2 數據。

🎯

market_maker.py

做市商策略
在買賣兩側掛單賺取價差。展示高頻交易和庫存管理的基本邏輯。

六、策略開發最佳實踐

1️⃣

配置分離

所有可調參數放在 StrategyConfig 中,不要硬編碼在策略內。方便回測時進行參數優化。

2️⃣

等待指標初始化

在 on_bar() 中,必須檢查 indicator.initialized,否則前幾根 K 線的指標值是不準確的。

3️⃣

記錄充分日誌

用 self.log.info() 記錄關鍵決策點。回測時可以回顧每一個交易決策的原因。

4️⃣

on_stop() 清理

策略停止時務必取消所有掛單和平倉。避免殘留訂單在沒有監控的情況下執行。

5️⃣

檢查持倉再下單

下單前先用 self.portfolio.is_flat() 檢查持倉狀態,避免重複加倉。

6️⃣

使用訂單工廠

始終用 self.order_factory 創建訂單,它會自動生成唯一的 ClientOrderId 和正確的 StrategyId。

七、互動練習

🧠 練習 1:設計一個 RSI 策略

設計一個基於 RSI 的策略:RSI > 70 時賣出(超買),RSI < 30 時買入(超賣)。列出你需要的回調方法和邏輯流程。

需要的結構:

  1. __init__: 建立 RSI 指標(period=14)
  2. on_start: 訂閱 K 線數據
  3. on_bar:
    • 更新 RSI:self.rsi.update_raw(bar.close.as_double())
    • 檢查初始化:if not self.rsi.initialized: return
    • RSI < 30 且無持倉 → self._buy()
    • RSI > 70 且有多頭持倉 → self._sell()
  4. on_order_filled: 記錄成交日誌
  5. on_stop: 取消所有訂單、平倉

💡 額外加分:

  • 加入 ATR 計算止損距離
  • RSI 從超賣區回升(穿過 30)時才買入
  • 設置最大持倉限制
🧠 練習 2:找出代碼錯誤

以下策略代碼有 3 個錯誤,你能找到嗎?

class BuggyStrategy(Strategy):
    def __init__(self, config):
        self.ema = ExponentialMovingAverage(20)  # 🤔 錯誤 1

    def on_start(self):
        self.subscribe_bars(self.config.bar_type)

    def on_bar(self, bar: Bar):
        self.ema.update_raw(bar.close)            # 🤔 錯誤 2
        if self.ema.value > bar.close.as_double():
            self._buy()

    def _buy(self):
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            quantity=Quantity.from_str("0.01"),
        )
        self.submit_order(order)                  # 🤔 錯誤 3(邏輯問題)

❌ 錯誤 1: 缺少 super().__init__(config)

  • 沒有初始化父類,self.cache, self.log 等全部不可用
  • ✅ 正確: 加上 super().__init__(config)

❌ 錯誤 2: bar.close 是 Price 對象,不是 float

  • update_raw() 需要 float
  • ✅ 正確: self.ema.update_raw(bar.close.as_double())
    (這個寫對了一半:as_double() 需要加括號)

❌ 錯誤 3: 沒有檢查持倉狀態就下單

  • 每根 K 線都會觸發買入,越買越多
  • ✅ 正確: 先檢查 if self.portfolio.is_flat(self.instrument_id)
🧠 練習 3:Actor 還是 Strategy?

以下場景,你應該用 Actor 還是 Strategy?

  1. 你想持續監控 BTC 和 ETH 的價差,當價差超過閾值時記錄日誌
  2. 你想根據 RSI 信號自動買賣
  3. 你想從外部 API 獲取情緒數據並廣播到系統中
  4. 你想做一個統計套利策略,同時在兩個品種上下單
  1. Actor ← 只是觀察和記錄,不需要下單
  2. Strategy ← 需要下單
  3. Actor ← 數據處理和廣播,不涉及交易
  4. Strategy ← 需要在多個品種上下單

💡 判斷標準很簡單:

需要下單 → Strategy

不需要下單 → Actor

八、本模組重點回顧

📋 關鍵點
  1. Strategy 基類提供完整的框架,你只需填入邏輯
  2. 生命週期:init → on_start → 運行(回調)→ on_stop
  3. 事件回調:on_bar、on_quote_tick、on_order_filled 等
  4. 指標系統:30+ 內建指標,三步使用
  5. 最佳實踐:配置分離、等待初始化、記錄日誌、停止時清理
  6. 官方範例:從 ema_cross.py 開始學習