自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

張丹:如何借均值回歸發(fā)現(xiàn)逆市中的投資機會?

原創(chuàng)
大數(shù)據(jù)
在股票市場中有兩種典型的投資策略:趨勢追蹤(Trend Following) 和 均值回歸(Mean Reversion)。 趨勢追蹤策略的特點在大行情的波動段找到有效的交易信號,不僅簡單而且有效,我之前寫的一篇文章 兩條均線打天下 就屬于趨勢追蹤策略。而均值回歸策略則是一種反趨勢策略,一波大幅上漲后容易出現(xiàn)下跌,而一波大幅下跌后容易出現(xiàn)上漲。其特點在振蕩的在震蕩的市場中非常有效,捕捉小的機會,本文就將介紹這種策略。

[[141914]] 

前言

在股票市場中有兩種典型的投資策略:趨勢追蹤(Trend Following) 和 均值回歸(Mean Reversion)。 趨勢追蹤策略的特點在大行情的波動段找到有效的交易信號,不僅簡單而且有效,我之前寫的一篇文章 兩條均線打天下 就屬于趨勢追蹤策略。而均值回歸策略則是一種反趨勢策略,一波大幅上漲后容易出現(xiàn)下跌,而一波大幅下跌后容易出現(xiàn)上漲。其特點在振蕩的在震蕩的市場中非常有效,捕捉小的機會,本文就將介紹這種策略。

目錄

  1. 均值回歸原理
  2. 均值回歸模型和實現(xiàn)
  3. 量化選股
  4. 關于作者

1. 均值回歸原理

在金融學中,均值回歸是價格偏離均衡價格水平一定程度后向均衡價格靠攏的規(guī)律。本質上,均值回歸就是哲學思想中所說的物極必反,可以簡單地概括為“漲多必跌,跌多必漲”的規(guī)律。

均值回歸是指股票價格無論高于或低于均值(均衡價格水平)都會以很高的概率向均值回歸的趨勢。根據(jù)這個理論,股票價格總是圍繞其均值上下波動。一種上漲或者下跌的趨勢不管其延續(xù)的時間多長都不能永遠持續(xù)下去,最終均值回歸的規(guī)律一定會出現(xiàn):漲得太多了,就會向均值移動下跌;跌得太多了,就會向均值移動上升。如果我們認為事物總要回歸常態(tài),并且基于這樣的預期來做任何決策的時候,我們就是在應用均值回歸的理論。

下面以平安銀行(000001)股票日K線圖為例,可以非常直觀的了解均值回歸這種現(xiàn)象, 截取2005年到2015年7月的股票數(shù)據(jù),股價為向前復權的價格。

平安銀行

上圖中有3條曲線,黑色線是平安銀行向前復權后的每日股價,紅色線為20日均線,藍色線為60日均線。關于均線的介紹,請參考文章 兩條均線打天下。圖中還有一條紅色的水平線虛線,是這10年的股價平均值等于7.14元。這10年間,平安銀行的股價經(jīng)歷了幾波上漲和下跌,多次穿越7.14平均值。那么這個現(xiàn)象就是我們要討論的均值回歸。

1.1 均值回歸的3個特性

均值回歸是價值投資理論成立的一個核心理論,具有3個特性:必然性、不對稱性、政府調控。

必然性,股票價格不能總是上漲或下跌,一種趨勢不管其持續(xù)的時間多長都不能永遠持續(xù)下去。在一個趨勢內,股票價格呈持續(xù)上升或下降,我們稱之為均值回避(Mean Aversion)。當出現(xiàn)相反趨勢時就呈均值回歸(Mean Reversion),但回歸的周期有隨機性是我們不能預測。不同的股票市場,回歸的周期會不一樣的,就算是相同的市場,回歸的周期也是不一樣的。

我們換支股票,以蘇寧云商(002024)股票日K線圖為例, 同樣截取2005年到2015年7月的向前復權的股價數(shù)據(jù),如下圖所示。我們看到蘇寧云商在2006年到2007年有一波大漲隨后下跌;從2009到2010年時,第二波大漲;2013年下半年迎來第三波大漲;2014年下半年到2015年第四波大漲。從圖形上可以直觀看到,2015年這波漲的最急,波動率也是最大的;從現(xiàn)象中,我們可以判斷一種趨勢不管其持續(xù)的時間多長都不能永遠持續(xù)下去。

必然性

不對稱性,股價波動的幅度與速度是不一樣的,回歸時的幅度與速度具有隨機性。對稱的均值回歸才是不正常的、偶然的,這一點也也可以從股票中所驗證。

我們合并平安銀行(000001)和蘇寧云商(002024)股票日K線圖為例,所下圖所示。兩支股票在2007年中,都趕上了大的上漲行情,曲線基本吻合。到2008年2支股票都遇到了大跌,但波動率和速度都是不一樣的,隨后在2010年到2012年出現(xiàn)了完成不一樣的走勢,無規(guī)律可尋,體現(xiàn)了均值回歸時的隨機性和不對稱性。

不對稱性

政府行為,股票收益率不會偏離價值均值時間太久,市場的內在力量會促使其向內在價值回歸。市場在沒有政府政策的作用下,股票價格會在市場機制下自然地向均值回歸。但這并不否定政府行為對促進市場有效性的作用,因為市場偏離內在價值后并不等于立即就會向內在價值回歸,很可能會出現(xiàn)持續(xù)地均值回避。政府行為會起到抑制市場調節(jié)市場的作用,是必不可少的因素之一,市場失靈也是政府參與調控的直接的結果。

