前言 各位看官好,我是小陈。前两篇文章,我们一起写了个数据表格和走势图,恩,是不是很容易啊,有没有信心满满现在!那么,接下来,我们来个有点难度的。咱们一起写一下MACD股票策略吧。
移动平均线(MA)是股市中最常用的一种技术分析方法,用来在大行情的波动段找到有效的交易信号。移动平均线不仅简单,而且有效,对股市操作具有神奇的指导作用,能有效地打败大部分的主观策略,是炒股、炒期货的必备基本工具。
接下来,我们仔细研究一下MACD策略的简单而又神奇之处吧。
目录 1.MACD的介绍 2.均线模型展示 3.R语言实现MACD策略
1、MACD的介绍 在介绍MACD之前,我们来介绍一下MA,也就是移动平均线。 移动平均线(MA,Moving average)是以道·琼斯的”平均成本概念”为理论基础,采用统计学中”移动平均”的原理,将一段时期内的股票价格平均值连成曲线,用来显示股价的历史波动情况,进而反映股价指数未来发展趋势的技术分析方法。它是道氏理论的形象化表述。 移动平均线的计算方法就是求连续若干天的收盘价的算术平均。天数就是MA的参数。
计算公式: MA = (C1+C2+C3+C4+C5+….+Cn)/n ,C为收盘价,n为移动平均周期数。例如,5日移动平均价格计算方法为:
1 MA5 = (前四天收盘价+前三天收盘价+前天收盘价+昨天收盘价+今天收盘价)/5
移动平均线依时间长短可分为三种,即短期移动平均线,中期移动平均线,长期移动平均线。短期移动平均线一般以5日或10日为计算期间,中期移动平均线大多以30日、60日为计算期间;长期移动平均线大多以100天和200天为计算期间。有根据处理数据的方式的不同,移动平均线又分为简单移动平均线(SMA)、加权移动平均线(WMA),以及指数平滑移动平均线(EMA)。
而今天我们说的,MACD(Moving Average Convergence / Divergence)也是基于移动平均线发展而来。
MACD称为指数平滑移动平均线,是从双指数移动平均线发展而来的,由快的指数移动平均线(EMA)减去慢的指数移动平均线,MACD的意义和双移动平均线基本相同,但阅读起来更方便。当MACD从负数转向正数,是买的信号。当MACD从正数转向负数,是卖的信号。当MACD以大角度变化,表示快的移动平均线和慢的移动平均线的差距非常迅速的拉开,代表了一个市场大趋势的转变。
2、均线模型展示 这里,为了让大家能够跟更好的理解,我特地制作一下走势图。
这是以IBM2010年1月到2012年1月的股价走势。其中黑线代表的是K线(该公司每天的股价),蓝线代表的是ma5(该公司每5天收盘价的均值连成的曲线),以及绿线ma20(该公司每20天收盘价的均值连成的曲线)。可以看到,ma20被无数个红点以及紫点覆盖掉了。红点是表示此时ma5>ma20,紫点是ma5<ma20.
从图中,我们可以直观的看到,红线总是在上升,而紫线则总是在下降。自然而然地,大家就会想到,我们应该在红线的第一个点买入,在紫线的第一个点卖出,这样,我们就可以挣得更多钱了。
当然也不是说,使用了MACD策略,我们就一定赚钱,坐着收钱了。毕竟股市可不是这样简单的东西。但是MACD策略却可以让我们更加清晰的看到市场的本质,提高了胜率。不过,即便是有输有赢,我们也要找到胜率最高的那一只股票是不。以下这幅图便是我们今天的任务了。使用我们今天学到的MACD策略,并对这么一组股票进行模拟交易,然后按照收益率高低进行排序。第一行是沪深300指数,以作比较。
3、R语言实现MACD策略 1)初始化
1 2 3 4 5 6 7 8 library(quantmod) library(plyr) library(TTR ) library(scales) library(ggplot2) library(qutke) key <- 'faca4c8ff4dc1502d87944ea9cfd74f19918f0aef37fde6746b61d4d339bfcf3' init(key)
2)选定参数
1 2 3 4 5 6 sDate<-as.Date("2015-1-1" ) eDate<-Sys.Date() tradingDay <- getDate(data='tradingDay' ,startdate=sDate,enddate=eDate,key=key) length <- length(tradingDay) sDate <- tradingDay[1 ] eDate <- tradingDay[length]
可以通过getIndudtry方法获取况客的股票代码。不过因为有点多,所以我这里只选其中的20多支股票的股票代码。
1 2 3 4 5 qtid <- c('002230.SZ' ,'002715.SZ' ,"603789.SH" , "603799.SH" , "603806.SH" , "603808.SH" , "603818.SH" ,"603828.SH" , "603869.SH" , "603883.SH" , "603885.SH" , "603889.SH" , "603898.SH" , "603899.SH" , "603901.SH" , "603918.SH" , "603939.SH" , "603968.SH" , "603969.SH" , "603988.SH" ,"603989.SH" , "603993.SH" , "603997.SH" , "603998.SH" )
3)获取每支股票的数据,包括每个交易日的收盘价、ma5、ma20.
1 2 3 4 dailyQuote <- getDailyQuote(data='mktFwdDaily' ,qtid=qtid,startdate=sDate,enddate=eDate,key=key) ldata <- dailyQuote[,grep("qtid|date|close|ma5|ma20" , names(dailyQuote)) ] ldata<-na.locf(ldata, fromLast=TRUE )
因为这次涉及到的数据处理比较多,听起来也比较虚,所以,我们在写代码的时候,可以多查看一下所得到的变量的数据结构是什么样子的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 > head(dailyQuote) qtid date close volume value fwdAdj fwdAdjOpen fwdAdjHi fwdAdjLo fwdAdjClose ret logRet 1 000001 .SZ 2015 -01 -05 16.02 286043643 4565387846 0.7574074 12.11094 12.33059 11.81556 12.13367 0.011364 0.011300 2 000001 .SZ 2015 -01 -06 15.78 216642140 3453446168 0.7574074 12.00491 12.41391 11.77769 11.95189 -0.014981 -0.015095 3 000001 .SZ 2015 -01 -07 15.48 170012067 2634796409 0.7574074 11.78526 11.98976 11.58833 11.72467 -0.019011 -0.019194 4 000001 .SZ 2015 -01 -08 14.96 140771421 2128003432 0.7574074 11.73981 11.79283 11.28537 11.33081 -0.033592 -0.034169 5 000001 .SZ 2015 -01 -09 15.08 250850023 3835378100 0.7574074 11.28537 12.02006 11.14146 11.42170 0.008021 0.007989 6 000001 .SZ 2015 -01 -12 14.77 155329086 2293104602 0.7574074 11.26265 11.39898 10.98241 11.18691 -0.020557 -0.020771 vwap vol30 ma5 ma10 ma20 ma60 1 15.9605 8427448605 11.72 11.45 11.17 9.23 2 15.9408 8529417547 11.82 11.51 11.22 9.30 3 15.4977 8476692763 11.91 11.52 11.23 9.37 4 15.1167 8460683917 11.83 11.54 11.27 9.43 5 15.2895 8493042075 11.71 11.62 11.31 9.49 6 14.7629 8437985489 11.52 11.62 11.34 9.55 > head(ldata) qtid date close ma5 ma20 1 002230 .SZ 2015 -01 -05 27.40 17.11 18.17 2 002230 .SZ 2015 -01 -06 27.72 16.99 18.12 3 002230 .SZ 2015 -01 -07 27.20 16.95 18.08 4 002230 .SZ 2015 -01 -08 27.20 17.07 18.04 5 002230 .SZ 2015 -01 -09 27.10 17.13 17.97 6 002230 .SZ 2015 -01 -12 27.91 17.19 17.93
4)以UP、Down标定。
对每只股票,以其每个交易日的ma20与ma5进行比较,当ma5>ma20时候,取UP,当ma5<ma20时候,取Down。然后,对数据以qtid为准进行分离并依照日期排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 genPoint<-function(arg=c(),ldata){ if (length(arg)>2 ){ stop('The length of args is two.' ) } if (length(arg)<2 ){ arg <- c(arg,2 ) } pdata <- ldata[which(ldata[,arg[2 ]]>ldata[,arg[1 ]]),] pdata <- cbind(pdata,'op' =c('Down' )) xdata <- ldata[which(ldata[,arg[2 ]]<=ldata[,arg[1 ]]),] xdata <- cbind(xdata,'op' ='UP' ) return (rbind(pdata,xdata)) } separateQtid<-function(pdata,qtid){ if (length(qtid)<=0 ){ stop('The length of qtid is at least one.' ) } xdata <- list() for (id in qtid){ xdata[[id]] <- pdata[which(pdata$qtid ==id),] } return (xdata) } pdata<-genPoint(c(5 ,4 ),ldata) pdata <- separateQtid(pdata,qtid) for (id in qtid){ pdata[[id]] <- pdata[[id]][order(pdata[[id]]$date ,decreasing=F),] }
这个时候pdata是一个list类型的结构,所以我只列出其中一个元素的前几行数据以展示。
1 2 3 4 5 6 7 8 > head(pdata[[1 ]]) qtid date close ma5 ma20 op 1 002230 .SZ 2014 -01 -02 48.07 17.36 17.28 Down2 002230 .SZ 2014 -01 -03 47.05 17.41 17.29 Down3 002230 .SZ 2014 -01 -06 45.12 17.27 17.29 UP 4 002230 .SZ 2014 -01 -07 45.08 17.12 17.27 UP 5 002230 .SZ 2014 -01 -08 45.44 16.95 17.24 UP 6 002230 .SZ 2014 -01 -09 45.42 16.75 17.18 UP
5)选择交易点。
也就是上面的走势图所说的,在红线的第一点买入,在紫线的第一点卖出。 模型设计思路:
以5日均线和20日均线的交叉,进行交易信号的判断。
当5日均线上穿20日均线则买入(红色),下穿20日均线卖出(蓝色)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Signal<-function(ldata=c(),pdata,qtid){ if (length(qtid)<=0 ){ stop('The length of qtid is at least one.' ) } op1 <- list() for (id in qtid){ pdata[[id]] <- pdata[[id]][order(pdata[[id]]$date ,decreasing=F),] op <- pdata[[id]][1 ,] tmp <- op$op i <- 2 nrow <- nrow(pdata[[id]]) while (i<=nrow){ if (tmp!=pdata[[id]][i,]$op ){ op <- rbind(op,pdata[[id]][i,]) } tmp <- pdata[[id]][i,]$op i=i+1 } op1[[id]] <- op } return (op1) } tdata<-Signal(ldata,pdata,qtid)
同pdata一样,这也是只展示了tdata的部分。
1 2 3 4 5 6 7 8 > head(tdata[[1 ]]) qtid date close ma5 ma20 op 1 002230 .SZ 2014 -01 -02 48.07 17.36 17.28 Down3 002230 .SZ 2014 -01 -06 45.12 17.27 17.29 UP 10 002230 .SZ 2014 -01 -15 48.80 17.14 17.10 Down36 002230 .SZ 2014 -02 -27 49.85 19.18 19.48 UP 64 002230 .SZ 2014 -04 -09 46.82 16.94 16.86 Down72 002230 .SZ 2014 -04 -21 25.71 16.88 16.91 UP
6)模拟交易
预先设定参数是:本金为100000元,每次都全部买入卖出,即持仓比例为1,手续费为0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 trade<-function(tdata,capital=100000 ,position=1 ,fee=0 ){ value <- as.numeric(tdata$close ) asset <- capital if (tdata[1 ,]$op =='Down' ){ amount <- (asset*position)%/%value[1] cash <- (asset*position)%%value[1] #奇数为0,偶数为asset减去上一次的。 diff <- 0.00 }else if(tdata[1,]$op=='UP'){ amount <- 0 cash <- asset diff <- 0.00 } i<-2 nrow <- nrow(tdata) while(i<=nrow){ #Down 买入 if(tdata[i,]$op=='Down'){ asset <- c(asset,tail(asset,1)) amount <- c(amount,(tail(asset,1)*position)%/ %value[i]) cash <- c(cash,(tail(asset,1 )*position)%%value[i]+(tail(asset,1)*(1-position))) diff <- c(diff,0.00) }else if(tdata[i,]$op=='UP'){ #UP 卖出 cash <- c(cash,(tail(amount,1)*value[i])+tail(cash,1)) amount <- c(amount,0) asset <- c(asset,tail(amount,1)*value[i]+tail(cash,1)) diff <- c(diff,tail(asset,1)-tail(asset,2)[1]) } i=i+1 } result <- list() result[['ticks']] <- cbind(tdata,cash,amount,asset,diff) indexRise <- c() indexFall <- c() i <- 2 while(i<=nrow){ if(result[['ticks']][i,]$diff > 0){ indexRise <- c(indexRise,i-1,i) }else if(result[['ticks']][i,]$diff < 0){ indexFall <- c(indexFall,i-1,i) } i <- i+1 } result[['rise']] <- result$ticks[indexRise,] result[['fall']] <- result$ticks[indexFall,] return(result) } #设定交易参数,以$10W为本金,满仓买入或卖出,手续为0,传入交易信号。 # 查看每笔交易,将会变成一个list,保存每一个qtid的交易记录result #每一个qtid的交易记录 tradeList <- list() for(id in qtid){ tradeList[[id]] <- trade(tdata[[id]],100000) }
7)计算收益率,并记录在一个列表里面,生成dataframe,信息包含日期,代码,名称,收益率,涨跌幅,近1周涨跌,近1月涨跌,年初至今涨跌。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 income <- c() quoteChangeDaily <- c() quoteChangeWeek <- c() quoteChangeMonth <- c() quoteChangeFYear <- c() for (id in qtid){ income <- c(income,(tail(tradeList[[id]]$ticks$asset ,1 )-tradeList[[id]]$ticks$asset [1 ])/tradeList[[id]]$ticks$asset [1 ]*100 ) mktYest <- getDailyQuote(data='mktFwdDaily' ,qtid = id,startdate=pdata[[id]]$date [nrow(pdata[[id]])-1 ],enddate=pdata[[id]]$date [nrow(pdata[[id]])-1 ],key=key) mktWeek <- getDailyQuote(data='mktFwdDaily' ,qtid = id,startdate=pdata[[id]]$date [nrow(pdata[[id]])-5 ],enddate=pdata[[id]]$date [nrow(pdata[[id]])-5 ],key=key) mktMonth <- getDailyQuote(data='mktFwdDaily' ,qtid = id,startdate=pdata[[id]]$date [nrow(pdata[[id]])-20 ],enddate=pdata[[id]]$date [nrow(pdata[[id]])-20 ],key=key) mktFYear <- getDailyQuote(data='mktFwdDaily' ,qtid = id,startdate=pdata[[id]]$date [1 ],enddate=pdata[[id]]$date [1 ],key=key) quoteChangeDaily <- c(quoteChangeDaily,(as.numeric(tail(pdata[[id]], 1 )$close )-mktYest$close )/mktYest$close *100 ) quoteChangeWeek <- c(quoteChangeWeek,(as.numeric(tail(pdata[[id]], 1 )$close )-mktWeek$close )/mktWeek$close *100 ) quoteChangeMonth <- c(quoteChangeMonth,(as.numeric(tail(pdata[[id]], 1 )$close )-mktMonth$close )/mktMonth$close *100 ) quoteChangeFYear <- c(quoteChangeFYear,(as.numeric(tail(pdata[[id]], 1 )$close )-mktFYear$close )/mktFYear$close *100 ) } ChiAbbr <- getMD(data='keyMap' ,qtid=qtid,key=key)$ChiAbbr maDF <- data.frame('1' =tail(dailyQuote,1 )$date ,'2' =qtid,'3' =ChiAbbr, '4' =income,'5' =quoteChangeDaily,'6' =quoteChangeWeek,'7' =quoteChangeMonth, '8' =quoteChangeFYear) maDF <- maDF[order(maDF$X4 ,decreasing=T),]
8)求取沪深300指数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 hs300<-getDailyQuote(data='mktDataIndex' ,qtid=c('000300.SH' ),startdate=sDate,enddate=eDate,key=key) id <- c('000300.SH' ) hs300mktWeek <- hs300[nrow(hs300)-5 ,] hs300mktMonth <- hs300[nrow(hs300)-20 ,] hs300mktFYear <- hs300[1 ,] quoteChangeDaily <- (tail(hs300,1 )$close -tail(hs300,1 )$prevClose )/tail(hs300,1 )$prevClose *100 quoteChangeWeek <- (tail(hs300,1 )$close -hs300mktWeek$close )/hs300mktWeek$close *100 quoteChangeMonth <- (tail(hs300,1 )$close -hs300mktMonth$close )/hs300mktMonth$close *100 quoteChangeFYear <- (tail(hs300,1 )$close -hs300mktFYear$close )/hs300mktFYear$close *100 hs300Income <- c() hs300Income <- (tail(hs300,1 )$close -hs300mktFYear$close )/hs300mktFYear$close *100 hs300Dt <- data.frame('1' =tail(dailyQuote,1 )$date ,'2' ='000300.SH' ,'3' ='沪深300' ,'4' =hs300Income,'5' =quoteChangeDaily,'6' =quoteChangeWeek,'7' =quoteChangeMonth,'8' =quoteChangeFYear)
沪深300指数的数据是这样的:
1 2 3 > hs300Dt X1 X2 X3 X4 X5 X6 X7 X8 1 2016 -01 -22 000300 .SH 沪深300 -14.50151 1.042311 -0.1688989 -18.69586 -14.50151
9)合并数据,并发送到况客投研平台,进行后期的修改。
1 2 3 maDF <- rbind(hs300Dt,maDF) names(maDF)<-c('日期' ,'代码' ,'名称' ,'收益率(%)' ,'涨跌幅(%)' ,'周涨跌幅(%)' ,'月涨跌幅(%)' ,'年初至今涨跌幅(%)' ) postData(maDF,name='maDataF' ,key=key)
这样,便算是完成了。大家如果有什么不明白的话,可以随时艾特我哦。