iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
AI & Data

用Python程式進行股票技術分析系列 第 16

Day16 MACD指標

  • 分享至 

  • xImage
  •  

今天介紹另外一個主要技術指標:MACD指標。

MACD指標定義

指數平滑異同移動平均線(英語:Moving Average Convergence / Divergence, MACD),用於研判股票價格變化的強度、方向、能量,以及趨勢周期,以掌握股票買進和賣出的時機。MACD指標由以下幾個部份組成:

  • 快線:DIF
  • 慢線:MACD
  • 柱狀圖(MACD bar / Oscillator,OSC)

看盤軟體上快慢線分別用不同顏色表示:快線DIF是以紅色表示, 慢線MACD是以藍色表示。與Day15 KD指標相同的是,快線與慢線一樣有黃金交叉、死亡交叉的現象;只不過MACD指標是開放指標,因此就不會有上下限。而柱狀體是快線與慢線的離差(OSC),在看盤軟體上會以紅綠柱狀體表示:快線(紅線)在上慢線(藍線)在下為紅色柱狀體, 慢線(藍線)再上快線(紅線)在下為綠色柱狀體;並且綠色柱狀體變成紅色柱狀體時為黃金交叉,紅色柱狀體變成綠色柱狀體時為死亡交叉。以下為MACD指標圖例:
Imgur

MACD指標的參數

  • 日MACD參數設定(12,26,9)
  • 實務上不會用周或月的MACD

程式實作:取得或計算日MACD指標

在看盤軟體將MACD指標設定在副圖後匯出,資料檔案如Day16(1).xlsx所示。我們就可以透過以下程式碼將資料檔案匯入至DataFrame資料型態中:

##### 使用2018年6月22日到2018年9月17日之日線圖資料 #####

# 載入從「好神通PLUS」輸出的Excel檔
df = pd.read_excel('Day16(1).xlsx')

# 保存K線的基本資訊(開、高、低、收、量)
df_k_line = df.drop(columns=['SMA5','SMA10','SMA20','SMA60','SMA120','SMA240','MA5','MA10','DIF12-26','MACD9','OSC'])

# 保存價的移動平均線
df_sma = df.drop(columns=['開盤價','最高價','最低價','收盤價','成交量','MA5','MA10','DIF12-26','MACD9','OSC']) 

# 保存MACD指標
df_macd = df.drop(columns=['開盤價','最高價','最低價','收盤價','成交量','SMA5','SMA10','SMA20','SMA60','SMA120','SMA240','MA5','MA10'])

# 將K線的Columns的名稱由中文改為英文
df_k_line = df_k_line.rename(columns={'時間':'Date','開盤價':'Open','最高價':'High','最低價':'Low','收盤價':'Close','成交量':'Volume'})

# 將價的移動平均線的Columns的名稱由中文改為英文
df_sma = df_sma.rename(columns={'時間':'Date'})

# 將MACD指標的Columns的名稱由中文改為英文
df_macd = df_macd.rename(columns={'時間':'Date'})

# 將K線的Date設為Index
df_k_line.set_index(df_k_line['Date'],inplace=True)
df_k_line = df_k_line.drop(columns=['Date'])

# 將價的移動平均線的Date設為Index
df_sma.set_index(df_sma['Date'],inplace=True)
df_sma = df_sma.drop(columns=['Date'])

# 將MACD指標的Date設為Index
df_macd.set_index(df_macd['Date'],inplace=True)
df_macd = df_macd.drop(columns=['Date'])

之後將MACD指標繪製於副圖,程式碼如下所示:

# 設定K線格式
mc = mpf.make_marketcolors(up='xkcd:light red', down='xkcd:almost black', inherit=True)
s  = mpf.make_mpf_style(base_mpf_style='yahoo', marketcolors=mc)