對于政府政策行為,比如升準、降準、升息、降息,在股市中都會有比如明顯的體現(xiàn)。房地產(chǎn)股、銀行股,都會受到國家宏觀調控的直接的影響。下如所示,在圖中增加萬科A(0000002)的股票,圖中3條線分別是平安銀行,萬科A,蘇寧云商3支股票。我們發(fā)現(xiàn)地產(chǎn)和銀行的股價走勢是比較相近的,而電商的走勢是不太一樣的。

另外,增加2種顏色的輔助線,紅色為升息的時間點和利率變動值,黃色為降息的時間點和利率變動值。當2007年股市超漲的時候,國家宏觀調控通過升息鼓勵存款,抑制高股價;當股票超跌的時候,通過降息推動投資和消費。2015年金融改革,政府一直都在降息拉動股市。從圖中,我們看到萬科A和平安銀行對于升息和降息的調控是比較明顯的,對于蘇寧云商就不是特別的明顯了。

政府行為

通過對市場的回顧,我們基本驗證了均值回歸的理論是和市場的行為是一致的。那么,接下來我們應該如何應用這個理論來找到投資的切入點呢?

1.2 計算原理和公式

從價值投資的角度,我們發(fā)現(xiàn)股價會在平均值上下波動,但如果考慮到資金的時間成本,把錢都壓在股市中,等待幾年的大行情,也是很不劃算的。那么我們就需要對價值均值進行重新定義,以20日均值來代替長期均值,找到短周期的一種投資方法。

計算原理:取日K線,以N日均線做為均值回歸的短期均衡價格水平(均值),計算股價到均值的差值,求出差值的N日的平均標準差,從而判斷差值的對于均值的偏離,當偏離超過2倍標準差時,我們就認為股價超漲或超跌,股價會遵循均值回歸的理論,向均值不停地進行修復。

計算公式:

  1. N日平均值     =  [T日股價 + (T-1)日股價 + ... + (T-(N-1))日股價]/N  
  2. 差值          =  N日平均值 - N日股價  
  3. N日平均標準差 =  [T日差值 + (T-1)日差值 + ... + (T-(N-1))日差值]/N 

如果N為20日,則

  1. 20日平均值     =  [T日股價 + (T-1)日股價 + ... + (T-19)日股價]/20 

計算偏離點

  1. 20日平均值     =  [T日股價 + (T-1)日股價 + ... + (T-19)日股價]/20 

我們以偏離點作為買入信號點,以均線和股價的下一個交點做為賣出信號點。這樣我們就把均值回歸的投資理論,變成了一個數(shù)學模型。

#p#

2.均值回歸模型和實現(xiàn)

接下來,我們利用R語言對股票數(shù)據(jù)的進行操作,來實現(xiàn)一個均值回歸模型的實例,從而驗證我的們投資理論,是否能發(fā)現(xiàn)賺錢的機會。

2.1 數(shù)據(jù)準備

R語言本身提供了豐富的金融函數(shù)工具包,時間序列包zoo和xts,指標計算包TTR,數(shù)據(jù)處理包plyr,可視包ggplot2等,我們會一起使用這些工具包來完成建模、計算和可視化的工作。關于zoo包和xts包的詳細使用可以參考文章,R語言時間序列基礎庫zoo,可擴展的時間序列xts。

我本次用到的數(shù)據(jù)是從 況客 直接導出的,況客 會提供各種類型的金融數(shù)據(jù)API,讓開發(fā)者可以免費下載。當然,你也可以用quantmod包從Yahoo財經(jīng)下載。

本文用到的數(shù)據(jù),包括A股日K線(向前復權)數(shù)據(jù),從2014年7月到2015年日7月,以CSV格式保存到本地文件stock.csv。

數(shù)據(jù)格式如下:

  1. 000001.SZ,2014-07-02,8.14,8.18,8.10,8.17,28604171  
  2. 000002.SZ,2014-07-02,8.09,8.13,8.05,8.12,40633122  
  3. 000004.SZ,2014-07-02,13.9,13.99,13.82,13.95,1081139  
  4. 000005.SZ,2014-07-02,2.27,2.29,2.26,2.28,4157537  
  5. 000006.SZ,2014-07-02,4.57,4.57,4.50,4.55,5137384  
  6. 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語言加載股票數(shù)據(jù),由于數(shù)據(jù)所有股票都是混合在一起的,而進行計算時又需要按每支票股計算,所以在數(shù)據(jù)加載時我就進行了轉換,按股票代碼進行分組,生成R語言的list對象,同時把每支股票的data.frame類型對象轉成XTS時間序列類型對象,方便后續(xù)的數(shù)據(jù)處理。

  1. #加載工具包  
  2. > library(plyr)  
  3. > library(xts)  
  4. > library(TTR)  
  5. > library(ggplot2)  
  6. > library(scales)  
  7.  
  8. # 讀取CSV數(shù)據(jù)文件  
  9. read<-function(file){   
  10. +   df<-read.table(file=file,header=FALSE,sep = ",", na.strings = "NULL") # 讀文件  
  11. +   names(df)<-c("code","date","Open","High","Low","Close","Volume")      # 設置列名  
  12. +   dl<-split(df[-1],df$code)                                             # 按ccode分組  
  13. +     
  14. +   lapply(dl,function(row){                                              # 換成xts類型數(shù)據(jù)  
  15. +     xts(row[-1],order.by = as.Date(row$date))  
  16. +   })  
  17. + }  
  18.  
  19. # 加載數(shù)據(jù)  
  20. > data<-read("stock.csv")  
  21.  
  22. # 查看數(shù)據(jù)類型  
  23. > class(data)  
  24. [1] "list" 
  25.  
  26. # 查看數(shù)據(jù)的索引值  
  27. > head(names(data))  
  28. [1] "000001.SZ" "000002.SZ" "000004.SZ" "000005.SZ" "000006.SZ" "000007.SZ" 
  29.  
  30. # 查看包括的股票數(shù)量  
  31. > length(data)  
  32. [1] 2782  
  33.  
  34. # 查看股票000001.SZ  
  35. > head(data[['000001.SZ']])  
  36.                Open     High      Low    Close   Volume  
  37. 2014-07-02 8.146949 8.180000 8.105636 8.171737 28604171  
  38. 2014-07-03 8.171737 8.254364 8.122162 8.229576 44690486  
  39. 2014-07-04 8.237838 8.270889 8.146949 8.188263 34231126  
  40. 2014-07-07 8.188263 8.204788 8.097374 8.146949 34306164  
  41. 2014-07-08 8.130424 8.204788 8.072586 8.204788 34608702  
  42. 2014-07-09 8.196525 8.196525 7.915596 7.973434 58789114 

