Another Volatility Strategy - VIX levels and Futures ratios

I dunno if the strategy was posted over and over again, because it is pretty simple and put together two of most used and simpliest idea on VIX. But I need some help to reduce volatility and dd. The ideas underlying the algo:

First: we want to go long on the XIV when we are in contango
Second: if the VIX is low we want it to grow (we go long VXX) and if it is high we want it to drop (we go long XIV)

However we want to avoid dangerous situations. So we want to switch from our positions on XIV or VXX to safer assets when the signals are in conflict. Moreover we want to avoid situations of market hysteria, and high VIX levels together with high ratio values may indicate this kind of situation (so when it occurs we invest in safe haven goods).

So is there something fallacious?
Any idea to further reduce volatility and dd?

317
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
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.data.quandl import cboe_vix

import numpy as np
import pandas as pd

def rename_col(df):
df = df.rename(columns={'Close': 'price','Trade Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['price', 'Settle','sid']]
# Shifting data by one day to avoid forward-looking bias
return df.shift(1)

def initialize(context):
"""
Called once at the start of the algorithm.
"""
#securities we are investing in

context.XIV = symbol('XIV')
context.VXX = symbol('VXX')
context.LQD = symbol('LQD') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns)
context.GLD = symbol('GLD') #Safe Haven when the market is "crazy" - Gold

my_pipe = Pipeline()
attach_pipeline(my_pipe, 'my_pipeline')

# Front month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',
date_format='%Y-%m-%d',
symbol='v1',
post_func=rename_col)
# Second month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv',
date_format='%Y-%m-%d',
symbol='v2',
post_func=rename_col)

# Rebalance every day, 1 minute after market open.
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=1))

"""
Called every day before market open.
"""
context.output = pipeline_output('my_pipeline')
context.vix = context.output["VixOpen"].iloc[0]

class GetVIX(CustomFactor):
window_length = 1
def compute(self, today, assets, out, vix):
out[:] = vix[-1]

def my_rebalance(context,data):
"""
Execute orders according to our schedule_function() timing.
"""
last_ratio = data.current('v1','Settle')/data.current('v2','Settle')
threshold_bot = 0.9
threshold_top = 0.95
vix_top = 21
vix_bot = 12.5

#VIX is very high and ratio tells us things are turning bad, we go in safe haven
if context.vix >= vix_top and last_ratio >= threshold_top:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
order_target_percent(context.LQD, 0)
order_target_percent(context.GLD, 1)

#vix is very low and we are not in contango, we suppose vix will raise
elif context.vix <= vix_bot and last_ratio > threshold_bot:
order_target_percent(context.VXX, 1)
order_target_percent(context.XIV, 0)
order_target_percent(context.LQD, 0)
order_target_percent(context.GLD, 0)

#vix is high and we are not in a situation in which the things are turning bad
elif context.vix >= vix_top and last_ratio < threshold_top:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 1)
order_target_percent(context.LQD, 0)
order_target_percent(context.GLD, 0)

#vix is in his standard range and we are in contango
elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 1) #a valuable option is to leverage here in order to achieve higher returns
order_target_percent(context.LQD, 0)
order_target_percent(context.GLD, 0)

#vix is in his standard range but we are not in contango, we wait switching to bonds
elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
order_target_percent(context.LQD, 1)
order_target_percent(context.GLD, 0)

#when there is conflict between signals we switch to bonds
elif context.vix < vix_bot and last_ratio < threshold_bot:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
order_target_percent(context.LQD, 1)
order_target_percent(context.GLD, 0)

record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount)
record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount)
record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount)
record(VIX=context.vix)
record(ratio=last_ratio)


There was a runtime error.
105 responses

