R语言构建追涨杀跌量化交易模型

用IT技术玩金融系列文章,将介绍如何使用IT技术,处理金融大数据。在互联网混迹多年,已经熟练掌握一些IT技术。单纯地在互联网做开发,总觉得使劲的方式不对。要想靠技术养活自己,就要把技术变现。通过“跨界”可以寻找新的机会,创造技术的壁垒。

金融是离钱最近的市场,也是变现的好渠道!今天就开始踏上“用IT技术玩金融”之旅!

关于作者:

  • 张丹(Conan), 程序员Java,R,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/finance-chase-sell/

chaseSell

前言

久经股市的老股民,通常都会使用一种常见的交易策略,追涨杀跌交易法。追涨杀跌法,是股市操作的一个重要技巧,就是在股市上涨时买入股票,股市下跌时卖出股票。如果操作得当是很好的赢利手段,在中国股市2015年上半年的牛市中,追涨杀跌交易法就是交易神器法门。

目录

  1. 什么是追涨杀跌?
  2. 追涨杀跌的建型和实现
  3. 模型优化

1. 什么是追涨杀跌?

追涨杀跌是金融市场的专业术语,是金融技术派操盘的一种方式,与抄底摸顶的操作方式刚好相反。追涨杀跌的操作方法是,金融市场中在金融产品(股票,期货,外汇等)价格上涨的时候买入,以期待涨得更多,并以更高的价格卖出获利;在价格下跌的时候卖出进行止损,不管之前金融产品买入的价格是多少,都立刻卖出,以避免更大的损失。

下面以平安银行(000001)股票日K线图为例,截取2005年到2015年7月的股票数据,股价为向前复权的价格。

01

上图中有3条曲线,黑色线是平安银行向前复权后的每日股价,红色线为20日均线,蓝色线为60日均线。关于均线的介绍,请参考文章 两条均线打天下。图中还有一条红色的水平线虚线,是这10年的股价平均值等于7.14元。这10年间,平安银行股价起起落落,每次上涨都会持续一段时间,那么在我们可以上涨中段追入买进,在大幅下跌之前卖出,就是追涨杀跌法。

追涨

追涨通常有2种情况,短线追涨和中线追涨。以股票交易为例。

  • 短线追涨:当天股价已经涨了5个点或更多,这时追进去买入,期待当天收盘涨停,等下一个交易日高价卖出,这就是短线追涨。
  • 中线追涨:某只股票的股价已经上涨了一段时间,走势很强,底部上涨已经有30%或更多,这个时追进去买入,期待后势持续上涨,是中线追涨。

不论短线追涨还是中线追涨,我们追涨的理由和判断的标准都是一样的,“涨得好的股票说明走势很强,那么追强势股期待股票持续上涨”。但从另一个角度看,追涨的风险也是很大的,关键在于我们不知道它能涨到什么时候,到什么位置可能会回调,什么时候是庄家出货。所以说追涨技巧性很强,什么情况下能追,什么情况下不能追,都是很有讲究的。

杀跌

杀跌也是一门较深的操作技巧,杀得好的话既可以回避风险。如果不能进行杀跌的准确判断,则很可能会发生硬性止损,导致严重的亏损。

采取杀跌战术,必须掌握以下操作要点:

  • 杀跌的重要前提是知变,就是对消息、对量能、对周边各种因素的影响有一个准确的、快速的知变过程。
  • 杀跌要善于观察盘面、形态、消息、舆论等方面的细微变化,在对大盘作出准确估量的基础上,迅速作出反应就是确定卖出哪支股票,卖出多少等。
  • 杀跌要善于思考大盘与目标股之问的正向、反向的相关性,及目标股的能量能等方向的关系,从中找出变动的规律。
  • 当市场利好消息接踵而至时,应随时准备卖出股票。当利好出尽就会转向利空,往往消息明朗时,股价早已下跌大半,导致措手不及。

杀跌的后续操作就比较简单了,只需保持良好的心态,耐心等待见底信号后再抄底。一般只有在底部成功接回股票或换股才算成功的杀跌,否则杀跌会失去意义。应保持耐心,千万不要在股价反弹时贸然介入,否则屡买屡跌、会加大损失,应待股价反转时再介入。

追涨杀跌的技巧

要观大势,看清整个市场多空对决的优势一方是谁。

追涨杀跌的假设条件:

  • 市场处于强势交易格局。强势格局赋予个股良好的运作基础;相反,如果市场处于平衡状态,就不适合进行追涨杀跌,更适合高抛低吸。
  • 市场形成了鲜明并有持续性上涨能力的热点或板块效应。

