程式交易大冒險(一)-免費的python交易策略回測框架-以0050的KD指標為例

用免費的python回測框架驗證自己的策略吧!

長期追蹤勳仔的朋友們,應該知道勳仔今年初許下的目標,就是要研究程式交易的部分。

而程式交易上線之前,有一個很重要的部分,就是要把策略進行回測

目前台灣這邊市場的主流是使用multichart 或是XQ之類的軟體,用其各自的策略撰寫語言,來進行回測。這類型軟體,雖然功能也都非常強大,不過要使用完整功能,都需要付費購買完整版或是訂閱加值模組。

而另一個麻煩的點,就是還要去學習其對應的策略或指標的撰寫語言,雖然語法對應傳統程式語言可能已經比較簡單了,不過還是需要花時間去了解一些特定的寫法。

而身為精打細算代表的勳仔,第一個是不想花大錢買套裝軟體或是想節省每個月的持續訂閱費,二來是勳仔也有點懶的去學那些套裝軟體的策略語言,於是乎一開始我就打算用目前最流行的python程式語言,來做策略開發跟回測。

而相對應的,勳仔也選擇在有提供python API的券商跟期貨商開戶,方便後續把策略串接到實際盤中下單,達到自動交易的功能。

用python來建自動交易系統的好處,是網路上有非常大量的資源可以使用,像是時下最流行的AI,python 有非常多前人開發的套件可以使用,所以可以迅速嘗試非常多種類的策略,不過對於希望專注策略開發的投資朋友,直接選擇功能完整的套裝軟體multichart跟XQ也是沒有問題的,如果真的能找到好的策略,則支付在軟體的成本很容易就可以直接忽略。

勳仔這篇主要著重在介紹python 的策略回測框架套件backtesting.py,關於python 環境的建置(ex:anaconda),還有基本的語法,不會在這篇做介紹,有需要的朋友可以投靠google 大神,網路上有很多免費教學的資源可以利用。

簡單易懂的Python 回測框架-Backtesting.py

Backtesting的這套免費策略回測框架,回測框本身跟API使用方式都潛顯易懂,等下後面會有一個簡單的程式碼範例可以給大家參考,而且回測後的結果也可以生成html網頁檔案,讓我們可以視覺化的去研究策略的成效,對於跟勳仔一樣還在程式交易新手村的投資朋友們,已經非常夠用了。

(Backtesting.py 回測結果顯示網頁)

我們這邊就用有投資網紅推薦的,利用0050的KD指標的黃金交叉跟死亡交叉當作進出場依據的策略當作範例,來實際回測過去八年(一般認為景氣循環是四年,八年跨過兩個景氣循換)的0050日k數據,看看是否真的是個能賺錢的策略。

KD指標

KD指標又稱隨機指標,是一個剛學習技術分析的投資朋友一定都聽過的指標。

而KD值的計算還會用到另一個指標,叫做RSV指標。RSV指標叫做未成熟隨機值,這個指標的意義是用來表示今天的股價對應近期一段時間內,究竟是弱勢還是強勢。

RSV的算法是找出最近N天股價的最高點跟最低點當作分母,而當日的股價減掉最近N天的最低價當作分子,兩者相除再乘上100之後會得到一個介於0-100的值

從上述RSV指標的定義,可以看出這個指標看的是今天股價相對於近期的股價,是處於偏弱勢區間還是偏強勢區間

而K值的計算,是計算昨日的k值跟今日的RSV值加權平均,昨日的k值佔2,今日的RSV值佔1。

而D值的計算則是昨日的D值,跟今日的K值,一樣照2:1的比例取加權平均來計算。所以相對k值,D值在反應股價的變化上,相對K值來的慢

所以一般認為,K值向上穿越D值,稱為黃金交叉股價後勢預期看漲,反之K值向下穿越D值,稱為死亡交叉則,代表股價後勢看跌

而KD在盤整時期,可能會在中線50的位置附近,頻繁交錯,所以投資達人或是書上常常會在加上幾個過濾條件,那就是黃金交叉而且KD值都低於20當作進場條件,而死亡交叉加上KD值都高於80當作出場條件。