# 設定移動平均線與MACD指標
added_plots={
    "SMA5": mpf.make_addplot(df_sma['SMA5'],width=0.4,color='xkcd:maroon'),
    "SMA10": mpf.make_addplot(df_sma['SMA10'],width=0.4,color='xkcd:cyan'),
    "SMA20": mpf.make_addplot(df_sma['SMA20'],width=0.4,color='xkcd:violet'),
    "SMA60": mpf.make_addplot(df_sma['SMA60'],width=0.4,color='xkcd:salmon'),
    "SMA120": mpf.make_addplot(df_sma['SMA120'],width=0.4,color='xkcd:blue grey'),
    "SMA240": mpf.make_addplot(df_sma['SMA240'],width=0.4,color='xkcd:sky blue'),
    'OSC': mpf.make_addplot(df_macd['OSC'],type='bar',panel=1,secondary_y=False,color='xkcd:light grey'),
    'DIF12-26': mpf.make_addplot(df_macd['DIF12-26'],width=0.8,panel=1,secondary_y=False,color='xkcd:red'),
    'MACD9': mpf.make_addplot(df_macd['MACD9'],width=0.8,panel=1,secondary_y=False,color='xkcd:blue')
            }

# 繪出K線圖
kwargs = dict(type='candle', style=s, figratio=(19,10), addplot=list(added_plots.values()), datetime_format='%Y-%m-%d')
mpf.plot(df_k_line,**kwargs)

看盤軟體的柱狀體會有紅綠兩種顏色表示,但在mplfinance柱狀體只能用一種顏色表示;這是兩者在繪圖上的差異。程式執行結果如下:
Imgur
除了讀取看盤軟體已經算好的MACD指標外,我們可以把K線資料(價量資料)交由talib套件的MACD函式計算以求得MACD指標,程式碼如下所示:

from talib.abstract import *

# 當使用talib Abstract API時,DataFrame欄位名稱需為小寫
# 請參考書籍:Python:量化交易 Ta-Lib 技術指標 139個活用技巧
df_k_line_talib = df_k_line.copy()
df_k_line_talib.columns=[ i.lower() for i in df_k_line_talib.columns]

talib_macd = MACD(df_k_line_talib, fastperiod=12, slowperiod=26, signalperiod=9)

要注意到talib套件的MACD函式回傳的指標名稱與我們在看盤軟體上看到的不同:talib的macd =看盤軟體的 DIF、talib的macdsignal = 看盤軟體的MACD、talib的macdhist = 看盤軟體的OSC。然後將計算好的MACD指標繪製於副圖,程式碼如下所示:

# 設定K線格式
mc = mpf.make_marketcolors(up='xkcd:light red', down='xkcd:almost black', inherit=True)
s  = mpf.make_mpf_style(base_mpf_style='yahoo', marketcolors=mc)

# 設定移動平均線與MACD指標
added_plots={
    "SMA5": mpf.make_addplot(df_sma['SMA5'],width=0.4,color='xkcd:maroon'),
    "SMA10": mpf.make_addplot(df_sma['SMA10'],width=0.4,color='xkcd:cyan'),
    "SMA20": mpf.make_addplot(df_sma['SMA20'],width=0.4,color='xkcd:violet'),
    "SMA60": mpf.make_addplot(df_sma['SMA60'],width=0.4,color='xkcd:salmon'),
    "SMA120": mpf.make_addplot(df_sma['SMA120'],width=0.4,color='xkcd:blue grey'),
    "SMA240": mpf.make_addplot(df_sma['SMA240'],width=0.4,color='xkcd:sky blue'),
    'OSC': mpf.make_addplot(talib_macd['macdhist'],type='bar',panel=1,secondary_y=False,color='xkcd:light grey'),
    'DIF12-26': mpf.make_addplot(talib_macd['macd'],width=0.8,panel=1,secondary_y=False,color='xkcd:red'),
    'MACD9': mpf.make_addplot(talib_macd['macdsignal'],width=0.8,panel=1,secondary_y=False,color='xkcd:blue')
            }

# 繪出K線圖
kwargs = dict(type='candle', style=s, figratio=(19,10), addplot=list(added_plots.values()), datetime_format='%Y-%m-%d')
mpf.plot(df_k_line,**kwargs)

程式執行結果如下:
Imgur
就如同KD指標,MACD指標一樣會有看盤軟體與talib套件的MACD函式計算出來的指標數值不同的現象。可以參閱Day15中關於這方面的說明,或是參考「股票發大財:用Python預測玩轉股市高手精解」書中第8.3.3節之內容。

MACD指標4種現象

以下為MACD指標現象:

  • MACD指標的強弱
  • MACD指標的柱狀體
  • MACD指標的雙線
  • MACD指標的背離

MACD指標的強弱