2016-12-30 13:00 pvr:186 INFO PvR 0.0661 %/day cagr 0.7 Portfolio value 252036
2016-12-30 13:00 pvr:187 INFO Profited 242036 on 242356 activated/transacted for PvR of 99.9%
2016-12-30 13:00 pvr:188 INFO QRet 2420.36 PvR 99.87 CshLw -232356 MxLv 2.00 RskHi 242356 MxShrt 0
2016-12-30 13:00 pvr:273 INFO 2011-01-03 to 2016-12-30 $10000 2017-02-22 14:41 US/Eastern Runtime 0 hr 5.5 min Hey Blue currently as is it's a pretty good algo, only concern I have with multi-class assets is, Bonds/Gold are safe heaven assets now, they might not be in the future, as opposed to an algorithm that just trades the same asset long/short. But my strategy around these multi asset algorithms is too just take the highest DD point, and add whatever number you feel safe, and then shut it down if it exceeds that limit because it means something has occured and the characteristics of these assets have changed. For example some Algo's get a lot of returns from TLT for example, because of the low interest bond market, but how TLT would perform in a rising rate environment is yet to be seen, but the returns will probably be lower and the DD will probably increase. Yeah, I agree with you. Bonds (and Gold also) could be not considered safe haven assets in every period, the correlations change, they could bad perform in some situation, and moreover Bonds would probably not perform very well in a rising rate enviroment like the one we are going to face. However I kept the securities to invest in fixed in order to get a more readable code, just because at the moment that was not the part I want to improve. What I would like to do is to reduce the vol and dd , for example improving the signals quality. I'm not interested in returns in absolute terms, I can let go some opportunity to make money if it will improve the percentage in which the signals are correct. Go for short term bonds. Over the long period according to Bank of England and Fed data there ain't so much difference in yield and you get very, very low vol. That's my preference anyway. I looked back to 1709 in the UK and 1870 in the US. Hey Anthony, I was playing around with long vs short, but when testing on most algorithms the long dated such as TLT had an insane amount of over performance, in low interest rate environments. I agree that during times of FALLING interest rates a long dated bond will perform well. However in general people use bonds to mitigate risk and I don't think long dated bonds with their great volatility is the right way to do this over the long term. Also you will find that 90% of the performance of any bond long or short dated over the long term is from the coupon. I would therefor perfer to accept the low volatility short dated bond since over the long term the yield is not dissimilar to that of longer term bonds. Yeah I was also reading about high yield performing decent during rising interest rate environments, and even during an 08 type scenario at least the coupon rate might make up for the price depreciation. Other than that I really like this algo, yes DD is kind of "high" depending on your tolerance but the returns are quite amazing, and I also love the fact that the drawdowns aren't highly correlated with the markets sell offs, some of them it even caught potentially well. Mirco, I also put SPY and VIX at 2X during contango and normal VIX levels, it adds to the performance tremendously and only increases DD by a 2-3%. For the safe heaven just move to Cash or a money fund like MINT. 239 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) context.leverage = 1.96 """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) schedule_function(record_leverage, date_rules.every_day()) def record_leverage(context, data): record(leverage = context.account.leverage) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') context.vix = context.output["VixOpen"].iloc[0] class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 if data.can_trade(context.VXX) and data .can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=context.vix) record(ratio=last_ratio) #record(leverage=context.leverage)  There was a runtime error. And this is if you can get higher leverage from a broker at 3X the returns are still phenomenal for the DD of about 35% 239 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=1)) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') context.vix = context.output["VixOpen"].iloc[0] class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 if data.can_trade(context.VXX) and data .can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 3) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, .5) order_target_percent(context.GLD, 0) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=context.vix) record(ratio=last_ratio)  There was a runtime error. Historical VIX Data back to 2004, going to try and manually figure out what would of happened in 2008, unless someone can code it somehow. Very easy to do in a spreadsheet. Very simple stuff. Just get hold of the individual futures contracts. Just use the rolling front month - probably a good enough compromise. When rolling from one series to the next eliminate the gap by taking the next days return as the return of the new contract. Ah - looks like that website has done the heavy lifting for you. So either import it into Q or do it in a spreadsheet. Easy enough when you have the data. Glad to see someone who likes this strategy. I like the idea to swtich to a money fund for the safe haven. I originally thought to switch to cash but then I chose gold, now I am becoming convinced gold was not the right choice. The idea was to switch to a safe haven good/security when the market is on the brink of a crisis (the high vix plus high ratio should anticipate that situation), so in the end something like cash is more appropriated. Instead I didn't choose the SPY as alternative when we have to wait just to lower the volatility and beta by a little bit. I was interested in backtesting it during the 2008 crisis too. However (in theory) it should switch to the safe haven during almost all the "falling" period...if I made the calculations well. Hey Mirco, Yeah during September 08' it would keep you in a safe heaven given high VIX backwardation. Also, If I understand the strategy correctly high VIX / Contango we still stayed long XIV? In the above situation would be the only slight issue, given that late 07' Early 08' had high VIX but mostly still in contango, but looking at XIV prices I guess worst case seemed like a 10-20%DD or Breakeven given/slight profit depending on when the system entered XIV. But I think it will hold up fairly well, I'm going to research how to load excel data for pricing data, unless you already know how to do it and try to backrest it. @Mirco or anyone, here is the download link for the historical data: https://dl.dropboxusercontent.com/s/8iua85r0ejtvtn3/Data.csv?dl=0 I only got as far as getting it to load, not familiar with messing with the rename_col function, and would also need to load the VIX data from the csv too. Can this algorithm be used to live trade as is? It seems that it would fetch_csv once only upon initialisation and not update in the next day. Is this correct? Johnny, I had the same question and found this: Fetcher gets called in initialize and in live trading the file gets re-parsed each morning when the algo is launched in production in the early morning (currently I think 2-3am). This means that new data is available to your production algo at most once per day with the current design (we are looking at options to increase this frequency and allow for intraday updates). @Johnny I don't think there would be any trades in the backtest if that was the case, also function myrebalance is scheduled daily. Could be wrong though @Johnny There's some discussion in another one of the XIV strategies that this data apparently gets updated around 11 am, so you'd have to add a schedule function to pull the data and make your trade decisions a little bit later in the day for it to work live. @Kevin Well I think it will still work, but you are making decisions with 1 day old data? I guess basically you're signals are a day late? I made a couple edits, the algo now trades two hours into the session so the Quandl csv file is available thus eliminating the subtle look-ahead bias. I also changed the VIX data from yesterday's open to yesterday's close, interestingly this did have a negative impact on performance, I'm not sure if there is any rationale for using the open of the previous day or not. This is likely a more realistic picture of performance, the only issue outstanding is the fact that fetcher runs in the early morning so the csv available to the algo will still be stale so this wouldn't trade well live until we can get the fetcher to run on demand or Quandl starts updating this data earlier in the day. 20 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('LQD') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('GLD') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=2)) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') #context.vix = context.output["VixOpen"].iloc[0] context.vix = context.output["VixClose"].iloc[0] class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 if data.can_trade(context.VXX) and data.can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) #record(VIX=context.vix) record(ratio=last_ratio) record(leverage=context.account.leverage)  There was a runtime error. @Justin I think that placing trades two hours into the session will make the performance worse in live trading. Because the algo will still use the info that was received from Quandl the night before (around 2 AM) when Quantopian parses the fetcher, so the info will have less value if the trade placed 2 hours later. Maybe the way around for live trading is to fetch Vix1 and Vix2 values from another source (dropbox, etc) instead of Quandle I agree, my point was that since the data isn't available until 11am this algo is more realistic in that it trades at 11:30am with the data delivered at 11am. We can grab VIX and VXV from the pipeline API before_trading _start but from what I can tell we can get the front month and second month futures info from the Quandl dataset available to Q users. To clarify, fetcher runs at 2-3am but at that point Quandl hasn't updated the CSV files yet so the 2-3am fetcher run delivers data that is already over 24 hours old. To simulate the live performance in Q, the data from Quandle was shifted by 2 days and trades placed at the market open. As expected, the algo performance decreased significantly 16 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by TWO dayS TO SIMULATE Q LIVE TRADING PERFORMANCE return df.shift(2) def initialize(context): """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('LQD') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('GLD') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') #context.vix = context.output["VixOpen"].iloc[0] context.vix = context.output["VixClose"].iloc[0] class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 if data.can_trade(context.VXX) and data.can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) #record(VIX=context.vix) record(ratio=last_ratio) record(leverage=context.account.leverage)  There was a runtime error. Justin wrote: To clarify, fetcher runs at 2-3am but at that point Quandl hasn't updated the CSV files yet so the 2-3am fetcher run delivers data that is already over 24 hours old. So if I understand correctly the backtest matches live trading if the my_rebalance function is scheduled at 11:30am, after the VIX futures data is refreshed from Quandl (which gets it from Yahoo). In other words in backtesting the algo is seeing the previous day's pipeline VIX data before market open but in live trading that data won't be available until ~11am. BTW I believe this problem will be solved when Quantopian adds futures data. While we wait for futures data to be available natively does anyone know how to fetch Quandl CSV data outside of initialize()? Also, while I agree that using the pipeline VIX close from the previous day seems logical, strangely enough using the open or low give the best results. Next best is the high, and finally in 4th place is the close (backtested 1/1/2011 - 3/3/2017). So if I understand correctly the backtest matches live trading if the my_rebalance function is scheduled at 11:30am, after the VIX futures data is refreshed from Quandl (which gets it from Yahoo). And that 11am refresh function for live trading is still to-be-done. BTW I believe this problem will be solved when Quantopian adds futures data. It's only accurate if we could call fetcher at 11:30am, at this point the data at 11:30 is still stale. The problem would be resolved if Quandl could update the CSV file before 2-3am or if fetcher could be run on demand or as you pointed out the problem will be resolved once we have futures data. @Justin Got it... I was asking about the accuracy of the backtest, assuming the fetch-at-11am in live trading is resolvable. I thought I read that Quantopian was beta testing futures data. I'll do some searching for the latest news. I see, yeah, other than fill price/slippage model inaccuracy on these volatile ETFs I think the backtest is accurate. @Maxim, I guess the accuracy you mention is according to how the algorithm will perform live with the current data restrictions correct? Not if you had real live data at the open? Also, if you really wanted too you could update another csv source a min before the open manuallly, or even automate the process with a script you would just need a machine running at that time if you want to automate the process. Yeah, the algo is not ready to be live traded, it has still some problems and the limited flexibility of the fetcher doesn't help. However even if the algo isn't yet, the strategy seems to be quite robust. @Justin I suppose the negative impact on performance you found is explainable by the fact I used 2 years of the period of the backtest as sample on which optimize the thresholds and for the risk analysis. So, yes, your backtest could be a good proxy of the real performance. @Maxim I didn't try to run the backtest with 2days delayed data, however they maintain part of the extra returns, probably due to the ac. I would see this positively. Another way to get rid of the fetcher is to use VIX/VXV ratio instead of the VX1 / VX2 ratio. VXV is available via Pipeline so in theory should be ready for live trading. See below the back-test of the same algo but with VIX/VXV ratio via Pipleine instead of VX1/VX2 . 43 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix from quantopian.pipeline.data.quandl import cboe_vxv import numpy as np import pandas as pd #def rename_col(df): # df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) # df = df.fillna(method='ffill') # df = df[['price', 'Settle','sid']] # Shifting data by TWO dayS TO SIMULATE Q LIVE TRADING PERFORMANCE #return df.shift(0) def initialize(context): """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('LQD') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('GLD') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_high]), 'VixHigh') my_pipe.add(GetVIX(inputs=[cboe_vxv.close]), 'VxvClose') # Front month VIX futures data #fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', # date_column='Trade Date', # date_format='%Y-%m-%d', # symbol='v1', #post_func=rename_col) # Second month VIX futures data #fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', # date_column='Trade Date', # date_format='%Y-%m-%d', # symbol='v2', #post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=1)) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') #context.vix = context.output["VixOpen"].iloc[0] context.vix = context.output["VixClose"].iloc[0] #context.vix = context.output["VixHigh"].iloc[0] context.vxv = context.output["VxvClose"].iloc[0] print(context.vix, 'vix') print(context.vxv, "vxv") class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ #last_ratio = data.current('v1','Settle')/data.current('v2','Settle') last_ratio = context.vix/context.vxv threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 #print('vx1', data.current('v1','Settle')) if data.can_trade(context.VXX) and data.can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) #record(VIX=context.vix) record(ratio=last_ratio) record(leverage=context.account.leverage)  There was a runtime error. Here's the same algo but now recording the previously used ratio and the new ratio so everyone can see how they track each other. The VIX/VXV ratio results in a higher max DD, maybe we just need to optimize the ratio thresholds for this new ratio. 30 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix from quantopian.pipeline.data.quandl import cboe_vxv import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('LQD') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('GLD') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_high]), 'VixHigh') my_pipe.add(GetVIX(inputs=[cboe_vxv.close]), 'VxvClose') #Front month VIX futures data fetch_csv('https://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) #Second month VIX futures data fetch_csv('https://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=1)) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') #context.vix = context.output["VixOpen"].iloc[0] context.vix = context.output["VixClose"].iloc[0] #context.vix = context.output["VixHigh"].iloc[0] context.vxv = context.output["VxvClose"].iloc[0] #print(context.vix, 'vix') #print(context.vxv, "vxv") class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ old_ratio = data.current('v1','Settle')/data.current('v2','Settle') last_ratio = context.vix/context.vxv threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 #print('vx1', data.current('v1','Settle')) if data.can_trade(context.VXX) and data.can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) #record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) #record(VIX=context.vix) record(VIX_VXV_ratio=last_ratio) record(V1_V2_ratio=old_ratio) record(leverage=context.account.leverage)  There was a runtime error. Hey guys can somebody explain to me the exact data we would need. Would we need VX1/VX2 opening price for that day, or the prior day open/close? At 11AM does Quandl update the data for the current day or the past day? Also, looking at the algo it's trading off of the settle price which is basically closing price right? So it's trading off of the prior days closing data, and entering the order 1m the current day? Is this correct? If so, it would be easier to run a something to fetch and update the csv before opening. I can probably code a java applet to automatically get a quote, write to csv, and upload it to drop box or something. I guess to clarify my rambling for others and myself. Do we need prior day closing data or current day opening data? You could use either with slightly different results. I can't really see the necessity to automate such a simple system but as you say you can easily enough get the data you need when you need it. I take mine from Interactive Brokers. I also have CSI data which is updated daily after the market close. CSI data.com I mean yeah the strategy is simple but it will still take up time looking at prices EOD calculating orders rebalancing and inputting trades every few days. Is there a way to download a .csv from CSIData through a URL without logging into their website each time like Quandl? Hey guys can somebody explain to me the exact data we would need. Would we need VX1/VX2 opening price for that day, or the prior day open/close? At 11AM does Quandl update the data for the current day or the past day? The algo needs yesterday's VX1 close, VX2 close and VIX close in my opinion. At 11am Quandl updates the csv with end-of-day data from the previous day Also, looking at the algo it's trading off of the settle price which is basically closing price right? So it's trading off of the prior days closing data, and entering the order 1m the current day? Is this correct? That's correct, if we had the data already available it would trade 1 minute after the open If so, it would be easier to run a something to fetch and update the csv before opening. I can probably code a java applet to automatically get a quote, write to csv, and upload it to drop box or something. That's a possibility, it would have to update a csv file before fetcher runs at 2-3am Do we need prior day closing data or current day opening data? Current day open would be ideal but I don't think that's a possibility until Q unleashes the futures data, which could be soon so it might make sense to wait for that before writing custom csv creation code. With CSI you download their software and you automate the daily download to your laptop, AWS, whatever. I tried to reoptimize the thresholds for the VIX/VXV ratio, I recorded where the VIX/VXV ratio was when the V1/V2 ratio was at the top or bottom threshold and then averaged them over the backtest period, this went poorly though, either the ratio needs to be optimized in another way or VIX/VXV doesn't have the same power as V1/V2. 30 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix from quantopian.pipeline.data.quandl import cboe_vxv import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('LQD') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('GLD') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') #my_pipe.add(GetVIX(inputs=[cboe_vix.vix_high]), 'VixHigh') my_pipe.add(GetVIX(inputs=[cboe_vxv.close]), 'VxvClose') context.VIXVXV_top = [] context.VIXVXV_bot = [] #Front month VIX futures data fetch_csv('https://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) #Second month VIX futures data fetch_csv('https://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=1)) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') #context.vix = context.output["VixOpen"].iloc[0] context.vix = context.output["VixClose"].iloc[0] #context.vix = context.output["VixHigh"].iloc[0] context.vxv = context.output["VxvClose"].iloc[0] #print(context.vix, 'vix') #print(context.vxv, "vxv") class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ #last_ratio = data.current('v1','Settle')/data.current('v2','Settle') last_ratio = context.vix/context.vxv #V1V2_ratio = data.current('v1','Settle')/data.current('v2','Settle') VIXVXV_ratio = context.vix/context.vxv threshold_bot = 0.86 threshold_top = 0.98 vix_top = 21 vix_bot = 12.5 #print('vx1', data.current('v1','Settle')) if data.can_trade(context.VXX) and data.can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) context.VIXVXV_top.append(VIXVXV_ratio) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) context.VIXVXV_bot.append(VIXVXV_ratio) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) context.VIXVXV_top.append(VIXVXV_ratio) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) #a valuable option is to leverage here order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) context.VIXVXV_bot.append(VIXVXV_ratio) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) context.VIXVXV_bot.append(VIXVXV_ratio) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) context.VIXVXV_bot.append(VIXVXV_ratio) if len(context.VIXVXV_top) != 0: if len(context.VIXVXV_bot) != 0: print 'VIXVXV_top:', sum(context.VIXVXV_top) / float(len(context.VIXVXV_top)) print 'VIXVXV_bot:', sum(context.VIXVXV_bot) / float(len(context.VIXVXV_bot)) #record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) #record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) #record(VIX=context.vix) record(VIX_VXV_ratio=last_ratio) #record(V1_V2_ratio=old_ratio) record(leverage=context.account.leverage)  There was a runtime error. Yeah who knows when they will support real time data makes sense to do something in the meantime, I remember reading the futures data thing a pretty long time ago. I'll check out csi or write my own. Futures are in Alpha stage, still no timeline though Jamie McCorriston Jan 11, 2017 Hey guys, The futures project is currently in a private alpha stage. If you would like to participate in the alpha, please email in to [email protected]. It's a bit tough to give a timeline right now, there's still some work to do, but we are definitely making progress! If you're interested, a lot of the work can be tracked in Zipline. @Patrick: When we publicly launch futures in backtesting, we won't have data feeds for them right away, but we definitely plan to add them going forward. Futures are coming to Quantopian Is this algo using just the closing and settlement price to trade? I created my own CSV's with dates 02/28-03/02 with Columns: Trade Date Close Settle The values match quandls data, when i run the backtest for those dates the quandl backtest shows a much lower return 0.7% vs 2.4% What is the issue here? The most helpful thing I have done recently was to use Excel to backtest my Vix algo. Completely transparent and it forced me to code everything myself from scratch. It was revelatory, really. Highly recommended. Q is a phenomenal achievement but it's good to check out my ideas manually as well. Is this algo using just the closing and settlement price to trade? I created my own CSV's with dates 02/28-03/02 with Columns: Trade Date Close Settle The values match quandls data, when i run the backtest for those dates the quandl backtest shows a much lower return 0.7% vs 2.4% What is the issue here? It looks to me like it's only using the settle price for the V1 and V2 contracts and using the previous day's VIX close, not sure what the issue is. Hi Mirco Lamperti, I like your ideas and it indeed performs very well. I happen to have the underlying SPVXSP index data for VXX back to 12/20/2005 together with the VX1, and VX2 data and I directly tested the algorithm using the close price of VIX (while you used the open prices of VIX) back to 01/01/2006 locally in R and assuming you can close your position at the end of the day instead of the next morning. My results since 01/2011 are very similar to yours with a cagr of ~60-70% and a maxdd of ~29%. But the fixed levels for vix_top and bot caused much trouble pre-2009. Even when short vol was very profitable as in 2006 the algo was losing money. I think the fixed levels are "too" optimized for the QE era and I am not sure whether it is the new norm or we might revert back to the old days. I have attached the equity curves in a dropbox file for your reference. https://www.dropbox.com/s/o4ml70tzu7hx9g1/Mirco%20Lamperti%20algo.docx?dl=0 Thank you for sharing such a good algo and hopefully we can make it better. @Jiaqi What kind of DD did it have in 07-08' ? From the equity curves you posted it seemed like 45% DD or something? yes, 45.7% the 45.7% in 2008 might be understandable. But the 40% dd in 2006 might be due to too much optimization for the levels. Hey Qi, This is what I wrote down for 06' with a dataset I have, so it still looked pretty profitable in 06' You can get the updated dataset here: http://investing.kuchita.com/2012/06/28/xiv-data-and-pricing-model-since-vix-futures-available-2004/ Maybe you can try and figure out why the results seem different. 1/3/06 XIV$10.10
5/17/06 XIV $12.49 5/17/06 - 06/21/06 mostly backwardation, a few days of contango might of entered XIV for a day or two. 6/21/06 XIV$9.45
07/14/06 XIV $8.76 07/24/06 XIV$9.56
12/31/06 XIV $17.65 Thank you Qi for your pre-2011 analysis, and thank you too Elsid for your interest in this strategy. I will try to locally backtest the algo back to 2006 between one thing and another and then I will try to improve the algo, if you get some results please let me know. Thank you again. I've played a little with fetch_csv but I don't understand how to use the simulated pre-2011 XIV data in my backtesting. Can anyone share code showing that? I'm referring to the calculated/simulated XIV data for 2004-2011 in the spreadsheet at: http://investing.kuchita.com/2012/06/28/xiv-data-and-pricing-model-since-vix-futures-available-2004/ This is a reworked version I added a 200MA & some extra logic when vix is below 12.5, in 08 type of scenario's drawdown's should be around 35%, during 04-06 hopefully the extra logic for extremely low vix levels should of caught some of the gains as you would be invested in SPY, and lower DD. This version also uses leverage during contango and vix being in range. Extra thanks too @Qi, for being able to backtest past 11' , please take a look at my 200MA code, and order logic. 239 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) context.leverage = 1.96 """ Called once at the start of the algorithm. """ #securities we are investing in context.SPY = symbol('SPY') context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) schedule_function(record_leverage, date_rules.every_day()) def record_leverage(context, data): record(leverage = context.account.leverage) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') context.vix = context.output["VixOpen"].iloc[0] class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.90 threshold_top = 0.95 vix_top = 21 vix_bot = 12.5 spy_price_history = data.history(context.SPY, "price", 200, "1d") ma200 = spy_price_history.mean() current_spy_price = data.current(context.SPY, 'price') if data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price > ma200: #VIX > 21 & High Backwardation Chance Ratio >=95 if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX > 21 & steady contango Ratio < 95 #vix is high and we are not in a situation in which the things are turning bad, elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 High Contango Ratio < .90 #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 Normal Backwardation Risk range .90 - .95 #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) # VIX < 12.5 and ratio range 90-95 #between backwardation and extreme contango elif context.vix < vix_bot and last_ratio < threshold_top and last_ratio >= threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #VIX < 12.5 High Contango Ratio < .90 elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) # VIX < 12.5 & High Backwardation Risk Ratio > .95 #vix is very low and we are not in contango, we suppose vix will raise elif context.vix < vix_bot and last_ratio > threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) elif data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price < ma200: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=context.vix) record(ratio=last_ratio) #record(leverage=context.leverage) There was a runtime error. I have a very "I'm new" question: How does one use the prices of option contracts to determine the "settlement" price for the options that are expiring for at that date? How do I look at the typical option spread and calculate the "VX1" and "VX2" used here? I think the method used to determine the forward vix is the same you use to determine the spot vix, instead the options you use change. But you are not using just a couple of options, but a portfolio of otm options. Then the futures are driven by expectation, because they have not a tradeable underlying. Hey Mirco, Can you explain this code? def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') context.vix = context.output["VixOpen"].iloc[0]  At what time before market open is this executed? Seems that it's getting the VIX open , also is this data for the prior day open or current day open? There seems to be a discrepancy, it's probably just misunderstanding of the code, for 09/09/2016 VIX open for that day was 12.52 and VIX was in Contango, so it should of entered XIV, but did not. Might make sense if it's pulling VIX before the open and it was lower than 12.5 or even using the prior days open. UPDATE: Seems like it's pulling 8:45 now just don't know if it's prior day, or current day. UPDATE: There are issues with Pipeline Data at least for near around those days, it's pulling the wrong VIX values. I'm not 100% sure this is "the" answer, but it seems that the close reported in VX1 and VX can be figured out using the 10.00 (lowest strike) + the price of the option. It seems like it would be better to NOT shift the data by a day and use the open data (no look-ahead bias if we use Open on the day of Open right?). However, It seems to drop the 2010-2017 performance from ~2200 down to ~1152. But I can't quit tell if the VIX data is also shifted somewhere and I didn't properly undo that. This VX1/VX2 ratio seems to work much better than the VIX/VXV ratio strategies. I've seen a lot of articles on the VIX/VXV, but not as much on using the actual option data as a ratio, does someone perhaps have some references that I could read up on? I've modified the Algo to be traded live, you can piggyback off of my dropbox data I suggest creating your own to not rely on mine. The current logic performed well from 06+ with about a 35% DD including 07-08'. If you want to run TLT, you will need half positions with all long VXX,XIV,SPY orders, but it gets about the same results as short term treasuries at 100%. Also, if somebody could help with the EMA, the EMA and SMA print out the same value probably coded it wrong. Special thanks to @Qi again for helping backtest past 2011 12 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd import talib as ta def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) context.leverage = 1.96 """ Called once at the start of the algorithm. """ #securities we are investing in context.SPY = symbol('SPY') context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('SHV') #Safe Haven when the market is "crazy" - Gold # Front month VIX futures data fetch_csv('https://dl.dropboxusercontent.com/s/cusb3w85vt928zq/v1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('https://dl.dropboxusercontent.com/s/9pcyq632pel4a6o/v2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Prior Day VIX Open Data fetch_csv('https://dl.dropboxusercontent.com/s/knoct0rk14enjrw/VIX.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='VixOpen', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) schedule_function(record_leverage, date_rules.every_day()) def record_leverage(context, data): record(leverage = context.account.leverage) def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.90 threshold_top = 0.95 vix_top = 21 vix_bot = 12.55 vix_open = data.current('VixOpen', 'Settle') #MA spy_price_history = data.history(context.SPY, "price", 300, "1d") sma = spy_price_history.mean() current_spy_price = data.current(context.SPY, 'price') ema=ta.EMA(spy_price_history,300)[-1] #print ema300 #print ma200 if data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price > sma: #VIX > 21 & High Backwardation Chance Ratio >=95 if vix_open >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) # VIX > 21 & steady contango Ratio < 95 #vix is high and we are not in a situation in which the things are turning bad, elif vix_open >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 High Contango Ratio < .90 #vix is in his standard range and we are in contango elif vix_open > vix_bot and vix_open < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 Normal Backwardation Risk range .90 - .95 #vix is in his standard range but we are not in contango, we wait switching to bonds elif vix_open > vix_bot and vix_open < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) # VIX < 12.5 and ratio range 90-95 #between backwardation and extreme contango elif vix_open < vix_bot and last_ratio < threshold_top and last_ratio >= threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #VIX < 12.5 High Contango Ratio < .90 elif vix_open < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) # VIX < 12.5 & High Backwardation Risk Ratio > .95 #vix is very low and we are not in contango, we suppose vix will raise elif vix_open < vix_bot and last_ratio > threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) elif data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price < sma and last_ratio > 1.08: order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) elif data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price < sma: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=vix_open) record(ratio=last_ratio) record(leverage=context.leverage) There was a runtime error. I like the SMA-200 idea, will definitely try adding that in, this one just has a slightly lower bottom threshold. Also, if we know we are "no where near" the bottom or top vix thresholds, I think it would be a prime point to add leverage. The value of 15 is somewhat arbitrary to just test the waters...but indeed it had no impact on the drawdown. 29 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias return df.shift(1) #return df.shift(2) #return df def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) context.leverage = 1.75 """ Called once at the start of the algorithm. """ #securities we are investing in context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) schedule_function(record_leverage, date_rules.every_day()) def record_leverage(context, data): record(leverage = context.account.leverage) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') context.vix = context.output["VixOpen"].iloc[0] class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.9 threshold_top = 0.95 vix_top = 21 vix_agressive = 15 # when to get agressive with our position XIV vix_bot = 11.75 # 12.5 if data.can_trade(context.VXX) and data.can_trade(context.XIV): #VIX is very high and ratio tells us things are turning bad, we go in safe haven if context.vix >= vix_top and last_ratio >= threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) #vix is very low and we are not in contango, we suppose vix will raise elif context.vix <= vix_bot and last_ratio > threshold_bot: order_target_percent(context.XIV, 0) if context.vix >= vix_agressive: # poor mans' way of assuming vix is going high order_target_percent(context.VXX, 1 * context.leverage) else: order_target_percent(context.VXX, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is high and we are not in a situation in which the things are turning bad elif context.vix >= vix_top and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) if context.vix > vix_agressive: order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns else: order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #when there is conflict between signals we switch to bonds elif context.vix < vix_bot and last_ratio < threshold_bot: order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=context.vix) record(ratio=last_ratio) #record(leverage=context.leverage)  There was a runtime error. @Kevin you want threshold to be 12.55 even 12.50 had one nasty day where it dropped like 20%, I guess you can call it curve fitting, but it seemed like an outlier out of almost 10 years. 11.75 increases DD by around 8% and decreases performance by 10%/year, and this is during 2016, where this event occurs. Also, I wouldn't rely on Quantopians backtest, given it has discrepancies in the data, you need to feed in your own data source, for example for VIX opening price, given that 10-20 days had discrepancies in 2016 alone. It didn't take any live orders today, if someone can look at the code i posted and find any issues, does the datas date need to be Todays date for it to trade? It looks likethe date in the CSV should be a today date from this post https://www.quantopian.com/posts/insider-trading-algo-need-help 1) The reason why your algorithm does not place any orders during live trading is because the imported csv file does not contain data for the current trading day. The way fetch_csv works is by using the current date as an index to the DataFrame constructed from your csv file (index specified by date_column), and making data that corresponds to that index available to your algorithm. This behavior is similar in the backtester, the difference is that data is forward-filled for days (indexes) missing in the csv file. This is why the algo seems to work while backtesting. You could try writing a pre/post_func using pandas that forward-fills data in live trading as well. I have been working on this myself, but I haven't found a general, robust solution yet. Hey Max, thanks for the reply, how about if I just date the CSV data to current day then that should work correct? for example 03/21 instead of 03/20? Hey Elsid, shifting the date by one day forward, in theory should fix it. I will try to test it in my IB paper account tomorrow. Here is another thread that discussed this problem https://www.quantopian.com/posts/is-fetch-csv-still-not-allowed-in-live-trading Also, it seems that one can use pre_funciton to shift the data automatically instead of doing it manually every night. @Maxim, Yeah just shifted it manually we'll see how it does tomorrow live. Remember Friday though you have to shift +3, let me know if you get anything working, or else just going to code this up in python, or will just change the logic that create my csv, given I do it automatically through Java. Also, here is a java applet, put it in a folder and it will create todays closing VX1, VX2, and VIX Open in separate .csv it will re-write everyday's values, didn't feel like doing logic to make it build history. https://www.dropbox.com/s/911q6kic50c7x7o/VIX.jar?dl=0 (You need to have google chrome installed) Well it didn't work today either even with todays date, maybe I updated my CSVs to late? What time does quantopian fetch the CSV? 2AM EST? @Elsid Maybe the problem is in "df.shift(1)". We dont have to shift anything in live trading... I'll try to run the algo without this part to see if it can fix the problem. def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias # return df.shift(1) return df  I'll try it also, should I still date the data with todays trading date? i am not sure which date it will use. Tomorrow, I will try to test with a CSV that has both dates (today and tomorrow) and log in the values taken by Q to figure out which ones are used Trade Date Close Settle 3/23/17 13.175 13.175 3/22/17 12.175 12.175 Out of curiosity, would the error have anything to do with a lack of an api key for the Quandl data url? https://www.quandl.com/api/v3/datasets/CHRIS/CBOE_VX1.csv?api_key=yourAPIKeyHere @Maxim, did any of those days place an order? Mine again didn't take a trade. @Elsid, the algo read the CSV correctly this morning (see below) but didn't place any orders, probably because of the trading logic of the algo. By the way, i keep my CSV in google sheets, cause Dropbox doesnt allow public urls anymore. 2017-03-24 09:32 PRINT ('vix open', 13.119999999999999) 2017-03-24 09:32 PRINT ('vix v2', 14.68) 2017-03-24 09:32 PRINT ('vix v1', 14.1) 2017-03-24 09:32 PRINT ('last ratio', 0.96049046321525888) @Maxim, I think the logic is fine, there currently is no logic for VIX in Range & Ratio > .95 given that this has happened the last few days it shouldn't place a order. If you add the above logic you should stay long SPY, all the other instruments give you a 10-15%/year performance decrease, so would probably be best to just leave it out, and hold the last position. Also, did you remove the dayshift? Let's see once the ratio goes below .95, or vix rises above 21 for a trade to occur. @Elsid, yes i removed the dayshift, there is no use for it in live mode. Also, i am wondering if Quantopian fetches the CSV file during the 'initialize' process around 9am or at ~ 2am? What if one updates the CSV with the most recent values of vix, v1, v2 around 8 AM (~1 hour before 'initialize function run') instead of updating it the night before? In theory, if the algo can use most recent values, it should increase the returns and decrease the drawdowns @Maxim Supposedly between 12-2AM EST. Yeah I'm not sure how more recent values would behave it's something that would need to be tested, I also thought about a real-time version, where you have live data fed intraday, you could react faster, but who knows if that would make performance better or worst, at least with the daily you can miss out some of the false signals, as you get a whole day for people to change their minds, and that buy/sell signal is still valid, instead of flip flopping with real time data. Also, something interesting for example using VIX prior day closing price, which would be more recent provides worst performance & DD, for some reason older VIX opening price data works better. I think the first step to analyze any of this would be complete historical intraday tick data for$VIX and all of it's corresponding futures contracts, which would probably cost a huge chunk.

