Archive for December, 2010

High readings of VIX index during 2 days

During last two sessions (December 23th and 27th), VIX index posted returns (close to close) above 6 %. My question is – what return from S&P500 index can we expect next day after such event?

As you can see from the graph below, expected return from S&P500 index is positive. During 1995-2010 were 53 such events and mean return was 1.02 % and median 0.6% and win rate 65%.
What can be the explanation for such consistency in returns? It is known, that volatility is mean reverting process and the value of VIX index tends to return to its mean. So, on third day volatility dries up and S&P500 index on average shows positive return that day.

Photobucket

Worth to note, that despite VIX index spike, S&P 500 index was very very still during the last days.

?View Code RSPLUS
 
require('xts')
require('quantmod')
require('blotter')
require('PerformanceAnalytics')
require('FinancialInstrument')
Sys.setenv(TZ="GMT")
 
#data part
getSymbols(c('SPY','^VIX'),from='1995-01-01',index.class=c("POSIXt","POSIXct"))
dividends<-getDividends('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct"))
 
temp<-cbind(dividends,SPY)
temp[,1][is.na(temp[,1])]<-0
 
SPY<-cbind(temp[,2],temp[,3],temp[,4],temp[,1]+temp[,5])
colnames(SPY)<-c('Open','High','Low','Close')
spy.delta<-Delt(Cl(SPY))
 
vix.delta<-Delt(Cl(VIX))
 
signal<-ifelse(vix.delta>0.06& lag(vix.delta>0.06,1),1,0)
png('2highdays.png',width=650)
chart.CumReturns(lag(signal,1)*spy.delta,main='when VIX >6% during two days')
dev.off()
 
#blotter code
symbols<-c('SPY')
SPY<-Cl(SPY)
initDate=time(get(symbols)[1])
initEq=50000
rm(list=ls(envir=.blotter),envir=.blotter)
ltportfolio='2high'
ltaccount='2high'
initPortf(ltportfolio,symbols, initDate=initDate)
initAcct(ltaccount,portfolios=c(ltportfolio), initDate=initDate,initEq=initEq)
currency("USD")
stock(symbols[1],currency="USD",multiplier=1)
 
signal[is.na(signal)]<-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)
	{		
		#open a new position if signal is >0
		if(signal[i]>0 )
		{
			print('open position')
			closePrice<-as.double(Cl(SPY[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)
 
		}
 
	}
	else
	{
		#position is open. If signal is 0 - close it.
		if(position>0 &signal[i] ==0)
		{
			position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate)
			closePrice<-as.double((Cl(SPY[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)
 
		}
 
	}	
	updatePortf(ltportfolio, Dates = currentDate)
	updateAcct(ltaccount, Dates = currentDate)
	updateEndEq(ltaccount, Dates = currentDate)
}
rez1<-(getPortfolio(ltaccount))
png('2highdays_2.png',width=650)
chart.CumReturns(rez1$symbols$SPY$txn[,7]/initEq)
dev.off()

Comments (6)

Interesting volatility measurement

Long time ago I stumbled across interesting volatility measurement at quantifiableedges.blogspot.com.

The idea is following: take 3-day historical volatility of S&P 500 index and divide that by 10-day historical volatility. Then mark all points which are less that 0.25 and measure the volatility of 3 following days. On average, the volatility of following 3 days will be 5 times higher.

?View Code RSPLUS
require('xts')
require('quantmod')
Sys.setenv(TZ="GMT")
 
getSymbols(c('SPY','^VIX'),from='1995-01-01')
 
spy.delta<-Delt(Cl(SPY))
 
short.vol<-as.xts(rollapply(spy.delta,3,sd,align='right'))
long.vol<-as.xts(rollapply(spy.delta,10,sd,align='right'))
 
future.vol<-(head(lag(short.vol,-3),-3))
 
past.vol<-short.vol/long.vol
 
signal<-index(past.vol[past.vol<0.25])#ifelse(past.vol<0.3,1,0)
 
 
 
temp<-cbind(future.vol,short.vol)
temp<-(tail(temp,-1))
temp<-(head(temp,-3))
 
print('all days:')
summary(as.double(temp[,1])/as.double(temp[,2]))
 
print('days, then past volatility < 0.25:')
summary(as.double(temp[,1][signal])/as.double(temp[,2][signal]))

I was tweaking the result to squeeze some profit, but not so much luck. Basically, you need to trade either VIX index derivatives or S&P 500 index options to get direct impact. Before doing that, you need to test historical performance. Unfortunately, I don’t have data for these instruments. What about ETF, like VXX? Nope, because only few data points in the testing sample.

Later, I will try to incorporate GARCH model to see if this going to help. Any fresh ideas on this?

Comments (3)

3 weak days in a row

Recently, Trading the odds posted one of many flavors of mean reverting strategies and I decided to get my hands dirty by writing R code and testing it.

You can find full description of the strategy by following latter link above. Long story short – if SPY shows lower open, high and close 3 days in a row, then buy on the close of third day and sell it 1 days later.
Let’s do simple test:

?View Code RSPLUS
require('xts')
require('quantmod')
getSymbols('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct"))
dividends=getDividends('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct"))
 
temp=cbind(dividends,SPY)
temp[,1][is.na(temp[,1])]=0
 
SPY=cbind(temp[,2],temp[,3],temp[,4],temp[,1]+temp[,5])
colnames(SPY)=c("Open","High","Low","Close")
 
#one day before
lag1=lag((SPY),1)
 
#two days defore
lag2=lag((SPY),2)
 
signal=ifelse( (Cl(lag2)>Cl(lag1) & Cl(lag1)>Cl(SPY))&
			(Hi(lag2)>Hi(lag1) & Hi(lag1)>Hi(SPY)) &
			(Op(lag2)>Op(lag1) & Op(lag1)>Op(SPY)),
			1,0
)
#one day later
lag3=lag(Cl(SPY),-1)
 
profit=(lag3/Cl(SPY)-1)*signal
profit[is.na(profit)]=0
png(file='first.png',width=500)
plot(cumprod(profit+1),main='Profit 1995-2010')
dev.off()

The code above supposed to produce something similar:

Photobucket

Nice curve, isn’t it? But neither commissions nor slippage were taken into account. So, let’s run more complicated test. For that purpose I utilized blotter package. Here’s the code:

?View Code RSPLUS
require('xts')
require('quantmod')
require('blotter')
require('PerformanceAnalytics')
require('FinancialInstrument')
getSymbols('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct"))
dividends=getDividends('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct"))
 
temp=cbind(dividends,SPY)
temp[,1][is.na(temp[,1])]=0
 
SPY=cbind(temp[,2],temp[,3],temp[,4],temp[,1]+temp[,5])
colnames(SPY)=c('Open','High','Low','Close')
 
#one day before
lag1=lag((SPY),1)
 
#two days defore
lag2=lag((SPY),2)
 
signal=ifelse( (Cl(lag2)>Cl(lag1) & Cl(lag1)>Cl(SPY))&
			(Hi(lag2)>Hi(lag1) & Hi(lag1)>Hi(SPY)) &
			(Op(lag2)>Op(lag1) & Op(lag1)>Op(SPY)),
			1,0
)
#one day later
lag3=lag(Cl(SPY),-1)
 
symbols=c('SPY')
 
initDate=index(get(symbols)[1])
initEq=10000
rm(list=ls(envir=.blotter),envir=.blotter)
ltportfolio='3days'
ltaccount='3days'
initPortf(ltportfolio,symbols, initDate=initDate)
initAcct(ltaccount,portfolios=c(ltportfolio), initDate=initDate,initEq=initEq)
currency("USD")
stock("SPY",currency="USD",multiplier=1)
 
signal[is.na(signal)]=0
 
counter<-0
 
for(i in 2:length(signal))
{
	currentDate= time(signal)[i]
	equity = 10000 #getEndEq(ltaccount, currentDate)
	#print(paste("equity ",equity))
	position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate)	
	print(currentDate)
	if(position==0)
	{		
		#open a new position if signal is >0
		if(signal[i]>0 &counter ==0)
		{
			print('open position')
			closePrice<-as.double(Cl(SPY[currentDate]))
			unitSize = as.numeric(trunc((equity/closePrice)))
			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 &counter>=1)
		{
			print('close position>>>>')
			position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate)
			closePrice<-as.double((Cl(SPY[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(equity)
	updatePortf(ltportfolio, Dates = currentDate)
	updateAcct(ltaccount, Dates = currentDate)
	updateEndEq(ltaccount, Dates = currentDate)
 
	#equity = getEndEq(ltaccount, currentDate)
	#print(paste("equity ",equity))
}
result=rez1$symbols$SPY$txn[,7]
result=result[result!=0]
 
png(file='second.png',width=500)
#fix commission rate 2*3
plot(cumsum(result-6))
#next line will allow you to compare the performace with and without commissions
#chart.CumReturns(cbind((result)/10000,(result-6)/10000))
dev.off()

Photobucket

Nice curve, but let’s look beyond that. First of all, here’s nice function in PerformanceAnalytics package, AnnulizedReturns:
table.AnnualizedReturns((result-6)/10000)
Gross.Txn.Realized.PL
Annualized Return 0.0265
Annualized Std Dev 0.0494
Annualized Sharpe (Rf=0%) 0.5366

Well, Sharpe ratio is not impressive. The profit percentage of this strategy is 57% and mean of profitable return is 111$ against 98$ loss. Profit factor is ~1.55.

I think, this strategy can be as one of the parameter or vote in another system, but alone it is weak.

Comments (5)