第三階段:實戰應用

模組 12:回測系統與實盤部署同一份策略代碼,從歷史回測無縫切換到實盤交易

一、三種運行環境

flowchart LR
                        subgraph bt["🔬 回測 Backtest"]
                        hist["歷史數據"] --> sim1["模擬交易所"]
                        sim1 --> report["績效報告"]
                        end
                        subgraph sb["🏖️ 沙箱 Sandbox"]
                        live_data["即時數據"] --> sim2["模擬交易所"]
                        sim2 --> monitor["即時監控"]
                        end
                        subgraph lv["🚀 實盤 Live"]
                        live_data2["即時數據"] --> real["真實交易所"]
                        real --> pnl["真金白銀"]
                        end

                        bt -->|"驗證通過"| sb
                        sb -->|"觀察一段時間"| lv
圖 1:策略的三階段驗證流程
⚠️ 核心優勢

在這三種環境中,你的策略代碼完全不需要修改。只需要改變配置中的數據源和交易所適配器。這就是 NautilusTrader 的核心價值主張之一。

💡 為什麼回測配置需要這麼複雜?

❌ 如果只用簡單的 「price * quantity」 回測:你會忽略手續費、滑點、延遲,回測結果可能比實盤好 30-50%,上線後才發現全部虎頭蛇尾。

✅ 用 NautilusTrader 的真實模擬:配置 FillModel(滑點)、FeeModel(手續費)、LatencyModel(延遲),回測結果更接近實盤。

二、回測配置完整範例

from nautilus_trader.backtest.engine import BacktestEngine
from nautilus_trader.backtest.config import (
    BacktestEngineConfig,
    BacktestVenueConfig,
    BacktestDataConfig,
    BacktestRunConfig,
)
from nautilus_trader.model.enums import AccountType, OmsType

# ===== 1. 配置引擎 =====
engine_config = BacktestEngineConfig(
    trader_id="BACKTESTER-001",
    logging=LoggingConfig(log_level="INFO"),
    risk_engine=RiskEngineConfig(
        max_order_submit_rate="100/00:00:01",
    ),
)

# ===== 2. 配置模擬交易所 =====
venue_config = BacktestVenueConfig(
    name="BINANCE",
    oms_type=OmsType.NETTING,        # 淨額結算
    account_type=AccountType.MARGIN,  # 保證金帳戶
    starting_balances=["100_000 USDT"],
    default_leverage=10,              # 10 倍槓桿
    fill_model=FillModel(
        prob_fill_on_limit=0.2,       # 限價單成交概率
        prob_fill_on_stop=0.95,       # 止損單成交概率
        prob_slippage=0.5,            # 滑點概率
    ),
)

# ===== 3. 配置數據源 =====
data_config = BacktestDataConfig(
    catalog_path="./data/catalog",
    data_cls="QuoteTick",
    instrument_id="BTCUSDT.BINANCE",
    start_time="2023-01-01",
    end_time="2023-12-31",
)

# ===== 4. 配置策略 =====
strategy_config = MyStrategyConfig(
    instrument_id=InstrumentId.from_str("BTCUSDT.BINANCE"),
    bar_type=BarType.from_str("BTCUSDT.BINANCE-1-MINUTE-LAST"),
    fast_ema_period=10,
    slow_ema_period=20,
)

# ===== 5. 運行回測 =====
engine = BacktestEngine(config=engine_config)
engine.add_venue(venue_config)
engine.add_data(data_config)
engine.add_strategy(MyStrategy(strategy_config))

engine.run()

# ===== 6. 查看結果 =====
engine.trader.generate_order_fills_report()
engine.trader.generate_positions_report()
engine.trader.generate_account_report()

三、數據目錄 (Data Catalog)

NautilusTrader 使用 Parquet 格式高效存儲回測數據:

📁

數據寫入

適配器自動將收到的數據寫入 Parquet 文件。也可以手動從 CSV 導入。

⚡

高效讀取

Parquet 是列式格式,讀取 1 年的分鐘 K 線只需要幾秒鐘。

🔍

按需加載