Around $8,000 from CBOE, maybe there are cheaper places but still cost you an insane amount lol. https://datashop.cboe.com/volatility-index-futures-data I still would prefer to use the most recent VIX data vs. using a prior day open price even though the backtests showed less drawdawns/higher returns with older VIX values. For now, i don't see any logical explanation for this phenomenon. As for the data, www.tickdata.com/product/historical-futures-data sells complete history for one symbol for$750 , so the set of 3 (V1, V2, Vix) would cost $2,250, it's a bit less than from CBOE but still out of my budget :) @Maxim, Yeah I saw that website too, still expensive unless you are going to go deep on volatility strategies or had something promising. Anyway quantopian tried to fetch the CSV at 2AM, just received an error message from google docs. Can you tell me the format of the google link? I tried the recommended quantopian link, but it says access denied: I have used a VIX close price 30-day rolling standard deviation to add within the logic of determining 'vix_top' and 'vix_bot' levels. It looks to have improved returns slightly, however I am not too happy about the ugly loss in September 2016 contributing largely to the DD. 60 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias #return df.shift(1) print(df.tail()) #DO NOT SHIFT THE DATA IN LIVE TRADING return df.shift(1) def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) context.leverage = 1.75 """ Called once at the start of the algorithm. """ #securities we are investing in context.SPY = symbol('SPY') context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') my_pipe.add(StdDev(inputs=[cboe_vix.vix_close]), 'Std') my_pipe.add(MA30(inputs=[cboe_vix.vix_close]), 'MA30') my_pipe.add(MA30(inputs=[cboe_vix.vix_close])+2*StdDev(inputs=[cboe_vix.vix_close]), 'Upper') my_pipe.add(MA30(inputs=[cboe_vix.vix_close])-2*StdDev(inputs=[cboe_vix.vix_close]), 'Lower') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) schedule_function(record_leverage, date_rules.every_day()) def record_leverage(context, data): record(leverage = context.account.leverage) def before_trading_start(context, data): """ Called every day before market open. """ context.output = pipeline_output('my_pipeline') context.vix = context.output["VixClose"].iloc[0] context.vix_top = context.output['Upper'].iloc[0] context.vix_bot = context.output['Lower'].iloc[0] ''' if context.vix_top < 21: context.vix_top = context.vix_top else: context.vix_top = 21 if context.vix_bot > 12.5: context.vix_bot = context.vix_bot else: context.vix_bot = 12.5 ''' class StdDev(CustomFactor): window_length = 30 def compute(self,today,assets,out,values): out[:] = np.nanstd(values,axis=0) class MA30(CustomFactor): window_length = 30 def compute(self,today,assets,out,values): out[:] = np.nanmean(values,axis=0) class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') threshold_bot = 0.90 threshold_top = 0.95 vix_top = context.vix_top vix_bot = context.vix_bot spy_price_history = data.history(context.SPY, "price", 200, "1d") ma200 = spy_price_history.mean() current_spy_price = data.current(context.SPY, 'price') if data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price > ma200: #VIX > 21 & High Backwardation Chance Ratio >=95 if context.vix >= vix_top and last_ratio >= threshold_top: #log.info('VIX is above 21 and futures ratio is near enough at backwardation, liquidating all positions......') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX > 21 & steady contango Ratio < 95 #vix is high and we are not in a situation in which the things are turning bad, elif context.vix >= vix_top and last_ratio < threshold_top: #log.info('SENDING ORDER FOR XIV! VIX is above 21 and future ratio is below 0.95') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 High Contango Ratio < .90 #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: #log.info('SENDING AGGRESSIVE ORDER FOR XIV! VIX is ranging from 12.5 - 21 and future ratio is in comfortable contango') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 Normal Backwardation Risk range .90 - .95 #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: #log.info('SWITCHING TO SAFE HAVEN ASSET! We dont know what the F is going on') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) # VIX < 12.5 and ratio range 90-95 #between backwardation and extreme contango elif context.vix < vix_bot and last_ratio < threshold_top and last_ratio >= threshold_bot: #log.info('SWITCHING TO SAFE HAVEN ASSET') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #VIX < 12.5 High Contango Ratio < .90 elif context.vix < vix_bot and last_ratio < threshold_bot: #log.info('SENDING ORDER FOR GOLD!') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) # VIX < 12.5 & High Backwardation Risk Ratio > .95 #vix is very low and we are not in contango, we suppose vix will raise #CHANGED FROM GOLD POS TO VXX POS elif context.vix < vix_bot and last_ratio > threshold_top: #log.info('TIME TO LONG VOLATILITY! SENDING ORDER FOR VXX! Vix is too low for a future ratio which is near enough at backwardation') order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) elif data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price < ma200: #log.info('SPY is below 200MA indicating short momentum therefore we will switch to gold') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=context.vix) record(ratio=last_ratio) #record(leverage=context.leverage) There was a runtime error. Not sure if I am going mad here but running the same algo as above for today with only 1.75x leverage yields a significantly better return and sharpe! 60 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 from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor from quantopian.pipeline.data.quandl import cboe_vix import numpy as np import pandas as pd def rename_col(df): df = df.rename(columns={'Close': 'price','Trade Date': 'Date'}) df = df.fillna(method='ffill') df = df[['price', 'Settle','sid']] # Shifting data by one day to avoid forward-looking bias #return df.shift(1) #DO NOT SHIFT THE DATA IN LIVE TRADING return df def initialize(context): set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35)) set_slippage(slippage.FixedSlippage(spread=0)) context.leverage = 1.75 """ Called once at the start of the algorithm. """ #securities we are investing in context.SPY = symbol('SPY') context.XIV = symbol('XIV') context.VXX = symbol('VXX') context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns) context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold my_pipe = Pipeline() attach_pipeline(my_pipe, 'my_pipeline') my_pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose') my_pipe.add(StdDev(inputs=[cboe_vix.vix_close]), 'Std') my_pipe.add(MA30(inputs=[cboe_vix.vix_close]), 'MA30') my_pipe.add(MA30(inputs=[cboe_vix.vix_close])+2*StdDev(inputs=[cboe_vix.vix_close]), 'Upper') my_pipe.add(MA30(inputs=[cboe_vix.vix_close])-2*StdDev(inputs=[cboe_vix.vix_close]), 'Lower') # Front month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v1', post_func=rename_col) # Second month VIX futures data fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv', date_column='Trade Date', date_format='%Y-%m-%d', symbol='v2', post_func=rename_col) # Rebalance every day, 1 minute after market open. schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2)) schedule_function(record_leverage, date_rules.every_day()) def record_leverage(context, data): record(leverage = context.account.leverage) def before_trading_start(context, data): """ Called every day before market open. """ log.info('Good Morning RNJ Capital LLP') context.output = pipeline_output('my_pipeline') context.vix = context.output["VixClose"].iloc[0] context.vix_top = context.output['Upper'].iloc[0] context.vix_bot = context.output['Lower'].iloc[0] log.info('Today we are targeting a VIX upper boundary of '+str(context.vix_top)+' and a VIX lower boundary of '+str(context.vix_bot)) ''' if context.vix_top < 21: context.vix_top = 21 else: context.vix_top = context.vix_top if context.vix_bot > 12.5: context.vix_bot = 12.5 else: context.vix_bot = context.vix_bot ''' class StdDev(CustomFactor): window_length = 30 def compute(self,today,assets,out,values): out[:] = np.nanstd(values,axis=0) class MA30(CustomFactor): window_length = 30 def compute(self,today,assets,out,values): out[:] = np.nanmean(values,axis=0) class GetVIX(CustomFactor): window_length = 1 def compute(self, today, assets, out, vix): out[:] = vix[-1] def my_rebalance(context,data): """ Execute orders according to our schedule_function() timing. """ last_ratio = data.current('v1','Settle')/data.current('v2','Settle') log.info('Todays future ratio has been calculated as '+str(last_ratio)) threshold_bot = 0.90 threshold_top = 0.95 vix_top = context.vix_top vix_bot = context.vix_bot spy_price_history = data.history(context.SPY, "price", 200, "1d") ma200 = spy_price_history.mean() current_spy_price = data.current(context.SPY, 'price') if data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price > ma200: #VIX > 21 & High Backwardation Chance Ratio >=95 if context.vix >= vix_top and last_ratio >= threshold_top: log.info('VIX is above 21 and futures ratio is near enough at backwardation, liquidating all positions......') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX > 21 & steady contango Ratio < 95 #vix is high and we are not in a situation in which the things are turning bad, elif context.vix >= vix_top and last_ratio < threshold_top: log.info('SENDING ORDER FOR XIV! VIX is above 21 and future ratio is below 0.95') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 High Contango Ratio < .90 #vix is in his standard range and we are in contango elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot: log.info('SENDING AGGRESSIVE ORDER FOR XIV! VIX is ranging from 12.5 - 21 and future ratio is in comfortable contango') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) # VIX 12.5-21 Normal Backwardation Risk range .90 - .95 #vix is in his standard range but we are not in contango, we wait switching to bonds elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top: log.info('SWITCHING TO SAFE HAVEN ASSET! We dont know what the F is going on') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) # VIX < 12.5 and ratio range 90-95 #between backwardation and extreme contango elif context.vix < vix_bot and last_ratio < threshold_top and last_ratio >= threshold_bot: log.info('SWITCHING TO SAFE HAVEN ASSET') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 1) order_target_percent(context.GLD, 0) #VIX < 12.5 High Contango Ratio < .90 elif context.vix < vix_bot and last_ratio < threshold_bot: log.info('SENDING ORDER FOR GOLD!') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) # VIX < 12.5 & High Backwardation Risk Ratio > .95 #vix is very low and we are not in contango, we suppose vix will raise #CHANGED FROM GOLD POS TO VXX POS elif context.vix < vix_bot and last_ratio > threshold_top: log.info('TIME TO LONG VOLATILITY! SENDING ORDER FOR VXX! Vix is too low for a future ratio which is near enough at backwardation') order_target_percent(context.VXX, 1) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 0) elif data.can_trade(context.VXX) and data.can_trade(context.XIV) and current_spy_price < ma200: log.info('SPY is below 200MA indicating short momentum therefore we will switch to gold') order_target_percent(context.VXX, 0) order_target_percent(context.XIV, 0) order_target_percent(context.LQD, 0) order_target_percent(context.GLD, 1) record(XIV_exposure=context.portfolio.positions[symbol('XIV')].amount) record(VXX_exposure=context.portfolio.positions[symbol('VXX')].amount) #record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount) record(VIX=context.vix) record(ratio=last_ratio) #record(leverage=context.leverage) There was a runtime error.$1.8M margin. Usually partial fills. Try track_orders and ...