把數(shù)據(jù)準備好了,我們就可以來建立模型了。

2.2 均值回歸模型

為了能拉近我們對市場的了解,我們取從2015年1月1日開始的數(shù)據(jù),來創(chuàng)建均值回歸模型。以平安銀行(000001)的為例,畫出平安銀行的2015年以來的日K線和均線。

  1. # 獲得時間范圍  
  2. > dateArea<-function(sDate=Sys.Date()-365,eDate= Sys.Date(),before=0){  #開始日期,結束日期,提單開始時  
  3. +     if(class(sDate)=='character') sDate=as.Date(sDate)  
  4. +     if(class(eDate)=='character') eDate=as.Date(eDate)    
  5. +     return(paste(sDate-before,eDate,sep="/"))  
  6. + }  
  7.    
  8. # 計算移動平均線  
  9. > ma<-function(cdata,mas=c(5,20,60)){  
  10. +     if(nrow(cdata)<=max(mas)) return(NULL)  
  11. +     ldata<-cdata  
  12. +     for(m in mas){  
  13. +         ldata<-merge(ldata,SMA(cdata,m))  
  14. +     }  
  15. +     names(ldata)<-c('Value',paste('ma',mas,sep=''))  
  16. +     return(ldata)  
  17. + }  
  18.  
  19. # 日K線和均線  
  20. > title<-'000001.SZ' 
  21. > SZ000011<-data[[title]]                             # 獲得股票數(shù)據(jù)  
  22. > sDate<-as.Date("2015-01-01")                        # 開始日期  
  23. > eDate<-as.Date("2015-07-10")                        # 結束日期  
  24. > cdata<-SZ000011[dateArea(sDate,eDate,360)]$Close    # 獲得收盤價  
  25. > ldata<-ma(cdata,c(5,20,60))                         # 選擇移動平均指標  
  26.  
  27. # 打印移動平均指標  
  28. > tail(ldata)  
  29.            Value    ma5    ma20     ma60  
  30. 2015-07-03 13.07 13.768 15.2545 15.84355  
  31. 2015-07-06 13.88 13.832 15.1335 15.82700  
  32. 2015-07-07 14.65 13.854 15.0015 15.79850  
  33. 2015-07-08 13.19 13.708 14.8120 15.74267  
  34. 2015-07-09 14.26 13.810 14.6910 15.70867  
  35. 2015-07-10 14.86 14.168 14.6100 15.67883 

我們設置3條移動平均線,分別是5日平均線,20日平均線,60日平均線,當然也可以按照自己的個性要求設置符合自己的周期。畫出日K線和均線圖。

  1. > drawLine<-function(ldata,titie="Stock_MA",sDate=min(index(ldata)),eDate=max(index(ldata)),breaks="1 year",avg=FALSE,out=FALSE){  
  2. +     if(sDate<min(index(ldata))) sDate=min(index(ldata))  
  3. +     if(eDate>max(index(ldata))) eDate=max(index(ldata))    
  4. +     ldata<-na.omit(ldata)  
  5. +       
  6. +     g<-ggplot(aes(x=Index, y=Value),data=fortify(ldata[,1],melt=TRUE))  
  7. +     g<-g+geom_line()  
  8. +     g<-g+geom_line(aes(colour=Series),data=fortify(ldata[,-1],melt=TRUE))  
  9. +   
  10. +     if(avg){  
  11. +         meanVal<<-round(mean(ldata[dateArea(sDate,eDate)]$Value),2) # 均值  
  12. +         g<-g+geom_hline(aes(yintercept=meanVal),color="red",alpha=0.8,size=1,linetype="dashed")  
  13. +         g<-g+geom_text(aes(x=sDate, y=meanVal,label=meanVal),color="red",vjust=-0.4)  
  14. +     }  
  15. +     g<-g+scale_x_date(labels=date_format("%Y-%m"),breaks=date_breaks(breaks),limits = c(sDate,eDate))  
  16. +     g<-g+ylim(min(ldata$Value), max(ldata$Value))  
  17. +     g<-g+xlab("") + ylab("Price")+ggtitle(title)  
  18. +     g  
  19. + }  
  20.  
  21. > drawLine(ldata,title,sDate,eDate,'1 month',TRUE)    # 畫圖 

如圖所示,60日的移動平均線是最平滑的,5日的移動平均線是波動最大的。5日平均線和股價的交叉,明顯多于60日平均線和股價的交叉。那么可以說在相同的時間周期內,短周期的移動平均線,比長周期的移動平均線更具有均值回歸的特點。

