Shorting VXX based on VIX Future's contango

The code and idea is quite simple, the algorithm pulls daily settlement prices for VIX front month and second month futures and calculates the ratio of the Front Month/Second Month. If this ratio is below a certain threshold, in my case I chose 0.90, short VXX, otherwise close out all positions and move to cash. I'm testing this strategy and the results look really nice, but I'm hoping for input to help reduce the drawdown. Also, if anyone has spotted errors in my code or any biases, please let me know!

358
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
# Algorithm uses front month and second month settlement prices for VIX futures to
# determine whether or not to short the ETF VXX. Current threshold is 0.95 representing
# the percentage contango of F1/F2. If the settlements F1/F2 is below 0.95, short VXX,
# otherwise move to cash.
import datetime
import numpy as np
import pandas as pd

# Post Function for fetch_csv where futures data from Quandl is standardized
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):
# Pulling 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)
# Pulling 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)
# Declaring VXX as the stock for shorting
context.stock = sid(38054)
# Scheduling the order function to occur everyday 5 minutes after market open
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours = 0, minutes = 5))

def my_rebalance(context, data):
# Calculating the contango ratio of the front and second month VIX Futures settlements
last_ratio = data.current('v1','Settle')/data.current('v2','Settle')

# Specifying the contango ratio threshold to short VXX
threshold = 0.90
log.info(context.portfolio.cash)
if last_ratio < threshold:
if context.stock in context.portfolio.positions:
else:
# Calculating the number of shares of VXX to short to reach the target
# portfolio percentage allocation
target_pct = 1
target_mv = context.portfolio.portfolio_value*target_pct
shares = np.floor(target_mv/data.current(context.stock,'price'))
order(context.stock,-shares)
log.info("Short VXX - %.2f" %last_ratio)
record(shares=shares)
else:
# If the contango ratio is above specified threshold, purchase back VXX shares and remain in cash
order_target_percent(context.stock,0)
log.info("Move to cash - %.2f" %last_ratio)
record(ratio=last_ratio)

There was a runtime error.
75 responses

If instead of going to cash you invest in SPY then the total return doubles and the max drawdown drops to 20%. However, I am no programmer and I may have made a mistake. If you can double check that and confirm, and if it indeed checks out, then you can simply hold half the portfolio in cash and invest the rest in the modified strategy (which now goes to SPY instead of cash), get the same 10x return over 7 years, and get a drawdown of 10%.

However, just buying and holding XIV will easily outperform this algorithm.

Hi Macro Investor, thanks for your input!

To reply to your first suggestion, the reason I'm using the VIX term structure as a signal is also because in normal market conditions, VIX futures are in contango. When the VIX term structure begins to backwardate, it is normally correlated with bearish market movements, in which case, I do not believe moving to SPY to be safe when the F1/F2 > 0.90 or 0.95.

As well, I don't think a simple buy and hold strategy for XIV performs too well given how volatile the instrument is. I've ran a backtest using XIV as the benchmark to compare against the short VXX strategy proposed and included it here; it does significantly outperform XIV. In addition, I've tested rather than shorting VXX to just long XIV using the same signal, however, I believe that because of the structure difference between VXX and XIV (they are not direct inverses of each other or correlation != -1), shorting VXX performs better and allows for more robust signals.

One thing I'm looking for is to perhaps reduce the whipsaw that occurs when the VIX Futures term structure hovers around the .90 - .95 mark by possibly an indicator based on a different instrument.

358
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
# Algorithm uses front month and second month settlement prices for VIX futures to
# determine whether or not to short the ETF VXX. Current threshold is 0.95 representing
# the percentage contango of F1/F2. If the settlements F1/F2 is below 0.95, short VXX,
# otherwise move to cash.
import datetime
import numpy as np
import pandas as pd

# Post Function for fetch_csv where futures data from Quandl is standardized
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):
# Pulling 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)
# Pulling 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)
# Declaring VXX as the stock for shorting
context.stock = sid(38054)
set_benchmark(sid(40516))
# Scheduling the order function to occur everyday 5 minutes after market open
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours = 0, minutes = 5))

def my_rebalance(context, data):
# Calculating the contango ratio of the front and second month VIX Futures settlements
last_ratio = data.current('v1','Settle')/data.current('v2','Settle')

# Specifying the contango ratio threshold to short VXX
threshold = 0.95
log.info(context.portfolio.cash)
if last_ratio < threshold:
if context.stock in context.portfolio.positions:
else:
# Calculating the number of shares of VXX to short to reach the target
# portfolio percentage allocation
target_pct = 1
target_mv = context.portfolio.portfolio_value*target_pct
shares = np.floor(target_mv/data.current(context.stock,'price'))
order(context.stock,-shares)
log.info("Short VXX - %.2f" %last_ratio)
record(shares=shares)
else:
# If the contango ratio is above specified threshold, purchase back VXX shares and remain in cash
order_target_percent(context.stock,0)
log.info("Move to cash - %.2f" %last_ratio)
record(ratio=last_ratio)

There was a runtime error.

Hi Macro Investor, thanks for your input!

To reply to your first suggestion, the reason I'm using the VIX term structure as a signal is also because in normal market conditions, VIX futures are in contango. When the VIX term structure begins to backwardate, it is normally correlated with bearish market movements, in which case, I do not believe moving to SPY to be safe when the F1/F2 > 0.90 or 0.95.

As well, I don't think a simple buy and hold strategy for XIV performs too well given how volatile the instrument is. I've ran a backtest using XIV as the benchmark to compare against the short VXX strategy proposed and included it here; it does significantly outperform XIV. In addition, I've tested rather than shorting VXX to just long XIV using the same signal, however, I believe that because of the structure difference between VXX and XIV (they are not direct inverses of each other or correlation != -1), shorting VXX performs better and allows for more robust signals.

One thing I'm looking for is to perhaps reduce the whipsaw that occurs when the VIX Futures term structure hovers around the .90 - .95 mark by possibly an indicator based on a different instrument.

358
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
# Algorithm uses front month and second month settlement prices for VIX futures to
# determine whether or not to short the ETF VXX. Current threshold is 0.95 representing
# the percentage contango of F1/F2. If the settlements F1/F2 is below 0.95, short VXX,
# otherwise move to cash.
import datetime
import numpy as np
import pandas as pd

# Post Function for fetch_csv where futures data from Quandl is standardized
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):
# Pulling 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)
# Pulling 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)
# Declaring VXX as the stock for shorting
context.stock = sid(38054)
set_benchmark(sid(40516))
# Scheduling the order function to occur everyday 5 minutes after market open
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours = 0, minutes = 5))

def my_rebalance(context, data):
# Calculating the contango ratio of the front and second month VIX Futures settlements
last_ratio = data.current('v1','Settle')/data.current('v2','Settle')

# Specifying the contango ratio threshold to short VXX
threshold = 0.95
log.info(context.portfolio.cash)
if last_ratio < threshold:
if context.stock in context.portfolio.positions:
else:
# Calculating the number of shares of VXX to short to reach the target
# portfolio percentage allocation
target_pct = 1
target_mv = context.portfolio.portfolio_value*target_pct
shares = np.floor(target_mv/data.current(context.stock,'price'))
order(context.stock,-shares)
log.info("Short VXX - %.2f" %last_ratio)
record(shares=shares)
else:
# If the contango ratio is above specified threshold, purchase back VXX shares and remain in cash
order_target_percent(context.stock,0)
log.info("Move to cash - %.2f" %last_ratio)
record(ratio=last_ratio)

There was a runtime error.