def initialize(context):
context.cash_low = context.portfolio.cash

def handle_data(context, data):
if context.portfolio.cash < context.cash_low:
context.cash_low = context.portfolio.cash
record(cash_low = context.cash_low)    # Record lowest cash encountered


Nikhil, you switched to using VIX close, and I think that is close of the current day (so look ahead bias) instead of the previous day. I think it is only the VX1/VX2 that is shifted by a day, not the VIX. But..this is still the best of the VIX/XIV I have seen on Q as far as balanced good reward with <30% drawdown -- or did I miss a thread? --there happens to be one or two of them ;-)

One key thing I dug in to this weekend a bit was the lack of an "else" statement on the XIV vs. VIX decision. Specifically: I started looking at trading this live and it didn't match the back test. That's because the decision to buy XIV happened back on March 1/2 and then we drifted in to VIX_BOT < VIX < VIX_TOP which won't force another purchase/re-balance....so if you trade live you have to wait for a signal (which could take a while) or force the purchase. I think the if statements can be redone to determine the course of action (which maybe will yield even more ?), but I didn't dive in to that yet.

Oops, sorry just noticed the VIX close vs. open thing has been around a while...I'm a bit confused by that part of the API, I need to add some logging in there to understand which one we are getting.

@Kevin,

The only missing logic that I've seen is VIX in range and threshold > .95, when i added this into the backtest the performance dropped a good amount, so it seems to perform better by just holding on to the past position, until something is triggered. Friday it triggered a buy into bonds or cash, given VIX < 12.55 and Ratio > .95