支援只加載指定時間範圍和品種的數據,節省記憶體。

# 將數據寫入目錄
from nautilus_trader.persistence.catalog import ParquetDataCatalog

catalog = ParquetDataCatalog("./data/catalog")

# 寫入報價數據
catalog.write_data(quote_ticks)

# 讀取數據(按時間範圍)
ticks = catalog.quote_ticks(
    instrument_ids=["BTCUSDT.BINANCE"],
    start="2023-01-01",
    end="2023-06-30",
)

三B、FillModel / FeeModel / LatencyModel — 確保回測真實

這三個 Model 是區分「玩具回測」和「專業回測」的關鍵:

Model控制什麼為什麼重要
FillModel訂單成交的滑點和部分成交。可模擬「市價單滑 2 tick」、「限價單只成交 80%」實盤中幾乎不可能總以理想價格成交
FeeModel交易手續費。可配置 maker/taker 費率,也可配置固定手續費高頻策略每天數百筆交易,手續費累積影響巨大
LatencyModel網路延遲。可模擬「提交訂單到交易所確認」的時間差實盤中延遲可能導致錯過最佳入場點
"""配置真實的回測環境"""
from nautilus_trader.backtest.models import (
    FillModel, FeeModel, LatencyModel,
)

# 滑點模型:市價單滑 1 tick,限價單 80% 機率全額成交
fill_model = FillModel(
    prob_fill_on_limit=0.80,    # 限價單 80% 機率成交
    prob_fill_on_stop=0.95,     # 停損單 95% 機率成交
    prob_slippage=0.50,         # 50% 機率有滑點
    random_seed=42,             # 固定隨機種子,確保可重現
)

# 手續費模型:make 單 0.02%, take 單 0.04%
fee_model = FeeModel(
    maker_fee=0.0002,
    taker_fee=0.0004,
)

# 延遲模型:提交 50ms, 取消 30ms
latency_model = LatencyModel(
    submit_order_latency_ms=50,
    cancel_order_latency_ms=30,
)

# 將 model 加入交易所配置
venue_config = BacktestVenueConfig(
    name="BINANCE",
    oms_type=OmsType.NETTING,
    account_type=AccountType.MARGIN,
    starting_balances=["100_000 USDT"],
    fill_model=fill_model,
    fee_model=fee_model,
    latency_model=latency_model,  # 可選
)

三C、Tearsheet — 回測結果可視化

跑完回測後,一行代碼生成完整的互動式績效報表:

from nautilus_trader.analysis import create_tearsheet

# 回測結束後,用這一行生成報表
create_tearsheet(engine, output_path="tearsheet.html")
# → 打開 tearsheet.html 就能看到互動式 Plotly 圖表!

# 也可以單獨生成某一張圖
from nautilus_trader.analysis import (
    create_equity_curve,        # 資金曲線
    create_drawdown_chart,      # 回撤圖
    create_monthly_returns_heatmap,  # 月度熱力圖
    create_rolling_sharpe,      # 滾動 Sharpe
    create_returns_distribution,# 收益分布
)

# 資金曲線圖
fig = create_equity_curve(
    engine,
    output_path="equity.html",
    title="My EMA Strategy",
)
📈

Equity Chart

資金曲線,可疊加 Benchmark 對比

📉

Drawdown Chart

回撤深度和持續時間

📅

Monthly Returns

月度收益熱力圖,一眼看出季節性

📊

Rolling Sharpe

滾動 Sharpe 比率,觀察策略穩定性

🔔

Returns Distribution

收益分布圖,看是否偏斜

🕯️

Bars with Fills

K 線 + 買賣點標記

四、實盤部署

# 實盤配置 — 只改適配器,策略代碼不變!
from nautilus_trader.live.node import TradingNode
from nautilus_trader.adapters.binance.config import (
    BinanceLiveDataClientConfig,
    BinanceLiveExecClientConfig,
)

node = TradingNode(config=TradingNodeConfig(
    trader_id="LIVE-001",
    data_clients={
        "BINANCE": BinanceLiveDataClientConfig(
            api_key="your-api-key",
            api_secret="your-api-secret",
        ),
    },
    exec_clients={
        "BINANCE": BinanceLiveExecClientConfig(
            api_key="your-api-key",
            api_secret="your-api-secret",
        ),
    },
))

