一、R代碼實現。
用google在網上搜索到的回測語句都大同小異,下面給出一個示例,改編自:量化策略回測:
# 以S&P500為例,其雅虎代碼為^GSPC;我們要計算5日均線指標的函數就是SMA(),
# 先載入需要的擴展包
> library(quantmod)
> library(PerformanceAnalytics)
# 先獲取S&P500的交易數據,然后根據其收盤價(由函數Cl()抽?。┯嬎闫?日均線值:
> getSymbols('^GSPC') #S&P500 OHLC data
> close <- Cl(GSPC)
> mv5 <- SMA(close, 5)
# 策略是當收盤價大于5日均線,代表可以入市,取1,否則代表清倉,取0。(-1是代表賣空,不適用。)
> sig <- ifelse(close < mv5, 1, 0)
# 使用Lag()將信號序列向“過去”推遲一天,代表將昨天的信號,應用到今天。
> sig <- Lag(sig) #將該序列向“過去”延遲一天
# 計算收益序列
# discrete代表用離散方式計算當天收益率,即(Close-Close(-1))/Close(-1)
# continous代表用連續方式計算當天收益率,即ln(Close/Close(-1))
> roc <- ROC(type='discrete',close)
> ret <- roc * sig
# 畫出策略收益圖
> charts.PerformanceSummary(ret)
# 最上面的板塊是積累收益,相當于對cumprod(1+ret)的繪圖;
# 第二個是日收益,相當于對ret原始收益數據的繪圖;
# 最下面的是下跌圖(又稱“水下圖”),將下跌成分獨立繪出,有助于我們分析虧損狀況和研究彌補措施。
以上需要注意的地方是制定策略的語句
> sig <- ifelse(close < mv5, 1, 0)
# 當收盤價大于5日均線時,sig取1,否則取0。
在網上公布的代碼當中,有的是取-1,這里-1代表賣空,不是清倉,得到的結果是不對的。
二、excel模擬。
當初得到這個結果的時候我很懷疑,用幾條R語句就把量化策略及結果模擬出來了?看上去這么神奇,結果對不對呢?我在excel里對該策略進行模擬:
以上面的策略為基礎,細化得到的交易方案是這樣的:
- 當股票收盤價格高于五日均線,且無持倉,那么第二天滿倉入市。
- 當股票收盤價格高于五日均線,且滿倉,那么第二天繼續持倉。
- 當股票收盤價格低于五日均線,且無持倉,那么第二天繼續空倉。
- 當股票收盤價格低于五日均線,且滿倉,那么第二天全部平倉。
為了簡化計算,假設以前一天的收盤價作為成交價:即以前一天的收盤價滿倉入市,或以前一天的收盤價全部平倉。當然這可以在進一步的研究中細化,但這里我們只是為了演示,就不過于復雜了。
excel表格里得到的結果如下:
CLOSE | SMA | sig | ROC | ROC*sig | 股票 | 現金 | 總收益 | |
---|---|---|---|---|---|---|---|---|
1994-1-3 | 5.6 | 0 | 1 | 1 | ||||
1994-1-4 | 5.55 | 0 | 1 | 1 | ||||
1994-1-5 | 5.65 | 0 | 1 | 1 | ||||
1994-1-6 | 6.1 | 0 | 1 | 1 | ||||
1994-1-7 | 6.25 | 5.83 | 0 | 1 | 1 | |||
1994-1-10 | 6.45 | 6 | 1 | 0.032 | 0.032 | 1.032 | 0 | 1.032 |
1994-1-11 | 6.15 | 6.12 | 1 | -0.047 | -0.047 | 0.984 | 0 | 0.984 |
1994-1-12 | 6.15 | 6.22 | 1 | 0 | 0 | 0.984 | 0 | 0.984 |
1994-1-13 | 6.25 | 6.25 | 0 | 0.016 | 0 | 0 | 0.984 | 0.984 |
1994-1-14 | 6.1 | 6.22 | 0 | -0.024 | 0 | 0 | 0.984 | 0.984 |
1994-1-17 | 6 | 6.13 | 0 | -0.016 | 0 | 0 | 0.984 | 0.984 |
1994-1-18 | 6.2 | 6.14 | 0 | 0.033 | 0 | 0 | 0.984 | 0.984 |
1994-1-19 | 6 | 6.11 | 1 | -0.032 | -0.032 | 0.952 | 0 | 0.952 |
1994-1-20 | 6 | 6.06 | 0 | 0 | 0 | 0 | 0.952 | 0.952 |
由于是取5日平均,所以5日平均值SMA從第5個交易日開始出現。這幾天的資產都是現金,總收益為1。
從第6個交易日開始,通過判斷收盤價與5日均線之間的大小,得到交易信號sig,注意,這里的sig是滯后一日的,代表前一天的收盤價與5日均線之間的相對大小。
1994-1-10 close>SMA,sig=1,以前一天的收盤價6.25滿倉入市,當天收盤價為6.45,日內收益率為0.032,股票價值1.032,現金為0,總資產價值為1.032。
1994-1-11 close>SMA,sig=1,鑒于前一天已入市,繼續持倉,當天收盤價為6.15,日內收益率為-0.047,股票價值0.984,現金為0,總資產價值為0.984。
1994-1-12 close>SMA,sig=1,繼續持倉,當天收盤價為6.15,日內收益率為0,股票價值0.984,現金為0,總資產價值為0.984。
1994-1-13 close<SMA,sig=0,以前一天的收盤價6.15全部清倉,股票價值為0,現金為前一天的股票價值0.984,總資產價值為0.984。
此時,經歷了一次持倉以及清倉之后,投資者手里只擁有現金資產,總資產價值為0.984,虧損0.016。
通過對比excel計算的ret(ROC*sig)與R代碼計算的ret,兩者的結果完全一致。
通過用excel對以上回測策略的模擬,可以發現以上回測策略的隱含前提:
(1) 入市價格和清倉價格均為前一天的收盤價。
(2) 入市則全部滿倉,離市則全部清倉。
三、神秘的charts.PerformanceSummary()函數。
在R代碼中,一條charts.PerformanceSummary()語句,畫出在該策略下,對應股票的總資產收益,日收益和下跌圖。它就像一個神奇的黑箱,給我們以策略是否有效以最直觀的圖形表示。但光有圖形還不夠,這三條曲線分別對應哪些數據,能不能分別導出方便進一步研究呢?
-
總資產收益。
對應的是excle表格當中的總收益,直觀表示了應用該策略,投資者總資產在回測時間內的增減情況。雖然在excel模擬中用股票、現金、總資產三個條目來代表,但其實用一條R代碼就可以實現了:
cumprod(1+ret)
也就是對(1+ret)的連乘。很容易理解:假設到了第n天,那么這一天的總收益Rn應該等于前一天的總收益R(n-1)乘以當天的收益率,不是ret,而應當是(1+ret)。
R代碼得到的結果與excel是一致的。
-
日收益。
就是R代碼當中的ret和excel表格中的ROC*sig。
-
下跌圖(這部分參考了R包計算回撤)。
這里只是將下跌成分獨立繪出,對應的excel數據也是ROC*sig,但這里的下跌圖經過了整理。對應的R函數有:
a)chart.Drawdown:下跌圖,也就是charts.PerformanceSummary()的第三張圖。
data(edhec) chart.Drawdown(edhec[,c(1,2)], main="Drawdown from Peak Equity Attained", legend.loc="bottomleft")
b)findDrawdowns:返回回撤的起始時間,時間間隔,回撤數值,常與sortDrawdowns連用找最大回撤。
data(edhec) findDrawdowns(edhec[,"Funds of Funds", drop=FALSE]) sortDrawdowns(findDrawdowns(edhec[,"Funds of Funds", drop=FALSE]))
c)maxDrawdown:返回收益時間序列的最大回撤。
data(edhec) t(round(maxDrawdown(edhec[,"Funds of Funds"]),4))
d)table.Drawdowns:返回最差回撤的統計量表格。
data(edhec) table.Drawdowns(edhec[,1,drop=FALSE])