Also, there was an issue when that order went through Interactive Brokers, the position size was never returned, so their might be issues with the integration too, which is basically making quantopian utterly useless at backtesting, paper trading, and live trading.

Best suggestion is code it up directly in Python and send it through the IB API directly.

Wow, yep, just holding the last decision definitely seems the way to go. I'm coding it up and talking to IB API directly for now until we get spot VIX and maybe if the futures works out easy to use and reliable.

Is there any way to trick the backtest to pull VIX open from the "current" day? I see now that it is indeed VIX close from the previous day, but it is by design and no shifting is being done here in this algo...It would be nice if we could shift the data and pull the VIX the open to the decision , but I can't seem to figure that out. I realize that definitely doesn't live trade, but it would be nice to confirm that the behavior improves / stays the same / in the back test.

@Kevin,

I think if you read a lot of replies above, Qi confirmed that opening VIX works better. So basically:

For Tomorrows Signal:

Today VX1 Closing
Today VX2 Closing
Today VIX Open

Also, the original algo with this code is already pulling the open:

y_pipe.add(GetVIX(inputs=[cboe_vix.vix_open]), 'VixOpen')


To clarify it's basically yesterdays closing data, besides for vix it's yesterdays opening price.

I took at stab at using Open for VIX and VX1/VX2...and hopefully I just really messed up somewhere, because performance dropped pretty bad.

