配對交易 Pairs Trading 由零開始:Python 實現、Jesse 回測與手續費陷阱

第 8 課・共 18 課

⚠️ 本站內容僅作教育用途,不構成投資建議。投資涉及風險,作出任何投資決定前請諮詢持牌專業人士。
目錄

配對交易 Pairs Trading 是 Edward Thorp 賴以建立八億美元基金的市場中性策略,本課筆記整理自 Algo-trading with Saleh 的教學影片:講者 Saleh 由零開始,先在 Jupyter Notebook 推導數學原理,再用 Jesse 框架寫成可回測的策略。這是同頻道較早期的「原始版本」教學——第 18 課記錄的是他其後實盤改良的版本,兩課對照閱讀,剛好看到一套策略由「看似完美」到「勉強可用」的完整演化。

學習重點

詳細筆記

Thorp 與市場中性的誘惑

講者開首交代他迷上這策略的原因:讀了 Thorp 的自傳《A Man for All Markets》後,發現這位發明二十一點算牌術的數學家,其基金幾乎每月賺錢,最差的一個月虧損不足 1%。配對交易是市場中性策略——任何時刻同時持有一個好倉與一個淡倉,利潤不取決於大市方向,而取決於兩隻資產的相對關係是否回歸常態。

他用 TradingView 圖表演示:把 ETH 與 ETC(以太坊經典)的價格放在同一百分比變化尺度上,兩者走勢高度相關,但不時出現背馳——某段時間 ETH 跑輸 ETC,其後又反超前。若在 ETH 相對偏弱時做多 ETH、同時做空 ETC,待關係逆轉時平倉,即使兩者價格同時下跌,淡倉賺的錢仍多過好倉虧的錢,差額就是利潤。圖上類似機會反覆出現,看似俯拾皆是——但講者提醒,這只是事後用肉眼看圖,真正落盤時沒有圖表可看,一切判斷必須改由統計數字做出,這正是下半部分寫程式的原因。

ETH 與 ETC 同一尺度下的走勢背馳(截圖出自原片(Algo-trading with Saleh))

Notebook 內的前期研究

正式寫策略前,講者先在 Jupyter Notebook 做可行性研究。Notebook 放在 Jesse 項目目錄內,便可直接使用其 research 模組與工具函數。關鍵步驟:

# 取數並轉成回報序列(摘錄自原片教學)
exchange = 'Binance Perpetual Futures'
_, c1 = research.get_candles(exchange, 'ETH-USDT', '15m', start, finish)
_, c2 = research.get_candles(exchange, 'ETC-USDT', '15m', start, finish)
c1 = utils.prices_to_returns(c1[:, 2])
c2 = utils.prices_to_returns(c2[:, 2])

兩條回報序列疊畫:藍線 ETH、紅線 ETC(截圖出自原片(Algo-trading with Saleh))

價差、Z 分數與協整

圖表只是演示,落盤機械人看不到圖,一切要化為統計量。第一步是計算價差——兩條回報序列相減。由於轉回報後首個值是 NaN,要由索引 1 起取值。第二步是把價差標準化成 Z 分數:價差減去序列均值、除以標準差,答案代表「當前背馳有幾多個標準差」。Jesse 有現成的 utils.z_score 函數。講者在 Z 分數圖上畫出均值線與 ±1.2 門檻線:跌穿 -1.2 做多第一隻、做空第二隻;升穿 +1.2 反向操作;回到均值平倉。

但還有一道關卡:協整檢定。均值回歸策略要求數據「平穩」——均值長期穩定不漂移。單一資產價格肯定不平穩,但協整的意思是:兩條各自不平穩的序列,其某種組合(這裏是價差的 Z 分數)可以是平穩的;若成立,兩者才可互相對沖交易,否則價差可能永不回歸。講者用 Jesse 的 utils.are_cointegrated 一行完成檢定(底層是 statsmodels 的 p 值檢驗),ETH 與 ETC 通過。他明言數學細節從簡,建議深入研究者看 Quantopian 的相關教學。講者還補充了一個實務細節:Notebook 示範用整段兩天數據計均值畫門檻線,是事後諸葛;實戰時均值只能用「到當刻為止」的數據計算,這一點直接影響下一節的策略寫法。

Jesse 雙策略檔架構