我們分別計算不同周期的,股價與移動平均線的差值的平均標準差。

  1. > getMaSd<-function(ldata,mas=20,sDate,eDate){}) # ...代碼省略  
  2.  
  3. # 5日平均線的差值、平均標準差  
  4. > ldata5<-getMaSd(ldata,5,sDate,eDate)  
  5. > head(ldata5)  
  6.               Value      ma5        dif        sd  rate  
  7. 2015-01-05 13.23673 12.78724 -0.4494869 0.1613198 -2.79  
  8. 2015-01-06 13.03842 12.89961 -0.1388121 0.1909328 -0.73  
  9. 2015-01-07 12.79055 12.99215  0.2016081 0.3169068  0.64  
  10. 2015-01-08 12.36089 12.90292  0.5420283 0.4472248  1.21  
  11. 2015-01-09 12.46004 12.77733  0.3172848 0.3910700  0.81  
  12. 2015-01-12 12.20390 12.57076  0.3668606 0.2533165  1.45  
  13.  
  14.  
  15. # 20日平均線的差值、平均標準差  
  16. > ldata20<-getMaSd(ldata,20,sDate,eDate)  
  17. > head(ldata20)  
  18.               Value     ma20         dif        sd  rate  
  19. 2015-01-05 13.23673 12.18613 -1.05059293 0.6556366 -1.60  
  20. 2015-01-06 13.03842 12.23778 -0.80064848 0.6021093 -1.33  
  21. 2015-01-07 12.79055 12.24810 -0.54244141 0.4754686 -1.14  
  22. 2015-01-08 12.36089 12.29975 -0.06114343 0.5130410 -0.12  
  23. 2015-01-09 12.46004 12.33651 -0.12352626 0.5150453 -0.24  
  24. 2015-01-12 12.20390 12.37163  0.16773131 0.5531618  0.30  
  25.  
  26.  
  27. # 60日平均線的差值、平均標準差  
  28. > ldata60<-getMaSd(ldata,60,sDate,eDate)  
  29. > head(ldata60)  
  30.               Value     ma60       dif       sd  rate  
  31. 2015-01-05 13.23673 10.06939 -3.167340 1.264792 -2.50  
  32. 2015-01-06 13.03842 10.14678 -2.891644 1.271689 -2.27  
  33. 2015-01-07 12.79055 10.22087 -2.569677 1.269302 -2.02  
  34. 2015-01-08 12.36089 10.28752 -2.073368 1.258813 -1.65  
  35. 2015-01-09 12.46004 10.35527 -2.104766 1.247967 -1.69  
  36. 2015-01-12 12.20390 10.41821 -1.785691 1.233989 -1.45 

5日的平均線的差值和平均標準差是最小的,而60日的平均線的差值和平均標準差是最大的。如果我們以5日移動平均線做為均值時,會頻繁進行交易,但每次收益都很小,可能都不夠手續(xù)費的成本;另一方面,如果我們以60日移動平均線做為均值時,交易次數(shù)會較少,但可能會出現(xiàn)股票成形趨勢性上漲或下跌,長時間不能回歸的情況,可能會造成現(xiàn)金頭寸的緊張。綜合上面的2種情況,我們可以選擇20日均線作為均值的標的。

根據(jù)模型的計算公式,當差值超過2倍的平均標準差時,我們認為股價出現(xiàn)了偏離,以偏離點做為模型的買入信號,當均線和股價再次相交時做為賣出信號。

上一步,我們已經(jīng)計算出了偏離值,并保存在rate列中。下面我們要找到大于2倍標準化差的點,并畫圖。

  1. # 差值和平均標準差,大于2倍平均標準差的點  
  2. > buyPoint<-function(ldata,x=2,dir=2){})     # ...代碼省略  
  3.  
  4. # 畫交易信號點  
  5. > drawPoint<-function(ldata,pdata,titie,sDate,eDate,breaks="1 year"){  
  6. +     ldata<-na.omit(ldata)  
  7. +     g<-ggplot(aes(x=Index, y=Value),data=fortify(ldata[,1],melt=TRUE))  
  8. +     g<-g+geom_line()  
  9. +     g<-g+geom_line(aes(colour=Series),data=fortify(ldata[,-1],melt=TRUE))  
  10. +       
  11. +     if(is.data.frame(pdata)){  
  12. +         g<-g+geom_point(aes(x=Index,y=Value,colour=op),data=pdata,size=4)  
  13. +     }else{  
  14. +         g<-g+geom_point(aes(x=Index,y=Value,colour=Series),data=na.omit(fortify(pdata,melt=TRUE)),size=4)    
  15. +     }  
  16. +     g<-g+scale_x_date(labels=date_format("%Y-%m"),breaks=date_breaks(breaks),limits = c(sDate,eDate))  
  17. +     g<-g+xlab("") + ylab("Price")+ggtitle(title)  
  18. +     g  
  19. + }  
  20.    
  21. > buydata<-buyPoint(ldata20,2,2)                                       # 多空信號點  
  22. > drawPoint(ldata20[,c(1,2)],buydata$Value,title,sDate,eDate,'1 month')  # 畫圖 

圖中藍色的點就是買入的信號點,由于股票我們只能進行單向交易,即低買高賣,并不能直接做空,所以我們要過濾股價高于移動平均線的點,只留下股價低于移動平均線的點,就是我們的買入信號點。

#p#

畫出買入信號點,只保留股價低于移動平均線的點。

  1. > buydata<-buyPoint(ldata20,2,1)        # 做多信號點  
  2. > drawPoint(ldata20[,c(1,2)],buydata$Value,title,sDate,eDate,'1 month') # 畫圖 

計算賣出的信號點,當買入后,下一個股價與移動平均線的交點就是賣出的信號點,我們看一下是否可以賺到錢?!

  1. # 計算賣出的信號點  
  2. > sellPoint<-function(ldata,buydata){})     # ...代碼省略  
  3. > selldata<-sellPoint(ldata20,buydata)  
  4.  
  5. # 買出信號  
  6. > selldata  
  7.            Value  ma20   dif        sd  rate  
  8. 2015-07-10 14.86 14.61 -0.25 0.7384824 -0.34 