Since I'm using "Open" for all of them I removed the shift...There are minor tweaks were I moved to cash instead of other equities....hopefully I have a bug that I just haven't found yet..

20
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
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.data.quandl import cboe_vix

import numpy as np
import pandas as pd

def rename_col(df):
df = df.rename(columns={'Open': 'price','Trade Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['price', 'Settle','sid']]
# Shifting data by one day to avoid forward-looking bias
#return df.shift(1)
print(df.tail())
#DO NOT SHIFT THE DATA IN LIVE TRADING
#return df.shift(1)
return df

def rename_col_vix(df):
df = df.rename(columns={'VIX Open': 'Open', 'Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['Open', 'sid']]
# Shifting data by one day to avoid forward-looking bias
#return df.shift(1)
print(df.tail())
#DO NOT SHIFT THE DATA IN LIVE TRADING
#return df.shift(1)
return df

def initialize(context):

set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35))
context.leverage = 1
"""
Called once at the start of the algorithm.
"""
#securities we are investing in
context.SPY = symbol('SPY')
context.XIV = symbol('XIV')
context.VXX = symbol('VXX')
context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns)
context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold

context.vix_history = []
context.history_len = 30

# REMOVE -- old pipeline code
#my_pipe = Pipeline()
#attach_pipeline(my_pipe, 'my_pipeline')
# was 2 for upper...or maybe use some other number to make sure Upper is some minimum upper threshold

# Front month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',
date_format='%Y-%m-%d',
symbol='v1',
post_func=rename_col)
# Second month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv',
date_format='%Y-%m-%d',
symbol='v2',
post_func=rename_col)

# Second month VIX futures data
fetch_csv('https://www.quandl.com/api/v3/datasets/CBOE/VIX.csv',
date_column='Date',
date_format='%Y-%m-%d',
symbol='vix',
post_func=rename_col_vix)

# Rebalance every day, 1 minute after market open.
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2))
# wait some time to make sure rebalance is done
#schedule_function(my_stops, date_rules.every_day(), time_rules.market_open(minutes=4))

schedule_function(record_leverage, date_rules.every_day())

def record_leverage(context, data):
record(leverage = context.account.leverage)

"""
Called every day before market open.
"""
# OLD pipeline code was here
return
#context.output = pipeline_output('my_pipeline')
#context.vix = context.output["VixClose"].iloc[0]
#context.std = context.output["Std"].iloc[0]

#context.vix_top = 21
#context.vix_bot = 12.5
#context.vix_top = context.output['Upper'].iloc[0]
# used for XIV
#context.vix_bot = context.output['Lower'].iloc[0]
# used for VXX
#context.vix_bot2 = 12.5

'''
if context.vix_top < 21:
context.vix_top = context.vix_top
else:
context.vix_top = 21

if context.vix_bot > 12.5:
context.vix_bot = context.vix_bot
else:
context.vix_bot = 12.5
'''

days_for_ma = 30

class StdDev(CustomFactor):
window_length = days_for_ma
def compute(self,today,assets,out,values):
out[:] = np.nanstd(values,axis=0)

class MA30(CustomFactor):
window_length = days_for_ma
def compute(self,today,assets,out,values):
out[:] = np.nanmean(values,axis=0)

class GetVIX(CustomFactor):
window_length = 1
def compute(self, today, assets, out, vix):
out[:] = vix[-1]

def my_stops(context, data):
for security, position in context.portfolio.positions.items():
price = position.cost_basis
stop_price = price * 0.90
print("STOP Placed at %f"%stop_price)
order_target_percent(security, 0, style=StopOrder(stop_price))

def my_rebalance(context,data):
"""
Execute orders according to our schedule_function() timing.
"""
last_ratio = data.current('v1','price')/data.current('v2','price')
vix_price = data.current('vix','Open')
print(vix_price)
context.vix_history.append(vix_price)
# history not full enough return
if len(context.vix_history)<=context.history_len:
return
# remove top entry
context.vix_history.pop(0)
vix_df = pd.DataFrame(context.vix_history)

vix_mean = vix_df[0].mean()
print("Vix Mean: %f"%vix_mean)
vix_std = vix_df[0].std()

threshold_bot = 0.90
threshold_top = 0.95

vix_top = vix_mean + 4*vix_std
vix_bot = vix_mean - 2*vix_std
vix_top = 21
vix_bot = 12.5
context.vix = vix_price
# using just on VXX trade did not help
#vix_bot2 = context.vix_bot2

spy_price_history = data.history(context.SPY, "price", 200, "1d")
ma200 = spy_price_history.mean()
current_spy_price = data.current(context.SPY, 'price')

log.info("VIX        : {:f}".format(context.vix))
log.info("VIX_TOP    : {:f}".format(vix_top))
log.info("VIX_BOT    : {:f}".format(vix_bot))
log.info("VX1        : {:f}".format(data.current('v1','price')))
log.info("VX2        : {:f}".format(data.current('v2','price')))
log.info("LAST_RATIO : {:f}".format(last_ratio))

if current_spy_price < ma200:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
return

#VIX > 21 & High Backwardation Chance Ratio >=95
if context.vix >= vix_top and last_ratio >= threshold_top:
#log.info('VIX is above 21 and futures ratio is near enough at backwardation, liquidating all positions......')
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
log.info("GO: SAFE")

# VIX > 21 & steady contango Ratio < 95
#vix is high and we are not in a situation in which the things are turning bad,
elif context.vix >= vix_top and last_ratio < threshold_top:
#log.info('SENDING ORDER FOR XIV! VIX is above 21 and future ratio is below 0.95')
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 1)
log.info("GO: XIV")