If you run the backtest using the same time frame that you used in the original you would see that XIV buy and hold outperforms. I will clone your algorithm and post how switching to SPY instead of cash improves results.

I attached the results of the backtest. Instead of putting to cash I put it in UPRO which is triple leveraged SPY. The return triple and the max drawdown goes to 50%. So you can now increase cash percentage to lower the drawdown while still beating the original.

102
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
# Algorithm uses front month and second month settlement prices for VIX futures to
# determine whether or not to short the ETF VXX. Current threshold is 0.95 representing
# the percentage contango of F1/F2. If the settlements F1/F2 is below 0.95, short VXX,
# otherwise move to cash.
import datetime
import numpy as np
import pandas as pd

# Post Function for fetch_csv where futures data from Quandl is standardized
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):
# Pulling 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)
# Pulling 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)
# Declaring VXX as the stock for shorting
context.stock = sid(38054)
context.spy = sid(38533)
# Scheduling the order function to occur everyday 5 minutes after market open
schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours = 0, minutes = 5))

# set_benchmark(sid(40516))

def my_rebalance(context, data):
# Calculating the contango ratio of the front and second month VIX Futures settlements
last_ratio = data.current('v1','Settle')/data.current('v2','Settle')

# Specifying the contango ratio threshold to short VXX
threshold = 0.90
log.info(context.portfolio.cash)
if last_ratio < threshold:
if context.stock in context.portfolio.positions:
log.info("Already in vxx - %.2f" %last_ratio)
else:
order_target_percent(context.spy,0)
# Calculating the number of shares of VXX to short to reach the target
# portfolio percentage allocation
target_pct = 1
target_mv = context.portfolio.portfolio_value*target_pct
shares = np.floor(target_mv/data.current(context.stock,'price'))
order(context.stock,-shares)
log.info("Short VXX - %.2f" %last_ratio)
record(shares=shares)
else:
# If the contango ratio is above specified threshold, purchase back VXX shares and invest in SPY
if context.spy in context.portfolio.positions:
log.info("Already in spy - %.2f" %last_ratio)
else:
order_target_percent(context.stock,0)
# Calculating the number of shares of VXX to short to reach the target
# portfolio percentage allocation
target_pct = 1
target_mv = context.portfolio.portfolio_value*target_pct
shares = np.floor(target_mv/data.current(context.spy,'price'))
order(context.spy,shares)
log.info("long spy - %.2f" %last_ratio)
record(shares=shares)
record(ratio=last_ratio)

There was a runtime error.

In line 41 where you have:

last_ratio = data.current('v1','Settle')/data.current('v2','Settle')


Do you have look-ahead bias here? Today's data won't be available until trading ends. Or I get it wrong in how this thing works?

Thanks.

HP Peng,

Does line 14-15 affect this?

    # Shifting data by one day to avoid forward-looking bias
return df.shift(1)


Hi all,

I tried testing this algo in live trading, but the logs show NaN for the calculated contango ratio. Algo works as expected and logs show real values during backtesting. Any idea what is going on?

Hi Ben Shyong/HP Peng,

The look-ahead bias is correct from line 14-15 with the shift downwards; the rebalancing function will always be looking at t-1 contango ratios.

The NaN may be from the Quandl data because futures data is delayed by one day, e.g., the latest settlement prices would be for 05/18/2016 (today being 05/20/2016). I would double check your live-trading outputs to make sure data is available. vixcentral has better data for live trading.

Hope this helps and thanks for the interests!

Cheers

I had the same problem and there's really no fix as far as I can see. We have to wait for live futures data in Quantopian, which will hopefully come second half of the year.

You need to shift data to prevent look-ahead bias in backtesting, and ALSO forward fill the latest value to the next expected valid trading date, to have a value during live trading. There is code to do this here: https://www.quantopian.com/posts/function-like-history-but-for-fetch-csv-data

Note that that is Q1 code, so it'll probably need to be updated to Q2.

Thanks Simon! I'll take a look and see if I can get it to work.

instead of futures set values, have you considered looking at the previous close of vix/vxv to calculate the ratio?

Hi Cameron, I have actually tested VIX/VXV to calculate the ratio, but it is more volatile and the DD is higher as well. I set the threshold with 0.95 as above, but I haven't played around with optimizing the ratio. Here's the results for the same time period as my initial entry.

50
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
import pandas

def rename_col(df):
df = df.fillna(method='ffill')
df = df[['price', 'sid', 'Open']]
return df.shift(1) #To avoid forward looking bias

def initialize(context):
fetch_csv('http://ichart.finance.yahoo.com/table.csv?s=%5EVXV&a=1&b=1&c=1902&d=07&e=4&f=2039&g=d&ignore=.csv',
date_column='Date',
symbol='vxv',
post_func=rename_col,
date_format='%Y-%m-%d'
)
fetch_csv('http://ichart.finance.yahoo.com/table.csv?s=%5EVIX&a=1&b=1&c=1902&d=07&e=4&f=2039&g=d&ignore=.csv',
date_column='Date',
date_format='%Y-%m-%d',
symbol='vix',
post_func=rename_col)

schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=0,minutes=10))

context.stock = sid(38054)

def my_rebalance(context, data):
last_ratio = data['vix'].price/data['vxv'].price
threshold = 0.95
log.info(context.portfolio.cash)
if last_ratio < threshold:
if context.stock in context.portfolio.positions:
else:
order_target_percent(context.stock,-1.00)
log.info("Short VXX - %.2f" %last_ratio)
else:
order_target_percent(context.stock,0)
log.info("Move to cash - %.2f" %last_ratio)
print data['vix'].price
record(ratio=last_ratio)


There was a runtime error.

This performs very well, anyone have any ideas how it would of done in 08? clearly we can't backtest 08, but given that it did very well in 2015 with the Vix spikes, how would have the overall strategy handle down periods like 08 in your opinion Fang?

Why would you want to short VXX when you can simply go long XIV?

The strategy works fine between 2004 and the commencement of trading of XIV in late 2010 - at least if you substitute futures for XIV/VXX.

However you could well face an even bigger Black Swan than 08 - in which case you could be wiped out.

@Elsid
I did do some quick and dirty math to just see what the worst case scenario would've been prior to 2009's inception of VXX. Actually, the VIX term would've backwardated well before the major market collapse in September 2008 (The term backwardated on September 8th, 2008) so you actually would've been sitting out.

Given how VXX is constructed (weighted time to expiry of the Front and Second month futures), the worst day I calculated pre 2009 would've been 02/27/2007 when VIX spiked 64.22% (from 11.15 to 18.31). I'm not entirely sure what that event was, but I would imagine it to be something similar to Brexxit. My estimated one day loss given that spike would've been around -23% should you have been holding VXX short.

@Anthony FJ Garner
The reason why you want to short VXX directly is because of the way inverse ETFs are constructed. XIV actually has a problem with path dependency, which would exacerbate the volatility drag of inverse ETFs - a good explanation to what this means can be found at this link here:
https://sixfigureinvesting.com/2012/03/under-the-hood-of-a-leveraged-etf/

A good real world case is just to look at XIV and VXX during August 2015. You can see that XIV still hasn't really recovered from the drop whereas VXX has decayed well past the spike.

Overall, I think Brexxit is actually the better proxy for a Black Swan event since 2008 had a build up.

Thanks Fang,

Also, saw there were some issues running this live with Data, is this still an issue?

22% isn't too bad, and we can always apply some sort of stop loss, unless it would of blew past your SL. It will be interesting to see how the new VMIN/VMAX will perform.

