內容目錄
MACD的中文為平滑異同移動平均線,為一種判斷股價中長期趨勢的指標。當快線(DIF)由下而上穿越慢線(MACD)時,代表股價有上漲的動能存在;反之快線(DIF)向下跌破慢線(MACD)時,代表股價下跌的機率相對高。由於此類型的指標有明確的買賣訊號,所以非常適用利用回測來驗證該策略表現。
而一個有效的回測,除了有明確的買賣點之外,買賣股票的成本也必須考量到買賣訊號產生的時機、手續費與證交稅,甚至是最低消費20元的限制,計算報酬率時亦要考慮手中現金部位,才會最貼近實際採用該策略的表現!
本文使用 Windows OS 並以 Jupyter Notebook 作為編輯器
#基本功能 import numpy as np import pandas as pd
#繪圖 import plotly.graph_objects as go from plotly import subplots
#TEJ import tejapi tejapi.ApiConfig.api_key = "Your Key" tejapi.ApiConfig.ignoretz = True
stock_data = tejapi.get('TRAIL/TAPRCD', coid= '3481', mdate={'gte': '2020-01-01', 'lte': '2020-12-31'}, opts={'columns': ['coid', 'mdate', 'open_d','close_d']}, chinese_column_name=True, paginate=True)
本文以撈取以群創光電(3481)於2020年間股價做為示範,欄位選擇開盤價與收盤價,前者計算買賣價格與成本,後者用於計算買賣訊號
stock_data['12_ema'] = stock_data['收盤價(元)'].ewm(span = 12).mean() stock_data['26_ema'] = stock_data['收盤價(元)'].ewm(span = 26).mean() stock_data['dif'] = stock_data['12_ema'] - stock_data['26_ema'] stock_data['macd'] = stock_data['dif'].ewm(span = 9).mean()
接下來以收盤價計算MACD。首先利用 ewm().mean()
的方式計算12天與26天的指數移動平均線,兩者之間的差即為快線DIF,再以此快線計算9日指數移動平均,則可以得到慢線MACD
stock_data['買賣股數'] = 0
#如果黃金交叉,隔天開盤買進 stock_data['買賣股數'] = np.where((stock_data['dif'].shift(1)>stock_data['macd'].shift(1)) & (stock_data['dif'].shift(2)<stock_data['macd'].shift(2)), 1000, stock_data['買賣股數'])
#如果死亡交叉,隔天開盤賣出 stock_data['買賣股數'] = np.where((stock_data['dif'].shift(1)<stock_data['macd'].shift(1)) & (stock_data['dif'].shift(2)>stock_data['macd'].shift(2)), -1000, stock_data['買賣股數'])
有了慢線與快線之後,即可建立買賣股數訊號。當兩日前的DIF仍小於MACD線stock_data[‘dif’].shift(2)<stock_data[‘macd’].shift(2)
,但卻於昨日DIF大於MACD stock_data[‘dif’].shift(1)>stock_data[‘macd’].shift(1)
,代表昨日出現黃金交叉買點,而因為這個訊號於昨日收盤才能確定,因此於今日開盤才購買1000股,若不符合買點條件,則維持原欄位內容stock_data[‘買賣股數’]
。同理,出現死亡交叉時,亦是隔日開盤時才賣出1000股。
stock_data['手續費'] = stock_data['開盤價(元)']* abs(stock_data['買賣股數'])*0.001425 stock_data['手續費'] = np.where((stock_data['手續費']>0)&(stock_data['手續費'] < 20), 20, stock_data['手續費']) stock_data['證交稅'] = np.where(stock_data['買賣股數']<0, stock_data['開盤價(元)']* abs(stock_data['買賣股數'])*0.003, 0) stock_data['摩擦成本'] = (stock_data['手續費'] + stock_data['證交稅']).apply(np.floor)
摩擦成本包含了手續費(0.1425%)與證交稅(0.3%)。當買入股票時,所需負擔的是手續費,但要注意的是如果券商沒有提供相關優惠,則往往會有低消20元限制;而賣出時要負擔的是手續費與證交稅。
stock_data['股票價值'] = stock_data['買賣股數'].cumsum() * stock_data['收盤價(元)'] stock_data['現金價值'] = 10000 - stock_data['摩擦成本'] + (stock_data['開盤價(元)']* -stock_data['買賣股數']).cumsum() stock_data['資產價值'] = stock_data['股票價值'] + stock_data['現金價值']
假設初始現金有10000元台幣,資產價值為股票部位價值與現金部位價值的加總。股票價值隨著股票持有數量、股價波動;現金價值則隨著買賣操作變動,買入股價時減少,賣出時增加,並考慮摩擦成本。
stock_data['當日價值變動(%)'] = (stock_data['資產價值']/stock_data['資產價值'].shift(1) - 1)*100 stock_data['累計報酬(%)'] = (stock_data['資產價值']/10000 - 1)*100
接著即可利用資產價值的每日變化,計算每日報酬率;也能以初始現金計算累計報酬。
可以看到MACD策略的確可以掌握部分波段,但在盤整時有反覆買賣的風險,仍需特別注意。
可以看到採用MACD策略,資產價值每日的波動在8%上下以內,若波動為0代表目前手中僅有現金部位。最後的累計報酬約為63%,優於同時期0050約30%左右報酬。
讀者應該可以發現,當買賣點觸發過於頻繁時,則累積的摩擦成本不容小覷,導致表現結果可能還不如期初買入並持有。而再反覆調整初始資金後也能發現資產配置的重要性,因為當手中現金部位過大時,會拉低整體的報酬率,但相對地每日價值波動幅度也較低。其實如果要貼近現實的回測,也必須考慮持有期間的除權息,但本文為了搭配當時股價顯示買賣點,才選擇採用未調整的股價計算報酬率;若操作方向為做空的話,更需要考慮保證金問題,而本文首筆交易為買入,故皆為一買一賣操作。若讀者想回測更長的區間、使用調整後的股價,推薦到 TEJ E-Shop 挑選最適合的方案!
電子報訂閱