A few weeks ago I have mentioned about an interesting volatility prediction. It is based on two periods of historical volatility (standard deviation). The remaining question was – does it really works? I could not give the answer, because I didn’t have VIX futures data at that time. Later on, I was contacted by Brian G. Peterson, who provided necessary data to finish this test. By the way, I just found, that CBOE shares VIX futures data on its website.
Now I want you to show, what are returns of VIX futures for the next 3 days, then historical volatility ratio of 3 days vs 10 days is less than 0.25:
Sys.setenv(TZ="GMT") require('xts') require('quantmod') require('blotter') require('PerformanceAnalytics') tmp<-as.matrix(read.table('tickers/various_day_close/VIXc1.csv',sep=',',header=TRUE)) vix<-as.xts(as.double(tmp[,9]),order.by=as.POSIXct(strptime(tmp[,2],'%d-%b-%Y'),tz='GMT')) vix<-(vix[!is.na(vix)]) colnames(vix)<-c('Close') tmp<-as.matrix(read.table('tickers/various_day_close/ESc1.csv',sep=',',header=TRUE)) es<-as.xts(as.double(tmp[,9]),order.by=as.POSIXct(strptime(tmp[,2],'%d-%b-%Y'))) es<-(es[!is.na(es)]) colnames(es)<-c('Close') #-----------------data end----------------- #-----------------signal------------------- es.delta<-Delt(Cl(es)) delta<-Delt(Cl(vix))#Front contract #Historical volatility during 3 and 10 days short.vol<-as.xts(rollapply(es.delta,3,sd,align='right')) long.vol<-as.xts(rollapply(es.delta,10,sd,align='right')) past.vol<-short.vol/long.vol future.vol<-lag(past.vol,-3) future.delta<-lag(vix,-3)/vix-1 signal<-ifelse(past.vol<0.25,1,0) #here we see, increase in historical volatility summary(as.double(future.vol[index(signal[signal!=0])]))/summary(as.double(past.vol[index(signal[signal!=0])])) #-----------------signal end------------------- #--------------blotter code------------------ symbols<-c('vix') initDate=time(get(symbols)[1]) initEq=50000 rm(list=ls(envir=.blotter),envir=.blotter) ltportfolio='volatility' ltaccount='volatility' initPortf(ltportfolio,symbols, initDate=initDate) initAcct(ltaccount,portfolios=c(ltportfolio), initDate=initDate,initEq=initEq) currency("USD") stock(symbols[1],currency="USD") signal<-signal[index(vix)] signal[is.na(signal)]<-0 counter<-0 #date counter - exit on 3th day for(i in 2:length(signal)) { currentDate= time(signal)[i] equity = initEq #getEndEq(ltaccount, currentDate) position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate) print(position) print(currentDate) if(position==0 &counter==0) { #open a new position if signal is >0 if(signal[i]>0) { print('open position') closePrice<-as.double(get(symbols[1])[currentDate]) print(closePrice) unitSize = as.numeric(trunc((equity/closePrice))) print(unitSize) commssions=-unitSize*closePrice*0.0003 addTxn(ltportfolio, Symbol=symbols[1], TxnDate=currentDate, TxnPrice=closePrice, TxnQty = unitSize , TxnFees=commssions, verbose=T) counter<-1 } } else { #position is open. If signal is 0 - close it. if(position>0 & as.integer(signal[i])==0 &counter>=3) { position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate) closePrice<-as.double(get(symbols[1])[currentDate])#as.double(get(symbols[1])[i+100]) commssions=-position*closePrice*0.0003 addTxn(ltportfolio, Symbol=symbols[1], TxnDate=currentDate, TxnPrice=closePrice, TxnQty = -position , TxnFees=commssions, verbose=T) counter<-0 } else counter<-counter+1 } print('>>>>>>>>>>>>') updatePortf(ltportfolio, Dates = currentDate) updateAcct(ltaccount, Dates = currentDate) updateEndEq(ltaccount, Dates = currentDate) } rez1<-(getPortfolio(ltaccount)) #--------------blotter code end------------------ #----------------results------------------------ png('vix_front.png',width=650) #net profit - commissions, slipage excluded chart.TimeSeries(cumsum(rez1$symbols$vix$txn[,7]),main='VIX front contract') dev.off() #----------------results end------------------------ |
The graph shows, that this strategy is pure random or just follows VIX index. Now let’s see, what are returns of this strategy, if S&P500 futures are used instead of VIX.
signal<-ifelse(past.vol<0.25,1,0) #signal<-signal[index(es)] #------------------------blotter code----------------------- symbols<-c('es') initDate=time(get(symbols)[1]) initEq=15000 rm(list=ls(envir=.blotter),envir=.blotter) ltportfolio='volatility' ltaccount='volatility' initPortf(ltportfolio,symbols, initDate=initDate) initAcct(ltaccount,portfolios=c(ltportfolio), initDate=initDate,initEq=initEq) currency("USD") future(symbols[1],currency="USD",multiplier=50,1/4) signal[is.na(signal)]<-0 counter<-0 for(i in 2:length(signal)) { currentDate= time(signal)[i] equity = initEq #getEndEq(ltaccount, currentDate) position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate) print(position) print(currentDate) if(position==0 &counter==0) { #open a new position if signal is >0 if(signal[i]>0) { print('open position') closePrice<-as.double(get(symbols[1])[currentDate]) print(closePrice) unitSize = 1#as.numeric(trunc((equity/closePrice))) print(unitSize) commssions=-2 addTxn(ltportfolio, Symbol=symbols[1], TxnDate=currentDate, TxnPrice=closePrice, TxnQty = unitSize , TxnFees=commssions, verbose=T) counter<-1 } } else { #position is open. If signal is 0 - close it. if(position>0 & as.integer(signal[i])==0 &counter>=3) { position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate) closePrice<-as.double(get(symbols[1])[currentDate])#as.double(get(symbols[1])[i+100]) commssions=-2 addTxn(ltportfolio, Symbol=symbols[1], TxnDate=currentDate, TxnPrice=closePrice, TxnQty = -position , TxnFees=commssions, verbose=T) counter<-0 } else counter<-counter+1 } updatePortf(ltportfolio, Dates = currentDate) updateAcct(ltaccount, Dates = currentDate) updateEndEq(ltaccount, Dates = currentDate) } rez1<-(getPortfolio(ltaccount)) #-------------------------results--------------------- #net profit png('vix.png',width=650) chart.TimeSeries(cumsum(rez1$symbols$es$txn[,9]),main='ES future contract') dev.off() |
Well, that is exact opposite of expectations – if we expect volatility increase, as it was described in the first post, then the returns of S&P index have to be negative in long run.
From the beginning I suspected, that it has more to do with standard deviation formula and less with forecast.
Now funny part – I generated 2500 random returns and got median 0.9930 and mean 1.6360 for all days. Then I took all days, when buy signal suppose to be generated and guess what mean did I get? Median was 4.3170 and mean 6.3450. Once again, significant difference but on random data.
Source code on github
R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



Zero Inflated Models and Generalized Linear Mixed Models with R.
Zuur, Saveliev, Ieno (2012).