Brexit wasn't too bad either, you could just went flat ahead of the referendum, and even when it did happen VIX only spiked to like 25%ish, Black swans in my mind recently would be complete intraday unknown events, like the flash crash in 2010, but it seems this system has held up fairly well.

Also for 08, yes I saw some information on when it started backwardation in September, but from the late 07, and early 08 Highs it was still a long way down to September 08, I believe around 20%+ I guess we would of been short VXX at this time? but even here I'm assuming drawdowns of maybe 50% max?

Looks like 08' might of been the highest volatility period, including many other crises.

What is "path dependency" in this context?

Yes, not much performance in XIV since end 2015 but that is or can be made up 1) by exit rules to avoid the worst of the drop and 2) by going long VXX at the time of the drop.

Nothing is perfect of course but certainly this strategy looks good in back testing. I am anxious to avoid shorting for all the obvious reasons. Also, you can't simulate shorting accurately in Q I believe?

I guess you would have to factor in your margin % rate when shorting, and I guess shares might not be available to short.

"Path dependency" in this context relates to when inverse ETFs must maintain a constant C X leverage, e.g., for XIV, it's -1X the futures in notional exposure. This would be why during a large VIX spike, XIV has difficulty recovering: the short futures could easily jump to -2X leverage, in which case, the fund must close out a lot of contracts to reduce the exposure and thereby immediately realizing losses.

There are a whole bunch of ways to reduce the drawdown in this case, and I personally trade XIV in one of my accounts that has a long-only restriction. However, if possible, shorting VXX is much less "noisier" than longing XIV. But like you guys mentioned, shorting VXX also carries its own risk such as margin requirements and a very real likelihood of not being able to borrow shares.

Also for me personally, I'm not a fan of going long VXX or UVXY even when in a very volatile environment; good timing becomes more crucial since volatility crushes are just as sporadic as spikes and I've yet to determine a decent algorithm to long these instruments.

Well just checked on IB VXX had multiple blocks of 3,000,000 shares to short, so I don't foresee it being an issue, might happen rarely I guess. Also, lowered the exposure to 70% and used TVIX instead you can get higher returns and a lower drawdown, if you add treasuries, instead of cash helps with returns and drawdowns, but with rising interest rates cash might be good the next couple of years.

Reworked the figures again using futures. No, sorry I can't see it. Very little difference over the long term shorting futures, vxx or going long XIV. Guess which one is safest.

Going Long XIV I'm assuming is "Safest", but depends on what your definition of safe is. Nothing is really safe as these are all derivatives CME can go go bust any day especially with their completely bogus paper gold/silver contracts, where they would never be able to satisfy contracts if people demanded physical.

But without taking that into account, futures are safer as you can get out 24hrs a day. So shorting futures/vxx provides the same returns? When I change to go long XIV the returns are drastically in favor of shorting VXX.

Safest I merely not going short one of the wildest contracts in the financial markets. Safest is limiting your loss to 100% when the upside is exactly the same for an alternative strategy with exactly the same upside but unlimited downside. Safest is understanding that the trading plan outlined in this thread is about as dumb as it gets unless you have the pockets of Midas and suicidal tendencies.

What??? Going long XIV does not give the same performance so what alternative strategy are you speaking of? shorting vix futures? If the OP's code is correct I don't think 25% DD is suicidal, or 50% DD in markets like 08'

Again please point me to what alternative strategy you are talking about that has the same performance as shorting VXX.

I use a different definition of contango - front month vs spot. Works much better in backtesting. Can't help you on your backtest I'm afraid. All I can say is that in my own backtesting with my own definition of contango the results of short vxx or long XIV are pretty well identical.

In which case the rational individual chooses the instrument with limited as opposed to unlimited risk.

Again my trigger/ contango definition makes for a good reversal system long XIV or long VXX.

If I have time I will rework it in Q. Re getting out in order to avoid a major spike up or down you can rely 100% on the fact that it will sometimes fail.

Given the vast volatility, gaps, fast markets and jihadists you are guaranteed to lose the lot on this strategy at some point on the future. So better limit the risk and not go naked short.

Personally I would use long XIV, long VXX and operate as weekly or monthly rebalancing algo : perhaps 50/50 or perhaps more conservative. 50% vix system, 50% cash.

Suggest you download all the futures data since 2004. Be careful, very careful how you deal with the roll. Work out what a 1x short equity curve looks like. This will bear a strong resemblance to XIV. Now revers it and see what going long looks like. This will closely resemble vxx.

Use Excel not Quantopian then you can see exactly what is going on under the hood.

Devise a few simple formulae to calculate contango from the futures contracts and spot Vix.

A few more formulae to put entry and exit rules into practice.

I think you will see what I mean.

I have not looked at this coding in this thread so I can't comment on it. What I can say is that I always feel utterly blind using Q since you can't download data or results or output for further analysis.

Try shorting the fake VXX equity curve. See if it produces greater profit than short the futures contracts.

Somewhat mathematically improbable methinks.

Don't rely on Q - the software is too complex. You need to get back to basics to see what is really going on and then by all means return to Q and redraft, experiment.

Anyway, do as you wish. Perhaps I am wrong. A sad deranged fool.

Perhaps I am deliberately trying to mislead you.

Perhaps I am just one of those endless random arseholes one tends to meet on trading forums.

Perhaps I'm just plain wrong!
🌋🌋🌋💣💣💣

Possibly all the above lol

And then do the same exercise in Excel with the ETN data. Same results. Well, very similar anyway.

Wow, I'm glad this thread's become so popular. Anyway, like you mentioned Anthony, I've actually been following the contango definition of VIX Spot Close/VIX Front Month Settlement. The threshold that I optimized (careful of model overfitting bias) was 1.005, i.e., when VIX/F1 < 1.005, take on short VXX exposure.

As to the risks mentioned, I agree, this strategy is incredibly risky, but only if you fail to understand what you're being paid for and also the amount you size your portfolio - 100% of your portfolio in this is indeed suicide. However, diversifying ~5% of my portfolio into this security that is very reasonably known to converge to 0 doesn't seem like a terrible decision. Just taking a look at the inception-to-date price chart on Yahoo for VXX and UVXY gives you a pretty clear idea that this thing has a very clear downward trend. Here's some more reading material on just how VXX works:
https://sixfigureinvesting.com/2013/04/how-does-vxx-work/.

