MACD,中文名称为指数平滑移动平均线,是最常用的技术指标之一。该指标由双指数移动平均线发展而来,其意义和双移动平均线基本相同,即由快、慢均线的离散、聚合表征当前的多空状态和股价可能的发展变化趋势。与双指数移动平均线相比,MACD采用红绿柱的表现形式,阅读起来更加方便。其计算方法如下:
1、由快的指数移动平均线(EMA12)减去慢的指数移动平均线(EMA26)得到DIF;
2、计算DIF的9日指数移动平均线DEA;
3、MACD = 2×(DIF - DEA)。
值得注意的是,在一般的计算方法中,DIF是EMA12和EMA26之间的绝对差别,即DIF = EMA12 – EMA26,但是这种算法易受股价大小的影响,在对股价进行除权或复权操作后,尽管K线走势没有变化,但DIF的值也会随着除权或复权产生相应的变化;为了避免这种变化,DIF也可以考虑EMA12和EMA26之间的相对差别,即DIF = EMA12/EMA26 × 100 - 100。
在R语言中,MACD可以通过TTR包中的MACD函数计算。MACD函数的输出值有两个:macd和signal。但是这里的macd其实并不是各种常用股票软件中显示的MACD,而是DIF,而signal实际上是DEA。
MACD函数的用法如下:
MACD(x, nFast = 12, nSlow = 26, nSig =9, maType, percent = TRUE, ...)
x:要处理的数据,可以是xts格式,也可以是matrix格式或vector格式;
nFast:快速平均线的计算天数;
nSlow:慢速平均线的计算天数;
nSig:计算DEA时的平均线计算天数;
maType:计算平均线的方法,默认为指数平均,即EMA;
percent:默认为TRUE,即计算DIF时采用相对差别的方法。
下面我们使用上证指数进行一下实际运算:
# 使用Wind下载上证指数
library(WindR)
w.start()
data_wind <- w.wsd(codes ='000001.SH', fields = 'open,high,low,close', startdate = '20100101', enddate ='20180327')
stock <- data_wind$Data
stock_use <- xts(stock$CLOSE,order.by = as.Date(stock$DATETIME))
# 使用TTR包计算MACD
library(TTR)
macd_data <- MACD(stock_use, percent= F)
DIFF <- macd_data$macd
DEA <- macd_data$signal
MACD <- 2 * (DIFF - DEA)
MACD['20180327']
> MACD['20180327']
macd
2018-03-27 -23.0839
3月27日的MACD为-23.0839,与Wind上的结果一致:
根据网络中普遍流传的经验,使用MACD进行买卖可以遵循以下原则:
1.DIF、DEA均为正,DIF向上突破DEA,买入信号参考。
2.DIF、DEA均为负,DIF向下跌破DEA,卖出信号参考。
3.DIF线与K线发生背离,行情可能出现反转信号。
4.DIF、DEA的值从正数变成负数,或者从负数变成正数并不是交易信号,因为它们落后于市场。
以上原则在实际操作中是否管用,我们使用前面下载的上证指数验证一下。下图给出不同策略的收益和风险情况,其中红色部分为最大收益,绿色部分为最大亏损,“买入信号”表示使用上文“DIF、DEA均为正,DIF向上突破DEA”,“卖出信号”表示上文“DIF、DEA均为负,DIF向下跌破DEA”,“随机择时”表示随机进行500次择时。从平均收益(图中黑色圆圈)的角度看,的确买入信号出现时,收益最高,亏损最少,但是与其它策略相比,该结果并不显著,没有实际应用价值。至于背离信号,更多的是一种定性分析,难以使用定量方法进行判断,这里不再进行计算。
本文对MACD指标的计算方法和使用效果进行了探讨,可以看出,MACD指标更多的体现了一种对价格走势的归纳,具有一定的滞后性。单独使用MACD指标进行择时、选股,效果较差,实际应用中需配合其他指标使用。
代码如下:
#### 数据范围(2008年3月-2018年3月)
stock_use <- stock_use['20080301/20180301']
### 计算MACD
macd_data <- MACD(stock_use$CLOSE, percent = F)
DIFF <- macd_data$macd
DEA <- macd_data$signal
macd <- 2 * (DIFF - DEA)
#### 计算收益
# 涨幅以收盘价计算
close_temp <- lag.xts(stock_use$CLOSE, k = c(-1:-30))
HCP_5 <- (close_temp[, 1:5] %>% apply(MARGIN = 1, FUN = max)) / stock_use$CLOSE - 1
HCP_10 <- (close_temp[, 1:10] %>% apply(MARGIN = 1, FUN = max)) / stock_use$CLOSE - 1
HCP_15 <- (close_temp[, 1:15] %>% apply(MARGIN = 1, FUN = max)) / stock_use$CLOSE - 1
HCP_20 <- (close_temp[, 1:20] %>% apply(MARGIN = 1, FUN = max)) / stock_use$CLOSE - 1
HCP_25 <- (close_temp[, 1:25] %>% apply(MARGIN = 1, FUN = max)) / stock_use$CLOSE - 1
HCP_30 <- (close_temp[, 1:30] %>% apply(MARGIN = 1, FUN = max)) / stock_use$CLOSE - 1
HCP <- 100 * cbind(HCP_5, HCP_10, HCP_15, HCP_20, HCP_25, HCP_30)
colnames(HCP) <- paste('HCP_', seq(5, 30, 5), sep = '')
# 跌幅以最收盘价计算
LCP_5 <- (close_temp[, 1:5] %>% apply(MARGIN = 1, FUN = min)) / stock_use$CLOSE - 1
LCP_10 <- (close_temp[, 1:10] %>% apply(MARGIN = 1, FUN = min)) / stock_use$CLOSE - 1
LCP_15 <- (close_temp[, 1:15] %>% apply(MARGIN = 1, FUN = min)) / stock_use$CLOSE - 1
LCP_20 <- (close_temp[, 1:20] %>% apply(MARGIN = 1, FUN = min)) / stock_use$CLOSE - 1
LCP_25 <- (close_temp[, 1:25] %>% apply(MARGIN = 1, FUN = min)) / stock_use$CLOSE - 1
LCP_30 <- (close_temp[, 1:30] %>% apply(MARGIN = 1, FUN = min)) / stock_use$CLOSE - 1
LCP <- 100 * cbind(LCP_5, LCP_10, LCP_15, LCP_20, LCP_25, LCP_30)
colnames(LCP) <- paste('LCP_', seq(5, 30, 5), sep = '')
#### 二者都为正,diff上穿dea为买入信号
a_1 <- which((DIFF > 0) & (DEA > 0) & (DIFF > DEA)) # 当日 diff > dea
a_2 <- which(DIFF < DEA) + 1 # 前一日 diff < dea
aa <- intersect(a_1, a_2) # 取二者交集
sig_buy <- aa
print(stock_use[sig_buy]) # 输出
#### 二者都为负,diff下穿dea为卖出信号
a_1 <- which((DIFF < 0) & (DEA < 0) & (DIFF < DEA)) # 当日 diff < dea
a_2 <- which(DIFF > DEA) + 1 # 前一日 diff > dea
aa <- intersect(a_1, a_2) # 取二者交集
sig_sell <- aa
print(stock_use[sig_sell]) # 输出
#### 随机择时
set.seed(20180430) # 随机数种子
sig_rd <- sample(1:nrow(stock_use), 500) # 随机择时500次
#### 作图
## 设置图纸
mar <- c(3, 4, 2, 2)
mat <- cbind(1, 2)
widths <- c(12, 12)
heights <- 12
par(mar = mar,las = 1)
## 绘图参数
red <- rgb(250, 40, 40, maxColorValue = 255)
green <- rgb(130, 220, 66, maxColorValue = 255)
lab <- paste(c('买入', '卖出', '随机'), c('信号', '信号', '择时'), sep = '\n')
tit_1 <- '10日收益(%)'
tit_2 <- '20日收益(%)'
## 10日收益情况
layout(mat,widths = widths, heights = heights)
# 最高涨幅
i <- 2
data_plot <- list(as.vector(HCP[sig_buy, i]), as.vector(HCP[sig_sell, i]), as.vector(HCP[sig_rd, i]))
mean <- sapply(data_plot, mean, na.rm = T)
boxplot(data_plot, border = red, xaxt='n')
points(mean)
lines(c(-1, 4), rep(max(mean), 2), lty = 2)
axis(1, 1:3, lab, mgp=c(0, 1.7, 0))
axis(3, 1.3, tit_1, tcl = 0, mgp=c(0, 0.3, 0))
# 最低跌幅
data_plot <- list(as.vector(LCP[sig_buy, i]), as.vector(LCP[sig_sell, i]), as.vector(LCP[sig_rd, i]))
mean <- sapply(data_plot, mean, na.rm = T)
boxplot(data_plot, border = green, xaxt='n')
points(mean)
lines(c(-1, 4), rep(max(mean), 2), lty = 2)
axis(1, 1:3, lab, mgp=c(0, 1.7, 0))
axis(3, 1.3, tit_1, tcl = 0, mgp=c(0, 0.3, 0))
## 20日收益情况
layout(mat,widths = widths, heights = heights)
# 最高涨幅
i <- 4
data_plot <- list(as.vector(HCP[sig_buy, i]), as.vector(HCP[sig_sell, i]), as.vector(HCP[sig_rd, i]))
mean <- sapply(data_plot, mean, na.rm = T)
boxplot(data_plot, border = red, xaxt='n')
points(mean)
lines(c(-1, 4), rep(max(mean), 2), lty = 2)
axis(1, 1:3, lab, mgp=c(0, 1.7, 0))
axis(3, 1.3, tit_2, tcl = 0, mgp=c(0, 0.3, 0))
# 最低跌幅
data_plot <- list(as.vector(LCP[sig_buy, i]), as.vector(LCP[sig_sell, i]), as.vector(LCP[sig_rd, i]))
mean <- sapply(data_plot, mean, na.rm = T)
boxplot(data_plot, border = green, xaxt='n')
points(mean)
lines(c(-1, 4), rep(max(mean), 2), lty = 2)
axis(1, 1:3, lab, mgp=c(0, 1.7, 0))
axis(3, 1.3, tit_2, tcl = 0, mgp=c(0, 0.3, 0))