追涨操作的对象:

  • 市场形成鲜明的可持续性的热点时,可追涨这个热点。从理论上讲,只要把握热点板块就能获利,追涨时应重点关注龙头企业。比如:沪深300指数的成分股,就是不错的选择。
  • 形成明显的上涨趋势的个股可以考虑追涨。个股突破重要的压力线,如头肩底形态的颈线位、整理箱体的箱顶位、三角形的上边眼、长期下降趋势线等,待回抽确认其突破的有效性后,可考虑追涨。

追涨杀跌的注意事项:

  • 追涨时应设好止盈位和止损位,确定自己的大致盈利目标。
  • 追涨杀跌应该注意频率的减少,过于频繁的操作,一旦失误增多会严重影响情绪。
  • 追涨杀跌中要多一分平和的心态,少一些侥幸;多一分理性,少一分感性。

追涨杀跌的市场操作

在弱市中,短线追涨往往容易被套,因为弱市里大部分股票的行情只有一天,当天追得高了,第二天多半没有获利逃出的机会。相反,弱市中,中线追涨比较可取,能够在弱市中保持强势的股票不多,但这种强势反而可以持续。但很多时候,弱市中这样的股票反倒不敢追了。

有些人专门追涨停板,追新股开板,追次新股开板等,对于2015年上半年IPO的新股来说收获颇丰,如暴风科技,中超电缆等,其他的时候可能用这套追涨停策略就行不通了。

001

我自己操作并不会盲目追涨,安全第一,虽然还没有吃过追涨的亏,但由于本人目前还不具备基本面的分析能力,也没有消息的渠道,仅仅从技术指标入手,投机性太重,很容易被庄家玩死的。道理很简单,因为如果追涨了,下跌的空间自然就大了,尤其中线追涨,庄家获利后随时可能出货。这时候就必须要做好杀跌的策略。庄股出货跌个50%太普遍了,而且跌下去就很难再起来,这种时候不杀跌就等于自杀。所以,追涨之前一定要想清楚,务必先设好止损位,不能只憧憬获利的美妙。

杀跌也有讲究,不是一跌就杀,震荡、洗盘是很常见的,问题在于正确地区别震仓与出货是很难的,所以很多人是硬性止损,即预先设定价位或百分比,到这个位置就坚决出局,不管它是震仓还是庄家出货。这个时候如果没有基本面的判断或第一手消息的来源,就容易被震荡出局。当然,和不确定的风险相比,我其实更愿意早点出来。

2. 追涨杀跌的建型和实现

既然我们要进行追涨杀跌的操作,就要定义什么是追涨,什么又是杀跌,需要把追涨杀跌的概念量化出来,从而进行建模和实现。对于短线追涨策略可以简单粗暴地选择当日涨幅超过5%的股票进行买入,第二日开盘卖出或到止损位卖出,并没有太多的技术细节,再计算一下胜率概率,就能知道我们要不要短线追涨。

下文我们将以中线追涨为例,进行建模和程序实现。

那么对于中线追涨的情况,我们核心的策略思路就可以转化为,当股价(成交量)向上突破最近20日最高价格(量)时买入,当股价向下突破最近10日最低价格卖出,并以沪深300成分股做为股票交易的标的。其中,为什么是20日最高和10日最低都是经验值,可以做为模型参数进行训练和优化。选择沪深300成分股为标的,是考虑到这些股票都是各个板块的强势股或龙头股,要符合追涨杀跌的假设条件。

计算公式:


20日最高价 = max(T日股价,T-1日股价 ... , T-19日股价)
20日最高量 = max(T日成交量,T-1日成交量 ... , T-19日成交量)
10日最低价 = min(T日股价,T-1日股价 ... , T-9日股价)

当股价大于等于20日最高价时作为买入信号点,当股价小于等于10日最低价时作为卖出信号点。这样我们就把追涨杀跌的投资理论,变成了一个数学模型。

接下来,我们利用R语言对股票数据的进行操作,来实现一个追涨杀跌模型的实例,从而验证我的们投资理论,是否能发现赚钱的机会。

2.1 数据准备

R语言本身提供了丰富的金融函数工具包,时间序列包zoo和xts,指标计算包TTR,数据处理包plyr,可视包ggplot2等,我们会一起使用这些工具包来完成建模、计算和可视化的工作。关于zoo包和xts包的详细使用可以参考文章,R语言时间序列基础库zoo可扩展的时间序列xts

我本次用到的数据是从 况客 直接导出的,况客 会提供各种类型的金融数据API,让开发者可以免费下载。当然,你也可以用quantmod包从Yahoo财经下载数据。

本文用到的数据,包括A股日K线(向前复权)数据,从2014年7月到2015年日8月,以CSV格式保存到本地文件stock.csv。

数据格式如下:


000001.SZ,2014-07-02,8.14,8.18,8.10,8.17,28604171
000002.SZ,2014-07-02,8.09,8.13,8.05,8.12,40633122
000004.SZ,2014-07-02,13.9,13.99,13.82,13.95,1081139
000005.SZ,2014-07-02,2.27,2.29,2.26,2.28,4157537
000006.SZ,2014-07-02,4.57,4.57,4.50,4.55,5137384
000010.SZ,2014-07-02,6.6,6.82,6.5,6.73,9909143

一共7列:

  • 第1列,股票代码,code,000001.SZ
  • 第2列,交易日期,date,2014-07-02
  • 第3列,开盘价,Open,8.14
  • 第4列,最高价,High,8.18
  • 第5列,最低价,Low,8.10
  • 第6列,收盘价,Close,8.17
  • 第7列,交易量,Volume,28604171

通过R语言加载股票数据,由于数据所有股票都是混合在一起的,而进行计算时又需要按每支票股计算,所以在数据加载时我就进行了转换,按股票代码进行分组,生成R语言的list对象,同时把每支股票的data.frame类型对象转成XTS时间序列类型对象,方便后续的数据处理。


#加载工具包
> library(plyr)
> library(xts)
> library(TTR)
> library(ggplot2)
> library(scales)

# 读取CSV数据文件
> read<-function(file){ 
+   df<-read.table(file=file,header=FALSE,sep = ",", na.strings = "NULL") # 读文件
+   names(df)<-c("code","date","Open","High","Low","Close","Volume")      # 设置列名
+   dl<-split(df[-1],df$code)                                             # 按ccode分组
+   
+   lapply(dl,function(row){                                              # 换成xts类型数据
+     xts(row[-1],order.by = as.Date(row$date))
+   })
+ }

# 加载数据
> data<-read("stock.csv")

# 查看数据类型
> class(data)
[1] "list"

# 查看数据的索引值
> head(names(data))
[1] "000001.SZ" "000002.SZ" "000004.SZ" "000005.SZ" "000006.SZ" "000007.SZ"

# 查看包括的股票数量
> length(data)
[1] 2782

# 获得时间范围
dateArea<-function(sDate=Sys.Date()-365,eDate= Sys.Date(),before=0){  #开始日期,结束日期,提单开始时
  if(class(sDate)=='character') sDate=as.Date(sDate)
  if(class(eDate)=='character') eDate=as.Date(eDate)  
  return(paste(sDate-before,eDate,sep="/"))
}

# 查看股票000001.SZ
> head(data[['000001.SZ']])
               Open     High      Low    Close   Volume
2014-07-02 8.146949 8.180000 8.105636 8.171737 28604171
2014-07-03 8.171737 8.254364 8.122162 8.229576 44690486
2014-07-04 8.237838 8.270889 8.146949 8.188263 34231126
2014-07-07 8.188263 8.204788 8.097374 8.146949 34306164
2014-07-08 8.130424 8.204788 8.072586 8.204788 34608702
2014-07-09 8.196525 8.196525 7.915596 7.973434 58789114

把数据准备好了,我们就可以来建立模型了。

2.2 追涨杀跌模型

为了能拉近我们对市场的了解,我们取从2015年1月1日开始的数据,来创建追涨杀跌的模型。以乐视网(300104)的为例,画出乐视网自2015年以来的每日收盘价,20日最高价和10日最低价。乐视网作为创业板标杆的上市公司,在2015年7月首次被纳入沪深300指数。


# 日K线数据
> title<-'300104.SZ'
> stock<-data[[title]]                                # 获得股票数据
> sDate<-as.Date("2015-01-01")                        # 开始日期
> eDate<-as.Date("2015-08-24")                        # 结束日期
> cdata<-stock[dateArea(sDate,eDate,360)]$Close       # 获得收盘价
> vdata<-stock[dateArea(sDate,eDate,360)]$Volume      # 获得交易量

# 收盘价
> names(cdata)<-"Value"   # 重置列名
> tail(cdata)
           Value
2015-08-14 49.81
2015-08-17 48.30
2015-08-18 45.57
2015-08-19 46.98
2015-08-20 45.79
2015-08-21 42.14
 
# 交易量
> tail(vdata)
             Volume
2015-08-14 42108324
2015-08-17 35939096
2015-08-18 52745702
2015-08-19 43447844
2015-08-20 32916746
2015-08-21 34802494

定义画图函数drawLine(),支持画出多条曲线,包括收盘价,最高价,最低价。


