實作手記: 台期簡單日交易回測框架

Hallblazzar
Hallblazzar :Developer Journal
10 min readJun 13, 2018

其實這篇本來早在四月初就要發表,可惜原先打好的回測環境,後來發現在結算資產方面的計算規則有誤,又花了時間重新梳理計算的流程和框架的運作。另外,原先對於研究方向的想法過於單純(以我的指導教授的話來講:「沒有學術價值!」),所以花了很多功夫研讀文獻,以及思考對於量化交易這個領域,有沒有什麼我能夠做、或是我能改進的地方。所幸我的運氣算不錯,找到了一個我的二位指導教授們都覺得可行的方向。所以趁著開發到一小段落,等待回收結果的空檔,把開發回測框架的過程記述下來,方便往後留給後人和自己參考。

做研究真的好難...

(另外,前一次的VGG16AutoEncoder收到讀者的來信,指出我的Encoder部分,最後並沒有如原文一般壓成512D的Feature。我在發布的當下,並沒有詳細驗證這件事,加上後來事務繁忙與研究方向轉換的原因,這個Project就被我放置了。既然這陣子有空,我會再把Model修一修,重新跑一跑。假如各位對我的文章或Project有任何疑問、抑或是想要討論,都歡迎留言或來信。各位的迴響會是讓我更加成長的動力。)

一、動機

目前正在研究的課題是量化交易與Deep Learning的結合,除了策略或交易的Agent本體的實作之外,另一個重要的部分便是驗證--不論文獻說得多麼天花亂墜、方法本身多麼新穎或複雜,會不會獲利這件事才是真正吸引人願意投入寶貴的金錢與時間的原因。這個道理放諸資料分析的領域皆然。

回歸到「驗證策略」這個主題上。要瞭解策略在真實市場的獲利能力,最直覺的方式即是將策略投入一個接近真實的環境中運作。這件事在量化交易領域稱之為「回測」(Backtest)。另外,在回測過程中,我們會盡可能對策略的行為(交易訊號)以及產出(回報、資產變化等)進行詳盡的紀錄,以利後續進行分析與參數調校。

起先我本來想使用現成的回測框架來驗證我的策略,這部分在Open Source界已經有非常多的選擇,諸如:

都有不少使用者,尤其是Quantopian最盛。由於其平台的成功,連帶讓Quantopian的工具成為許多量化交易開發者的首選。但是針對我的需求:

  • 日交易
  • Deep Learning Strategy
  • 期貨交易

而言,這些框架的彈性便顯得不足,尤其不少框架對於輸入出入資料的要求又非常擾人嚴謹,導致我必須花費更多心力在校準我的資料格式上。而像是Quantopian的zipline,還得自己額外建構時間與報價間的對應表。

與其為了導入這些現成框架,造成必須犧牲資料本身的簡潔與彈性的惡果,加上還得花時間改寫成符合我需要的回測方式、更得冒著這些Open Source的回測框架隨時有被棄坑、或是功能大躍進的風險,倒不如自行建構一套符合自己需求的回測框架。抱著這個想法,我踏上了自行開發的不歸路。

二、實作

1. 系統架構

下面是系統架構的簡圖:

一般我在規劃系統時,都會盡量讓系統的每個元件功能越單一越好,同時也會盡量減少元件間的交互(高內聚,低耦合原則(High Cohesion, Low Coupling ))、單一職責原則(Single Responsibility Principle))。除了維護方便,亦容易撰寫測試。這觀點除了應用在微觀的Function、Class,亦能拓展到宏觀的系統元件層級、甚至是單一服務的功能規劃。保持一切簡潔是延長系統壽命的不二法門。

回到這次要實作的回測框架,框架本身由三大元件構成,每個元件的職責為:

整體運作的流程可以簡單用下面這個流程圖來表示:

值得注意的是,在這個框架上的所有交易,皆是基於下列的前提而行:

  1. 策略掛出的訂單會在下一個交易日的「開盤」時被完成,即不論買入部位的買價/賣出部位的賣價,都是基於下一個交易日的開盤價。
  2. 不做空

所以在運作時,會是依循

  1. 給出今日的開盤/最高/最低/收盤價
  2. 依照今日的開盤/最高/最低/收盤價進行資產結算
  3. 依照收到的今日開盤/最高/最低/收盤價重新計算交易決策

的次序,循環而行。

剩下的工作就是實作這三個元件。

2. 部位結算規則

這一步是這一次開發中最困難也是最複雜的部份。在此之前,我並沒有任何進行期貨交易的經驗,不論是實際或是模擬。但是要開發模擬環境,就必須對結算規則有透徹的了解,畢竟在程式中不能有任何模糊地帶。

這時問題來了:要怎麼蒐集規則?

不得不說,金融、法律、商業…這些領域,在訂定規則上都是以條文化的方式進行紀錄,但是這些條文本身的語意多是較曖昧不明,因此這些領域往往給人高深莫測、不可捉摸之感。即使現今的需求只是需要釐清某些規則,也必須接近通盤閱讀所有條文,才能明瞭究竟有哪些誤區需要避開、有哪些核心事項需要特別注意。著實令人頭痛。(這些修、訂條文的人絕對沒有學過程式設計,不然會搞出和這些條文一般、盤根錯節的系統的工程師,肯定在業界活不久)

不管怎麼說,既然都下定決心要撩落去了,就得硬著頭皮好好幹完。台灣期貨交易所上對於交割、結算、下單…這些行為都有公布規章,不過規章終究是規章,要轉化為程式還是需要更精確的語法。起初耗費了約二周左右的時間將這些條文初步轉化成pseudo code:

  • 買入部位( bought_position )

這邊的規則是,針對每口買入部位(bought_position)先計算原始價值(original_value),即買入部位當日最低價值。若 :

  • 賣出部位(sold_position)

針對每口掛單賣出部位(sold_position):

  • 持有部位(holding_position)

針對繼續持有部位(holding_position):

這些規則最核心的思路是,只要契約價值在盤中有觸及維持保證金以下,就必須補充至維持保證金,方能交易至收盤。

聽起來好像非常合理,當下我也沒有多做懷疑,就著手進行開發了。不過實質上事情並沒有這麼簡單…。

3. 領域專家(Domain Expert)

故事要從三月底說起。那時方完成整個回測框架,正好空下一點時間能夠讀讀論文和處理一些雜事。趁著這個空檔,約了一位在做期貨營業員的朋友,請他幫我看看結算規則的部分有沒有問題。不看還好,看下去事情就大條了。

「欸欸欸...你用你這個規則算出來的結果,怎麼跟我算出來的都不一樣?」「!!!!!」

這真的、非、常、糟、糕。

經過一下午的反覆驗證後,大致釐清了我的誤區:

  1. 盤中要補充保證金只有在觸及低於起始保證金「25%」的情形,其餘盤中不論如何盈虧,都不必付出額外成本。
  2. 收盤補充則是在低於維持保證金的情形下。
  3. 要在盤中/收盤補充保證金,也是要補充到「起始保證金」。

另外,關於部位買賣也有一點非常重要,原先我以為部位買入後,會把新部位與所有的部位的價值加總後取平均,作為每一口契約的價值,即:

為什麼我會這麼認為呢?因為除了從期交所的網站看規則外,我也有測試過幾套免費的回測軟體。每當有新的部位進來的時候,回測軟體對部位的報價都是依上面的邏輯去取的。但是經過我的朋友的解釋是這樣的:

實質上,每一口契約都是分開紀錄,掛單賣出時會從買入時間最遠的契約開始(First In, First Out),持有人並不能自行控制。而看盤軟體之所以會這樣顯示,是因為這樣顯示比較方便、明瞭。

