策略進階#

上一章介紹了策略的基本進出場指令。本章深入探討影響回測結果準確性的重要設定:手續費、滑點、部位大小,以及如何避免常見的回測陷阱。

手續費設定#

手續費是回測中不可忽略的成本。在 strategy() 函式中設定:

//@version=6
strategy("含手續費策略",
         overlay=true,
         initial_capital=100000,
         commission_type=strategy.commission.percent,  // 百分比手續費
         commission_value=0.1425)                       // 0.1425%(台股買進費率)

commission_type 選項:

常數說明
strategy.commission.percent按成交金額的百分比收取
strategy.commission.cash_per_contract每口合約收取固定費用
strategy.commission.cash_per_order每筆訂單收取固定費用

台股參考費率:買進 0.1425%、賣出 0.1425% 手續費 + 0.3% 證交稅,合計單邊約 0.4425%,來回約 0.585%。

滑點設定#

滑點 (Slippage) 模擬實際成交價與訊號價格的差異:

strategy("含滑點策略",
         overlay=true,
         slippage=2)  // 每次成交滑動 2 個最小變動單位

部位大小#

固定數量#

strategy("固定口數",
         default_qty_type=strategy.fixed,
         default_qty_value=1)  // 每次固定交易 1 口/股

固定金額#

strategy("固定金額",
         default_qty_type=strategy.cash,
         default_qty_value=10000)  // 每次投入 10,000 元

資金比例#

strategy("資金比例",
         default_qty_type=strategy.percent_of_equity,
         default_qty_value=10)  // 每次投入總資金的 10%

動態計算口數#

strategy.entry 中直接指定 qty,可根據當下資金動態計算:

//@version=6
strategy("動態口數", overlay=true, initial_capital=100000)

riskPct = input.float(2.0, "每筆風險(%)") / 100
atrMult = input.float(2.0, "停損ATR倍數")

atrValue   = ta.atr(14)
stopAmount = atrValue * atrMult                        // 每股停損金額
riskAmount = strategy.equity * riskPct                 // 本次可承受虧損
qty        = math.floor(riskAmount / stopAmount)       // 計算口數

ma20 = ta.sma(close, 20)

if ta.crossover(close, ma20) and strategy.position_size == 0
    strategy.entry("Long", strategy.long, qty=qty)
    strategy.exit("Exit", "Long", stop=close - stopAmount)

pyramiding — 加碼#

pyramiding 參數設定同方向最多可以有幾筆未平倉單:

strategy("加碼策略", overlay=true, pyramiding=3)  // 最多同時 3 筆多單

// 每次拉回到均線附近都加碼
ma = ta.sma(close, 20)
if close > ma and close < ma * 1.02
    strategy.entry("Long", strategy.long, qty=1)

避免回測陷阱#

1. 未來資料 (Lookahead Bias)#

Pine Script 的 strategy.entry 預設 在訊號K棒收盤後的下一根K棒開盤成交,這是正確的行為。

但某些寫法會造成「偷看未來」:

// ❌ 錯誤:用當根 K 棒的高點做為進場價(該價格在訊號產生前就存在)
if close > ma
    strategy.entry("Long", strategy.long, limit=high)

// ✅ 正確:下一根 K 棒的開盤成交(預設行為)
if close > ma
    strategy.entry("Long", strategy.long)

2. 過度優化 (Overfitting)#

避免在同一段歷史數據上反覆調整參數,建議:

  • 留出「樣本外」測試區間(例如最近 20% 的數據)
  • 參數越少越好,越簡單的策略越穩健

3. barmerge.lookahead 設定#

使用 request.security 取得其他週期數據時(詳見 CH15),必須使用 barmerge.lookahead_off 避免偷看未來 K 棒的收盤價。

常用的 strategy.* 查詢變數#

變數說明
strategy.equity當前總資產(含未實現損益)
strategy.netprofit已實現淨損益
strategy.position_size當前持倉數量(正多負空)
strategy.position_avg_price持倉平均成本
strategy.opentrades當前未平倉交易筆數
strategy.closedtrades已完成交易總筆數
strategy.wintrades獲利交易筆數
strategy.losstrades虧損交易筆數

實用範例:RSI 策略含完整風控#

//@version=6
strategy("RSI 策略", overlay=false,
         initial_capital=100000,
         default_qty_type=strategy.percent_of_equity,
         default_qty_value=10,
         commission_type=strategy.commission.percent,
         commission_value=0.1425)

// 參數
rsiLen    = input.int(14,  "RSI 週期")
oversold  = input.int(30,  "超賣門檻")
overbought = input.int(70, "超買門檻")
stopPct   = input.float(3.0, "停損(%)", step=0.5) / 100
targetPct = input.float(6.0, "停利(%)", step=0.5) / 100

rsiValue = ta.rsi(close, rsiLen)

// 進場:RSI 從超賣回升
buySignal  = ta.crossover(rsiValue,  oversold)
sellSignal = ta.crossunder(rsiValue, overbought)

if buySignal
    strategy.entry("Long", strategy.long)

if strategy.position_size > 0
    entryPx = strategy.position_avg_price
    strategy.exit("Exit Long", "Long",
                  stop=entryPx * (1 - stopPct),
                  limit=entryPx * (1 + targetPct))

// 繪圖
plot(rsiValue, "RSI", color=color.purple)
hline(overbought, color=color.red,   linestyle=hline.style_dashed)
hline(oversold,   color=color.green, linestyle=hline.style_dashed)
hline(50, color=color.gray, linestyle=hline.style_dotted)

Reference#