Risk management, like any other trading strategy, is the crucial part for an algo like this. There's numerous ways to hedge yourself such as:
- Longing VXX puts instead of shorting the actual underlying (though you'd be fighting Theta decay).
- Short VXX and buy an OTM call to cover any serious spikes and roll the calls.
- Short VXX and Long VXV (this is a different mechanism because it's taking advantage of the less sensitive longer-dated futures versus the very sensitive shorter dated futures).
- Or again, just longing XIV.
All these hedges has pros and cons and we all know that there's no perfect strategy (except for putting your money in the Medallion Fund at Ren Tech).

Finally, I'll add that I did some more number crunching in Python using Quandl's continuous VIX futures data to build estimates of VXX returns before 2009: http://fchen.info/2017/01/03/worst-case-scenario-for-shorting-vxx/.

69
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
# Active current portfolio with specified weights for shorting VXX and VXZ

import datetime
import numpy as np
import pandas as pd

def rename_ql(df):
df = df.rename(columns={'Close': 'price','Trade Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['price', 'Settle','sid']]
return df.shift(1)

def rename_yahoo(df):
df = df.fillna(method='ffill')
df = df[['price', 'sid', 'Open']]
return df.shift(1) #To avoid forward looking bias

def initialize(context):
"""
Called once at the start of the algorithm.
"""
# For vxx Portion
# Pulling F1 VIX
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',
date_format='%Y-%m-%d',
symbol='v1',
post_func=rename_ql)
# Pulling VIX Spot
fetch_csv('http://ichart.finance.yahoo.com/table.csv?s=%5EVIX&a=1&b=1&c=1902&d=07&e=4&f=2039&g=d&ignore=.csv',
date_column='Date',
date_format='%Y-%m-%d',
symbol='vix',
post_func=rename_yahoo)

# Setting up universe for vxx
context.vxx = sid(38054)
context.vxx_weight = 1
context.vxx_threshold = 1.005

set_max_order_count(2)

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

# Before each trading day, calculate all necessary SMA and Ratios
# Calculating Ratio for VIX/F1
context.vxx_ratio = data.current('vix','price')/data.current('v1','Settle')
context.vxx_signal = context.vxx_ratio < context.vxx_threshold
log.info("VIX/F1 Ratio: %.2f" %context.vxx_ratio)

record(vix_ratio = context.vxx_ratio)

# Rebalancing function for ordering
def my_rebalance(context,data):

if context.vxx_signal:
if context.vxx in context.portfolio.positions:
else:
mv = context.portfolio.portfolio_value
vxx_mv =mv*context.vxx_weight
order_target_value(context.vxx,-vxx_mv)
log.info("Long vxx - Bought %.2f"%vxx_mv)
else:
order_target_percent(context.vxx,0)
log.info("vxx Move to Cash - %.2f"%context.vxx_ratio)
There was a runtime error.

Hey Fang,

Great algorithm! After fiddling with it a bit and doing some backtests, I am nearing confidence levels to give this a shot with a small portion of my portfolio.

Here's a version that picks up XIV instead of shorting VXX. :)

39
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
# Active current portfolio with specified weights for shorting VXX and VXZ

import datetime
import numpy as np
import pandas as pd

def rename_ql(df):
df = df.rename(columns={'Close': 'price','Trade Date': 'Date'})
df = df.fillna(method='ffill')
df = df[['price', 'Settle','sid']]
return df.shift(1)

def rename_yahoo(df):
df = df.fillna(method='ffill')
df = df[['price', 'sid', 'Open']]
return df.shift(1) #To avoid forward looking bias

def initialize(context):
"""
Called once at the start of the algorithm.
"""
# For vxx Portion
# Pulling F1 VIX
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',
date_format='%Y-%m-%d',
symbol='v1',
post_func=rename_ql)
# Pulling VIX Spot
fetch_csv('http://ichart.finance.yahoo.com/table.csv?s=%5EVIX&a=1&b=1&c=1902&d=07&e=4&f=2039&g=d&ignore=.csv',
date_column='Date',
date_format='%Y-%m-%d',
symbol='vix',
post_func=rename_yahoo)

# Setting up universe for vxx
context.vxx = sid(38054)
context.vxx_weight = 1
context.vxx_threshold = 1.005

# Setting up universe for xiv
context.xiv = sid(40516)

set_max_order_count(2)

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

# Before each trading day, calculate all necessary SMA and Ratios
# Calculating Ratio for VIX/F1
context.vxx_ratio = data.current('vix','price')/data.current('v1','Settle')
context.vxx_signal = context.vxx_ratio < context.vxx_threshold
log.info("VIX/F1 Ratio: %.2f" %context.vxx_ratio)

record(vix_ratio = context.vxx_ratio)

# Rebalancing function for ordering
def my_rebalance(context,data):

if context.vxx_signal:
if context.xiv in context.portfolio.positions:
else:
mv = context.portfolio.portfolio_value
vxx_mv =mv*context.vxx_weight
order_target_value(context.xiv,vxx_mv)
#order_target_value(context.xiv,1)
log.info("Long vxx - Bought %.2f"%vxx_mv)
else:
order_target_percent(context.xiv,0)
log.info("vxx Move to Cash - %.2f"%context.vxx_ratio)
There was a runtime error.

@Daniel

I don't think this can be traded live when you try you get nan for volatility calculations, unless you changed the code. Also, if you go back further to like 2011 you have 60% drawdowns I really don't understand what the point of posting backtests with cherry picked dates.

I tried Fang Zhou's version of the algo in my IB paper account but it didnt generate any trades due to NAN values  2017-01-10 08:45 before_trading_start:55 INFO VIX/F1 Ratio: nan  It looks like some problem with the fetcher.
The algo works fine in the backtest...

What a lot of nonsence and fuss. The strategy is so simple you can read the signal of the IB screen every day and it will take 5 minutes to trade manually.

As for cherry picking, totally correct. But again what's the big deal? Roll your own futures contracts back to 2004 and you have a full 12 year back test.

Again the strategy is so ultra simple it will take about half an hour to back test in Excel.

You don't need to use Q either for back testing this strategy or trading it.

I think my post above sounds very rude! Not really meant to be. Its just that people seem to get stuck or hung up on such simple stuff. And in many ways Quantopian is not an ideal first stop for investigating something this simple. Firstly because you can't download what you need to inspect the signals, the trades etcc etc. Second because so many have reported problems getting IB or Robin Hood to work automatically it does not seem like its worth the bother for something which takes one order a day.

Fine, the estimation of slippage is useful....but I think one can probably make an intelligent guess at it with daily data.

Also after so many years at this infuriating game you come to realise that the apparent accuracy of back testing is an utter nonsense. And has as big a chance as Mystic Meg of accurately forecasting the future.

In this particular trading scheme it is really all very simple. You are selling volatility in return for a premium. It is the proverbial picking of dimes off the railway track. Mostly very profitable then you get hit by the freight train.

So the big essential here is controlling risk. And in a market with such huge volatility that seems to me to be very difficult with shorting the futures or VXX unless you trade very small size in relation to your total pot. Hence the logical investor takes advantage of XIV.

Hey Fang,

Very interesting, learned a lot. Thanks
I suggest if you want to use vix/vx1 as a signal you'd do much better finding a way to normalize it depending on how many days are left until the front month future expires. Obviously just before it expires the signal will alway be 1 and is not indicative of anything.

Of course I don't know how to program that into Q...

Best

I trade vol using options for part of my living. (amongst other trading) I am not a coder but trying to learn. Shorting vol is one of the easiest trades to make if you know what you are doing. I trade vol with options (usually VXX options but also VIX) so also have to track 'vol of vol' or the implied volatility of vix options. Anyway, contango is one of the key indicators I use in my manual trades. So, you are right on there. I suggest backtesting in 2008-09 when we saw historic vol characteristics with long duration of Vix futures in backwardardation and VIX levels unheard of before. Rare period in history since the VIX was created. . If you're algo works, it should have kept you out of the trade for most of that period. If it has you in the trade and going underwater, then you need a filter for understanding vol characteristics more secular. Also per above, XIV is mathematically the same trade as short VXX, albeit some level of variation via structure, but not the margin required with being short.

I tested back to 2004 using VXX and XIV back-filled with futures data. Long XIV when contango, exit and long VXX when backwardation. 98% CAGR with a whopping 70% DD and vol of 63%. This compares well with by and hold XIV from 2004: CAGR 41%, max DD 93%, vol 65%. One simple parameter.

I am trading it live. There will be horrendous moments but who cares if you trade it with a small amount of capital and periodically take money out rather than continuously compound the whole amount.

I has been very interesting to note some of the comments on this and other similar threads relating to this trade. It is also interesting to see some of the blogs and websites on this trade - particularly ones offering "signals".

Back in 2003 I bought a trend following system from a guy who clearly knew nothing about trading, coding or much else. Except for web marketing. This only became evident after I had parted with $600. The arsehole has gone on to write books and presumably make some decent money out of his ignorance. certainly more than he would make trading his ridiculous systems. He gives absurd seminars all over the world and interviews people who should know better than to talk to him - presumably they assume their hedge funds will benefit from any sort of publicity. The point is this: Quantopian is an excellent opportunity for people to learn and not get conned like I did back in the day. However people should be equally and acutely aware that people may still (on occasion) talk absolute crap - even here. Happily, this is usually through lack of experience and knowledge of the markets and not through an attempt to sell crap to the unwary. Good for Quantopian - an excellent tool. But all that glitters is not gold and people need to do a great deal of research and thinking outside of this environment. Or they will assuredly regret it! Incidentally, for those with less experience: I will NOT be expecting such returns in my own trading! They will be a great deal less favourable. I think a way to trade these VIX strategies would be to take the average DD, which from the backtests I ran modifying some settings, from 09 until now was about 22%. Therefore if it's above this number you know something is "occurring", and can just stop the system, for example a hard stop of 30-35%DD. In my own ignorant, prejudiced and jaded view simple is best and fewest parameters is best and that's the way I trade. Stops are one area in which you can add complexity and parameters. I choose not to. A few things have had me wondering recently: How many people have taken the trouble to concatenate a time series of futures contracts themselves? If they have not done this they will very probably have a very hazy idea of what contango is and how it applies to different markets across the spectrum of asset classes available in futures. Are people aware of the difference in back adjusting methods? Do they realize the need to account for rolls proportionately if their time series is to make any sense when calculating daily percentage returns? Have people downloaded historic futures contracts back to 2004? Have people read the prospectus for VXX and XIV? Have people downloaded the methodology document freely available on the S&P website for the S&P 500 Vix Short Term Futures Inverse Daily Index? Have people downloaded and read the methodology for the S&P 500 Vix Short Term Futures Daily Index? Have people properly compounded a back tested short trade on VXX by rebalancing daily as is necessary? IE adding and subtracting short sales as their account value varies? Have people put the various possible contango measures into a spreadsheet and charted them? Are they aware of the range? Have people looked at the number of trades produced and the many other outputs ? etc etc etc In short there is a great deal which can and should be done outside of Quantopian. My suspicion would be that perhaps some of people on this forum have taken someone else's generously provided coding, fiddled a bit and come to some (perhaps sub optimal) conclusions. But there you go. No malice intended but after so many years of compounded errors I take all this stuff with large doses of salts. Sometimes Epsom. I am assuming that Q correctly handles the amount of shares on a short - it must do since it operates (or can be made to operate) on a daily rebalance basis. they also account therefore for slippage and commission. Q does not account for the cost of borrowing, margin interest etc. I have never borrowed stock and have thus no idea of the costs involved (I have only ever shorted futures). But I think I am right in saying these extras are not taken into account in Q? If taken into account how much would they reduce the profits from shorting VXX? No idea but there would definitely be an impact. I assume Fang you understand this based on how you wrote this, but for the trading novices here these instruments do a futures roll each day from front month to near month, from 0% at VIX expiration to 99.99% or (something like that) at next expiration. So, VXX, while VIX futures are in contango is selling cheap front month futures and buying expensive near month futures and thereby decaying as near becomes front month and loses value. This is structural to the instrument and experienced vol traders know this and game it. XIV is in reverse so goes up in contango (shorts both dates and rolls). When in a protracted bull market in the S+P since all this is based on S+P options implied volatility, VXX bleeds like nothing on the market (except UXVY). If you understand this it is a money machine. I am not joking. But you must know when you are in a bull market and you must understand the cycles of fear and greed which drive the VIX trade. Short VXX will get killed in vol events and very fast. I personally use several levels of VIX signals, some based on Elliot Wave Theory, others pedestrian technicals, and contango/backward considered that guide how large my short vol position, or when I go long vol. (latter is rare last 3 years). But I'm talking manual trades. I'm trying hard to learn how to code and so far from success. My friend and I have one running right now that has great returns, but the drawdowns are too heavy to deploy. But returns after Financial crisis are craziness. @ Javier Guillen Cruz - It's true, I should use a normalized VIX/F1 ratio to avoid the biased ratio of 1 at maturity. I'm using pretty much the same methodology as VXX to construct the normalized ratio: a day-weighted average of the VIX/F1 and F1/F2 ratios. Essentially, Front Weight = Days to Maturity / Current Futures Term Back Weight = 1 - Front Weight --> Normalized Ratio = Front Weight * VIX/F1 + Back Weight * F1/F2 This way, the Spot VIX and F1 Settlement is continuously adjusted for expiration. I've included the backtest here with the code; just a side note, I'm aware that you can't trade these algorithms live and its because you need to adjust the date index as well as find a consistent source of data for VIX, F1, and F2. I've attempted Quandl's plugin for Q, except the data has been delayed some days which causes your algo to do nothing (not good if it needs to close). Personally, I just use this as a guide and manually enter trades. 218 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 # Algorithm uses front month and second month settlement prices for VIX futures to # determine whether or not to short the ETF VXX. Current threshold is 0.95 representing # the percentage contango of F1/F2. If the settlements F1/F2 is below 0.95, short VXX, # otherwise move to cash. import datetime as dt import numpy as np import pandas as pd # Post Function for fetch_csv where futures data from Quandl is standardized 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 rename_vix(df): df = df.rename(columns={'Adj Close': 'price'}) df = df.fillna(method='ffill') df = df[['price', 'sid', 'Open']] log.info(' \n %s % df.head()') return df.shift(1) #To avoid forward looking bias def initialize(context): # Pulling 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) # Pulling 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) # Pulling VIX Spot data fetch_csv('http://ichart.finance.yahoo.com/table.csv?s=%5EVIX&a=1&b=1&c=1902&d=07&e=4&f=2039&g=d&ignore=.csv', date_column='Date', date_format='%Y-%m-%d', symbol='vix', post_func=rename_vix) # Declaring VXX as the stock for shorting context.vxx = sid(38054) set_benchmark(sid(8554)) context.vxx_pct = 1 # Scheduling the order function to occur everyday 5 minutes after market open schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours = 0, minutes = 5)) # Setting trading gurads set_max_order_count(2) def maturities(context,data): # Calculate today, but note that since we are adjusting for lookback bias, we need to change the current date to one day prior today = get_datetime().to_datetime().date() - dt.timedelta(1) curr_month = today.month curr_year = today.year # Finding Current Third Wed curr_eigth_day = dt.date(curr_year,curr_month,7) curr_fourth_day = dt.date(curr_year,curr_month,5).weekday() curr_third_wed = curr_eigth_day - dt.timedelta(curr_fourth_day) + dt.timedelta(14) # Finding Last Third Wed if curr_month == 1: last_month = 12 last_year = curr_year - 1 else: last_month = curr_month - 1 last_year = curr_year last_eigth_day = dt.date(last_year,last_month,7) last_fourth_day = dt.date(last_year,last_month,5).weekday() last_third_wed = last_eigth_day - dt.timedelta(last_fourth_day) + dt.timedelta(14) # Finding Next Third Wed if curr_month == 12: next_month = 1 next_year = curr_year + 1 else: next_month = curr_month + 1 next_year = curr_year next_eigth_day = dt.date(next_year,next_month,7) next_fourth_day = dt.date(next_year,next_month,5).weekday() next_third_wed = next_eigth_day - dt.timedelta(next_fourth_day) + dt.timedelta(14) # Finding Term: When current date is after expiry, should be 100% of spot/f1 if today < curr_third_wed: dte = curr_third_wed - today term = curr_third_wed - last_third_wed else: dte = next_third_wed - today term = next_third_wed - curr_third_wed # print (float(dte.days)/term.days) front_weight = float(dte.days)/term.days return front_weight def before_trading_start(context,data): spot_wgt = maturities(context,data) front_wgt = 1 - spot_wgt context.threshold = 0.95 # Creating ratio from weighting sf1_ratio = data.current('vix','price')/data.current('v1','Settle') f1f2_ratio = data.current('v1','Settle')/data.current('v2','Settle') context.last_ratio = spot_wgt*sf1_ratio +front_wgt*f1f2_ratio context.signal = context.last_ratio < context.threshold def my_rebalance(context, data): # Specifying the contango ratio threshold to short VXX log.info(context.portfolio.cash) if context.signal: if context.vxx in context.portfolio.positions: log.info("Already in - %.4f" %context.last_ratio) else: # Calculating the number of shares of VXX to short to reach the target # portfolio percentage allocation target_mv = context.portfolio.portfolio_value vxx_mv = -target_mv*context.vxx_pct order_target_value(context.vxx,vxx_mv) log.info("Short - %.4f" %context.last_ratio) else: # If the contango ratio is above specified threshold, purchase back VXX shares and remain in cash order_target_percent(context.vxx,0) log.info("Move to cash - %.4f" %context.last_ratio) vxx_record = context.portfolio.positions[context.vxx].amount record(vxx_shares=vxx_record, ratio_vxx=context.last_ratio)  There was a runtime error. Awesome work, Fang. Thank you very much. This code is so far ahead of my undertanding, I will need some time to understand it, but looks great. Great work. THANK YOU. Fang Zhou: I thought I smelt a whiff of too good to be true with this one so put it through its paces with Pyalgotrade tonight. It's still a decent strategy but over the same period I only registered the following on$10,000 initial capital.

2017-01-27 00:00:00 strategy [INFO] Final portfolio value: \$304684.52
2017-01-27 00:00:00 strategy [INFO] Cumulative returns: 2946.85 %
2017-01-27 00:00:00 strategy [INFO] CAGR 46.17 %
2017-01-27 00:00:00 strategy [INFO] Sharpe ratio: 1.41
2017-01-27 00:00:00 strategy [INFO] Max. drawdown: 36.17 %

I wouldn't say don't trade it but with XIV/VXX you can do better. I am not sure why you got such good results in Quantopian but as many others have mentioned it's a giant black box so at least I know my results are good vs quantopian.

It's good as far as blended futures compared to spot strategies go as a lot of them manage to walk straight into the chasm of 80% drawdowns in the midst of the GFC and any vol spikes, which this one neatly manages to avoid.

Keep meaning to look at Pyalgotrade. I have it loaded. I'm not sure "black box" is totally fair but it certainly feels like it. I feel blind without being able to download anything and everything to excel - can't handle it. Zipline of course is open source and XIV/VXX are easily downloadable from Yahoo and elsewhere if you are prepared to rely on daily data. But Zipline is a nightmare unless you have enormous energy - haven't tried t for months now but it was a herculean task to get it even partially working.

GFC?

Re 80% DD difficult to avoid IMHO....without a plethora of unwanted rules.

@Kimble Young

Thanks for the input, I've never actually looked at Pyalgotrade before so I'll probably check it out when I get a chance. Also, when you mention:

"I wouldn't say don't trade it but with XIV/VXX you can do better. I
am not sure why you got such good results in Quantopian but as many
others have mentioned it's a giant black box so at least I know my
results are good vs quantopian."

Could you elaborate on how XIV/VXX could do better? I'd be interested to see what your logic is if you wouldn't mind sharing.

Cheers!

Go long XIV when in contango - 5% trailing stop. Boom. Very easily manually traded as well. Just consult http://vixcentral.com at the end of each day.

This one will also do marginally better although again nothing like what they claim:

On taking a second look but without access to the data to make sure you really aren't getting a look ahead bias here I think your spectacular results have to do with completely unrealistic short leverage. I think I remember reading this was an issue with Quantopian?

I'm not too familiar with it but I'll try shorting only what we have cash for which would help meet a 50% drawdown. Long XIV with no leverage also helps ground yourself.

Let's try a different approach...
Instead of looking at the futures market to predict the performance of a proxy, let's look at another proxy designed to function in the inverse: UVXY.
The Algorithm
Algorithm orders XIV with 70% percent of equity capital on 3:1 leverage; the algorithm holds the other 30% in cash until a buy signal is generated for UVXY.
The buy signal: if UVXY opens positive, then take that 30% and open a position in UVXY; that position is held until 5 minutes before the end of trading.
If UVXY is bought, then XIV is sold and the algorithm goes to cash until UVXY opens down and XIV opens up; at that point, the algorithm reenters XIV with the same 3:1 leverage.
When XIV is bought, generate a sell limit order at 1% below the purchase price.

Unfortunately, I lack the coding skills to implement this and perform a back test, but on paper, it should address both drawdown and the potential black swan. If someone happens to have a better sell signal on UVXY, by all means please implement that; I just tossed that out there for simplicity's sake. Not only that, it actually performs fairly well. True, it doesn't capture UVXY at the crest, but it should offer a respectable rate of return to counteract any loses incurred on XIV. The other possibility I considered, and I would love to hear some feedback on this, was is it possible to make this market neutral? In other words, is there some ratio for XIV and UVXY where it essentially becomes a breakeven trade and all you have to do is scale the leverage on XIV to profit? If there is, then that makes the problem quite a bit easier because now all you have to do is determine the leverage on XIV.

I kept thinking about it and the other problem I thought of (and I see another trader even mentioned it too) is the fact that the futures market is open even when the ARCA is closed. That means that both UVXY and XIV could either appreciate or depreciate while the algorithm can not buy and sell them. In an ideal world, this is hedged by taking an opposing position in the futures market opposite of your overnight holdings. For example, say the algorithm is long XIV (read: short the VIX), then when the market closes, it would enter the futures market and go long the VIX (likely using the VIX Weekly's, or maybe options are better here?) overnight using the remaining equity, that 30%, and sell in the morning. That hedge would definitely be a drag on the returns and so to compensate, there are really three options. Firstly, modify the ratio to, say, 60% in XIV and 40% in cash/UVXY/VIX futures or apply leverage to the UVXY trades. The third option, which would be even more complicated, would be for the algorithm to hedge only in certain situations (i.e. if XIV falls a certain percentage, then hedge).

The other issue I would just like to mention is the expense ratio on both UVXY and XIV. I don’t know if that math is done automatically in the backtest, but I would certainly hope so because otherwise will cool down substantially.

The other thing I’ve noticed about UVXY is that it almost never has two consecutive days in positive territory. Unfortunately, when it does, that usually means that we’re in the midst of a potential black swan.

Theoretically, the returns of UVXY and XIV levered 3X should be the exact opposite of each other, no? Yesterday for example though, they weren’t; XIV finished down 3.11%, which multiplied by the leverage factor is a loss of 9.33%, whereas UVXY ended up 5.75%, although at one point in the day it was up almost 11% so perhaps it's just a matter of finding a better sell signal?

If we assume though that a 3X levered XIV will always outpace UVXY (and by the way, the above math assumes a 50/50 equity capital split, which is not true in our case), then the algorithm will suffer from drawdown, although they won’t be as large. For the algorithm to earn a profit independent of market condition would require some kind of highly leveraged hedged and that can only really be found in the futures market. I’m reluctant to apply leverage to UVXY because that’s already insanely volatile at 3X leverage and I wonder what the broker would say if the algorithm tried to leverage an inherently leveraged investment.

Today for example, or should I say yesterday as I will post this in the morning, the algorithm would have suffered a 4.6% loss and that’s with the stop loss order set at 3% and the algorithm only capturing a 5.75% gain on UVXY...

Fang,

I have a question on the maturity. What do you do on the expiration day when today == curr_third_wed?

In your code, you do not have this. If today is the third Wed, you will have front_weight = 1.0, which I think it should be 0. Am I missing something?

for example, if today is 2/15/17, your next_third_wed is 3/15/17, and your curr_third_wed = 2/17/17. This makes front_weight = 1. Is it correct?

    if today < curr_third_wed:
dte = curr_third_wed - today
term = curr_third_wed - last_third_wed
else:
dte = next_third_wed - today
term = next_third_wed - curr_third_wed
front_weight = float(dte.days)/term.days
return front_weight


I think you should add the following:

...
elif today == curr_third_wed:
dte = curr_third_wed - today # This makes dte = 0, causing front_weight = 0
term = curr_third_wed - last_third_wed



Good catch! I thought I accounted for that, but noticed I missed an "=" sign lol. If you just change line 89 to be <=, it'll give you the same thing as you mentioned:

        if today < curr_third_wed:


Change to:

     if today <= curr_third_wed:


You should get the front_weight to equal 0.

I've found that the below do not always match day for day. IE. Today VX1 only has 2/15/17 data and VX2 has 2/16/17 data. So would the calculation be 0 - or would it be based on the last date that matches? Excuse my ignorance as I am new to futures.

# Pulling first month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',
# Pulling second month VIX futures data
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv',


Tyler, it uses the last available value in the calculation. The ratio on 2/16 would use VX1's 2/15 value.

Greetings, all!

This has been my first real exploration into backtesting with Quantopian. I find the VXX fascinating and observing its relationships with volatility is certainly worthwhile. To keep this brief, I'll quickly explain the backtest I've been experimenting with and how I tried to add to Fang Zhou's original strategy. Perhaps different threshold values would lead to a higher sharpe; I have yet to explore any optimization analysis on those parameters.

I'm also quite new to futures, so some of my thoughts below may not be completely accurate. Does anyone have ideas about potential strategies for when the market is in backwardation (aside from holding cash)?

Thesis: This algorithm is based on the assumption that when contango of the VIX futures is above a certain threshold percentage, the VXX (that tracks the ratio of the front and back month VIX futures contract price) will decrease.

THREE SCENARIOS FOR PORTFOLIO:

1. If the VIX is ever in NORMAL BACKWARDATION, or if it is in CONTANGO but not a high enough percentage of contago to justify a VXX short (see threshold value), or it is not low (negative) enough to justify a short SPY position, then the portfolio is all cash until one of the thresholds is triggered.

2. If the VIX is in CONTANGO, and has a percent contango higher than our threshold, we short the VXX. You might ask yourself: In contango, back month contracts are higher than front month. This means volatility is expected to increase, why would we short an instrument that tracks volatility? Well, it is actually the case that the futures prices will decrease over time back to the spot price as volatility does NOT increase to the back month price. Inversely, when the VIX is in normal backwardation, the market is expecting volatility to go DOWN (front month price > back month), but this is actually associated with BEAR markets as the back month increase to match the high spot price of volatility.

3. If this VIX is in a significant BACKWARDATION, which is typically associated with BEAR MARKETS, then we short the SPY (other assets also listed). This short is halted when the backwardation no longer fulfills our minimum thresholds, explained more in the code below.

84
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.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume
from quantopian.pipeline.filters.morningstar import Q500US

import datetime
import numpy as np
import pandas as pd

# CREDITS TO ORIGINAL CODE AND FUTURES DATA GOES TO: Fang Zhou
# https://www.quantopian.com/posts/shorting-vxx-based-on-vix-futures-contango

# Thesis: This algorithm is based on the assumption that when contango of the VIX futures is above a certain threshold percentage, the VXX (that tracks the ratio of the front and back month VIX futures contract price) will decrease.

# http://www.macroption.com/vix-futures-curve/

# THREE SCENARIOS FOR PORTFOLIO:

# 1. If the VIX is ever in NORMAL BACKWARDATION, or if it is in CONTANGO
# but not a large enough contago to justify a VXX short (see threshhold value),
# then the portfolio is all cash until one of the threshholds is triggered.

# 2. If the VIX is in CONTANGO, and has a percent contango higher than our
# threshhold, we short the VXX. You might ask yourself: In contango, back month # contracts are higher than front month. This means volatility is expected to
# increase, why would we short an instrument that tracks volatility? Well,
# it is actually the case that the futures prices will decrease over time
# back to the spot price as volatility does NOT increase to the back month
# price. Inversely, when the VIX is in normal backwardation, the market is
# expecting volatility to go DOWN (front month price > back month), but this is # actually associated with BEAR markets as the back month increase to match the
# high spot price of volatility.

# 3. If this VIX is in a significant BACKWARDATION, which is typically associated with BEAR MARKETS, then we short the SPY (other assets also listed). This short is halted when the backwardation no longer fulfills our minimum threshholds, explained below.

# FUNCTION ONE: This function constructs our dataframe format,
# and deals with edge cases like potential NAN's, as well as avoids
# look-forward bias.
def rename_col(df):
# rename columns to PRICE and DATE, which are the only things we care
# about from the data set we're pulling from.

df = df.rename(columns = {'Close': 'price','Trade Date' : 'Date'})

# To make sure we account for potential holes in data, fill potential
# NAN's with the last previously known value.
df = df.fillna(method = 'ffill')
df = df[['price','Settle','sid']]

# Shift data by one day to avoid forward-looking bias
return df.shift(1)

# FUNCTION TWO: This function pulls the VIX futures data required for our thesis to utilize, as well as contructs the parameters of our order entry.
def initialize(context):

# fetch the CSV data for the FRONT MONTH futures contracts, F1,
# and reformat them to fit the way our dataframe is structured
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX1.csv',

date_format = '%Y-%m-%d',
symbol = 'v1',
post_func = rename_col)

# fetch the CSV data for the BACK MONTH futures contracts, F2,
# and reformat them to fit the way our dataframe is structured
fetch_csv('http://www.quandl.com/api/v1/datasets/CHRIS/CBOE_VX2.csv',

date_format = '%Y-%m-%d',
symbol = 'v2',
post_func = rename_col)

# Declare the Exchange Trade Note, VXX, as our asset of interest to short.
# 38054 corresponds to the serial number for the VXX in the Quantopian API.
context.vxx = sid(38054)
# potential assets to short during normal backwardation:
context.spy = sid(8554)
context.gold = sid(26807)
context.psq = sid(32265)

# Schedule the order to occur 5 minutes after the trading day opens,
# which will be 6:35 AM PST.
# EXPLORE: Does creating a variable trade time affect the overall returns?
# What if the time in trade is based on the volume, or other parameter?
schedule_function(rebalance,date_rules.every_day(),
time_rules.market_open(hours=0,minutes=5))

# FUNCTION THREE: This function is passed into FUNCTION TWO, and provides
# updated data and logic for the trading function to utilize.
def rebalance(context,data):

# Following the thesis of this algorithm, we need to get the
# current contango of the VIX futures contracts, which
# corresponds to a ratio of the percent of (1- F1/F2).
current_contango = ( 1 -
data.current('v1','Settle')/data.current('v2','Settle'))

# Now that we have current_contango for daily market conditions,
# we can use it as a threshold to decide whether or not we
# want to short the VXX (depending on the magnitude of the contango)
# EXPLORE: What is the optimal threshhold value that produces
# the highest sharpe ratio? What about highest alpha?
# So, I set the default % contango to 5%. Higher threshholds will mean that       # we trade less, because the VIX will be in higher contango for a smaller
# amount of time before it starts to backwardate. Lower % thresholds will         # mean that we trade MORE, but incur more risk as well.
# What is the optimal pair of threshholds to produce the highest sharpe?
contango_threshhold_high = .066
contango_threshhold_low = -.117

# Print the current value of the portfolio.
log.info('Current Portfolio Value: %.2f' %context.portfolio.cash)

# If the current contango is higher than the threshhold, we want to either         # start to short the VXX or continue
# to short the VXX, following our thesis.
if current_contango > contango_threshhold_high:
# Check our holdings in the portfolio. If we have stock (short)
# the VXX already, and the contango is still above our threshold,                 # then we want to KEEP shorting it!
if (context.vxx in context.portfolio.positions):
# Print the current contango %, and a message that says
# we're still shorting the VXX on current trading date.
log.info('Still Short VXX, Current Contango is: %.2f'                             %current_contango)
# Now, if we're not already short the VXX, and the contango is now
# above our acceptable threshhold, we get SHORT on this day.
else:
# So, we set a target % that we want to make on the trade.
# In order to reach that %, we can conclude that we want to short
# a number of shares proportional to this percentage.
target_percent = 1
target_move = context.portfolio.portfolio_value*target_percent
# Now, get the number of shares, rounded down with the floor function,
# as the desired move over the current price of the VXX.
shares = np.floor(target_move/(data.current(context.vxx,'price')))
# Now, we execute our SHORT order:
order(context.vxx,-shares)
# display the current contango and the number of shares we shorted.
log.info('VXX shorted today, with number of shares: %.2f' %shares)
log.info('-> Also, contango for this day was: %.2f' %current_contango)
# Record the number of shares we shorted.
record(shares = shares)

# Now, we deal with market cases with backwardation. If the current               # contango is negative below our threshold, then short market:
# ~~~~~ SHORT MARKET SECTION ~~~~~
elif current_contango < contango_threshhold_low:
# Check our holdings in the portfolio. If we have stock (short)
# the VXX, and the current contango is below our threshhold, this means           # the market is likely in a bear market. So, we want to buy to cover               # position in VXX and buy GOLD. (an asset that performs well in bear               # markets).
if (context.spy in context.portfolio.positions):
# if we're short VXX, buy to cover, and go long gold.
order_target_percent(context.vxx,0);
target_percent = 1
target_move = context.portfolio.portfolio_value*target_percent
# Now, get the number of shares, rounded down with the floor function,
# as the desired move over the current price of the VXX.
shares = np.floor(target_move/(data.current(context.gold,'price')))
# Now, we execute our LONG order:
order(context.spy,-shares)
# display the current contango and the number of shares we shorted.
log.info('GOLD long today, with number of shares: %.2f' %shares)
log.info('-> Also, contango for this day was: %.2f' %current_contango)
# Record the number of shares we bought for gold.
record(shares = shares)

# Now, if we're not already short the VXX, and the contango is now
# lower than the low threshhold, we go LONG gold.
else:
# So, we set a target % that we want to make on the trade.
# In order to reach that %, we can conclude that we want to trade
# a number of shares proportional to this percentage.
target_percent = 1
target_move = context.portfolio.portfolio_value*target_percent
# Now, get the number of shares, rounded down with the floor function,
# as the desired move over the current price of the VXX.
shares = np.floor(target_move/(data.current(context.spy,'price')))
# Now, we execute our LONG order:
order(context.spy,-shares)
# display the current contango and the number of shares we shorted.
log.info('GOLD bought today, with number of shares: %.2f' %shares)
log.info('-> Also, contango for this day was: %.2f' %current_contango)
# Record the number of shares we shorted.
record(shares = shares)

# Now that we have all (contango > threshold) situations taken care of, we just # # either continue holding cash or buy back the VXX shares and flatten the position # if the contango is either too low, OR it's negative which means we're in normal # backwardation (typically associated with bear markets)

else:
# Do we have a current short position in VXX? If we do, we want to buy             # the shares back. The contango is not juicy enough for a short to be             # justified.
# Also, the LOWER threshhold that would have triggered a long gold
# position was not met either, so hold cash during this time.
# It means lower threshhold < current contango < upper threshhold.
# Buy back the VXX and remain in cash until contango is looking                   # like a good short again.
order_target_percent(context.vxx,0);
order_target_percent(context.spy,0);
log.info('Portfolio flattened today, with cotango of: %.2f' %current_contango)
record(ratio = current_contango)
There was a runtime error.

Sean -

Seems like a good strategy but when you trade SPY it seems like you are using a lot of leverage according to the positions held. I am not sure if that is intentional. Maybe you should track leverage.

@Kimble are you live trading contango + 5% trailing stop? What do you do for re-entry after the 5% trailing stop is triggered, do you wait to see if the contango is present at the end of the next day and re-enter?

That's right Tyler. You can optimise the Contango threshold based on history. Somewhere around 5% is the sweet spot.

Sometimes the 5% stop is a false alarm but it's also a quick way out when the VIX spikes and the term structure changes rapidly.

I do live trade this and especially over the last 12 months it has been a great little money earner. Just not on quantopian though.

Kimble do you use spot/front month or front month/second month for contango?

Also do you trade Vxx in backwardation?

Hey Kimble, what kind of returns do you get and DD with the 5% stop?

Front and second month Tyler. What's the pointing choosing another metric if VIX/VXx is base on futures? It's a play on terms structures.

Elsid why not try backtest it in quantopian yourself? Just don't apply leverage. In real life trading with such volatile strategies a 30% drawdown means realistically you can't relay on 2/3 times leverage without a margin call at some point.

That's why you will never achieve the imaginary returns on these quantopian backtest in the real world.

@Kimble

Yeah I'll try it, have to look up how to do stop losses in quantopian.

Looking at the Options Expiration Calendar, VIX expires on March 22nd. The weighted version shows 15 days from March 1st. Every three months it is five weeks instead of four weeks between expiry. Doesn't look like the weight takes that into account.

Hello! I really liked Fang algo. However I think it could improve a lot by going long VXX when there is backwardation. This could allow us to capture the volatility premium in backwardation scenarios. Also in 08 type scenarios with a sustained price of the spot we wil be very profitable. We can calculate the backwardation with the same process SPOTVIX/VIXFRONT and keeping the same 1,005 thresold, o maybe getting it a little bit higher.

Also, regarding the methods of calculation backwardation or contango, Tony Cooper in his paper of easy volatility investing tested many differents approaches to calculate them and concludes that short term is the better. He proposes this one:

if the 5 day moving average of (VIX - 10 day historical volatility) > 0 go long XIV else go long VXX

We can add a thresold, instead of 0 just to improve the results a little bit. But we will have some overfit risks.

Another idea can be to move to value companies when there is not backwardation/contango or SP500 instead of cash.

Hi all,

I have compared the fetched data with Q futures one (now that we have it) and it seems that current VIX futures (V1) and next moths futures (V2) from the fetched parts are settled prices. Meanwhile, it seems that Q futures data is tradable price, and theferore I assume its the closing price. Can anybody confirm? Appr!