策略進階#
上一章介紹了策略的基本進出場指令。本章深入探討影響回測結果準確性的重要設定:手續費、滑點、部位大小,以及如何避免常見的回測陷阱。
手續費設定#
手續費是回測中不可忽略的成本。在 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)