配對交易要同時操作兩個品種,Jesse 的回測以「交易路線」為單位,一條路線配一個策略檔,所以要建立兩個策略檔。講者的分工:PairsTrading2 只是執行層,純粹讀取指令;PairsTrading 負責全部計算與決策。兩者靠 Jesse 的 shared_vars 字典溝通——在此定義的變數對所有路線可見。四個共享變數:s1-positions2-position(1 = 好倉、-1 = 淡倉、0 = 平倉),以及兩邊的保證金 s1-margins2-margin。執行層的 should_long 只檢查共享變數是否等於 1,update_position 在變數歸零時調用 liquidate 平倉,結構簡單。這種「一個大腦、兩對手」的設計,好處是所有狀態集中在一處維護,不會出現兩個策略檔各自判斷、訊號互相矛盾的問題。

決策全部集中在主策略的 before() 方法(Jesse 每支 K 線最先執行的鉤子),重點有三:

  1. 取數防前視:回測中不能像 Notebook 那樣一次過取整段數據,否則會偷看未來。改用 self.get_candles,框架保證只提供「到當刻為止」的 K 線;並且只取最近 200 支收市價——窗口太長,均值會漂移得失去參考價值;品種與時段用 self.routes[0].symbolself.timeframe 動態讀取,改設定不用改代碼。
  2. 每日重檢協整:每 24 小時重新做一次協整檢定,一旦關係失效,立即把兩邊倉位指令歸零、停止開倉。
  3. 進出場訊號:只取 Z 分數序列最後一個值做判斷。
# 主策略 before() 內的進出場邏輯(摘錄自原片教學)
z = self.z_score
if self.is_close and z < -1.2:
    self.shared_vars["s1-position"] = 1
    self.shared_vars["s2-position"] = -1
elif self.is_long and z > 0:
    self.shared_vars["s1-position"] = 0
    self.shared_vars["s2-position"] = 0
elif self.is_close and z > 1.2:
    self.shared_vars["s1-position"] = -1
    self.shared_vars["s2-position"] = 1

首次回測的除錯插曲

值得一提講者首次按運行鈕時結果明顯有誤。追查後發現:他在策略檔直接把收市價序列拿去計價差,忘記了 Notebook 裏「先轉回報」這一步——價格量綱不同,相減出來的「價差」毫無意義。修正方法是在兩個 property 末端補上 prices_to_returns。這個小插曲對自學者其實有價值:把研究代碼搬進策略框架時,數據前處理的每一步都要原樣照搬,少一步結果就全盤走樣。另外 Jesse 的快速回測模式(fast mode)暫不支援多路線策略,回測前記得關掉,否則直接報錯。

倉位比例:對沖比率 Beta

兩邊倉位不能簡單各放一半本金。講者解釋:市值越大的資產波動越小,ETH 的價格變動幅度通常小於 ETC;若兩邊各放 1,000 美元,價格齊升時 ETC 那邊賺或虧得更多,組合就不再中性。做法是用 utils.calculate_alpha_beta 對兩條回報序列做線性迴歸,求出對沖比率 beta,再按比例分配保證金:第一倉佔 1/(1+beta)、第二倉佔 beta/(1+beta)。講者坦白這部分公式他借助了 AI 工具撰寫、靠自己反覆測試驗證有效,未能逐行解釋數學推導——想深究的讀者同樣可參考 Quantopian 的教學。

回測:由 +69% 到 -97%

首次回測結果「好得難以置信」:2024 年 1 月至 12 月約一年,10,000 美元本金錄得 69% 回報,最大回撤僅 -5.21%,勝率 53%,Sharpe ratio 2.94,Calmar ratio 13.77。疊加基準走勢後可見:升市賺錢、跌市賺錢、橫行也賺錢——只要兩隻資產維持協整的假設成立。

不計手續費的回測績效:7,818 宗交易、回報 69.98%(截圖出自原片(Algo-trading with Saleh))

但講者隨即揭盅:這次回測關掉了手續費。一年 7,818 宗交易,一般散戶沒有交易所的特約費率,一旦按 Binance 實際費率重跑,權益曲線直線向下——本金由 10,000 美元跌至 343 美元,單是手續費已付約 11,000 美元。而且現實還有滑點:這策略很多交易本來只賺微利,滑點會令真實結果比這更差。

計入真實手續費後權益曲線直線向下,終值僅 343 美元(截圖出自原片(Algo-trading with Saleh))