# 画图函数
> drawLine<-function(cdata,titie="Stock",sDate=min(index(cdata)),eDate=max(index(cdata)),breaks="1 year"){
+     if(sDate<min(index(cdata))) sDate=min(index(cdata))
+     if(eDate>max(index(cdata))) eDate=max(index(cdata))  
+     cdata<-na.omit(cdata)
+     
+     g<-ggplot(aes(x=Index, y=Value),data=fortify(cdata[,1],melt=TRUE))
+     g<-g+geom_line()
+     
+     if(ncol(cdata)>1){ # 多条线
+         g<-g+geom_line(aes(colour=Series),data=fortify(cdata[,-1],melt=TRUE))  
+     }
+     
+     g<-g+scale_x_date(labels=date_format("%Y-%m"),breaks=date_breaks(breaks),limits = c(sDate,eDate))
+     g<-g+ylim(min(cdata$Value), max(cdata$Value))
+     g<-g+xlab("") + ylab("Price")+ggtitle(title)
+     g
+ }

# 画出收盘价
# drawLine(cdata,title,sDate,eDate,'1 month')    # 画图

# 计算最近20日的最高价和10日的最低价
> minmax<-function(data,max=20,min=10){
+   d1<-na.locf(data,fromLast=TRUE)
+   d2<-merge(d1,min=runMin(d1,min),max=runMax(d1,max))
+   return(d2[,-1])
+ }
 
# 画出股价,最高价和最低价
> ldata<-cbind(cdata,minmax(cdata))
> drawLine(ldata,title,sDate,eDate,'1 month')    # 画图

01

上图中有3条线,黑色线为乐视网的每日收盘价,蓝色线为最近20日最高价,红色线为最近10日最低价。

根据模型的计算公式,我们计算买入信号,当股价向上突破最近20日最高价格时买入。


# 买入信号函数
> buyPoint<-function(ldata){   
+  idx<-which(ldata$Value == ldata$max)
+  return(ldata[idx,])                                  
+ }

# 计算买入的点
> buydata<-buyPoint(ldata)
> buydata
              Value      min      max
2015-01-08 17.43721 13.70164 17.43721
2015-01-09 17.98709 13.74254 17.98709
2015-01-12 19.53222 13.74254 19.53222
2015-01-15 20.21389 14.74232 20.21389
2015-01-16 22.23619 16.08749 22.23619
2015-01-19 23.04056 16.36016 23.04056
2015-01-20 23.89947 16.36016 23.89947
2015-01-26 24.77656 19.22774 24.77656
2015-01-27 25.16284 19.40043 25.16284
2015-02-05 26.91247 21.99533 26.91247
2015-02-10 28.68482 21.99533 28.68482
2015-02-11 31.55239 21.99533 31.55239
2015-02-12 31.87960 21.99533 31.87960
2015-02-13 35.06983 22.72245 35.06983
2015-02-16 38.57817 24.22213 38.57817
2015-02-17 40.99130 24.46753 40.99130
2015-03-16 41.07764 34.32453 41.07764
2015-03-18 41.94564 34.32453 41.94564
2015-03-24 45.34946 37.17393 45.34946
2015-04-23 46.27199 37.06031 46.27199
2015-04-24 50.89829 37.06031 50.89829
2015-04-27 50.90283 37.06031 50.90283
2015-04-28 55.44277 37.06031 55.44277
2015-04-29 60.98705 37.06031 60.98705
2015-05-06 62.25497 45.19495 62.25497
2015-05-07 66.20413 46.27199 66.20413
2015-05-08 67.23573 50.89829 67.23573
2015-05-11 73.96157 50.90283 73.96157
2015-05-12 81.36000 55.44277 81.36000
2015-05-13 82.49000 57.16514 82.49000

画出买入的信号图,让我们可以直观的看到效果。


# 画图函数
> drawPoint<-function(ldata,pdata,titie,sDate,eDate,breaks="1 year"){
+     ldata<-na.omit(ldata)
+     g<-ggplot(aes(x=Index, y=Value),data=fortify(ldata[,1],melt=TRUE))
+     g<-g+geom_line()
+     g<-g+geom_line(aes(colour=Series),data=fortify(ldata[,-1],melt=TRUE))
+     
+     if(is.data.frame(pdata)){
+         g<-g+geom_point(aes(x=Index,y=Value,colour=op),data=pdata,size=4)
+     }else{
+         g<-g+geom_point(aes(x=Index,y=Value,colour=Series),data=na.omit(fortify(pdata,melt=TRUE)),size=4)  
+     }
+     g<-g+scale_x_date(labels=date_format("%Y-%m"),breaks=date_breaks(breaks),limits = c(sDate,eDate))
+     g<-g+xlab("") + ylab("Price")+ggtitle(title)
+     g
+ }

> drawPoint(ldata,buydata$Value,title,sDate,eDate,'1 month')  # 画图

02

如上图所示,蓝色的点为股价大于等于最近20日最高价的点,作为买入信号。所有买入信号点,都是出现在单边上行的牛势中,对于2015年上半年以来的行情来说,追涨的信号会被大量触发。

接下来,我们继续计算卖出信号点,当股价小于等于最近10日最低价时作为卖出信号点。


