一條鞭法程式化教學

法意宣稱我不熟悉程式交易,只好寫這篇自娛娛人一下

在開始這個話題之前,我們要先了解甚麼是時間序列,以及如何對時間序列作分析。所謂時間序列(Time Series),就是指以時間先後順序出現的觀測值集合,像是氣象預報研究中,某一地區的每天氣溫與氣壓的數據;或者醫學領域中,病人在不同時刻的心跳與體溫變化;以台股來說,證交所每天收盤後提供的發行量加權股價指數歷史資料,就是一種典型的時間序列資料。

有了歷史資料之後,我們就可以對這些數字做進一步的分析,發掘蘊藏在這些資料之中的模式(Pattern)。為了方便觀察和理解,我們可以把這些數據圖像化,像是下圖就是某位女性每日的體溫變化圖,可以發現排卵前體溫較低,排卵後體溫較高,因此有些人會藉由體溫推估排卵時間,並在前後幾天禁止行房以達到避孕的效果 (至於準不準就是另外一回事了)。


同樣的方式運用到台股上就是我們熟知的線圖:


從上圖我們可以大致知道從2006到2013年的台灣股市漲跌,以及成交量的變化。假設在看了這幾年的歷史走勢之後,希望未來如果出現像2008和2009的大漲大跌走勢時,自己可以賺到錢的話,應該怎麼做呢?我之前提供過一個很簡單的方法:一條鞭法,即指數站上60日平均線(Moving average, MA)做多,跌破就空手或放空,加上MA之後的線圖會長這樣:


有了方法之後,我們要如何評估成效呢?最直觀的方法就是目視計算,假設在操作時,只要指數收盤價跌破60MA,就在隔日開盤時放空,那麼回到2008年5月的話,會發生甚麼事呢?


5/26的收盤價8707.83,跌破了當時的60MA,即8740.79(綠色向下箭頭處),因此要在隔日5/27的開盤價放空,即8779.34(綠色向右箭頭)。

但5/27的收盤價8778.39,又超過了60MA(8723.72,紅色向上箭頭),所以在5/28的開盤價8826.94做多(紅色向右箭頭)。

不幸的是5/28的收盤價8665.73又再度跌破60MA(8753.60,第二個綠色向下箭頭),故在5/29的開盤價放空(8773.81,第二個綠色向下箭頭)。

接下來就出運了,2008年指數再也沒碰過季線,空單可以沿路抱到2009年的1/5收盤價4698.31,突破60MA(4596.76,右側紅色向上箭頭),並在1/6開盤價轉多4719.54為止(右側紅色向右箭頭)。


接下來我們可以計算在沒有槓桿的狀況下,這三筆交易的粗略報酬率:

1. (8779.34-8826.94)/8740.79 = -0.54%
2. (8773.81-8826.94)/8826.94 = -0.60%
3. (8773.81-4719.54)/8773.81 = 46.20%

當然這邊都只是模擬,實際交易時若加入手續費等交易成本和買賣造成的滑價,績效勢必會更差;如果用ETF等工具還需考慮除權息所造成的指數下滑;用期貨則要評估期貨現貨間的價差,以及換月時的差價。

問題來了,取樣時間短的話,自己計算還可行,如果取樣時間拉長,像一條鞭法如果回測近40年,交易次數可是高達500多筆,要是像上面這樣自己計算的話會累死,而且加減乘除之間還很容易算錯,這時候這些交易軟體的回測功能就派上用場了。


市面上的同性質軟體很多,像是tradestation、HTS、奇狐、Multichart、wealthlab,甚至國內也有人開發過一套叫做土狼的交易軟體,這些交易軟體大部分都有許多預設的函式庫,通常也有許多預設範例可以供新手參考。這邊以奇狐為例說明一條鞭法怎麼程式化:


下面這段就是一條鞭法的程式碼:

ENTERLONG: CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));
EXITLONG: CROSS(MA(CLOSE,LONG),MA(CLOSE,SHORT));

ENTERSHORT: CROSS(MA(CLOSE,LONG),MA(CLOSE,SHORT));
EXITSHORT: CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));