我們把買入信號和賣出信號,合并到一張圖上顯示,如圖所示。

  1. > bsdata<-merge(buydata$Value,selldata$Value)  
  2. > names(bsdata)<-c("buy","sell")  
  3. > drawPoint(ldata20[,c(1,2)],bsdata,title,sDate,eDate,'1 month') #畫圖 

從圖上看,我們在綠色點位置進行買入,而在藍色點位置進行賣出,確實是賺錢的。那么究竟賺了多少錢呢?我們還需要精確的計算出來。

  1. # 合并交易信號  
  2. > signal<-function(buy, sell){})    # ...代碼省略  
  3.  
  4. # 交易信號數(shù)據(jù)  
  5. > sdata<-signal(buydata,selldata)                                     
  6. > sdata  
  7.            Value    ma20     dif        sd  rate op  
  8. 2015-06-19 14.63 16.0965  1.4665 0.6620157  2.22  B  
  9. 2015-06-26 13.77 15.7720  2.0020 0.8271793  2.42  B  
  10. 2015-06-29 13.56 15.6840  2.1240 0.9271735  2.29  B  
  11. 2015-07-03 13.07 15.2545  2.1845 1.0434926  2.09  B  
  12. 2015-07-10 14.86 14.6100 -0.2500 0.7384824 -0.34  S 

利用交易信號數(shù)據(jù),進行模擬交易。我們設定交易參數(shù)和規(guī)則:

  • 以10萬元人民幣為本金
  • 買入信號出現(xiàn)時,以收盤價買入,每次買入價值1萬元的股票。如果連續(xù)出現(xiàn)買入信號,則一直買入。若現(xiàn)金不足1萬元時,則跳過買入信號。
  • 賣出信號出現(xiàn)時,以收盤價賣出,一次性平倉信號對應的股票。
  • 手續(xù)費為0元
  1. # 模擬交易  
  2. > trade<-function(sdata,capital=100000,fixMoney=10000){})    # ...代碼省略  
  3.  
  4. # 交易結果  
  5. > result<-trade(sdata,100000,10000)   

來看一下,每筆交易的明細。

  1. > result$ticks  
  2.            Value    ma20     dif        sd  rate op      cash amount     asset     diff  
  3. 2015-06-19 14.63 16.0965  1.4665 0.6620157  2.22  B  90007.71    683 100000.00     0.00  
  4. 2015-06-26 13.77 15.7720  2.0020 0.8271793  2.42  B  80010.69   1409  99412.62  -587.38  
  5. 2015-06-29 13.56 15.6840  2.1240 0.9271735  2.29  B  70016.97   2146  99116.73  -295.89  
  6. 2015-07-03 13.07 15.2545  2.1845 1.0434926  2.09  B  60018.42   2911  98065.19 -1051.54  
  7. 2015-07-10 14.86 14.6100 -0.2500 0.7384824 -0.34  S 103275.88      0 103275.88  5210.69 

一共發(fā)生了5筆交易,其中4筆買入,1筆賣出。最后,資金剩余103275.88元,賺了3275.88元,收益率3.275%。

在賣出時,賺錢的交易有1筆。

  1. > result$rise  
  2.            Value  ma20   dif        sd  rate op     cash amount    asset    diff  
  3. 2015-07-10 14.86 14.61 -0.25 0.7384824 -0.34  S 103275.9      0 103275.9 5210.69 

在賣出時,賠錢的交易,沒有發(fā)生。

  1. > result$fall  
  2.  [1] Value  ma20   dif    sd     rate   op     cash   amount asset  diff    
  3. <0 行> (或0-長度的row.names) 

接下來,我們再對比一下,資產(chǎn)凈值和股價。

  1. # 資產(chǎn)凈值曲線  
  2. > drawAsset<-function(ldata,adata,sDate=FALSE,capital=100000){  
  3. +     if(!sDate) sDate<-index(ldata)[1]  
  4. +     adata<-rbind(adata,as.xts(capital,as.Date(sDate)))  
  5. +       
  6. +     g<-ggplot(aes(x=Index, y=Value),data=fortify(ldata[,1],melt=TRUE))  
  7. +     g<-g+geom_line()  
  8. +     g<-g+geom_line(aes(x=as.Date(Index), y=Value,colour=Series),data=fortify(adata,melt=TRUE))  
  9. +     g<-g+facet_grid(Series ~ .,scales = "free_y")  
  10. +     g<-g+scale_y_continuous(labels=dollar_format(prefix = "¥"))  
  11. +     g<-g+scale_x_date(labels=date_format("%Y-%m"),breaks=date_breaks("2 months"),limits = c(sDate,eDate))  
  12. +     g<-g+xlab("") + ylab("Price")+ggtitle(title)  
  13. +     g  
  14. + }  
  15.  
  16. > drawAsset(ldata20,as.xts(result$ticks['asset']))  # 資產(chǎn)凈值曲線 

剛才我們是對一支股票進行了測試,發(fā)現(xiàn)是有機會的,那么我再換另外一支股票,看一下是否用同樣的效果呢?我們把剛才數(shù)據(jù)操作的過程,封裝到統(tǒng)一的quick函數(shù),就可以快速驗證均值回歸在其他股票的表現(xiàn)情況了。

  1. > quick<-function(title,sDate,eDate){}  # ...代碼省略 