> # 计算卖出的信号点
> stopPoint<-function(ldata,buydata){  
+     idx<-which(ldata$Value == ldata$min)
+     idx<-idx[which(c(0,diff(idx))!=1)]   # 第一点用0表示
+     
+     selldata<-ldata[idx,]               # 所有低于最小值的点  
+     idx2<-sapply(index(buydata),function(e){  # 买后的卖点
+         head(which(index(selldata)>e),1)
+     })
+     
+     return(selldata[unique(idx2),])
+ } 

# 卖出信号
> selldata<-stopPoint(ldata,buydata)
> selldata
              Value      min      max
2015-01-30 21.99533 21.99533 25.16284
2015-03-06 34.32453 34.32453 40.99130
2015-04-08 38.01011 38.01011 45.34946
2015-05-28 64.68000 64.68000 82.49000

一共有4笔卖出信号,为了让数据更加直观,我们合并买入信号和卖出信号,进行画图可视化。


# 买卖信号,画图
> bsdata<-merge(buydata$Value,selldata$Value)
> names(bsdata)<-c("buy","sell")
> drawPoint(ldata,bsdata,title,sDate,eDate,'1 month') #画图

03

上图中,紫色点为卖出信号点,红色点为买入信号点。我们可以很明显的看出,如果根据交易信号在红色点买入,紫色点卖出,我们是应该赚钱的。那么具体赚了多少呢,我需要计算出来?


> # 合并交易信号
> signal<-function(buy, sell){
+     selldf<-data.frame(sell,op=as.character(rep("S",nrow(sell))))
+     buydf<-data.frame(buy,op=as.character(rep("B",nrow(buy))))
+     sdata<-rbind(buydf,selldf)                                       # 交易信号数据
+     sdata[order(as.Date(row.names(sdata))),]
+ }

# 合并交易信号
> sdata<-signal(buydata,selldata)                                   
> sdata
              Value      min      max op
2015-01-08 17.43721 13.70164 17.43721  B
2015-01-09 17.98709 13.74254 17.98709  B
2015-01-12 19.53222 13.74254 19.53222  B
2015-01-15 20.21389 14.74232 20.21389  B
2015-01-16 22.23619 16.08749 22.23619  B
2015-01-19 23.04056 16.36016 23.04056  B
2015-01-20 23.89947 16.36016 23.89947  B
2015-01-26 24.77656 19.22774 24.77656  B
2015-01-27 25.16284 19.40043 25.16284  B
2015-01-30 21.99533 21.99533 25.16284  S
2015-02-05 26.91247 21.99533 26.91247  B
2015-02-10 28.68482 21.99533 28.68482  B
2015-02-11 31.55239 21.99533 31.55239  B
2015-02-12 31.87960 21.99533 31.87960  B
2015-02-13 35.06983 22.72245 35.06983  B
2015-02-16 38.57817 24.22213 38.57817  B
2015-02-17 40.99130 24.46753 40.99130  B
2015-03-06 34.32453 34.32453 40.99130  S
2015-03-16 41.07764 34.32453 41.07764  B
2015-03-18 41.94564 34.32453 41.94564  B
2015-03-24 45.34946 37.17393 45.34946  B
2015-04-08 38.01011 38.01011 45.34946  S
2015-04-23 46.27199 37.06031 46.27199  B
2015-04-24 50.89829 37.06031 50.89829  B
2015-04-27 50.90283 37.06031 50.90283  B
2015-04-28 55.44277 37.06031 55.44277  B
2015-04-29 60.98705 37.06031 60.98705  B
2015-05-06 62.25497 45.19495 62.25497  B
2015-05-07 66.20413 46.27199 66.20413  B
2015-05-08 67.23573 50.89829 67.23573  B
2015-05-11 73.96157 50.90283 73.96157  B
2015-05-12 81.36000 55.44277 81.36000  B
2015-05-13 82.49000 57.16514 82.49000  B
2015-05-28 64.68000 64.68000 82.49000  S

接下来,我们利用交易信号数据,进行模拟交易。我们设定交易参数和规则:

  • 以10万元人民币为本金。
  • 买入信号出现时,以收盘价买入,每次买入价值1万元的股票。如果连续出现买入信号,则一直买入。若现金不足1万元时,则跳过买入信号。
  • 卖出信号出现时,以收盘价卖出,一次性平仓信号对应的股票。
  • 手续费为0元

下面我们进行模拟交易。