不要看都是英文字很難懂,這邊讓我簡單說明一下這些函式:

ENTERLONG: 發出買進訊號,顧名思義,EXITLONG就是多頭平倉。
ENTERSHORT: 放空訊號,同理可知EXITSHORT就是空頭平倉。

CROSS(A,B): 交叉,例如CROSS(A,B)的意思就是,若前一筆資料A<B,下一筆A>B時就代表狀況成立。

MA(X,Y): X代表要用開高低收哪種值做計算,上面是使用收盤價,即CLOSE;Y則是均線的參數,在這個程式中我使用了兩個數值(LONG,SHORT),分別代表長短兩條均線要取的參數。

有了這些概念之後,就可以開始「翻譯」這些程式碼了:

像「ENTERLONG: CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));」和「EXITSHORT: CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));」這兩行程式碼,翻成中文就是:當短期均線向上突破長期均線時,空頭平倉且多頭進場。

同理可知,另外兩行的意思就是:當短期均線向下跌破長期均線時,空頭進場且多頭平倉。

若我把LONG設定為60,SHORT設定為1,那麼條件就會變成:當收盤價(1MA)向上突破60MA時,空頭平倉且多頭進場;當收盤價(1MA)向下跌破60MA時,空頭平倉且多頭進場。

把條件拿來回測之後,軟體就會自動計算出績效報表,如下圖(底色淡綠色是改windows設定,據說可以讓眼睛比較不會疲勞):


這邊的報酬率甚至沒有使用槓桿,看起來超漂亮的吧!

我們也可以去檢視逐筆交易,像是剛剛提到的三筆交易,跑出來就會像這樣:


要提醒大家的是,看起來很漂亮的回測結果,絕大部分只是假象,因為作弊的手法非常多 (像是萬惡的this bar,這也是新手寫程式時最容易犯的錯之一),如果無法得知原始程式碼自己驗證檢視,那麼小心為上。

像是上面的回測,如同之前所提到,並沒有考慮交易成本、除權息等問題,若儘量用保守的方式估計,年報酬大概會低個10%左右。

如果覺得用收盤價很容易被假訊號騙,一直進進出出,那麼剛剛設定的參數SHORT就可以派上用場了,以下是當短均線參數分別設定為1和5時的訊號:



可以看到震盪區域的交易筆數明顯減少,代價就是交易訊號比較遲鈍,若出現連續上漲或下跌,進出場的位置會比原本糟糕很多。

同樣的交易法則,如果換成MultiChart的語法,應該會變成下面這樣(//部分為註解):

//參數的宣告,一樣是短(1)長(60)兩個MA,價格採用的是close,即收盤價
inputs:Price(close),M_Short(1),M_long(60);

//設定變數,var1為短期均線,var2為長期均線
vars:var1(0),var2(0);
var1=Average(Price,M_Short);
var2=Average(Price,M_long);

//如果沒有部位(marketposition=0),且短均突破(Crosses)長均,
  就在下一筆市價買進(buy next bar at market),
  ("buy")是方便有多系統時不會混淆各系統的在倉部位,例如有三個獨立系統時,
  可以將交易訊號分別設定為buy 1,buy 2,buy 3,同理也可以設定來加減碼用。
if marketposition=0 and  var1  Crosses Above var2 then begin
buy("buy") next bar at market;
end;

//如果有多頭部位(marketposition>0),且短均跌破長均,就在下一筆市價平倉
if marketposition>0 and  var1  Crosses Under var2 then begin
sell("EXIT_buy") next bar at market;
end;

//如果沒有部位,且短均跌破長均,就在下一筆市價放空
if marketposition=0 and  var1 Crosses Under var2 then begin
sellshort("sell") next bar at market;
end;

//如果有空頭部位(marketposition<0),且短均突破長均,就在下一筆市價平倉
if marketposition<0 and  var1 Crosses Above var2 then begin
buytocover("EXIT_sell") next bar at market;
end;

延伸閱讀:法意教你伊利安星球答辯法

想要看到最新文章,記得追蹤FB:麥克風的市場求生手冊

留言

這個網誌中的熱門文章

季線之下不做多

股市包租公 2016

麥克風選股法使用教學