# VIX 12.5-21 High Contango Ratio < .90
#vix is in his standard range and we are in contango
elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot:
#elif context.vix < vix_top and last_ratio < threshold_bot:
#log.info('SENDING AGGRESSIVE ORDER FOR XIV! VIX is ranging from 12.5 - 21 and future ratio is in comfortable contango')
log.info("GO: XIV+")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns

# VIX 12.5-21 Normal Backwardation Risk range .90 - .95
#vix is in his standard range but we are not in contango, we wait switching to bonds
elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top:
#log.info('SWITCHING TO SAFE HAVEN ASSET! We dont know what the F is going on')
log.info("GO: SAFE")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)

# VIX < 12.5 and ratio range 90-95
#between backwardation and extreme contango
elif context.vix < vix_bot and last_ratio < threshold_top and last_ratio >= threshold_bot:
#log.info('SWITCHING TO SAFE HAVEN ASSET')
log.info("GO: SAFE")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)

#VIX < 12.5 High Contango Ratio < .90
elif context.vix < vix_bot and last_ratio < threshold_bot:
#log.info('SENDING ORDER FOR GOLD!')
log.info("GO: SAFE")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)

# VIX < 12.5 & High Backwardation Risk Ratio > .95
#vix is very low and we are not in contango, we suppose vix will raise
# CHANGED FROM GOLD POS TO VXX POS
# elif context.vix < vix_bot and last_ratio > threshold_top:
elif context.vix < vix_bot and last_ratio > threshold_bot: # had same results whether we did from bot or top
#log.info('TIME TO LONG VOLATILITY! SENDING ORDER FOR VXX! Vix is too low for a future ratio which is near enough at backwardation')
log.info("GO: XVV")
order_target_percent(context.VXX, 1)
order_target_percent(context.XIV, 0)

else:
log.info("GO: STAY or SAFE")

record(XIV_exposure=(context.portfolio.positions[symbol('XIV')].amount/context.portfolio.portfolio_value))
record(VXX_exposure=(context.portfolio.positions[symbol('VXX')].amount/context.portfolio.portfolio_value))
#record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount)
record(VIX=context.vix)
record(ratio=last_ratio)
#record(leverage=context.leverage)
There was a runtime error.

@Kevin

You are supposed to use closing VIX futures, the only opening price is for the VIX.

@Elsid

Yes, I am just surprised that using the more recent data would perform worse instead of better...But indeed that is the case. Using the latest VIX is good (attached) but using the "most recent" for VX1 and VX2 seems bad.

20
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
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.data.quandl import cboe_vix

import numpy as np
import pandas as pd

def rename_col(df):
df = df.rename(columns={'Settle': 'price','Trade Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['price', 'sid']]
# Shifting data by one day to avoid forward-looking bias
#return df.shift(1)
print(df.tail())
#DO NOT SHIFT THE DATA IN LIVE TRADING
return df.shift(1)
#return df

def rename_col_vix(df):
df = df.rename(columns={'VIX Open': 'Open', 'Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['Open', 'sid']]
# Shifting data by one day to avoid forward-looking bias
#return df.shift(1)
print(df.tail())
#DO NOT SHIFT THE DATA IN LIVE TRADING
#return df.shift(1)
return df

def initialize(context):

set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35))
context.leverage = 1
"""
Called once at the start of the algorithm.
"""
#securities we are investing in
context.SPY = symbol('SPY')
context.XIV = symbol('XIV')
context.VXX = symbol('VXX')
context.LQD = symbol('SPY') #Alternative asset when we are waiting for XIV or VXX (HYG is an alternative to lower dd by a little bit, SPY higher beta and returns)
context.GLD = symbol('MINT') #Safe Haven when the market is "crazy" - Gold

context.vix_history = []
context.history_len = 30

# REMOVE -- old pipeline code
#my_pipe = Pipeline()
#attach_pipeline(my_pipe, 'my_pipeline')
# was 2 for upper...or maybe use some other number to make sure Upper is some minimum upper threshold

# Front month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',
date_format='%Y-%m-%d',
symbol='v1',
post_func=rename_col)
# Second month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv',
date_format='%Y-%m-%d',
symbol='v2',
post_func=rename_col)

# Second month VIX futures data
fetch_csv('https://www.quandl.com/api/v3/datasets/CBOE/VIX.csv',
date_column='Date',
date_format='%Y-%m-%d',
symbol='vix',
post_func=rename_col_vix)

# Rebalance every day, 1 minute after market open.
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2))
# wait some time to make sure rebalance is done
#schedule_function(my_stops, date_rules.every_day(), time_rules.market_open(minutes=4))

schedule_function(record_leverage, date_rules.every_day())

def record_leverage(context, data):
record(leverage = context.account.leverage)

"""
Called every day before market open.
"""
# OLD pipeline code was here
return
#context.output = pipeline_output('my_pipeline')
#context.vix = context.output["VixClose"].iloc[0]
#context.std = context.output["Std"].iloc[0]

#context.vix_top = 21
#context.vix_bot = 12.5
#context.vix_top = context.output['Upper'].iloc[0]
# used for XIV
#context.vix_bot = context.output['Lower'].iloc[0]
# used for VXX
#context.vix_bot2 = 12.5

'''
if context.vix_top < 21:
context.vix_top = context.vix_top
else:
context.vix_top = 21

if context.vix_bot > 12.5:
context.vix_bot = context.vix_bot
else:
context.vix_bot = 12.5
'''

days_for_ma = 30

class StdDev(CustomFactor):
window_length = days_for_ma
def compute(self,today,assets,out,values):
out[:] = np.nanstd(values,axis=0)

class MA30(CustomFactor):
window_length = days_for_ma
def compute(self,today,assets,out,values):
out[:] = np.nanmean(values,axis=0)

class GetVIX(CustomFactor):
window_length = 1
def compute(self, today, assets, out, vix):
out[:] = vix[-1]

def my_stops(context, data):
for security, position in context.portfolio.positions.items():
price = position.cost_basis
stop_price = price * 0.90
print("STOP Placed at %f"%stop_price)
order_target_percent(security, 0, style=StopOrder(stop_price))

def my_rebalance(context,data):
"""
Execute orders according to our schedule_function() timing.
"""
last_ratio = data.current('v1','price')/data.current('v2','price')
vix_price = data.current('vix','Open')
print(vix_price)
context.vix_history.append(vix_price)
# history not full enough return
if len(context.vix_history)<=context.history_len:
return
# remove top entry
context.vix_history.pop(0)
vix_df = pd.DataFrame(context.vix_history)

vix_mean = vix_df[0].mean()
print("Vix Mean: %f"%vix_mean)
vix_std = vix_df[0].std()

threshold_bot = 0.90
threshold_top = 0.95

#vix_top = vix_mean + 4*vix_std
vix_bot = vix_mean - 2*vix_std
vix_top = 21
#vix_bot = 12.5
context.vix = vix_price
# using just on VXX trade did not help
#vix_bot2 = context.vix_bot2

spy_price_history = data.history(context.SPY, "price", 200, "1d")
ma200 = spy_price_history.mean()
current_spy_price = data.current(context.SPY, 'price')

log.info("VIX        : {:f}".format(context.vix))
log.info("VIX_TOP    : {:f}".format(vix_top))
log.info("VIX_BOT    : {:f}".format(vix_bot))
log.info("VX1        : {:f}".format(data.current('v1','price')))
log.info("VX2        : {:f}".format(data.current('v2','price')))
log.info("LAST_RATIO : {:f}".format(last_ratio))

if current_spy_price < ma200:
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
return

#VIX > 21 & High Backwardation Chance Ratio >=95
if context.vix >= vix_top and last_ratio >= threshold_top:
#log.info('VIX is above 21 and futures ratio is near enough at backwardation, liquidating all positions......')
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)
log.info("GO: SAFE")

# VIX > 21 & steady contango Ratio < 95
#vix is high and we are not in a situation in which the things are turning bad,
elif context.vix >= vix_top and last_ratio < threshold_top:
#log.info('SENDING ORDER FOR XIV! VIX is above 21 and future ratio is below 0.95')
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 1)
log.info("GO: XIV")

# VIX 12.5-21 High Contango Ratio < .90
#vix is in his standard range and we are in contango
elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot:
#elif context.vix < vix_top and last_ratio < threshold_bot:
#log.info('SENDING AGGRESSIVE ORDER FOR XIV! VIX is ranging from 12.5 - 21 and future ratio is in comfortable contango')
log.info("GO: XIV+")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 1 * context.leverage) #a valuable option is to leverage here in order to achieve higher returns

# VIX 12.5-21 Normal Backwardation Risk range .90 - .95
#vix is in his standard range but we are not in contango, we wait switching to bonds
elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top:
#log.info('SWITCHING TO SAFE HAVEN ASSET! We dont know what the F is going on')
log.info("GO: SAFE")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)

# VIX < 12.5 and ratio range 90-95
#between backwardation and extreme contango
elif context.vix < vix_bot and last_ratio < threshold_top and last_ratio >= threshold_bot:
#log.info('SWITCHING TO SAFE HAVEN ASSET')
log.info("GO: SAFE")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)

#VIX < 12.5 High Contango Ratio < .90
elif context.vix < vix_bot and last_ratio < threshold_bot:
#log.info('SENDING ORDER FOR GOLD!')
log.info("GO: SAFE")
order_target_percent(context.VXX, 0)
order_target_percent(context.XIV, 0)

# VIX < 12.5 & High Backwardation Risk Ratio > .95
#vix is very low and we are not in contango, we suppose vix will raise
# CHANGED FROM GOLD POS TO VXX POS
# elif context.vix < vix_bot and last_ratio > threshold_top:
elif context.vix < vix_bot and last_ratio > threshold_bot: # only enough had same results whether we did from bot or top
#log.info('TIME TO LONG VOLATILITY! SENDING ORDER FOR VXX! Vix is too low for a future ratio which is near enough at backwardation')
log.info("GO: XVV")
order_target_percent(context.VXX, 1)
order_target_percent(context.XIV, 0)