MACD指標中以零軸為分界,當MACD值在零軸以上,視為多頭/強勢,在零軸以下視為空頭/弱勢。中期盤整MACD指標會失靈,忽上忽下。MACD指標屬於開放指標,沒有鈍化的現象。

MACD指標的柱狀體

用柱狀體面積來分辨多空強弱,柱狀體由正值翻為負值,賣出。柱狀體由負值翻為正值,買進(但要先確認趨勢,空頭不太行)。

MACD指標的雙線

快線(DIF) 向上突破慢線(MACD),為買進訊號;快線(DIF) 向下跌破慢線(MACD),為賣出訊號。

程式實作:MACD指標黃金交叉與死亡交叉

會實作柱狀體與雙線的兩種交叉方式。首先是柱狀體的黃金交叉與死亡交叉,程式碼如下所示:

# 尋找黃金交叉:OSC由負值翻為正值
golden_points_len = len(np.array(df_macd['OSC']))
golden_points = np.array([np.nan]*golden_points_len)
for idx in range(1,golden_points_len) :
    if df_macd.iloc[idx-1]['OSC'] < 0 and  df_macd.iloc[idx]['OSC'] >= 0 :
        golden_points[idx] = df_macd.iloc[idx]['OSC']

# 尋找死亡交叉:OSC由正值翻為負值
death_points_len = len(np.array(df_macd['OSC']))
death_points = np.array([np.nan]*death_points_len)
for idx in range(1,death_points_len) :
    if df_macd.iloc[idx-1]['OSC'] > 0 and  df_macd.iloc[idx]['OSC'] <= 0 :
        death_points[idx] = df_macd.iloc[idx]['OSC']

程式執行結果如下:
Imgur
上圖在畫的時候我特意將柱狀體抽出來畫在另一個副圖上;原因在與雙線與柱狀體的尺度不太一樣所致,如果在有雙線與柱狀體的副圖上標註交叉點於視覺上面不容易辨識。
接下來是雙線的黃金交叉與死亡交叉,程式碼如下所示:

# 黃金交叉
def crossover(over,down):
    a1 = over
    b1 = down
    a2 = a1.shift(1)
    b2 = b1.shift(1)
    crossover =  (a1>a2) & (a1>=b1) & (b2>a2)
    return crossover

# 死亡交叉
def crossunder(down,over):
    a1 = down
    b1 = over
    a2 = a1.shift(1)
    b2 = b1.shift(1)
    crossdown =  (a1<a2) & (a1<=b1) & (b2<a2)
    return crossdown

# 尋找黃金交叉
ret_over=crossover(df_macd['DIF12-26'],df_macd['MACD9'])
golden_points_len = len(np.array(df_macd['MACD9']))
golden_points = np.array([np.nan]*golden_points_len)
for idx in range(0,len(ret_over)) :
    if ret_over[idx] :
        golden_points[idx] = df_macd['MACD9'][idx]

# 尋找死亡交叉
ret_under=crossunder(df_macd['DIF12-26'],df_macd['MACD9'])
death_points_len = len(np.array(df_macd['MACD9']))
death_points = np.array([np.nan]*death_points_len)
for idx in range(0,len(ret_under)) :
    if ret_under[idx] :
        death_points[idx] = df_macd['MACD9'][idx]

程式執行結果如下:
Imgur

MACD指標的背離

出現股價創新低,但指標沒有創新低的現象;或股價創新高,但指標沒有創新高。這是超漲或是超跌現象,之後容易伴隨多空反轉;長空頭的落底和買點確認,比KD指標背離更準確。

程式實作:標示MACD指標的背離

MACD指標的背離現象中可以用柱狀體或快線(DIF)的轉折來觀察,且需在K線圖上找轉折點(轉折點請參考Day9內容)。因此MACD指標的背離現象會被視為主觀性質,接下來將會使用Day1所提的「主觀性質客觀化」的方式一(將在看盤軟體繪製的圖形與線段以人工方式轉換成數據)把該現象轉換為客觀數據。在這邊會舉兩個例子:高檔背離(DIF)與低檔背離(柱狀體)。
首先是高檔背離(DIF),需要在快線上找轉折點。這邊無法用Day9的尋找轉折點函式,因此只能直接使用scipy.signal.argrelextrema函式;並以人工方式設定將轉折高點與高點連成直線且該直線向下傾斜。程式碼如下所示:

# 計算DIF轉折點(高點對高點)
macd_dif = np.array(df_macd['DIF12-26'])
local_max_idx = argrelextrema(macd_dif,np.greater,order=5)[0]
local_max_idx = np.array(local_max_idx)

# 人工方式設定DIF轉折點
dif_turning_point_line_x = [local_max_idx[3],local_max_idx[4]]
dif_turning_point_line_y = [df_macd['DIF12-26'].iloc[dif_turning_point_line_x[0]],df_macd['DIF12-26'].iloc[dif_turning_point_line_x[1]]]
slope,intercept = np.polyfit(dif_turning_point_line_x,dif_turning_point_line_y,1)
dif_turning_point_line_len = len(np.array(df_macd['DIF12-26']))
dif_turning_point_line = np.array([np.nan]*dif_turning_point_line_len)
for idx in range(0,dif_turning_point_line_len) :
    if idx >= dif_turning_point_line_x[0] and idx <= dif_turning_point_line_x[1] :
        dif_turning_point_line[idx] = slope * idx + intercept

並且還要找K線圖上的轉折點,並以人工方式設定將轉折高點與高點連成直線且該直線向上傾斜。程式碼如下所示:

# 尋找轉折點
_,_,max_min = myutils.FindingTurningPoints(df_k_line,'high_low',order=5)

# 人工方式設定轉折點
turning_point_line_start_date = max_min.loc[57]['Date'].strftime('%Y-%m-%d')
turning_point_line_start_price =  max_min.loc[57]['Price']
turning_point_line_end_date = max_min.loc[72]['Date'].strftime('%Y-%m-%d')
turning_point_line_end_price =  max_min.loc[72]['Price']

程式執行結果如下所示:
Imgur
接下來是低檔背離(柱狀體),需要在柱狀體上找轉折點。這邊無法用Day9的尋找轉折點函式,因此只能直接使用scipy.signal.argrelextrema函式;並以人工方式設定將轉折低點與低點連成直線且該直線向上傾斜。程式碼如下所示:

# 計算OSC轉折點(低點對低點)
macd_osc = np.array(df_macd['OSC'])
local_min_idx = argrelextrema(macd_osc,np.less,order=5)[0]
local_min_idx = np.array(local_min_idx)

print(local_min_idx)

# 人工方式設定OSC轉折點
osc_turning_point_line_x = [local_min_idx[1],local_min_idx[2]]
osc_turning_point_line_y = [df_macd['OSC'].iloc[osc_turning_point_line_x[0]],df_macd['OSC'].iloc[osc_turning_point_line_x[1]]]
slope,intercept = np.polyfit(osc_turning_point_line_x,osc_turning_point_line_y,1)
osc_turning_point_line_len = len(np.array(df_macd['OSC']))
osc_turning_point_line = np.array([np.nan]*osc_turning_point_line_len)
for idx in range(0,osc_turning_point_line_len) :
    if idx >= osc_turning_point_line_x[0] and idx <= osc_turning_point_line_x[1] :
        osc_turning_point_line[idx] = slope * idx + intercept

並且還要找K線圖上的轉折點,並以人工方式設定將轉折低點與低點連成直線且該直線向下傾斜。程式碼如下所示:

# 尋找轉折點
_,_,max_min = myutils.FindingTurningPoints(df_k_line,'high_low',order=5)

# 人工方式設定轉折點
turning_point_line_start_date = max_min.loc[24]['Date'].strftime('%Y-%m-%d')
turning_point_line_start_price =  max_min.loc[24]['Price']
turning_point_line_end_date = max_min.loc[47]['Date'].strftime('%Y-%m-%d')
turning_point_line_end_price =  max_min.loc[47]['Price']

print(turning_point_line_start_date,turning_point_line_start_price,turning_point_line_end_date,turning_point_line_end_price)

程式執行結果如下所示:
Imgur
一樣地將柱狀體抽出來畫在另一個副圖上,以便觀察直線的傾斜情況。
詳細程式碼請參照「第十六天:MACD指標.ipynb」。


上一篇
Day15 KD指標
下一篇
Day17 趨勢、位階、型態(1)
系列文
用Python程式進行股票技術分析30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言