這個策略看似非常簡單,但是真的有用嗎?還有為什麼是20跟80這個魔術數字,85跟25不行嗎?

勳仔不知道大家在看所謂投資達人出書再講這類型的操作時,會不會跟勳仔一樣浮出類似的問題。身為工程師,不自己來驗證看看怎麼行呢!這就是為什麼我們需要學回測框架目的,雖然過去經驗不完全代表未來,但是一套策略在過去績效慘澹,到未來能夠大放異彩的機率相對還是比較低的。

策略說明

我們利用python backtesting.py的框架,寫了KDCross下面這個非常簡單的策略。

基本上這個框架的使用方式很簡單,首先要先準備歷史股價資料,包含了Close/High/Low/Open/Volume這些資訊。

接著我們開始撰寫自己繼承自Strategy class的策略。在init函數要初始化一些需要用到的資料,而重點的策略則放在next函數中。

backtesting.py跑回測的時候,會從餵進來的歷史資料中,從時間序列的最前面開始一個一個時間點都去執行這個next,所以每一個next的呼叫都會判斷當時是否有某種訊號滿足一些預先定義好的行為,如果有,相對應要做什麼樣的交易動作,這些都會在next函數中被呼叫。

當歷史資料處理好,而且策略也寫好之後,就可以新增一個Backtest的類別然後執行他的run函數,則它就會自動開始執行回測,然後執行完之後會把結果回傳,我們在把它印出來看即可,同時也可以用plot函數,輸出並開啟html進行視覺化的分析,一切就是這麼簡單!

範例程式:

import pandas_datareader as web
import pandas as pd
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
import matplotlib.pyplot as plt
from talib import abstract
import numpy as np

# download stock history data
#df = web.DataReader(name='0050.TW', data_source='yahoo', start='2014-07-25', end='2022-07-25') 
#df.to_csv("stock_0050_2014_2022_0725.csv")

# build data from csv and select target data
df = pd.read_csv('stock_0050_2014_2022_0725.csv')
df = df.interpolate()                #flll empty data
date_sel = df['Date']>='2014-07-25'  #select target data 
df_sel = df[date_sel]
df_sel['Date'] = pd.to_datetime(df_sel['Date']) #convert type to datetime
df_sel = df_sel.set_index('Date') # set date as index


#calculate KD signal with talib
df_tmp = df_sel
df_tmp.rename(columns = {'High':'high', 'Low':'low','Adj Close':'close','Close':'non_adj close'}, inplace = True) #rename for talib
kd = abstract.STOCH(df_tmp)
kd.index=df_tmp.index
fnl_df = df_tmp.join(kd).dropna() #merge two data frame
fnl_df.rename(columns = {'high':'High', 'low':'Low','close':'Close'}, inplace = True) #rename column name for backtest

def I_bypass(data): # bypass data in Strategy
    return data

class KDCross(Strategy): 
    lower_bound = 20  
    upper_bound = 80  

    def init(self):
        self.k = self.I(I_bypass, self.data.slowk) #K 
        self.d = self.I(I_bypass, self.data.slowd) #D

    def next(self):
        if crossover(self.k, self.d) and self.k<self.lower_bound and self.d<self.lower_bound and not self.position: #long position
            self.buy() 
        elif crossover(self.d, self.k) and self.k>self.upper_bound and self.d>self.upper_bound: 
            if self.position and self.position.is_long:
                self.position.close()
#run backtest
bt = Backtest(fnl_df, KDCross, cash=10000, commission=.002)
rslt = bt.run()
print(rslt)
bt.plot()

當K值跟D值發生低於二十的黃金交叉而且我們目前沒有倉位,則開倉作多。反之,當KD值發生高於80死亡交叉且我們手上有多單,則平倉賣出。

回測區間從2014年的八月六號到2022年的七月二十五號。回測結果如下:

看起來策略績效有點慘啊,居然會是負的,等於單純套用這個策略,不只沒有賺錢還會虧錢。雖然勝率有61%,但是獲利因子(profit factor)只有1.02,代表這個策略比較像是小賺大賠,基本上是不可能可以實際操作的策略。

而我們試著來改善這個交易策略,因為目前獲利因子很低,所以先追求減少虧損交易的虧損金額,希望能提高獲利因子,因此先考慮加入停損機制,讓每次賠錢的交易虧損不會太多,藉由大賺小賠的方式,達到績效的改善。

因此把上述策略多加上停損的條件: “前一天k棒收盤價的99%當作停損價"。一旦觸到這個價格,手上的持倉就會被停損掉。

class KDCross(Strategy): 
    lower_bound = 20  
    upper_bound = 80  
    sl_ratio = 99     # stop loss ratio, 99 means 1% loss
    def init(self):
        self.k = self.I(I_bypass, self.data.slowk) #K 
        self.d = self.I(I_bypass, self.data.slowd) #D

    def next(self):
        if crossover(self.k, self.d) and self.k<self.lower_bound and self.d<self.lower_bound and not self.position: #long position
            self.buy(size=.99,sl=self.data.Close[-1]*self.sl_ratio/100) 
        elif crossover(self.d, self.k) and self.k>self.upper_bound and self.d>self.upper_bound: 
            if self.position and self.position.is_long:
                self.position.close()

再跑一次回測結果:

這次的總報酬總算翻正的,有19.69%的總報酬,雖然還是慘輸單純買進持有的77.62%的回報率,不過至少策略修正看起來是有效的。因為停損點設得很敏感,所以很容易觸發到停損點,所以勝率剩下30.77%,不過因為控制了虧損交易的最大虧損,所以獲利因子就提升到了1.63,這個策略的型態就變得比較像是大賺小賠了。

縱使加了停損點讓報酬率變正,不過這個離實戰都還很遠,還有很多進出場條件的濾網可以去試,例如趨勢盤的時候,KD指標會遇到鈍化的問題,所以一種可能的濾網就是拿可以判斷震盪或是趨勢型態的指標當作是否要暫停這個策略的判斷依據,只在震盪盤的時候使用,如此一來應該還能再改善一點報酬率。

上述例子只是一個很簡單的例子在說明回測的重要性,不要看了書或是一些財經達人介紹某某策略,讓它財富翻倍,就照單全收,直接進場用一樣的策略操作。通常虧錢的機會比較多,如果有回測框架,我們就可以先拿來驗證可信度,至少下次有人跟你說0050在KD低於20的黃金交叉買進,KD高於80的死亡交叉賣出這套策略可以賺錢,你就可以回他說,我可能還是無腦buy & hold就好,哈哈。

心得:

對於程式交易者來說,把策略做回測是很重要的,而對於一般投資人來說,縱使不打算使用程式交易,但是利用回測去證實投資達人書上提到的方法是否有用,也可以幫助自己不會被書上影響,走到歪路,畢竟,「盡信書不如無書」,用量化數據佐證,也更能在心理層面支持投資決策,下單有憑有據,心態也比較穩定

這篇示範了幾個非常簡單的case,如今python相對簡單易懂的免費程式語言再加上簡單易懂的回測框架,勳仔真的鼓勵投資朋友們可以勇於嘗試看看,其實真的挺有趣好玩的。

免責聲明: 本篇文章僅就公開資訊進行主觀分析,僅供投資朋友參考,不應當做投資依據,投資朋友仍然需要根據自身風險承擔能力作為投資判斷,盈虧自負。

如果覺得這篇文章對您有幫助的話,歡迎點擊下方分享按鈕,分享這篇文章給你的FB或是Twitter的親朋好友,或是加入>>勳仔的理財小角落 FB專頁<<,接收最新的理財文章資訊

延伸閱讀(一): 技術分析到底有沒有用呢? 聊聊隨機漫步以及效率市場假說

延伸閱讀(二): 賭徒與投機者的資金控管經典-凱利公式

延伸閱讀(二): 新冠肺炎疫情啟示錄 – 被動現金流量的重要性 – 2020年回顧