Back to Community
Hedge with the VIX/VXV - Need Help

This simple algorithm that trades the volatility. It calcuales the ratio between the 30 day to the 90 days volatility indexes and go long/short on SPY with some TLT hedging.
The logic of the approach: Usually in non fear periods, the 90 day volatility (VXV) is more costly than the 30 days volatility index (VIX) because we pay primium for having index for longer period. But in downtruns the 30 day may be priced with an increased primium (as investors buy short term shorts due to immidiate fear).

Read more here:
http://investingwithoptions.com/blog/2014/12/17/how-to-trade-the-vix-vxv-indicator/

Caution:

  1. The beta is low which is great
  2. In daily trade I got good results but those turned horrible in minue mode !
  3. I couldnt test it in prior to 2012 due to lack of fetcher data.
  4. Mid 2013-mid2014 weren't great and I could mitigate it and have great gains only by sensing the market sentiment using moving average of spy , but this increased my Beta to a quantopian non acceptable figures.

I am still trying to finalize the appraoch and how to trade , I may got it all wrong .... need help to continue the algo.

Thanks,

Clone Algorithm
113
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
#https://www.quantopian.com/posts/calculating-vxv-slash-vix-ratio
# http://investingwithoptions.com/blog/2014/12/17/how-to-trade-the-vix-vxv-indicator/
import pandas
import pandas as pd


def rename_col(df):  
    df = df.rename(columns={'CLOSE': 'price'})  
    df = df.rename(columns={'VIX Close': 'price'})  
    df = df.rename(columns={'Equity Put Volume': 'price'})  
    df = df.fillna(method='ffill')  
    df = df[['price', 'sid']]  
    # Correct look-ahead bias in mapping data to times  
    df = df.tshift(1, freq='b')  
#    log.info(' \n %s ' % df.head())  

    df['mean'] = pd.rolling_mean(df['price'], 12)
    df['std'] = pd.rolling_std(df['price'], 12)


    return df  
def initialize(context):  
    # import the external data  
    fetch_csv('https://www.quandl.com/api/v1/datasets/CBOE/EQUITY_PC.csv?trim_start=2012-01-01',  
        date_column='Date',  
        symbol='PutVolume',  
        post_func=rename_col,  
        date_format='%d-%m-%Y')

    fetch_csv('https://www.quandl.com/api/v1/datasets/CBOEFE/INDEX_VXV.csv?trim_start=2012-01-01',  
        date_column='Date',  
        symbol='VXV',  
        post_func=rename_col,  
        date_format='%d-%m-%Y')

    fetch_csv('https://www.quandl.com/api/v1/datasets/CBOEFE/INDEX_VIX.csv?trim_start=2012-01-01',  
        date_column='Date',  
        symbol='VIX',  
        post_func=rename_col,  
        date_format='%d-%m-%Y')  
    # SPX  
    context.market = symbol('SPY')

    schedule_function(trade, date_rules.every_day(), time_rules.market_open())
    
    
def trade(context,data):
    
     #print data
     if 'price' in data['VXV'] and 'price' in data['VIX']:
        #past_prices = history(5, '1d', 'price')
        #print past_prices
        vix = data['VIX'].price
        vxv = data['VXV'].price
        ratio = (vix / vxv) 
        spread = (vix - vxv) 
        #past_ratio = past_prices['VXV'].mean() / past_prices['VIX']
        #mean = ratio.mean()
        record(vix=vix)
        record(vxv=vxv)
        #record(ratio=ratio)  
        #record(leverage=context.account.leverage)
        #record(spread=spread)


     if 'mean' in data['VXV'] and 'mean' in data['VIX']:   
        vxv_mean = (data['VXV']['mean'])
        vix_mean = (data['VIX']['mean']) 
        ratio_mean = (data['VIX']['mean']) / (data['VXV']['mean'])
        record(vix_mean=vix_mean, vxv_mean=vxv_mean)
        record(ratio_mean=ratio_mean)
     
     if 'std' in data['VXV'] and 'std' in data['VIX']:   
        vxv_std = (data['VXV']['std'])
        vix_std = (data['VIX']['std'])  
        ratio_std = (data['VIX']['std']) / (data['VXV']['std'])
        #record(ratio_std=ratio_std)
        #record(vix_std=vix_std, vxv_std=vxv_std)
        
        
        if (ratio < .98 and ratio < ratio_mean):
            order_target_percent(sid(8554),-0.5)
            order_target_percent(sid(23921),0.5)
        else:
            order_target_percent(sid(8554),0.9)
            order_target_percent(sid(23921),0.1)    
            
def handle_data(context, data):
    pass

def market_trend_up(context,data):
  
    if data[context.market].price > data[context.market].mavg(120):
        return True
    else:
        return False       
        
 
There was a runtime error.
3 responses

Hi Joe,

What exactly do you need help with?

To see how if anyone has ideas how to improve / resolve items 2 and 4 above

Regarding item 2: it's no surprise that this doesn't really work at the minute level. VIX and VXV both track volatility at the daily level over the last 30/90 days. This implies movements that aren't really meaningful at the minute level, i.e. noise. To paraphrase you, the signal you're looking for is "periods of fear," which is based on investor sentiment. But does the bearish/bullishness of investors really change significantly at the minute level? I don't think it does. I think that VXV and VIX movements at the minute level are basically noise -- as such, you get bad results.

On the other hand, bearish/bullishness of investors does change significantly at the daily level.