求證後的當下,受到的衝擊著實不小。能夠明瞭自己的錯誤,一方面是好事,同時也是壞事。好的部分在於,在我還沒有正式把框架拿來使用,所以現在做出的修正,可以預防後續出錯時,得耗費更多功夫來補救;壞的部分在於,除了規則本體之外,原先寫好的 Unit Test 也得砍掉重練,畢竟預期輸出(Assertion Output)已經完全不同…。

下面是修正過後的 pseudo code:

  • 買入部位(bought_position)
  • 賣出部位(sold_position)
  • 持有部位(holding_position)

4. Buy and Hold

回歸到這一切的起源。耗費這麼多心神設計這個框架的初衷,還是希望能夠方便開發策略。所以我寫了一個針對大台( TX )近月合約的購買後放置( Buy & Hold )策略,作為這個框架的展示。

上圖是用Bokeh繪製的結果圖,圖片上幅是持有資產變化,下幅是TX報價(不得不說,比起 Matplotlib , Bokeh 的 API 以及圖片輸出的功能好用多了)。細部數據如下:

  • 起手資金( Asset )為1,000,000。
  • 最終以1,848,280做收。
  • 資料區間為1998/07/21-2016/12/26。

另外有幾點比較重要:

  • 框架本身只有提供紀錄資產變化與持有部位變化的 API(AssetMaintainer),如果有需要額外紀錄策略行為等,須由使用者自行撰寫。
  • 框架只提供做多,沒有做空。
  • 沒有任何預防策略能夠調整Asset的機制(可讀亦可寫)。開發時需要小心,避免意外在計算決策的過程中調整到資產。
  • 預設沒有負資產即出場機制,即使資產見底依然能繼續買入(可見 Buy &Hold 結果中的上幅,在2000-2005之間有數次資產至負,依然繼續交易),有需要的使用者請自行添加。

三、總結

總體下來,這個回測環境前後花上快一個月的時間實作。最大的體認還是領域專家這件事。如果在開發上沒有相對應的領域專家輔助,那很可能會耗費極大的心力,構築出一個毫無用處的系統。也許你會問:那為什麼不要一開始就直接找領域專家規劃?對我而言,在開發任何系統,首要的便是自己也要能掌握相關的知識。領域專家可以給予非常多的輔助,問題還是在「我要從領域專家身上拿到什麼資訊?」。如果一開始劈頭就問領域專家:「我要怎麼做一個回測系統?」,那你需要的是「有開發過回測系統的開發人員」而非「了解這方面知識的專家」,但是比起後者,找到前者遠要來得困難得多。

(這邊還是要再次感謝我朋友,如果沒有他,就沒有這個框架。要是各位想找他開戶,歡迎來信和我索取他的聯絡方式XD)

不過作為一個開發人員的難處在於,處在在功能不彰的組織時,甚至自己也得跳下去理解商業邏輯,才有辦法實際構築系統。當然,這絕對不能、也絕不應該作為組織把規劃的責任,任意往開發人員肩上扔的藉口。不過身為一個開發者多的是時候得面對這類狀況,尤其當大環境下,不少公司組織慣於如此行事之時。

總之,我還是傾向先自己了解過目標,再來驗證與討論。有句話說得很有道理:「要了解一件事,得從會問問題開始。」,你說對嗎?

(這一篇最終成品放在我的Github上,有興趣的朋友可以參考看看,不過我沒有單純放框架本身,而是Buy & Hold這個策略,畢竟有範本的話大家會比較方便修改與使用)

--

--

Hallblazzar
Hallblazzar :Developer Journal

興趣使然的開發者,專長於網路、軟體/系統架構及DevOps,目前努力進入Data Science的世界。用生命享受徜徉於程式碼與架構之美的樂趣,夢想即使80歲還能繼續熱血玩程式。Github: https://github.com/HallBlazzar Mail: hallblazzar@gmail.com