> # 模拟交易
> trade<-function(sdata,capital=100000,fixMoney=10000){ # 交易信号,总资金,每次定投资金
+     amount<-0
+     cash<-capital
+     
+     ticks<-data.frame()
+     for(i in 1:nrow(sdata)){
+         row<-sdata[i,]
+         if(row$op=='B'){
+             if(cash<fixMoney){
+                 print(paste(row.names(row),"No enough cash"))
+                 next
+             }
+             amount0<-floor(fixMoney/row$Value) # 本次交易量
+             amount<-amount+amount0
+             cash<-cash-amount0*row$Value
+         }
+         
+         if(row$op=='S'){
+             cash<-cash+amount*row$Value
+             amount<-0
+         }
+         
+         row$cash<-round(cash,2)
+         row$amount<-amount
+         row$asset<-round(cash+amount*row$Value,2)
+         ticks<-rbind(ticks,row)
+     }
+     
+     
+     ticks$diff<-c(0,round(diff(ticks$asset),2))
+     
+     rise<-ticks[intersect(which(ticks$diff>0),which(ticks$op=='S')),]   # 赚钱的交易
+     fall<-ticks[intersect(which(ticks$diff<0),which(ticks$op=='S')),]   # 赔钱的交易
+     
+     return(list(
+         ticks=ticks,
+         rise=rise,
+         fall=fall
+     ))
+ }

# 交易结果
> result<-trade(sdata,100000,10000)  

来看一下,每笔交易的明细。


> result$ticks
              Value      min      max op      cash amount    asset      diff
2015-01-08 17.43721 13.70164 17.43721  B  90008.48    573 100000.0      0.00
2015-01-09 17.98709 13.74254 17.98709  B  80025.65   1128 100315.1    315.08
2015-01-12 19.53222 13.74254 19.53222  B  70044.68   1639 102058.0   1742.91
2015-01-15 20.21389 14.74232 20.21389  B  60059.02   2133 103175.2   1117.26
2015-01-16 22.23619 16.08749 22.23619  B  50074.97   2582 107488.8   4313.56
2015-01-19 23.04056 16.36016 23.04056  B  40075.37   3016 109565.7   2076.90
2015-01-20 23.89947 16.36016 23.89947  B  30085.39   3434 112156.2   2590.46
2015-01-26 24.77656 19.22774 24.77656  B  20100.44   3837 115168.1   3011.92
2015-01-27 25.16284 19.40043 25.16284  B  10110.79   4234 116650.2   1482.16
2015-01-30 21.99533 21.99533 25.16284  S 103239.02      0 103239.0 -13411.23
2015-02-05 26.91247 21.99533 26.91247  B  93254.49    371 103239.0      0.00
2015-02-10 28.68482 21.99533 28.68482  B  83272.17    719 103896.6    657.54
2015-02-11 31.55239 21.99533 31.55239  B  73301.62   1035 105958.3   2061.78
2015-02-12 31.87960 21.99533 31.87960  B  63323.30   1348 106297.0    338.66
2015-02-13 35.06983 22.72245 35.06983  B  53328.40   1633 110597.4   4300.43
2015-02-16 38.57817 24.22213 38.57817  B  43336.66   1892 116326.6   5729.13
2015-02-17 40.99130 24.46753 40.99130  B  33375.77   2135 120892.2   4565.63
2015-03-06 34.32453 34.32453 40.99130  S 106658.65      0 106658.6 -14233.54
2015-03-16 41.07764 34.32453 41.07764  B  96676.78    243 106658.6      0.00
2015-03-18 41.94564 34.32453 41.94564  B  86693.72    481 106869.6    210.92
2015-03-24 45.34946 37.17393 45.34946  B  76716.83    701 108506.8   1637.24
2015-04-08 38.01011 38.01011 45.34946  S 103361.92      0 103361.9  -5144.89
2015-04-23 46.27199 37.06031 46.27199  B  93367.17    216 103361.9      0.00
2015-04-24 50.89829 37.06031 50.89829  B  83391.11    412 104361.2    999.28
2015-04-27 50.90283 37.06031 50.90283  B  73414.15    608 104363.1      1.87
2015-04-28 55.44277 37.06031 55.44277  B  63434.45    788 107123.4   2760.29
2015-04-29 60.98705 37.06031 60.98705  B  53493.56    951 111492.2   4368.89
2015-05-06 62.25497 45.19495 62.25497  B  43532.77   1111 112698.0   1205.79
2015-05-07 66.20413 46.27199 66.20413  B  33535.95   1262 117085.6   4387.51
2015-05-08 67.23573 50.89829 67.23573  B  23585.06   1410 118387.4   1301.88
2015-05-11 73.96157 50.90283 73.96157  B  13600.25   1545 127870.9   9483.44
2015-05-12 81.36000 55.44277 81.36000  B   3674.33   1667 139301.5  11430.58
2015-05-28 64.68000 64.68000 82.49000  S 111495.89      0 111495.9 -27805.56

一共发生了34笔交易,其中30笔买入,4笔卖出。最后,资金剩余111495.9元,赚了11495元,收益率11.5%。

3. 模型优化