我們用樂視網(wǎng)(300104)試一下,看看有沒有賺錢的機會??!

  1.  
  2. > title<-"300104.SZ" 
  3. > sDate<-as.Date("2015-01-01") #開始日期  
  4. > eDate<-as.Date("2015-07-10") #結束日期  
  5.  
  6. > quick(title,sDate,eDate)  
  7. $ticks  
  8.            Value    ma20     dif       sd  rate op      cash amount     asset     diff  
  9. 2015-06-19 55.04 69.9095 14.8695 5.347756  2.78  B  90037.76    181 100000.00     0.00  
  10. 2015-06-23 54.30 68.8075 14.5075 5.477894  2.65  B  80046.56    365  99866.06  -133.94  
  11. 2015-06-24 56.21 67.8735 11.6635 5.404922  2.16  B  70097.39    542 100563.21   697.15  
  12. 2015-06-25 51.80 66.8775 15.0775 5.770806  2.61  B  60099.99    735  98172.99 -2390.22  
  13. 2015-06-26 46.79 65.9830 19.1930 6.580622  2.92  B  50133.72    948  94490.64 -3682.35  
  14. 2015-06-29 47.05 64.9445 17.8945 7.096230  2.52  B  40159.12   1160  94737.12   246.48  
  15. 2015-07-07 47.86 58.8150 10.9550 5.401247  2.03  B  30204.24   1368  95676.72   939.60  
  16. 2015-07-10 57.92 57.3520 -0.5680 5.625309 -0.10  S 109438.80      0 109438.80 13762.08  
  17.  
  18. $rise  
  19.            Value   ma20    dif       sd rate op     cash amount    asset     diff  
  20. 2015-07-10 57.92 57.352 -0.568 5.625309 -0.1  S 109438.8      0 109438.8 13762.08  
  21.  
  22. $fall  
  23.  [1] Value  ma20   dif    sd     rate   op     cash   amount asset  diff    
  24. <0 行> (或0-長度的row.names) 

從數(shù)據(jù)結果看,我們又賺到了。一共發(fā)生了8筆交易,其中7筆買入,1筆賣出。最后,資金剩余109438.80元,賺了9438.80元,收益率9.43%。

畫出交易信號圖

  1. > title<-"300104.SZ" 
  2. > sDate<-as.Date("2015-01-01") #開始日期  
  3. > eDate<-as.Date("2015-07-10") #結束日期  
  4.  
  5. > stock<-data[[title]]  
  6. > cdata<-stock[dateArea(sDate,eDate,360)]$Close 
  7. > ldata<-ma(cdata,c(20))  
  8. > ldata<-getMaSd(ldata,20,sDate,eDate)  
  9. > buydata<-buyPoint(ldata,2,1)    
  10. > selldata<-sellPoint(ldata,buydata)  
  11. > bsdata<-merge(buydata$Value,selldata$Value)  
  12. > drawPoint(ldata[,c(1,2)],bsdata,title,sDate,eDate,'1 month') #畫圖 

在恐慌的6月份,當別人都被套牢30%以上的情況下,我們還有9%正收益,那么應該是多么舒心的一件事情?。?!

#p#

3. 量化選股

上文中,我們用2支股票進行了測試,發(fā)現(xiàn)均值回歸模型是適合于股票交易的。如果我們利用模型對全市場的股票進行掃描,應用會產(chǎn)生更多的交易信號,找到更多的投資機會,這樣我們就能如何能獲得更大的收益。

那么,接下來我們就根據(jù)均值回歸的理論進行量化選股。

根據(jù)我們之前的經(jīng)驗,當股價與平均標準差的偏離越大,有可能帶來的收益就越大。那么通過量化的手段,在整個的市場2700多支股票中,把每天偏離最大股票的找出來進行交易,就可以有效地分配我們的資金,進行更有效的投資。我們要試一下,市場是否是和我們的思路是一致的。

對全市場股票進行掃描,首先計算差值、平均值和平均標準差。

  1. > sDate<-as.Date("2015-01-01")                # 開始日期  
  2. > eDate<-as.Date("2015-07-10")                # 結束日期  
  3.  
  4. # 計算差值、平均值和平均標準差  
  5. > data0<-lapply(data,function(stock){})       # 代碼省略  
  6.  
  7. # 去掉空數(shù)據(jù)  
  8. > data0<-data0[!sapply(data0, is.null)]        
  9.  
  10. # 全市場股票  
  11. > length(data)  
  12. [1] 2782  
  13.  
  14. # 有效的股票  
  15. > length(data0)  
  16. [1] 2697  
  17.  
  18. # 查看第1支股票  
  19. > head(data0[[1]])  
  20.               Value     ma20         dif        sd  rate  
  21. 2015-01-05 13.23673 12.18613 -1.05059293 0.6556366 -1.60  
  22. 2015-01-06 13.03842 12.23778 -0.80064848 0.6021093 -1.33  
  23. 2015-01-07 12.79055 12.24810 -0.54244141 0.4754686 -1.14  
  24. 2015-01-08 12.36089 12.29975 -0.06114343 0.5130410 -0.12  
  25. 2015-01-09 12.46004 12.33651 -0.12352626 0.5150453 -0.24  
  26. 2015-01-12 12.20390 12.37163  0.16773131 0.5531618  0.30 

第一次掃描后,有2697支股票是符合條件的,有85支股票由于數(shù)據(jù)樣本不足被排除。