# 加入同一個策略!
node.trader.add_strategy(MyStrategy(strategy_config))
node.run()
✅ 注意看

策略代碼和配置一模一樣。唯一改變的是 DataClient 和 ExecClient 從模擬變成了 Binance 真實適配器。

實盤安全機制

NautilusTrader 為實盤提供了幾個關鍵的安全機制:

機制作用來源
Reconciliation線上訂單對帳:定期檢查本地狀態和交易所是否一致live/reconciliation.py
Retry訂單提交失敗自動重試(可配置重試次數和間隔)live/retry.py
Heartbeat定時心跳檢測,斷線自動重連各 Adapter 內建

五、回測常見陷阱

陷阱說明如何避免
前視偏差使用了未來才知道的數據做決策只用 on_bar 中收到的 bar 數據
過擬合策略只在特定歷史數據上表現好分成訓練集和測試集
忽略手續費不計入手續費導致虛假盈利配置真實的 maker/taker 費率
忽略滑點假設總以理想價格成交配置 FillModel 滑點
存活者偏差只測試現在還存在的品種包含已退市品種的數據

七、互動練習

🧠 練習 1:從回測到實盤

你的 EMA 策略回測結果很好,你需要修改哪些東西才能實盤運行?列出具體步驟。

步驟(只改配置,不改策略代碼):

  1. ❌ 不需要修改策略代碼
  2. ✅ 將 BacktestEngine 改為 TradingNode
  3. ✅ 將數據源從 BacktestDataConfig 改為 BinanceLiveDataClientConfig
  4. ✅ 將執行客戶端改為 BinanceLiveExecClientConfig
  5. ✅ 填入真實的 API Key 和 Secret
  6. ✅ 調整風控參數(實盤應更保守)
  7. ✅ 建議先用小資金的沙箱模式觀察一段時間

總結:策略邏輯 0 行改動,只改「基礎設施」配置。

🧠 練習 2:診斷回測問題

你的策略回測顯示年化回報 200%,但上實盤後卻持續虧損。可能是什麼原因?

可能原因(按可能性排序):

  1. 沒配置手續費 (FeeModel)
    • 回測時 0 手續費,實際每筆要扣 0.04%
  2. 沒配置滑點 (FillModel)
    • 回測假設總是以理想價成交,實際可能滑 1-3 tick
  3. 過擬合 (前視偏差)
    • 參數只在歷史數據上最優,未來失效
    • 使用 Walk-Forward 或 Out-of-Sample 測試
  4. 存活者偏差
    • 只測了現在還在交易的品種,忽略了已下架的
  5. 延遲影響
    • 回測無延遲,實盤有 50-200ms 延遲
🧠 練習 3:FillModel 配置

以下公式計算了回測 vs 實盤的差異。如果你的策略平均每天交易 50 筆,每筆成交金額 $1000,taker 費率 0.04%,一年下來手續費總共多少?

計算:

50 筆/天 × $1000/筆 × 0.04% × 250 交易日
= 50 × 1000 × 0.0004 × 250
= $5,000

💡 殘酷現實

如果你的回測年化收益只有 $3,000,那它在實盤中其實是虧損的!這就是為什麼 FeeModel 如此重要。

七、本模組重點回顧

📋 關鍵點
  1. 三種環境:回測 → 沙箱 → 實盤,策略代碼不變
  2. 回測配置:引擎 + 模擬交易所 + 數據 + 策略
  3. FillModel / FeeModel / LatencyModel:模擬真實的滑點、手續費、延遲
  4. Tearsheet:一行代碼生成互動式績效報表(6 種圖表)
  5. Data Catalog:Parquet 格式高效存儲歷史數據
  6. 實盤部署:只需將適配器從模擬改為真實
  7. 安全機制:Reconciliation 對帳、Retry 重試、Heartbeat 心跳
  8. 回測陷阱:前視偏差、過擬合、忽略費用和滑點