我们看到在强势格局的大牛市中,通过追涨能让我们获利颇丰。其实我们可以把模型再进一步优化的,在构建卖出信号时,是以最近10日最低价为卖出点来看,应该还有更好的卖出点可以选择。那么我们就要优化一下模型,比如按下面的优化条件。当股价低于前一个买入点价格的时进行卖出,把小于等于最近10日最低价设为止损点。按照这样的优化策略,我们是不是可以有更大的收益呢?

这样的优化思路,会让我们策略对波动更敏感,更容易被震荡出局;当然好外在于,可以更快的触发止盈和止损条件,牛市中收益更大。


# 优化条件,当股价低于前一个买入点价格时进行卖出,小于10日最低价为止损点。
> # 计算卖出的信号点
> sellPoint<-function(ldata,buydata){
+     
+     arr<-c()
+     for(i in 1:nrow(buydata)){
+         
+         if(i>1){ # 跳转第一个点
+             date<-index(buydata[i,])#;print(date)      
+             
+             # 价格 小于 上一次的买入的价格就卖出
+             last<-as.vector(buydata[i-1,]$Value) # 上一次买入的价格
+             lst<-ldata[paste(date,"/",sep="")]$Value      
+             idx<-head(which(lst < last),1)
+             
+             if(length(idx)>0){        
+                 arr<-rbind(arr,index(lst[idx]))
+             }
+         }
+     }
+     selldata<-ldata[as.Date(unique(arr)),]
+     
+     # 过滤多余的卖出点
+     bsdata<-merge(buydata$Value,selldata$Value)
+     names(bsdata)<-c("buy","Value")
+     idx1<-which(!is.na(bsdata$Value))
+     idx2<-idx1[which(c(0,diff(idx1))==1)]
+     bsdata$Value[idx2]<-NA
+     return(bsdata$Value[which(!is.na(bsdata$Value))])
+     
+ }

# 卖出信号
> selldata<-sellPoint(ldata,buydata)
> selldata
              Value
2015-01-21 22.81788
2015-01-28 23.60408
2015-02-25 36.89217
2015-03-17 39.97333
2015-03-19 40.96858
2015-03-26 39.25985
2015-05-14 74.24000

我们重新生成了,卖出的信号点有7个,比上次多出3个点,画出交易信号的图形。

04

合并买卖的交易信号。


> sdata<-signal(buydata$Value,selldata$Value)                                   # 合并交易信号
> sdata
              Value op
2015-01-08 17.43721  B
2015-01-09 17.98709  B
2015-01-12 19.53222  B
2015-01-15 20.21389  B
2015-01-16 22.23619  B
2015-01-19 23.04056  B
2015-01-20 23.89947  B
2015-01-21 22.81788  S
2015-01-26 24.77656  B
2015-01-27 25.16284  B
2015-01-28 23.60408  S
2015-02-05 26.91247  B
2015-02-10 28.68482  B
2015-02-11 31.55239  B
2015-02-12 31.87960  B
2015-02-13 35.06983  B
2015-02-16 38.57817  B
2015-02-17 40.99130  B
2015-02-25 36.89217  S
2015-03-16 41.07764  B
2015-03-17 39.97333  S
2015-03-18 41.94564  B
2015-03-19 40.96858  S
2015-03-24 45.34946  B
2015-03-26 39.25985  S
2015-04-23 46.27199  B
2015-04-24 50.89829  B
2015-04-27 50.90283  B
2015-04-28 55.44277  B
2015-04-29 60.98705  B
2015-05-06 62.25497  B
2015-05-07 66.20413  B
2015-05-08 67.23573  B
2015-05-11 73.96157  B
2015-05-12 81.36000  B
2015-05-13 82.49000  B
2015-05-14 74.24000  S

一共发生了37笔交易,其中30笔买入,7笔卖出。最后,资金剩余137483.8元,赚了37483元,收益率37.5%。

我们把卖号信号和止损信号,合并画到一张图上。


# 止损信号
> stopdata<-stopPoint(ldata,buydata)

# 合并买卖信号,止损信号
> bsdata<-merge(buydata$Value,selldata$Value,stopdata$Value)
> names(bsdata)<-c("buy","sell","stop")
> drawPoint(ldata,bsdata,title,sDate,eDate,'1 month') #画图

05

图中红色点为买点,蓝色点为优化的卖点,紫色点为止损点。从图中可以非常清楚的看到,蓝色卖出点要优于紫色的止损点。这样就达到了,模型优化的目的了。虽然只是一个很小的优化,就可以给我们带来不错的收益。

接下来,通过况客平台,我来够建一个每日更新的交易列表,实现把静态图变成了动态图。

追涨杀跌策略在牛市中会让我们赚取非常大的利润,那么对于2015年下半年行情,牛市已经不复存在,震荡市会一直持续,那么我们可以使用均值回归策略,发现逆市中的投资机会