接下來,繼續(xù)對2697支股票進行篩選,找到符合要求的買入信號點。

  1.  # 計算買入信號  
  2. > buys<-lapply(data0,function(stock){})  # ...代碼省略   
  3.  
  4. # 去掉空數(shù)據(jù)  
  5. > buys<-buys[!sapply(buys, is.null)]   
  6.  
  7. # 查看有買入信號的股票  
  8. > length(buys)  
  9. [1] 1819  
  10.  
  11. # 查看買入信號  
  12. > head(buys)  
  13. $`000001.SZ`  
  14.            Value    ma20    dif        sd rate  
  15. 2015-06-19 14.63 16.0965 1.4665 0.6620157 2.22  
  16. 2015-06-26 13.77 15.7720 2.0020 0.8271793 2.42  
  17. 2015-06-29 13.56 15.6840 2.1240 0.9271735 2.29  
  18. 2015-07-03 13.07 15.2545 2.1845 1.0434926 2.09  
  19.  
  20. $`000002.SZ`  
  21.            Value   ma20   dif        sd rate  
  22. 2015-03-05 11.90 12.568 0.668 0.2644101 2.53  
  23. 2015-03-06 11.94 12.509 0.569 0.2674732 2.13  
  24.  
  25. $`000004.SZ`  
  26.            Value    ma20     dif        sd rate  
  27. 2015-01-05 15.69 17.7210  2.0310 0.7395717 2.75  
  28. 2015-07-06 26.03 39.1540 13.1240 6.3898795 2.05  
  29. 2015-07-07 23.43 38.2025 14.7725 6.9421723 2.13  
  30. 2015-07-08 22.22 37.2635 15.0435 7.4287088 2.03  
  31.  
  32. $`000005.SZ`  
  33.            Value    ma20    dif       sd rate  
  34. 2015-07-06  6.02 10.9600 4.9400 2.381665 2.07  
  35. 2015-07-07  5.42 10.5655 5.1455 2.333008 2.21  
  36.  
  37. $`000006.SZ`  
  38.               Value     ma20       dif      sd rate  
  39. 2015-01-19 5.829283 6.519462 0.6901792 0.26929 2.56  
  40.  
  41. $`000007.SZ`  
  42.            Value    ma20    dif        sd rate  
  43. 2015-02-06 12.47 14.4200 1.9500 0.6182860 3.15  
  44. 2015-02-09 12.52 14.3270 1.8070 0.7440473 2.43  
  45. 2015-02-10 12.10 14.1845 2.0845 0.8484250 2.46 

通過計算發(fā)現(xiàn),有1819支股票,在這半年中產(chǎn)生過買入信號。每支股票產(chǎn)生的買入信號的時間和頻率都是不同,這樣我們就可以把錢分散投資到不同的股票上,同時分散風險。如果交易信號同一天出現(xiàn)在多支的股票上,而我們資金有限,又想讓收益最大化,那么我們可以選擇偏離值最大的股票進行交易。

接下來,我們用程序找到每日偏離最大的股票。

  1. # 合并數(shù)據(jù),從list轉型到data.frame  
  2. buydf<-ldply(buys,function(e){})    # ...代碼省略  
  3.  
  4. # 選出同一日rate最大的股票,做為買入信號  
  5. buydatas<-ddply(buydf, .(date), function(row){}) # ...代碼省略  
  6.  
  7. # 查看買入信號  
  8. > nrow(buydatas)  
  9. [1] 81  
  10.  
  11. # 查看買入信號細節(jié)  
  12. > head(buydatas)  
  13.          .id       date      Value       ma20        dif         sd rate  
  14. 1  002551.SZ 2015-01-05  16.573846  19.565446  2.9916000 0.74591596 4.01  
  15. 2  002450.SZ 2015-01-06  18.548809  19.766636  1.2178275 0.34008453 3.58  
  16. 3  300143.SZ 2015-01-07  11.480000  12.603000  1.1230000 0.32028018 3.51  
  17. 4  300335.SZ 2015-01-08  12.113677  13.139601  1.0259238 0.21760484 4.71  
  18. 5  300335.SZ 2015-01-09  12.243288  13.043888  0.8005994 0.22940845 3.49  
  19. 6  300335.SZ 2015-01-12  11.994036  12.941694  0.9476584 0.23168313 4.09 

最后,我們選出81個買入信號點,基本上每個交易日都是買入信號。有了買入信號,繼續(xù)找到賣出信號。

  1. # 賣出信號  
  2. > selldatas<-data.frame()     # ...代碼省略  
  3.  
  4. # 賣出信號去重  
  5. > selldatas<-unique(selldatas)    
  6. > nrow(selldatas)  
  7. [1] 33  
  8.  
  9. # 查看買出信號  
  10. > head(selldatas)  
  11.                 Value      ma20         dif        sd  rate       .id       date op  
  12. 2015-01-12  19.232308 18.848908 -0.38340000 0.9051374 -0.42 002551.SZ 2015-01-12  S  
  13. 2015-01-08  19.814257 19.729006 -0.08525126 0.3782955 -0.23 002450.SZ 2015-01-08  S  
  14. 2015-01-28  11.210000 11.019500 -0.19050000 0.7781848 -0.24 300143.SZ 2015-01-28  S  
  15. 2015-01-21  13.190448 12.899321 -0.29112706 0.3871871 -0.75 300335.SZ 2015-01-21  S  
  16. 2015-01-213  7.140000  6.989500 -0.15050000 0.2007652 -0.75 002505.SZ 2015-01-21  S  
  17. 2015-01-22   5.561561  5.490668 -0.07089242 0.2127939 -0.33 600077.SH 2015-01-22  S 

通過計算,一共有33個買出信號點。最后,合并買入信號和賣出信號,并計算收益。

  1.  
  2. > buydatas$op<-'B'                              # 買入標志  
  3. > selldatas$op<-'S'                             # 賣出標志  
  4. > sdatas<-rbind(buydatas,selldatas)             # 合并數(shù)據(jù)  
  5. > row.names(sdatas)<-1:nrow(sdatas)             # 重設行號  
  6. > sdatas<-sdatas[order(sdatas$.id),]            # 按股票代碼排序  
  7.  
  8. # 查看合并的信號  
  9. > head(sdatas)  
  10.           .id       date Value     ma20       dif         sd  rate op  
  11. 36  000002.SZ 2015-03-05 11.90 12.56800  0.668000 0.26441011  2.53  B  
  12. 100 000002.SZ 2015-03-16 12.49 12.38050 -0.109500 0.23702768 -0.46  S  
  13. 58  000553.SZ 2015-05-06 14.35 15.50882  1.158824 0.38429912  3.02  B  
  14. 110 000553.SZ 2015-05-21 16.57 15.18903 -1.380972 0.55647152 -2.48  S  
  15. 26  000725.SZ 2015-02-09  2.80  3.11400  0.314000 0.07934585  3.96  B  
  16. 94  000725.SZ 2015-02-16  3.09  3.06500 -0.025000 0.08182388 -0.31  S 