else:
log.info("GO: STAY or SAFE")

record(XIV_exposure=(context.portfolio.positions[symbol('XIV')].amount/context.portfolio.portfolio_value))
record(VXX_exposure=(context.portfolio.positions[symbol('VXX')].amount/context.portfolio.portfolio_value))
#record(LQD_exposure=context.portfolio.positions[symbol('LQD')].amount)
record(VIX=context.vix)
record(ratio=last_ratio)
#record(leverage=context.leverage)
There was a runtime error.

I guess the better performance of using "old" values might be due to the small amount of mean reversion of VIX ETFs (or even SPY) after a relatively big price movement. That can be partly observed when you see positive expected returns from short term mean reversion algos like RSI2 on XIV or SPY. The next question is that do you want to utilize such "mean-reversion" in your trend-following strategies?

@Maxim,

Were you able to place orders with the .csv? Should of placed an order for Treasuries last week.

@Elsid,
I update the .csv file each night manually and it looks like the algo works fine in live mode.
It's currently in SHV (since 03/29), that's in line with the algo logic (low contango and low vix)

@maxim, is your current google doc csv still in this format, and are you utilizing the last available backtest? https://docs.google.com/spreadsheets/d/1z0a9JoR5FVO8SxzHBPdQxhNKS00gqj9PdCF8TjADiuQ/pub?output=csv

@Jay
I am using the google sheet you 're referring to in my live trading only. It's not in the last available backtest

Fairly awful. Implemented this in R.

gldSig <- lag(Cl(VIX) >= vixTop & c1c2ratio >= threshTop, dayLag)
vxxSig <- lag(Cl(VIX) <= vixBot & c1c2ratio > threshBot, dayLag)
xivSig <- lag(Cl(VIX) >= vixTop & c1c2ratio < threshTop, dayLag)

# this uses leverage of 3 to 1

xivSig2 <- lag(Cl(VIX) > vixBot & Cl(VIX) < vixTop & c1c2ratio < threshBot, dayLag) * 3

lqdSig <- lag(Cl(VIX) > vixBot & Cl(VIX) < vixTop & c1c2ratio > threshBot & c1c2ratio < threshTop, dayLag)

# uses leverage of 1/2 to 1 (I.E. only 50% invested)

lqdSig2 <- lag(Cl(VIX) < vixBot & c1c2ratio < threshBot, dayLag) * .5

Using a dayLag of 1 (magical thinking--observe the close, buy the close), I get a max drawdown of 70% in the crisis using simulated XIV/VXX data. If I use a more conservative dayLag = 2, that drawdown goes to 80.

Those drawdowns are absurd.

They are not comfortable that's for sure. But as you know I have recently been talking about Bill Dunn and JWH. Dunn closed a program with a DD of 71% and yet it ran for years and made him a zillionaire. Not sure what it did for clients but his surviving program has a cagr of 13% over 40 years after fees. And a 51% drawdown.

At least theoretically these vix schemes have a higher MAR. And if you only invest twopence hapenny you are not going to be unduly worried if it comes unstuck.

Yes Anthony I agree.I read the article you posted on Trend Following, and it being re-adjusted with longer time-frames.But the new system still showed a drawdown period of 16 months.Probably too long for most investors.

I caution everyone using algos like this that use hard coded values based on history. Very over optimized - especially since we just made new all time lows in the VIX

Manual backtest from 2004 to present - algorithm was flat until June 2010 before making any returns... Overfitting

Check the performance of the Algorithm since about April 9, 2017 to now... over 1100% percent has been wiped out and barely any profitable trades made.
From the Custom Data, I can see that VXX Exposure hits all time highs at that point, and keeps those high levels longer than ever before. What do you think changed?

Over fitting... volatility hit a new all time low, which is why the algo was long vxx because it expected it to rise since it was below a certain threshold that worked from 2010-april 2017.

Ah, makes sense.

Slightly off topic: I think I can get the current VIX price through the pipeline...

from quantopian.pipeline.data.quandl import cboe_vix
...
class GetVIX(CustomFactor):
window_length = 1
def compute(self, today, assets, out, vix):
out[:] = vix[-1]
...
context.output = pipeline_output('my_pipeline')
context.vix = context.output["VixClose"].iloc[0]


...but is there an easy way to get the previous few days' VIX prices without having to fetch a CSV from somewhere? I could add the value from the pipeline to a list everyday, but then the algorithm would have to wait a few days before using it. I'm still fairly new, and the pipeline confuses me, but I think I'm asking the question correctly.

Thanks

Or, should I be doing whatever calculation I need the historic VIX prices for inside of a custom pipeline factor? Then I think I could just make the window_length however far back I need.

EDIT:
Yes

This is what I was able to create using this algo as a base. I'm going to go in and clean up my code. If you have any questions let me know.

162
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
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.data.quandl import cboe_vix

import numpy as np
import pandas as pd

def rename_col(df):
df = df.rename(columns={'Close': 'price','Trade Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['price', 'Settle','sid']]
# Shifting data by one day to avoid forward-looking bias
return df.shift(1)

def initialize(context):

set_commission(commission.PerShare(cost = 0.0035, min_trade_cost = .35))
context.leverage = 1.96
"""
Called once at the start of the algorithm.
"""
#securities we are investing in

context.XIV = symbol('XIV')
context.VXX = symbol('VXX')
context.SHY = symbol('LQD')
context.SPY = symbol('SPY')

my_pipe = Pipeline()
attach_pipeline(my_pipe, 'my_pipeline')

# Front month VIX futures data
fetch_csv('https://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv?api_key=kBrKnGzVAWhbLJvGu-NB',
date_format='%Y-%m-%d',
symbol='v1',
post_func=rename_col)
# Second month VIX futures data
fetch_csv('https://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv?api_key=kBrKnGzVAWhbLJvGu-NB',
date_format='%Y-%m-%d',
symbol='v2',
post_func=rename_col)

# Rebalance every day, 1 minute after market open.
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(minutes=2))

schedule_function(record_leverage, date_rules.every_day())

def record_leverage(context, data):
record(leverage = context.account.leverage)

"""
Called every day before market open.
"""
context.output = pipeline_output('my_pipeline')
context.vix = context.output["VixOpen"].iloc[0]

class GetVIX(CustomFactor):
window_length = 1
def compute(self, today, assets, out, vix):
out[:] = vix[-1]

def my_rebalance(context,data):
"""
Execute orders according to our schedule_function() timing.
"""
last_ratio = data.current('v1','Settle')/data.current('v2','Settle')
threshold_bot = 0.9
threshold_top = 0.95
ex_top = 1.1
ex_bot = .856
vr_top = 19.95
vr_bot = 12
vix_top = 24
vix_bot = 12.25

#VIX Opens above 24 and extreme backwardization = VXX w/ Leverage

if context.vix > vix_top and last_ratio > ex_top:
order_target_percent(context.XIV, 0)
order_target_percent(context.VXX, 1)#lev
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

#VIX Opens above 24 and backwardization = Cash

elif context.vix >= vix_top and last_ratio >= threshold_top:
order_target_percent(context.XIV, 0)
order_target_percent(context.VXX, 0)
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

elif context.vix <= vix_bot and context.vix >= vr_bot and last_ratio > threshold_bot:
order_target_percent(context.XIV, 0)
order_target_percent(context.VXX, 0)
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

elif context.vix > vr_top and last_ratio > threshold_bot and last_ratio < threshold_top:
order_target_percent(context.XIV, .5)#lev
order_target_percent(context.VXX, .25)#
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

#vix is very low and we are not in contango, we suppose vix will raise
elif context.vix <= vix_bot and last_ratio < ex_bot:
order_target_percent(context.XIV, 0)
order_target_percent(context.VXX, 1)#
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

#vix is high and we are not in a situation in which the things are turning bad
elif context.vix >= vix_top and last_ratio < threshold_top:
order_target_percent(context.XIV, 1)#lev
order_target_percent(context.VXX, 0)
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

#vix is in his standard range and we are in contango
elif context.vix > vix_bot and context.vix < vix_top and last_ratio < threshold_bot:
order_target_percent(context.XIV, 1)#lev
order_target_percent(context.VXX, 0)
order_target_percent(context.SHY, 0)
order_target_percent(context.SPY, 0)

#vix is in his standard range but we are not in contango, we wait switching to bonds
elif context.vix > vix_bot and context.vix < vix_top and last_ratio > threshold_bot and last_ratio < threshold_top:
order_target_percent(context.XIV, 0)
order_target_percent(context.VXX, 0)
order_target_percent(context.SHY, 1)#?lev
order_target_percent(context.SPY, 0)

#when there is conflict between signals we switch to bonds
elif context.vix < vix_bot and last_ratio < threshold_bot:
order_target_percent(context.XIV, 0)
order_target_percent(context.VXX, 0)
order_target_percent(context.SHY, 1)#
order_target_percent(context.SPY, 0)

record(XIV=context.portfolio.positions[symbol('XIV')].amount)
record(LQD=context.portfolio.positions[symbol('LQD')].amount)
record(VXX=context.portfolio.positions[symbol('VXX')].amount)

#record(Cash=context.portfolio.cash)
#record(VIX=context.vix)
#record(ratio=last_ratio)    
There was a runtime error.

Has someone tried using this by running the algo at regular intervals instead of only once a day?
I'm wondering of trying this at frequent intervals by getting all the data real time and running the strategy on a real time basis. Let me know if someone has some lead on this.