最后总结,本文从 追涨杀跌 的思路开始,到市场特征检验,再到数学公式,R语言建模,再到历史数据回测。通过R语言,很简单地就实现了一个我们脑子中的投资想法。类似的投资想法其实谁都有,利用IT人的技术优势,可以真正地与实际操作结合起来。

这样就可以更容易地实现,从IT技术到价值的转变。IT人,加油!

转载请注明出处:
http://blog.fens.me/finance-chase-sell/

打赏作者

This entry was posted in R语言实践, 金融

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

35 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
zoe

况客官网的新手指南和使用手册都打不开……请问具体怎么使用况客导出数据呢?

Conan Zhang

感谢你的关注。
1. 新手指南和使用手册,确实还没有写完,所以打不开。
2. 导出数据的功能,还没有完全开放,请再等待一些时间。

john

请问 我已经 加载 ruibe的包 没有key 怎么办, 怎么叫做个人专属key

skyi520

兄弟,你股市里用这套策略赚着钱了吗?

Conan Zhang

1. 这个不是实盘策略,只是一个例子。
2. 实盘策略,会比文章里写的复杂很多。
3. 实盘交易,是赚钱的。

[…] R语言量化投资数据分析应用,PPT下载,主要内容来自我的一篇博文:R语言构建追涨杀跌量化交易模型 […]

[…] 股市中的R语言量化算法模型,PPT下载,主要内容来自我的一篇博文:R语言构建追涨杀跌量化交易模型 […]

fm

能否提供个stock.csv下载地址,谢谢了。

蜀黍鱼

可以给你一个http://www.iquant.club/wp-content/uploads/2016/02/pinganstock.csv

sdupjj

非常好的例子!

Conan Zhang

🙂

sdupjj

借用你的思路,我用Python实现了一下,发现目前这段时间不适合这种操作。。。

Conan Zhang

最近比较适合均值回归,能抄底赚不少。
http://blog.fens.me/finance-mean-reversion/

Neal Zhang

能共享下你的python代码么,学习一下。

Jie

您好,我尝试应用您的模拟交易代码,但是其中一部分代码是缺失的。

Conan Zhang

文章的代码应该都是完整的。

Jie

“ if(row$p==’B’){
+ if(cash0),which(ticks$op==’S’)),] # 赚钱的交易
+ fall<-ticks[intersect(which(ticks$diff<0),which(ticks$op=='S')),] # 赔钱的交易”
我是看的这段代码,不是完整的。

Conan Zhang

已更新

Ragawa

Hi,Conan,文中说:“况客 会提供各种类型的金融数据API,让开发者可以免费下载”。请问,况客的金融数据API在哪里可以找到,谢谢!

Conan Zhang

不好意思,回复晚了。2015年12月底之前会正式开放。

目前测试版本可以先看这个项目
https://github.com/qutke/ruibe
https://uibe.qutke.com/i/api.html

Ragawa

Hi,Conan,你的第三本书什么时候出版,有没有具体的时间?谢谢

Conan Zhang

抱歉回复晚了,还没有具体时间,正在写。。。
如果时间充裕,6月份左右可以出版。

Mike Gao

想请问dateArea 是那里的函数,我这里一直提示 没有”dateArea”这个函数

john

同疑问!!!!没有dateArea这个函数

Conan Zhang

我检查一下,一会更新上去。

蜀黍鱼

然而并没更新,哈哈。这个dateArea函数您以前的文章中有的,http://blog.fens.me/category/%E7%A8%8B%E5%BA%8F%E7%AE%97%E6%B3%95/

Leon

赞!收藏啦~向技术流选手致敬!

Conan Zhang

🙂

蜀黍鱼

步骤和代码注释都很详细,照着教程本地实现了一遍对R语言构建交易系统有了很大的帮助,非常感谢! 小疑问:定义画图函数 drawLine()中的 sDatemax是在复制时的遗漏还是个新的函数呢?

Conan Zhang

HTML显示的问题,已经调整。

yoo

看到了,谢谢

Rogers

Hi, 卖出信号这地方一直出错,提示:Error in if (!is.na(zero.index)) i <- i[-zero.index] :

Conan Zhang

zero.index,是啥? 是不是没数据,空指针的问题

劉東

您好 为什么每次都显示 cannot open URL ‘https://datas.qutke.com/api/opendata?data=tradingDay&key=96afe49792a8dff5ca3efeb7f063104e3f5f680866e0800f0542c2584f672fe6’: HTTP status was ‘400 Bad Request’

Conan Zhang

这个之前是qutke的API,可以显示动态图,不过似乎已经不维护了。我已经把文章中引用部分删除了。

35
0
Would love your thoughts, please comment.x
()
x