最后,按照股票進行分組,分別計算個股的收益。

  1. # 計算個股的收益  
  2. > slist<-split(sdatas[-1],sdatas$.id)      # 按股票代碼分組  
  3. > results<-lapply(slist,trade)  
  4.  
  5. # 查看信號的股票  
  6. > names(results)  
  7.  [1] "000002.SZ" "000553.SZ" "000725.SZ" "000786.SZ" "000826.SZ" "002240.SZ" "002450.SZ" 
  8.  [8] "002496.SZ" "002505.SZ" "002544.SZ" "002551.SZ" "002646.SZ" "002652.SZ" "300143.SZ" 
  9. [15] "300335.SZ" "300359.SZ" "300380.SZ" "300397.SZ" "300439.SZ" "300440.SZ" "300444.SZ" 
  10. [22] "600030.SH" "600038.SH" "600077.SH" "600168.SH" "600199.SH" "600213.SH" "600375.SH" 
  11. [29] "600490.SH" "600536.SH" "600656.SH" "600733.SH" "600890.SH" "601179.SH" "601186.SH" 
  12. [36] "601628.SH" "601633.SH" "601939.SH" "603019.SH" 

我們查看萬科A(000002)的股票。

  1. > results[['000002.SZ']]$ticks  
  2.           date Value    ma20     dif        sd  rate op     cash amount    asset  diff  
  3. 36  2015-03-05 11.90 12.5680  0.6680 0.2644101  2.53  B  90004.0    840 100000.0   0.0  
  4. 100 2015-03-16 12.49 12.3805 -0.1095 0.2370277 -0.46  S 100495.6      0 100495.6 495.6 

通過優(yōu)化的規(guī)則設計,一共有2筆交易,賺了495元。如要我們沒有進行算法優(yōu)化,一直交易萬科A,那么會發(fā)生3筆交易,我們可以賺955.95元。

  1. > quick('000002.SZ',sDate,eDate)$ticks  
  2.            Value    ma20     dif        sd  rate op      cash amount    asset   diff  
  3. 2015-03-05 11.90 12.5680  0.6680 0.2644101  2.53  B  90004.00    840 100000.0   0.00  
  4. 2015-03-06 11.94 12.5090  0.5690 0.2674732  2.13  B  80010.22   1677 100033.6  33.60  
  5. 2015-03-16 12.49 12.3805 -0.1095 0.2370277 -0.46  S 100955.95      0 100955.9 922.35 

本文到此就要結束了!但其實還有很多的事情要做,比如對模型參數(shù)的優(yōu)化,用10日均線代替20日均線,用3倍標準差偏移代替2倍標準差偏移,對樣本進行正態(tài)分布的檢驗,結合其他趨勢類模型共同產(chǎn)生信號等,這些就不是一篇文章可以解決的事情了。大家可以況客金融平臺的網(wǎng)站上,發(fā)現(xiàn)更多不一樣的策略。

本文從均值回歸的理論的介紹開始,到市場特征檢驗,再到數(shù)學公式,R語言建模,歷史數(shù)據(jù)回測,最后找到投資機會,是一套完整的從理論到實踐的學習方法。雖然困難重重,但做為有理想的極客,我們是有能力來克服這些困難的。

4. 關于作者

張丹,況客科技(北京)有限公司,創(chuàng)始人/CTO。

《R的極客理想》系列圖書作者,個人博客: http://blog.fens.me。

從程序員開始到架構師,再到金融量化創(chuàng)業(yè)者,倡導跨學科思維,多語言編程!本文同時用到了計算機、金融、數(shù)學、統(tǒng)計等多學科知識的結合,我認為這是技術復合人才未來的發(fā)展方向。如果說過去10年是房地產(chǎn)的黃金10年,那么未來的10年將是金融的黃金10年。當我們IT人掌握了足夠的金融知識,一定會有能力去金融市場搶錢的。

抓住機會??!程序員,加油!

責任編輯:李英杰 來源: 51cto.com
相關推薦

2015-07-28 16:54:09

2016-11-24 10:01:38

服務器

2017-10-25 11:17:36

大數(shù)據(jù)R語言WOTD全球軟件開發(fā)技

2022-06-03 23:35:31

區(qū)塊鏈元宇宙NFT

2009-02-01 14:42:29

EMC虛擬化重復數(shù)據(jù)刪除

2009-12-03 10:55:26

IE火狐Chrome

2021-02-26 01:01:05

自動化AI人工智能

2022-09-03 13:35:39

機器視覺AI人工智能

2021-12-13 09:56:27

虛擬資產(chǎn)元宇宙加密貨幣

2013-07-19 09:50:56

OpenStackVMware

2018-05-28 16:12:22

區(qū)塊鏈公鏈投資

2010-08-25 14:52:09

唐駿

2013-01-15 10:26:04

大數(shù)據(jù)云計算

2015-12-15 14:33:27

安全投資信息安全策略CISO

2016-11-28 12:00:17

AWSTACK海云捷迅OpenStack

2020-03-12 08:15:01

新基建物聯(lián)網(wǎng)IOT

2013-04-27 14:50:00

大數(shù)據(jù)全球技術峰會

2022-11-14 15:06:16

2014-12-11 14:37:49

2016-06-12 15:46:47

風險投資人CTO
點贊
收藏

51CTO技術棧公眾號