講者的結論與出路:能拿到極低費率的對沖基金或可直接交易此策略;一般散戶則要加盡可能多的濾網,只做「值得做」的交易,或改用較長時間框架減少交易次數,又或揀選波動更大、單宗利潤空間更厚的相關資產(例如互相關聯的迷因幣)。他又指出對沖基金能享有特約低費率(他估計當年 Thorp 亦然),散戶先天處於劣勢,唯一武器是揀交易。他把源碼放上 GitHub,把「加濾網改良至可實戰」留作觀眾功課,並建議可試其他市場與費率較低的交易所——而這份功課的答案,正是第 18 課的改良版內容。

🙏 覺得內容有用?本站所有內容免費提供,使用推薦碼開戶就是支持我們繼續營運的最大鼓勵:

ZA Bank 邀請碼 P20923 —— 你享 HKD 2,000 迎新獎賞,本站獲得營運支持。 立即開戶 → (聯盟連結|投資涉及風險,受條款及細則約束,此資料僅可於香港境內分發)

MT5 可行性速評

項目評估
邏輯可移植性價差相減、均值、標準差、Z 分數在 MQL5 都要自建陣列用迴圈計算(無內建函數);協整檢定與線性迴歸 beta 更無現成方案,需自寫 OLS 或預先在 Python 算好定期匯入。
平台限制策略涉及兩個品種:MT5 的 EA 可用 iClose 讀取多品種數據並分別落盤,但策略測試器對多品種 EA 的回測支援有限,價差合成、協整重檢與兩倉同步開平都要自行處理,工程量大。
評級⚠️ 需自訂元件——核心統計元件(協整檢定、beta、Z 分數)全部要自寫,多品種回測可信度打折;建議研究與驗證留在 Python,MT5 只作執行層。
MQL5 寫法提示見下方示意。
// 示意:讀取兩品種收市價,合成回報價差(非完整 EA)
double r1 = iClose("ETHUSD", PERIOD_M15, 1) / iClose("ETHUSD", PERIOD_M15, 2) - 1;
double r2 = iClose("ETCUSD", PERIOD_M15, 1) / iClose("ETCUSD", PERIOD_M15, 2) - 1;
double spread = r1 - r2;
// 自建環形陣列儲存最近 200 個 spread,迴圈求均值與標準差,再計 z

注意:MT5 回測同樣會過度擬合——即使搬運成功,參數調校後的亮麗曲線與實盤表現可以是兩回事。

欣利克的看法

名詞解釋

常見問題

配對交易是否真的不論升跌市都賺錢?

只在「兩隻資產維持協整、價差最終回歸」的前提下成立。升市、跌市、橫行市都可能出現收斂機會,但協整關係一旦破裂,價差持續擴大,好淡兩倉會同時虧損。所以策略內建每日重檢協整的機制,失效即停。

為何兩邊倉位不能直接各放一半本金?

因為兩隻資產波動幅度不同:市值大的一般波動小,各放一半會令組合重新暴露於單邊風險。做法是以線性迴歸求出對沖比率 beta,按 1 比 beta 的比例分配保證金,令兩邊的盈虧敏感度對稱。

回測 +69% 的策略為何計入手續費後虧 97%?

因為一年交易 7,818 宗,平均每宗利潤極薄,單是手續費已超過本金;加上現實存在滑點,薄利交易會進一步轉虧。這正說明高頻策略的回測必須計入貼近真實的成本假設,否則數字沒有參考價值。

這課與第 18 課的配對交易教學有何分別?

本課是同頻道較早期的「原始版」:由零推導數學、固定 ±1.2 門檻、無濾網,回測證明計入成本後不可行。第 18 課是講者實盤後的改良版:布林帶門檻、ADX 與趨勢濾網、最低利潤要求與組合止損。建議先讀本課理解原理,再讀第 18 課看改良思路。

原影片

本文整理自以下影片,版權屬原創作者所有,建議配合原片觀看:

來源:https://www.youtube.com/watch?v=YDMSqal-RZ4

🏦 ZA Bank 開戶優惠

本站所有內容免費提供。多使用網站的推薦碼,就是對我們繼續製作優質內容的最大鼓勵和支持 🙏

去 ZA Bank 開戶,享價值 HKD 2,000 迎新獎賞,仲有機會享 USD 30 指定美股兌換券!

  • 💛 全線上開戶,無需前往分行
  • 💛 無最低存款要求,零賬戶管理費
  • 💛 24x7 銀行服務

專屬邀請碼:P20923

立即開戶 → (聯盟連結)