Back to Community
Robin Hood VIX Mix Rogue Trader

This is the most recent version of Robin Hood VIX Mix

That thread was getting a bit long. This version is enough different I thought it appropriate to move to a new thread.
Anybody following me on that thread the last several days knows I did manual trades since Monday 3 days ago June 26.

I've managed to increase my portfolio from $31,000 3 days ago to $41,000 as of this moment.
Must be said, you and I can and will lose a lot of money. Do your own research and make your own decisions.

This current version is a work in progress. It is a reasonable attempt to simulate what I did in the last 3 days. I did hedge a little against doing exactly what I did, because the hedged method / process seems more profitable long term per back testing.

What cannot be simulated is the rather profitable sense of timing that I experienced the last 3 days. There are so many things that can move the market. I see no alternative but to code for long term and hope the short term losses will be made up in the long run by the algo or perhaps the losses will be avoided by my intervention. Nobody has perfect timing. So I likely will lose large amounts again multiple times. But I am looking for long term results that improve my life. We shall see.

It would be wise to look at other algos for your own benefit.
I like, and I have been influenced by:

https://www.quantopian.com/posts/trade-xiv-based-on-vix-1
https://www.quantopian.com/posts/xiv-shotgun-trading-inverse-vix-with-wvf
https://www.quantopian.com/posts/deployed-two-xiv-slash-uvxy-slash-tqqq-strategies-for-paper-trading-dot-dot-dot-will-make-it-live-trading-after-a-month
https://www.quantopian.com/posts/vix-trading-algorithm-return-150-percent-a-year-over-past-5-years-but-has-50-percent-drawdown-from-2015-meltdown

Of course, it must be said, that if avoiding risk is your number one goal, then other algos are probably more appropriate.
Maybe the xiv-shotgun-trading-inverse-vix-with-wvf works well for low risk. Maybe.

By the way, I have started cloning / copying my current code to an algo I call Paper Trade. I paper trade that one starting at the same time that I start the live algo with Rh. So if the live algo Rh quits unexpectedly, I have a paper trade that keeps running, for reference.

Clone Algorithm
440
Loading...
Backtest from to with initial capital
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 talib
from math import trunc  
from pytz import timezone      
import math
import time
import re
import functools
import itertools

from zipline.utils import tradingcalendar

History = 128

import pandas
import pandas as pd
import datetime
import numpy as np
import scipy
from scipy import stats
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 CustomFactor, Latest, SimpleMovingAverage
from quantopian.pipeline.data.quandl import cboe_vix, cboe_vxv, cboe_vxd, cboe_vvix

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

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)



    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.wager_min = 0.666 # Bottom of wager range applied as factor of other calculated leverages
    context.wager_max = 1.00 # Top of wager range applied as factor of other calculated leverages
    context.wager = context.wager_min
    context.ShowMaxLev            = True # False for faster back testing

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX = context.ShortVIX = False
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # only using the pipe for VIX currently
    The_Pipe = Pipeline()
    attach_pipeline(The_Pipe, 'The_Pipeline')
    #get VIX at market close
    The_Pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose')

    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(hours = 0, minutes = 2))
    
    schedule_function(Rebalance, date_rules.every_day(),
        time_rules.market_open(minutes = 4)
    )

    schedule_function(RogueTrader, date_rules.every_day(),
        time_rules.market_open(minutes = 60)
    )

    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
def before_trading_start(context, data):

    # The Pipeline Output
    The_Output       = pipeline_output('The_Pipeline')
    context.VIXprice = The_Output["VixClose"].iloc[0] # VIX at market close
    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)

def Rebalance(context, data):

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    VIXprice  = context.VIXprice # at market close
    XIVprice  = data.current(context.XIV, 'price') 
    UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    record(VIX  = VIXprice)
    record(XIV  = XIVprice)
    record(UVXY = UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'Short' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' Long' if context.BigSignalLongVIX  else BigSignal
    BigSignal = ' TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'Short' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' Long' if context.LongVIX  else SyntheticVIX
    log.info('{} {} VIX: {:.2f} XIV: {:.2f} UVXY: {:.2f} TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, VIXprice, XIVprice, UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

def RogueTrader(context, data):

    cancel_open_orders(context, data)

    stock = context.UVXY
    if stock in context.portfolio.positions:

        # Set LimitPrice for first order
        RemainingFactor  = 0.50
        MaxOrders        = 10
        LimitPriceFactor = 1.02 if context.Agree else 1.01
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 

    stock = context.XIV
    if stock in context.portfolio.positions:

        # Set LimitPrice for first order
        RemainingFactor  = 0.25
        MaxOrders        = 3
        LimitPriceFactor = 1.05
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
            cancel_order(order)

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
               
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11
    
    if last_vix < threshold_vix_too_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
            
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
            
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv
            
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    else:
        target_sid = context.uvxy
        
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 
        
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    log.info("Pre-Shift")
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    log.info("VIX: Pre-Massage")
    df = fix_close(df,'VIX Close')
    log.info("VIX: Post-Massage")
    return df

def addFieldsVXV(df):
    log.info("VXV: Pre-Massage")
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    log.info("VXV: Post-Massage")
    return df

def addFieldsVX1(df):
    log.info("VX1: Pre-Massage")
    df = fix_closeVX(df,'F1')
    log.info("VX1: Post-Massage")
    return df

def addFieldsVX2(df):
    log.info("VX2: Pre-Massage")
    df = fix_closeVX(df,'F2')
    log.info("VX2: Post-Massage")
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s
  
def handle_data(context, data): 
    if context.ShowMaxLev:
        if context.account.leverage > context.mx_lvrg:  
            context.mx_lvrg = context.account.leverage  
            record(mx_lvrg = context.mx_lvrg)    # Record maximum leverage encountered
There was a runtime error.
152 responses

Hi Charles, I added some of the thresholds of the BigSignal sections. It improves it by 700% when stops are off (50%), by 400% when stops are at 30%. I have added code for trailing stops which are not useful for XIV but maybe for the other stocks. I put it at 30% now but it can should be tuned. Hope this helps

Clone Algorithm
53
Loading...
Backtest from to with initial capital
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 talib
from math import trunc  
from pytz import timezone      
import math
import time
import re
import functools
import itertools

from zipline.utils import tradingcalendar

History = 128

import pandas
import pandas as pd
import datetime
import numpy as np
import scipy
from scipy import stats
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 CustomFactor, Latest, SimpleMovingAverage
from quantopian.pipeline.data.quandl import cboe_vix, cboe_vxv, cboe_vxd, cboe_vvix

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

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)



    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    context.case_counter = [0.] * 16
    context.case_profits  = [1.] * 16
    context.prevCase = -1
    context.prev_portfolio_value = context.portfolio.portfolio_value
    context.countUp = 0
    context.countUpMax = 1
    context.countDown = 0
    context.countDownMax = 1
    
    
    # so that we can retry cancelled orders
    context.todays_orders = []
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.wager_min = 0.666 # Bottom of wager range applied as factor of other calculated leverages
    context.wager_max = 1.00 # Top of wager range applied as factor of other calculated leverages
    context.wager = context.wager_min
    context.ShowMaxLev            = True # False for faster back testing

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # only using the pipe for VIX currently
    The_Pipe = Pipeline()
    attach_pipeline(The_Pipe, 'The_Pipeline')
    #get VIX at market close
    The_Pipe.add(GetVIX(inputs=[cboe_vix.vix_close]), 'VixClose')

    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    context.stops         = {}
    context.stoploss      = 0.30
    context.stoppedout    = []
    
    
def before_trading_start(context, data):

    # Clear the list of todays orders and start fresh
    del context.todays_orders[:]
    
    # The Pipeline Output
    The_Output       = pipeline_output('The_Pipeline')
    context.VIXprice = The_Output["VixClose"].iloc[0] # VIX at market close
    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)

def Rebalance(context, data):

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    VIXprice  = context.VIXprice # at market close
    XIVprice  = data.current(context.XIV, 'price') 
    UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    record(VIX  = VIXprice)
    record(XIV  = XIVprice)
    record(UVXY = UVXYprice)
    record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if context.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if context.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, VIXprice, XIVprice, UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_id = order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_id = order_percent(stock, LevAdjust)
        # store the order id in case we need to retry the order
        context.todays_orders.append(order_id)
        if stock.symbol in context.stops: del context.stops[stock.symbol]

def RogueTrader(context, data):

    cancel_open_orders(context, data)

    stock = context.UVXY
    if stock in context.portfolio.positions:

        # Set LimitPrice for first order
        RemainingFactor  = 0.50
        MaxOrders        = 10
        LimitPriceFactor = 1.02 if context.Agree else 1.01
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
    stock = context.XIV
    if stock in context.portfolio.positions:

        # Set LimitPrice for first order
        RemainingFactor  = 0.25
        MaxOrders        = 3
        LimitPriceFactor = 1.05
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
            cancel_order(order)
    
    
def retry_cancelled_order(context, data):
    for order_id in context.todays_orders[:]:
        original_order = get_order(order_id)
        if original_order and original_order.status in [2,3] :
            # The order was somehow cancelled so retry
            retry_id = order(original_order.sid, original_order.amount)
            if original_order.symbol in context.stops: del context.stops[stock.symbol]
            log.info('order for %i shares of %s cancelled - retrying' 
                         % (original_order.amount, original_order.sid))
            
            # Remove the original order (typically can't do but note the [:]) and store the new order
            context.todays_orders.remove(order_id)
            context.todays_orders.append(retry_id)

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11
    
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        case = 0
        target_sid = context.uvxy
            
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        case = 1
        target_sid = context.xiv
            
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        case = 2
        target_sid = context.xiv

    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        case = 3
        target_sid = context.uvxy

    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        case = 4
        target_sid = context.uvxy

    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        case = 5
        target_sid = context.xiv

    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        case = 6
        target_sid = context.xiv
            
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        case = 7
        target_sid = context.uvxy

    elif vix_ratio > threshold_vc_high_2: #have to think
        case = 8
        target_sid = context.xiv

    else:
        case = 9
        target_sid = context.uvxy
        
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        case = 10
        target_sid = context.tqqq 
        
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        case = 11
        target_sid = context.tqqq
    
    # need to figure out how to handle case where SyntheticVIX disagrees
    # although still counts for TQQQ signal
    # not sure I want to move the SyntheticVIX to this section yet
    # because it might disagree earlier but agree later in the trading day
    context.case_counter[case] += 1
    if context.prevCase <> -1:
        context.case_profits[context.prevCase] *= (context.portfolio.portfolio_value / context.prev_portfolio_value)

    # Adjust wager up after loss and down after profit, limited by min / max range
    if context.prevCase <> -1:
        context.wager *= context.prev_portfolio_value/context.portfolio.portfolio_value
        context.wager = max(min(context.wager, context.wager_max), context.wager_min)
        if context.portfolio.portfolio_value > context.prev_portfolio_value:
            context.countDown = 0
            context.countUp +=1
            if context.countUp > context.countUpMax:
                context.countUpMax = context.countUp
                context.wager = context.wager_min
        elif context.portfolio.portfolio_value < context.prev_portfolio_value:
            context.countDown +=1
            context.countUp =0
            if context.countDown >= context.countDownMax:
                context.countDownMax = context.countDown
                context.wager = context.wager_max
        
    context.prev_portfolio_value = context.portfolio.portfolio_value
    context.prevCase = case
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def my_stat_print(context, data):
    if get_datetime() == get_environment('end'):
        for i in range(12):
            if context.case_counter[i] <> 0:
                log.info("%s %s %5.5s%% %5.5s%%/case"%(i, context.case_counter[i],
                                                       (context.case_profits[i]-1)*100,
                                                       ((pow(context.case_profits[i],
                                                        (1./context.case_counter[i]))-1)*100)))    

def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s
  
def handle_data(context, data): 
    if context.ShowMaxLev:
        if context.account.leverage > context.mx_lvrg:  
            context.mx_lvrg = context.account.leverage  
            record(mx_lvrg = context.mx_lvrg)    # Record maximum leverage encountered
    c=context
    for position in c.portfolio.positions.itervalues():
        if position.amount == 0:
            if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
            continue
        elif position.asset.symbol not in c.stops:
            stoploss= c.stoploss if position.amount > 0 else -c.stoploss
            c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
            log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
        elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
            #sell
            log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
            if not get_open_orders(position.sid):order_target_value(position.sid,0.0)
            c.stoppedout.append(position.asset.symbol)
        elif c.stops[position.asset.symbol] < position.last_sale_price and position.amount < 0:
            #sell
            log.info(' ! '+str(position.asset.symbol)+'- (Short) has hit stoploss @ '+str(position.last_sale_price))
            if not get_open_orders(position.sid):order_target_value(position.sid,0.0)
            c.stoppedout.append(position.asset.symbol)
        elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
            c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
            log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
        elif c.stops[position.asset.symbol] > position.last_sale_price*(1+ c.stoploss) and position.amount < 0:
            c.stops[position.asset.symbol]=position.last_sale_price*(1+ c.stoploss)
            log.info(' ! I have updated '+str(position.asset.symbol)+'- (Short) to stop @ '+str((position.last_sale_price)*(1+ c.stoploss)))    
    
    
    my_stat_print(context, data)    # Only happens at algo end
There was a runtime error.

Charles, can this be implemented live through Quantopian? Or is it best to "paper trade" with Quantopian?

Is there any data delay with futures numbers?

In ib you can trade live, robinhood seems unstable.

Is it stable in paper trading through Quantopian

@Peter,
That looks promising. Like you said, it needs some tuning.

@Tyler,
Paper trading has a 15 minute delay, I think. Somebody correct me if I am wrong.
A real negative of paper trading is that it does not auto update your current positions when you start it or after you make manual trades.
Paper trading tends to be very stable from what I've seen.
Live through stock broker sometimes quits unexpectedly. That is why I paper trade an exact copy at the same time for reference and comparison.
Obviously trading live is probably worth it most of the time, because trades are automatically sent to stock broker.

Yes, it is 15 minute delay - To get live trades, couldn't I setup an IB "paper trading" account and just watch for trades on there?

Also the noko.csv file - what is that? Is this manually updated?

Actually, I do not know the answer to either of those questions about IB or the noko.csv file.
From what I understand, noko.csv is a necessary temporary thing until we can use futures to determine stock trades using the trading calendar.
I think I said that right.

Ok, some real credit goes to Peter, for the trailing stop loss code.

I did some code clean up and put the context.stoploss factor variable into RogueTrader section. The stoploss is now different based on UVXY or others.

This does appear to be an improvement. This is what I am now trading.

Clone Algorithm
440
Loading...
Backtest from to with initial capital
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 datetime
import functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from math import trunc  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    
    
def before_trading_start(context, data):

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)

def RogueTrader(context, data):

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.40 if stock is context.UVXY else 0.15

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.25
        MaxOrders        = 10   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.02 if context.Agree else 1.01
        else:
            LimitPriceFactor = 1.05
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    XIVprice  = data.current(context.XIV, 'price') 
    UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    record(XIV  = XIVprice)
    record(UVXY = UVXYprice)
    record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if context.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if context.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, context.VIXprice, XIVprice, UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
            cancel_order(order)
    
def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s
  
def handle_data(context, data): 
    if context.ShowMaxLev:
        if context.account.leverage > context.mx_lvrg:  
            context.mx_lvrg = context.account.leverage  
            record(mx_lvrg = context.mx_lvrg)    # Record maximum leverage encountered
    c=context
    for position in c.portfolio.positions.itervalues():
        if position.amount == 0:
            if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
            continue
        elif position.asset.symbol not in c.stops:
            stoploss= c.stoploss if position.amount > 0 else -c.stoploss
            c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
            log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
        elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
            #sell
            log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
            if not get_open_orders(position.sid):order_target_value(position.sid,0.0)
            c.stoppedout.append(position.asset.symbol)
        elif c.stops[position.asset.symbol] < position.last_sale_price and position.amount < 0:
            #sell
            log.info(' ! '+str(position.asset.symbol)+'- (Short) has hit stoploss @ '+str(position.last_sale_price))
            if not get_open_orders(position.sid):order_target_value(position.sid,0.0)
            c.stoppedout.append(position.asset.symbol)
        elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
            c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
            log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
        elif c.stops[position.asset.symbol] > position.last_sale_price*(1+ c.stoploss) and position.amount < 0:
            c.stops[position.asset.symbol]=position.last_sale_price*(1+ c.stoploss)
            log.info(' ! I have updated '+str(position.asset.symbol)+'- (Short) to stop @ '+str((position.last_sale_price)*(1+ c.stoploss)))    
There was a runtime error.

Sorry guys, difficult to avoid the horrible max drawdown. I made adjustments that fit my preferences.
This is what I am now trading.

Clone Algorithm
440
Loading...
Backtest from to with initial capital
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 datetime
import functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from math import trunc  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)

def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.33 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.03 if context.Agree else 1.02
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    XIVprice  = data.current(context.XIV, 'price') 
    UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    record(XIV  = XIVprice)
    record(UVXY = UVXYprice)
    record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if context.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if context.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, context.VIXprice, XIVprice, UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s
  
def handle_data(context, data): 

    if context.ShowMaxLev:
        if context.account.leverage > context.mx_lvrg:  
            context.mx_lvrg = context.account.leverage  
            record(mx_lvrg = context.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if context.RogueTrader: # Not True until RogueTrader scheduled function runs
        c=context
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                order_target_value(position.sid,0.0)
                c.stoppedout.append(position.asset.symbol)
There was a runtime error.

Hey Charles, the VIX spike of 50% and the UVXY of 27% last Thursday, how did this fair through your program? Trying to see what this would do with a serious spike

@Nicholas,
I did very well with those by manually intervening. I turned $31,000 into $41,000 riding it both directions.
That was with me watching it every minute for several days and watching Fox Business News, etc.
It looks like it would not have done very well in that one if left on autopilot.
I had near perfect timing manually.
It is difficult to code that. There are so many factors that cause the direction to reverse either overnight or mid trading day.
I programmed it to do well on average. The very nature of that means there will be events that surprise and catch the algo flat footed. That is one that I would have lost some money if on autopilot. At least that is what the backtests show. This also illustrates the difficulty in trying to avoid all drawdowns.

Charles, if you test this back further - say 2013 the drawdowns are pretty big ~40% or so.

It is very challenging to minimize DDs and maximize profits.
xiv-shotgun-trading-inverse-vix-with-wvf is excellent at minimizing DD while still getting a very respectable return. I do recommend taking a good look at that algo and seriously consider using it.
However, if I am trying to maximize profits, it seems like I have to accept an occasional DD.
I have spent some time wondering how I could implement some of the ideas in that xiv shotgun algo in order to avoid some DDs.
So far, how to do that without losing large chunks of maximum profit, has escaped me. But I am very impressed with that algo!
I have a much higher risk tolerance than most. But I am sure that xiv shotgun algo will be just right for some.

Hi there, I tried algos with fetcher before. There was a 3-5 days delay before the algo would start trading. Is it still the case for this algo?
Thanks.

Ok, I really really tried to lower the DDs again. I ended up squeezing out some more profit, but lower DDs kept chopping down my returns. So I went for the bigger returns.

@Dat,
I believe it starts trading immediately as long as it sees some clear signals.

Clone Algorithm
440
Loading...
Backtest from to with initial capital
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 datetime
import functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from math import trunc  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)

def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.06 if context.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):

    if 0 < context.SellAndWait:
        context.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    context.XIVprice  = data.current(context.XIV, 'price')
    context.UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    record(XIV  = context.XIVprice)
    record(UVXY = context.UVXYprice)
    record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if context.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if context.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, context.VIXprice, context.XIVprice, context.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s
  
def handle_data(context, data): 

    c=context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
There was a runtime error.

Added Blue 's PVR code from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180

Clone Algorithm
440
Loading...
Backtest from to with initial capital
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 datetime
import functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from math import trunc  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        #record(Leverage=context.account.leverage)
        pass

def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.06 if context.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):

    if 0 < context.SellAndWait:
        context.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    context.XIVprice  = data.current(context.XIV, 'price')
    context.UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    #record(XIV  = context.XIVprice)
    #record(UVXY = context.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if context.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if context.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, context.VIXprice, context.XIVprice, context.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):  
    ''' Custom chart and/or logging of profit_vs_risk returns and related information  
    '''  
    import time  
    from datetime import datetime  
    from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Pacific'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 0,    # Cash available  
                'record_max_lvrg' : 1,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 1,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 0,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c=context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            #record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
There was a runtime error.

Hi Charles,
I am having difficulty understanding why you need the noko.csv file. It seems like you are trying to figure out if contracts are in contango. This task is accomplished easily internal to the Q environment. If you need any guide on how to do that take a look at this post by Jamie under Offset, he creates a normal history curve with the Futures prices inside the environment: https://www.quantopian.com/posts/futures-data-now-available-in-research.

Let me know if I have misinterpreted the problem, we at Quantopian are always trying to understand the communities pain points as best as possible.
Thanks!

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

@Tanay,
Thanks for looking into this situation.
Please see this link: https://www.quantopian.com/posts/deployed-two-xiv-slash-uvxy-slash-tqqq-strategies-for-paper-trading-dot-dot-dot-will-make-it-live-trading-after-a-month#5938da6df4c19700101aa580
That is a verson that does not use noko.csv I believe.
However, it does not work for live trading.
"Futures not allowed for live trading" error is given upon attempt to paper trade or broker trade. Maybe that has changed since 4 weeks ago. That would be cool if it did.

No big change here. I just tweaked the custom graph stuff.

Clone Algorithm
440
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from math import trunc  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    

    #VXX used for strategy to buy XIV
    context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.06 if context.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):

    if 0 < context.SellAndWait:
        context.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(context.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = context.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else context.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(context.vxx, 'price', context.AvgLength, '1d')
    context.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(context.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(context.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = context.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    context.SmoothedWVF1 = talib.EMA(WVF, timeperiod=context.LengthEMA1) 
    context.SmoothedWVF2 = talib.EMA(WVF, timeperiod=context.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > context.SmoothedWVF1[-1] and WVF[-2] < context.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > context.SmoothedWVF2[-1] and WVF[-2] < context.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(context.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < context.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > context.vxxAvg and vxx_prices[-2] < context.vxxAvg) or (WVF[-1] < context.SmoothedWVF2[-1] and WVF[-2] > context.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (context.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        context.ShortVIX    = True
        context.LongVIX     = False
        context.Agree       = True

    elif (context.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = True

    elif (context.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        context.ShortVIX    = False
        context.LongVIX     = True
        context.Agree       = False

    for stock in context.portfolio.positions:
        if stock not in context.VIXstocks and stock not in context.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = context.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = context.portfolio.positions

    if context.BigSignalTQQQ:
        if LevTooLow or (context.XIV in p or context.UVXY in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, 0.00)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.ShortVIX:
        if LevTooLow or (context.XIV not in p):
            TarPer(context, data, context.UVXY, 0.00)
            TarPer(context, data, context.XIV, context.VIX_GrowthLeverage)
            SetAsideStocks = context.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(context.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif context.LongVIX:
        if LevTooLow or (context.UVXY not in p):
            TarPer(context, data, context.XIV, 0.00)
            TarPer(context, data, context.UVXY, VIX_HedgeLeverage)
            for stock in context.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    context.XIVprice  = data.current(context.XIV, 'price')
    context.UVXYprice = data.current(context.UVXY, 'price')
    TQQQprice = data.current(context.tqqq, 'price')
    #record(XIV  = context.XIVprice)
    #record(UVXY = context.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if context.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if context.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if context.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if context.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if context.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, context.VIXprice, context.XIVprice, context.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            RhMargin = 12000.00 # Set to 0 if you do not have Robinhood Gold
            DoNotSpend = 600 # Try to prevent margin - related rejected orders
            RhPV = context.portfolio.portfolio_value + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c=context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
There was a runtime error.

Hey Charles I'm pretty new does your code have training stop losses?

Also would this code be ideal for Robinhood under $25k?

@Nicholas,
trailing stop losses?
yes, for XIV is probably close to what you are expecting, even though is tuned to maximize profit
yes for UVXY, however is tuned to maximize profit, larger losses are definitely possible
yes, for TQQQ is probably reasonable for what you are expecting, same setting as XIV
Rh under $25k?
not specifically, but it works well enough. TURN ON Rh day trade protection for additional day trade protection
I started out with about $5,000 before I increased to $31,000, so yeah the little investor is probably ok for this. Not claiming $31k is a lot, just for me it is.

Make sure you customize the TarPer section to your preference, especially the Rh Gold margin code.

However, the nature of these investments is that they are volatile.
You and I can and will lose lots of money.
Do your own research and make your own decisions.

Looks like some solid stuff to look into. My opinion is u should not be too aggressive.

Most recent algo sold off the XIV position on close of business, let's see how this progresses.

Yep, mine sold all XIV by 29 minutes before market close today.

yep, times like these...
you feel like you may be going down with the ship

Earlier today I back tested with:
after stop loss, wait for change of signal before buy again
result was slightly less profit, but not much difference otherwise

so ... left it alone ... not clear what to do
wait and see is probably best, as long as your already in place plans are reasonable
manual intervention can be appropriate
but it is challenging, because you are 2nd guessing your already existing plans
best wishes to all and to all a good night!

by the way, I like Blue ' s PVR code,
it makes my custom graphs purrrrty!

Yes today was a little crazy, still paper trading this one, Do you know who to get the shotgun xiv to work on live trading mine just sits there and doesn't buy. Not sure if it just hasn't met the signals to buy or just not trading due to code. Also whats the lowest Max Drawdown you were able to get while still remaining fairly profitable? I know earlier you said you were messing around with it but would cut into profits

As far as shotgun xiv algo goes, I think Kory Hoang is willing to collaborate. It says at top of algo:

For collaboration requests, email

To make the ordering a little more similar to mine:

add TarPer and DataCanTrade functions at end

Replace every

order_target_percent(context.BOND, context.BONDpct)

with

TarPer(context, data, context.BOND, context.BONDpct)

Do similar with all the

order_target_percent  

As far as MDD goes, for sure not better than shotgun xiv algo.
I was able to lower DDs somewhat (and obviously profits as I previously said) by:
adding c.SellAndWait = 1 in handle_data function:

log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
c.stoppedout.append(position.asset.symbol)
TarPer(context, data, position.sid, 0.00)
c.SellAndWait = 1

The 1 can be 2, 3, 4, whatever. You can also tighten up context.stoploss = 0.21 if stock is context.UVXY else 0.035 in RogueTrader function.
The problem with stop loss is that it either is locking in your loss or preventing further loss or in our case preventing higher profits because of difficult to avoid volatility.
If you get a series of stop losses in a row, then you just lost a lot of money.

I have not studied the signals in shotgun xiv algo. I will leave that to you and others.
However, I just now looked at a year long back test of the shotgun xiv algo. The last time it purchased was around May 25th, 2017. It buys at market close and sells the next morning at market open. There are things about that algo that are very appealing. You are in cash for most of the time. Check it out. You might like it. Here is the link to the shotgun xiv algo. I started paper trading it, so I can keep tabs on when it makes a move. Maybe that can inform our manual interventions. Or maybe not, the whole apples and oranges thing. But did I mention that I like that shotgun xiv algo?

Now that I seem to have this algo at a stable milestone, I am taking another look at my old algo Robin Hood Extreme Vetting

It tends to use very low leverage. If I could fit it in with this Robin Hood VIX Mix Rogue Trader, without breaking it or killing the returns, then I believe there could be some benefits to that arrangement.

Now that I think about it, maybe the shotgun xiv algo could be included. Or maybe shotgun xiv algo could be mixed with robin hood extreme vetting. Hmmmm.........

@Charles hi i'm pretty new to this but this looks very interesting!

If you were going to live trade with RH using this algo, is there a recommended minimum amount of money needed to invest? My RH has about 60 bucks in it which I've hooked up to the algorithm just for tests, but I have about $2500 in other trading accounts I feel comfortable working with. Right now the algorithm isn't doing anything with the 60 bucks I have in RH but I'm wondering if that's either because the algo hasn't deemed anything worth trading, or because it's trying to but I don't have enough money linked up.

Thanks for your hard work!

@ Matthew

You don't have enough money to even buy 1 share that's why it doesn't work for you.

@Charles @Jay

Re: the stop losses...the jury is out IMO as to whether or not they make sense here. I find that XIV will sometimes partially recover a given day's losses after and / or pre-market (e.g. its 1 point upside move from yesterday's close to today's open). Yesterday's triggered stop did not allow for recouping some of those losses because it exited XIV before close. Add to that the issue that Q had this morning whereby I had to restart the algo (in RH live trading) only to find out after open that the restart failed...so the algo missed today's XIV run-up, though I manually traded it this morning which helped.

So, at least in this case, the stop loss virtually locked in yesterday's XIV losses as Charles mentioned. I almost got taken out to the wood shed on that one. :)

I don't claim to know the correct answer here re: stops; maybe just wait and see.

Charles - thank you for your work on this algo thus far.

@ Chris - Not sure what version you are running, but the latest went all in UVXY this morning and is still holding.

@Chris @Tyler

Probably the same version. I would guess that the live algo stopped and later was started again, .... after the scheduled rebalance. Situations like that, is why I also run a clone of the algo in paper trade. Not perfect, but it gives you something to double check in situations like this.

XIV obviously did better than UVXY on Friday. but the algo is in UVXY at the moment
I manually went even further in at $10.32.. so now average cost $10.47... We shall see if that hurts or helps me next week.
I think I have reasonable odds, that either the bears will raise their heads again, or news events may cause some volatility.
I may temporarily turn off the live algo before market open, and refer to the paper trade. Haven't decided yet.

@Charles - agreed. There seems to be more bearish chatter in the current climate. We shall see how things progress.

@Charles @Tyler

I'm running (one of) the latest versions and, yes, the algo was started after the scheduled re-balance so I missed the entry into UVXY. That turned out to be a good thing on Friday, but I have the algo running again so let's see what it does on Monday.

For those that have the algo live trading, do you currently have the stops enabled?

I am considering, setting a manual limit sell of UVXY to cause the rebalance orders to be rejected. That way I can leave the algo running. After the rebalance orders get rejected, I would then cancel the manual limit sell of UVXY. That way the Rogue Trader limit sells will still be placed 1 hour after market open. That has the effect of delaying the rebalance by 1 day. And I could manually intervene if seems appropriate.

Feeling a bit wishy washy about it at the moment though. We shall see, as we get closer to market open.

Have you ever considered adding a buy rejection if contango is too high? For UVXY.. Just a thought!

Just an update from me: if you recall, I manually opened a position in XIV on Friday. Today XIV performed well and the algo sold my position around midday, locking in some profits. So I'm currently flat.

I have the latest version of the algo running live in both RH and paper, so we'll see what tomorrow brings.

@Chris
My paper trade is doing just like you described.
My live algo, I did as I said. I placed a limit sell for UVXY to cause the rebalance orders to be rejected. Then just before 1 hour after open (Rogue Trader), I cancelled my limit order, to allow the algo to place limit sell orders. I will have to do that each morning that I want to delay the rebalance.

By the way, this is the handle_data code that activated today and sold XIV, locking in some profits, and hopefully avoiding some losses from tomorrow.
if (
(position.sid is c.XIV and 0 < position.amount)
and
(10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
):
c.SellAndWait = 1

The SellAndWait = 1 means sell everything and skip 1 rebalance. So I believe it is going to skip tomorrow morning's rebalance in my paper trade. That code helped the back test results.

Feel free to manually intervene when rebalance is skipped.

The August recess for the US Congress begins July 31st. Between now and then could be interesting.

@Nick G
Do you have any proposed settings or code? This algo is largely crowd sourced, so if you have any ideas.

@Charles

Thanks for the info. Skipping rebalance are (for me) tough, especially after a day or two of big losses. But I agree with the general approach, as it does improve backtest results as you had indicated, and so I am live trading that code. It's just that the market has been all over the place lately.

Maybe the key here is to stick to the strategy and let the algo do its job, with perhaps some manual intervention on big down days. I know that I need to learn to follow that rule!

Charles,

I will try to see if I can get you any real time contango percentages.. best strategy I've had is anything under 4-5%, I simply do not play UVXY short. I'll be looking into it for you.

Continuing, I never play UVXY long unless VIX hits a 2.85-3 standard deviation event.

@Nick G
Forgive me if my questions are amateurish.
Would that be for strictly day to day deviation? Or intraday? Because I believe so far we only have access to one VIX read per day in the algos. And that is only for VIX up deviation not down deviation right?

Manually got out of a long XIV pos right before that recent jump. This might be too much risk for me to handle for a while, might consider the Shotgun algo for a period.

You could always combine the algos into one and make it so you can allocate percentages of your cash to either algo ex 60/40 0/100 and so on.

Charles,

This is exactly my formula

Long XIV: 5-day average of [VIX index – (2-day historical volatility of S&P 500 * 100)] > 1.25
Long VXX or UVXY : 5-day average of [VIX index – (2-day historical volatility of S&P 500 * 100)] < 1.25

I have made amazing gains from this with 10k back in 2004, you would have near 24M a decade later with 47% max DD (being in the drawdown only 210 days).. A return of 109% annualized

Have had 74% winning months, Sharpe Ratio of approx 1.65

Still trying to learn code here.. But this is a great strategy that, for me, has been time tested.

This strategy had actually made money during the the Global Financial Meltdown 08-09. Very promisiing for the coming years as market conditions may change quickly.

If there is a possibility of you entering/modifying this into your code I'd be very excited to see the results.

@ Nick G, help me out here

Can you give an example of your calculation - say for trading today?

What is the 2 day historical volatility of S&P500?

XIV Long
VXX/UVXY Long

@Tyler Search "Volatility Risk Premium". Here is an old thread on Q.

If you want to go down the rabbit hole of strategies, check out tons of them at volatilitymadesimple. Bring something for the eventual headache.

http://volatilitymadesimple.com/blog/page/3/

Here is another strategy similar to mine. Great results. Just looking to automate something similar.

@Charles Witt
Looking for standard deviation jumps in the VIX is likely intra-day. It's unfortunate that we can't get minute ticks on CBOE:VIX... we'd be picking up some pretty good gains!

You could use Synthetic VIX, based on SPY. SPY MACD crosses and volume momentum do give a couple minute lead time on VIX (and derivative ETFs) but I haven't had much luck coding for those conditions. Sucks when you have a chart with great technicals set up, but can't translate "signal smoothing" to talib.MACDEXT. It's also difficult to reproduce signals per tick because there are minute differences in Q data and other sources.

@Nick G,
This algo might be close to what you are asking for.

Take a look at it. I tried the 2 days with it,... but it does better with the 5 days setting that is already in there.
Let us know if that is close to what you are seeking. If it is, then maybe, if it seems appropriate we will try to add that signal to this algo. We already have Synthetic VIX integrated. At this point, I am not sure that adding that VRP signal will help. But I suppose we can brainstorm it. I am not an expert at this stuff.

Charles, thanks for all the updates on the evolution of this interesting algo over this thread and the previous thread.

How often does the algo fail in live trading with RH? What do you believe is causing the failure?

Have you found a way to backtest over periods of economic stress, such as 2007-08? If not, how do you anticipate it to fare? DD is quite large during stable times, albeit with great returns.

@Nick - I manually backtesting your strategy and there is a huge drawdown in 2015 - greater than 60%

So I noticed that the algorithm didn't sell my shares of XIV and TQQQ last night before markets closed on my paper trading account. It also bought about $1500 over the amount I allocated for trading with this. With my IB account I had the funds in the account so the algo took it but my buddy who's live trading with RH got an error that he didn't have enough funds in his account when it did the same for him. Anyone else run into this this morning?

Matthew - I did notice that my paper trading algo also goes into negative cash. Seems to be consistent that this dips into a little leverage, which is why your buddy's RH threw an error - unless he has GOLD.

My algo is holding TQQQ and XIV - there are some take profit limit orders for today, but none have hit yet

@Tyler

Is that planned or an error on the algorithms part? I was under the impression that the algorithm is set to sell off all shares before market closes. I guess I'm just a little confused on what the strategy here is. Is there any documentation that discusses what this algorithm is going for here? I'm hesitant to start adjusting code until I get a better sense of the strategy.

It's dipping because the code has margin you need to change to 0 if you do not have a margin account in Robinhood

I believe it has a percentage it uses on a limit take profit. You can always manually intervene and sell

@Tyler

If your friend is trading RH, he may have pattern day trading protection enabled. It is this way by default. In order to pattern day trade you need to have an excess of $25000.00 in equity in the RH account. A day trade is counted as buying and selling a position in the same day. If your algo takes several transactions to fill or sell, each of these count as a "day-trade". With that said, the pattern day trading protection kicks in quick if you are using an algo that trades at open and close.

Did anyone see that UVXY is 1:4 reverse splitting, effective 7/17 (Monday)? https://finance.yahoo.com/news/proshares-announces-etf-share-splits-201500038.html

I'm wondering if we'll need to update the ratio / threshold values in the signals of this algo. Thoughts?

I saw it. My Rh phone app says my
UVXY average cost per share is $41.83 (was $10.46).
The current price per said app says $8.45. I assume that will change to $33.80 between now and Monday morning.
It also says, this stock is temporarily untradeable.

As far as adjustments, I doubt it. Has anybody been through one of these before? If so, share your wisdom.

I am fairly impressed with how well the algo did through this last several weeks, considering all the craziness.
I also noticed that XIV Shotgun purchased XIV at $85.66 at market close 2012-07-12. It has not sold yet. Pretty good cherry pick so far!

I need to review the code in more detail for my own sanity, but you may be right.

Agreed, the algo has been performing well, all things considered. I'm waiting for XIV to drop more than a few points, though. On an unbelievable tear since Feb 2016.

I added a Cherry Picker function. It is 90 something % derived from XIV Shotgun algo.
I don't go essentially no risk with bonds like XIV Shotgun does. I just use the signal mostly to sell everything except XIV and go all in XIV for a short number of days, often just overnight. The profit is noticeably improved, but not huge. The DDs are occasionally trimmed, but just barely from what I have seen. The aforementioned XIV Shotgun algo is an excellent algo in my opinion.

Make sure you adjust the TarPer section for Rh Gold and DoNotSpend.

You and I can and will lose a lot of money. Do your own research. Make your own decisions.

Clone Algorithm
87
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    #set_benchmark(context.xiv) # careful, ALPHA BETA SHARPE will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.06 if context.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, VIX_HedgeLeverage)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # How much cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            RhMargin = min(RhMargin, PV * 0.33)
            RhPV = PV + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c=context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if ((WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1]) or (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1]) or (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

Still new and trying to figure out how everything works. How does the algorithm grow returns exponentially over time? It seems that we sit between 150% - 300% returns the first year and then at some point it skyrockets to 1000% returns. I'm probably being naive but how does that work exactly?

The VIX: Using The "Uncertainty Index" For Profit And Hedging Google search for
fear index
uncertainty index
volatility trading

If you just wanna dip your toes in the water. Maybe XIV Shotgun algo
However, you may fall into a coma waiting for it to do something. However, that is the beauty of it. It sits in bonds 90 something percent of the time. It cherry picks apparently maybe 3 or 4 times per year some high probability plays with VIX related ETFs. The rest of the time, you are in bonds.

Keep in mind, there is plenty of risk. You can and will lose a lot of money. Do your own research. Make your own decisions.

@Matthew Keep in mind that backtests are done with compounding funds. If an algo actually pulls off 100% gains every year and no money is taken out, it's going to look like a rocketship.

Until UVXY is publicly available for purchase again, what will be everyone's substitute, TVIX?

I am expecting UVXY to be available before trading begins Monday morning because there are not supposed to be gaps caused by splits. How is that possible unless the splits are timely and the stock remains tradeable during all trading hours.

The bottom line is that a stock split causes no change in the value of
a company or in the value of an investor's shares, nor does it cause
any gaps in a stock's chart, because past price movements are adjusted
for the split.

Read more: Does a stock split lead to the gapping up/down of the
stock?
http://www.investopedia.com/ask/answers/05/splitleadgapping.asp#ixzz4n2aH8TC5

I am not a professional and I do not advise one way or the other.
However, I do suggest to be careful. If you are risk averse, maybe stay away from my algo.

From https://seekingalpha.com/article/4087880-crash-cometh-4-6-weeks

For conservative investors, what needs to be done starting today is to
scale out of all equities on up days (except special situation stocks
with very short-term catalysts) over the next 3 weeks. There isn’t
much more time than that. By mid-to-late August, conservative
investors should be mostly cash.

For risk takers, please keep in mind that these predictions may still
be wrong, so do not bet the farm. For those who cannot trade options,
scaling into the Credit Suisse AG - VelocityShares Daily 2x VIX Short
Term ETN (TVIX) or the ProShares Ultra VIX Short Term Futures ETF
(UVXY) is the best choice if you’re playing the indexes and the VIX.
The VIX is likely to shoot past 40 by September. In 2015, TVIX and
UVXY both more than tripled from mid-July to Black Monday August 24th.
Whatever happens, these positions should be liquidated before October
at the very latest, whether they pay off or not.

No change.

I just changed the benchmark to XIV. I actually find this more useful than SPY as benchmark.

Clone Algorithm
87
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # careful, ALPHA BETA SHARPE will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.06 if context.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, VIX_HedgeLeverage)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # How much cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            RhMargin = min(RhMargin, PV * 0.33)
            RhPV = PV + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c=context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if ((WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1]) or (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1]) or (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

The Seeking Alpha article is interesting yet very concerning. Do you back the articles claims? Have you been able to find other articles claiming the same? How was the algo able to predict the 2015 event? Could it predict it again this time around?

I personally am convinced that there will be enough lack of progress and slowness in implementing President Trump's agenda, that at least temporarily there are likely to be significant pullbacks / DDs. There are factors and opinions pushing in both directions. So nothing is certain, especially the timing of pullbacks. Beyond that, I am as much a fool as the worst of them. One thing I have learned is that the market has a way of surprising in both directions. So, again, what do I know? As far as 2015, there were DDs, but there were many more surges. I posted that article because I know that DDs can be shockingly fast as well as a total surprise to most people. Keep in mind, that bears also lose money when they are surprised by extended bull markets.

Hi, I am using a version of your algorithm with 1k in a regular robin hood account. I have ran it for a week but keep running into a problem. After the first day my daily orders are being rejected. The algorithm is trying to buy 1 or 2 shares more than I have money for. How might I go about fixing this? I changed the margin number to 0 and made a few other minor adjustments but having no luck. Thanks for all your insights!

^ I have literally the same problem as the above poster (regular Robinhood, and margin is set to 0). Any thoughts you could provide would be appreciated. In my specific case, it tried to buy 4 more shares of UVXY than I had funds for. Should I complete the transaction manually and reload the algo?

Hey guys, it sounds like you should increase DoNotSpend in the TarPer function. Increase it enough to make sure there is enough cushion to prevent rejected orders. Then stop / start the algo. Then manually purchase a reasonable number of shares. Then manually place some reasonable limit sell orders. Also, if there are big gains today in your share price, you should probably sell the remainder of your shares to take profit. Tomorrow, the algo will be on schedule to hopefully do those things automatically for you.

Thanks, I figured that's what it was but I wanted to be sure before I changed it - I'm obviously relatively new to Quantopian and even with my programming background it's never wise to fiddle with cloned code unless you ask someone who knows more first.

I'd read that Seeking Alpha article already...

Seeking Alpha will publish anything, and I think there's a lot of market manipulation going on through that site. That said, a lot of novel insights can also be gleaned from SA articles, but for me it's the comments section that is absolutely invaluable. Almost all of the comments on that article are calling bullshit on his rationale.

How do you think this would do in a bear market? Does UVXY off set?

First, Charles, thank you for all the development and the help of the community.

Did the algo purchase UVXY for anyone today? My algo cashed out all my XIV and TQQQ position and longed on UVXY this morning. I'm trading the Jul 3rd version and not the latest. On the other end, the Shotgun algo only made one trade on XIV since 7/12. I'm trying to understand the rationale for going long on UVXY. Thank you!

Mine also purchased UVXY. I am also trading using the July 3rd version.

From what I understand from the code, there's 12 scenarios for target security, and 4 of them point to UVXY, including the catch-all #9. I'm not entirely sure which one was triggered, I might add logging for that to my personal modifications of it next time I deploy it.

Mine tried to buy UVXY but failed with the message "Unkown security" ...

My basic Robinhood account was definitely able to buy it. I'm not sure that it should have, but it definitely could.

Right now it says UVXY not traceable on Robinhood?

tradeable

I fired up the latest version in IB today. With VIX so low I expected UVXY to trade. It bought XIV and rejected tqqq. It appears that the algo did not compute the cash available correctly. Any thoughts?

@Nicholas Johnson: my RH is showing UVXY as available. Were you checking before markets opened?

All,

Please try to keep personal comments of UVXY/ stock availibility to a minimum! I mean this as nicely as possible. These are naturally occuring problems that wont improve the formula-- less clutter to see/improve the mechanics as the algo was intended, thank you!!

But it's just my .02

@nickg its a bit of a odd comment Nick, if an algo creates a signal that is not tradeable we need to know and we need to protect the algo from it and improve the algo. It’s highly relevant!

There was an issue with my Robinhood app today I had to close it and reopen and then it let me trade it so weird

I am new to Quantopian and I am trying to wrap my head around this algorithm, so sorry in advance if this question is obvious. I am wondering why you fetch the csv files in the initialize() function when the files are updated daily? Wouldn't you need to fetch them with a daily scheduled function?

@Dan Codos
from https://www.quantopian.com/help

If your algorithm uses fetch_csv(), it is important that all
historical data in your fetched data be accessible and unchanged. We
do not keep a copy of fetched data; it is reloaded at the start of
every trading day. If historical data in the fetched file is altered
or removed, the algorithm will not run properly. Providing additional,
new data in the file for dates in the future is fine. If you are
updating the CSV, add new data before midnight Eastern Time so the
file is ready for the next trading day.

Ok folks, manually intervening, not letting the algo switch back and forth, I just now lost about $20,000. I am now down to $21,000.
A fool and his money are soon parted.
On the upside, I was watching the algo paper trade, which seemed to be profitable nearly every day.
So, in the future, if I manually intervene, it will only be intraday or at most 2 days, to prevent boneheaded large losses.
Having said that, the algo will not always be right and will sometimes have a large drawdown.
It is very possible that you and I will lose a lot of money.

I added trailing stop loss buys for both XIV and UVXY. More profit squeezed out. Minimal impact on max DD.

Clone Algorithm
314
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # ALPHA and BETA will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            c.BoughtShortVIX = False
            c.PriceXIV = 0.00
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # How much cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            RhMargin = min(RhMargin, PV * 0.33)
            RhPV = PV + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c = context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # Buy XIV
    ##
    if c.ShortVIX and not c.BoughtShortVIX and not c.SellAndWait:
        PriceXIV = data.current(c.XIV, 'price')
        if not c.PriceXIV: c.PriceXIV = PriceXIV
        if PriceXIV < c.PriceXIV:
            c.PriceXIV = PriceXIV
        elif PriceXIV > 1.0025 * c.PriceXIV:
            c.BoughtShortVIX = True
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)

    ##
    # Buy UVXY
    ##
    if c.LongVIX and not c.BoughtLongVIX and not c.SellAndWait:
        PriceUVXY = data.current(c.UVXY, 'price')
        if not c.PriceUVXY: c.PriceUVXY = PriceUVXY
        if PriceUVXY < c.PriceUVXY:
            c.PriceUVXY = PriceUVXY
        elif PriceUVXY > 1.005 * c.PriceUVXY:
            c.BoughtLongVIX = True
            TarPer(context, data, c.UVXY, c.VIX_HedgeLeverage)

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if (
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1])
        or
        (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1])
        or
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])
    ):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

@Charles Sorry to hear that. Similarly, I sometimes try to offset algo losses manually with varying success, and I try not to hold longer than intraday. The hard part (for me at least) is that the volatility in XIV, UVXY, etc. is such that when I try to play them on an intraday basis, I often get stopped out at a loss only to find out that I was right had I sat in the position a bit longer.

I like your latest version of this algo. I'm now live trading it in RH. I'm also live trading another VIX algo (the one from which you sourced the 'more signals' code) in IB. I think prefer Rogue Trader, though I haven't tried it in IB just yet.

Ok, I am trying a strategy that might be a good fit for me, since I tend to keep close watch of the market.

Live trade https://www.quantopian.com/posts/robin-hood-extreme-vetting (only zero commissions work for this one)

Manual trade (by referring to paper trades of):
https://www.quantopian.com/posts/robin-hood-vix-mix-rogue-trader
and
https://www.quantopian.com/posts/xiv-shotgun-trading-inverse-vix-with-wvf

Automatic trailing stop loss buys and sells will be missed. But I think I will be ok by making manual trades after the fact.

If I want a break from watching it, then I could
stay in XIV and TQQQ which both tend to do well most of the time
or
switch live trade from RH Extreme Vetting to RH VIX Mix Rogue Trader

As a side note, if you have a position that you don't want the live trade algo to sell:
Manually place a GTC sell limit order at a price
that you like
or
that will not fill because it is extreme
Manually placed orders never get replaced by algo placed orders.

While collaborating with Nick Sulzer, I discovered that the TarPer function had logic errors in it. I fixed it. So now it should get closer to desired margin usage.

Clone Algorithm
314
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # ALPHA and BETA will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            c.BoughtShortVIX = False
            c.PriceXIV = 0.00
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # How much cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            RhMargin = min(RhMargin, PV * 0.33)
            RhPV = PV + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue   = float(amount * price)
            TarValue   = float(RhPV * TargetPercent)
            DiffValue  = float(TarValue - PosValue)
            DiffAmount = int(DiffValue / price)
            DiffAmount = 0 if 0 > DiffAmount and 0 == amount else DiffAmount
            order(stock, DiffAmount)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c = context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # Buy XIV
    ##
    if c.ShortVIX and not c.BoughtShortVIX and not c.SellAndWait:
        PriceXIV = data.current(c.XIV, 'price')
        if not c.PriceXIV: c.PriceXIV = PriceXIV
        if PriceXIV < c.PriceXIV:
            c.PriceXIV = PriceXIV
        elif PriceXIV > 1.0025 * c.PriceXIV:
            c.BoughtShortVIX = True
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)

    ##
    # Buy UVXY
    ##
    if c.LongVIX and not c.BoughtLongVIX and not c.SellAndWait:
        PriceUVXY = data.current(c.UVXY, 'price')
        if not c.PriceUVXY: c.PriceUVXY = PriceUVXY
        if PriceUVXY < c.PriceUVXY:
            c.PriceUVXY = PriceUVXY
        elif PriceUVXY > 1.005 * c.PriceUVXY:
            c.BoughtLongVIX = True
            TarPer(context, data, c.UVXY, c.VIX_HedgeLeverage)

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if (
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1])
        or
        (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1])
        or
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])
    ):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

@ Charles - My paper trade is up 6-7% right now. Are you saying your manual intervention lost that money?

@Tyler
exactly right
if I had left it on autopilot, instead of preventing the auto trades, then it would have made a profit nearly every day

Live trading on IB account selected UVXY today. The order was rejected. I think it is placing the buy order before selling the current positions. Any thoughts?

Clone Algorithm
1
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # careful, ALPHA BETA SHARPE will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.2 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = 0.666 # unless all positive conditions are met
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0.25 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    context.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    for stock in context.portfolio.positions:

        # Set stoploss
        context.stoploss = 0.21 if stock is context.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is context.UVXY else 0.22
        MaxOrders        = 20   if stock is context.UVXY else 3
        if stock is context.UVXY:
            LimitPriceFactor = 1.06 if context.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * context.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * context.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in context.stops: del context.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, VIX_HedgeLeverage)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # How much cash for withdrawals
            RhMargin = 0 # Set to 0 if you do not have Robinhood Gold
            RhMargin = min(RhMargin, PV * 0.33)
            RhPV = PV + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue = float(amount * price)
            NowLev = float(PosValue / RhPV)
            LevAdjust = TargetPercent - NowLev
            LevAdjust = 0 if 0 > LevAdjust and 0 == amount else LevAdjust
            order_percent(stock, LevAdjust)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY with the hope of a spike
        Case = 0
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        Case = 1
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        Case = 2
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        Case = 3
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        Case = 4
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        Case = 5
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        Case = 6
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        Case = 7
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        Case = 8
        target_sid = context.xiv

    # 9
    else:
        Case = 9
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        Case = 10
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        Case = 11
        target_sid = context.tqqq
    record(Case=Case)
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Pacific'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    #if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c=context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if ((WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1]) or (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1]) or (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

One of the issues are sells happening before buys, causes very quick margin - if you don't have a margin account.

update your version
that is a logic error
it does try to order UVXY before selling TQQQ
it is fixed in the latest version

Edited: I had yet another typo in there... sorry

Sorry guys! Thank you Chris Ricciuti for pinpointing the probable fix right away!
I added to initialize:
context.PriceXIV = context.PriceUVXY = 0.00
context.BoughtShortVIX = context.BoughtLongVIX = False
and I fixed the ordering in Rebalance which also had some logic errors

Clone Algorithm
314
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # ALPHA and BETA will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.PriceXIV = context.PriceUVXY = 0.00
    context.BoughtShortVIX = context.BoughtLongVIX = False
    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)
            c.BoughtShortVIX = False
            c.PriceXIV = 0.00

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # How much cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            RhMargin = min(RhMargin, PV * 0.33)
            RhPV = PV + RhMargin - DoNotSpend  
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue   = float(amount * price)
            TarValue   = float(RhPV * TargetPercent)
            DiffValue  = float(TarValue - PosValue)
            DiffAmount = int(DiffValue / price)
            DiffAmount = 0 if 0 > DiffAmount and 0 == amount else DiffAmount
            order(stock, DiffAmount)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c = context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # Buy XIV
    ##
    if c.ShortVIX and not c.BoughtShortVIX and not c.SellAndWait:
        PriceXIV = data.current(c.XIV, 'price')
        if not c.PriceXIV: c.PriceXIV = PriceXIV
        if PriceXIV < c.PriceXIV:
            c.PriceXIV = PriceXIV
        elif PriceXIV > 1.0025 * c.PriceXIV:
            c.BoughtShortVIX = True
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)

    ##
    # Buy UVXY
    ##
    if c.LongVIX and not c.BoughtLongVIX and not c.SellAndWait:
        PriceUVXY = data.current(c.UVXY, 'price')
        if not c.PriceUVXY: c.PriceUVXY = PriceUVXY
        if PriceUVXY < c.PriceUVXY:
            c.PriceUVXY = PriceUVXY
        elif PriceUVXY > 1.005 * c.PriceUVXY:
            c.BoughtLongVIX = True
            TarPer(context, data, c.UVXY, c.VIX_HedgeLeverage)

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if (
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1])
        or
        (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1])
        or
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])
    ):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

I am going to watch movie Valerian. Hopefully, I finally fixed all the problems because I won't be able to fix it for a few hours.

@Charles, Looks promising, do you live trade it right now?

Thanks for sharing guys!
Where can I set the leverage? For example 25% of portfolio_value.

@Terry
In the TarPer function,
PV = context.portfolio.portfolio_value
DoNotSpend = 0.75 * PV # How much cash for withdrawals

Thanks Charles!

My latest version of Robin Hood VIX Mix Rogue Trader is at Robin Hood VIX Mix Extreme Vetting.

Low volume stocks are a component of it. They are difficult to accurately backtest. The only real test is live trading.
If that worries you, then you should probably stay with Robin Hood VIX Mix Rogue Trader.

Hey Charles I want to change the threshold on this while I'm live trading if I stop it change it then make it live again while in a position will it cause an error? It's crazy as the Vix is in the mid 9's I think the threshold in this was 10.76

no error, but the change may cause a surprise sell probably the next morning if you are in UVXY

Updated the TarPer function to the same code that is in VIX Mix Extreme Vetting. Also added to Initialize function: context.Agree = False
Hopefully, fewer rejected orders will be the result.

Clone Algorithm
314
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # ALPHA and BETA will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.Agree                 = False
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.PriceXIV = context.PriceUVXY = 0.00
    context.BoughtShortVIX = context.BoughtLongVIX = False
    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)
            c.BoughtShortVIX = False
            c.PriceXIV = 0.00

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # 200 Cushion plus cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            MaxLeverage = 1.33 - .12 # Hard Limit for leverage minus seemingly necessary cushion
            MaxLeverage = min(MaxLeverage, max(MaxLeverage, context.account.leverage))
            RhMargin = min(RhMargin, PV * (MaxLeverage - 1))
            RhPV = PV + RhMargin - DoNotSpend  
            RhCash = RhPV - context.portfolio.positions_value
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue   = float(amount * price)
            TarValue   = float(RhPV * TargetPercent)
            DiffValue  = float(TarValue - PosValue)
            DiffValue  = min(DiffValue, RhCash)
            DiffAmount = int(DiffValue / price)
            DiffAmount = 0 if 0 > DiffAmount and 0 == amount else DiffAmount
            order(stock, DiffAmount)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c = context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # Buy XIV
    ##
    if c.ShortVIX and not c.BoughtShortVIX and not c.SellAndWait:
        PriceXIV = data.current(c.XIV, 'price')
        if not c.PriceXIV: c.PriceXIV = PriceXIV
        if PriceXIV < c.PriceXIV:
            c.PriceXIV = PriceXIV
        elif PriceXIV > 1.0025 * c.PriceXIV:
            c.BoughtShortVIX = True
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)

    ##
    # Buy UVXY
    ##
    if c.LongVIX and not c.BoughtLongVIX and not c.SellAndWait:
        PriceUVXY = data.current(c.UVXY, 'price')
        if not c.PriceUVXY: c.PriceUVXY = PriceUVXY
        if PriceUVXY < c.PriceUVXY:
            c.PriceUVXY = PriceUVXY
        elif PriceUVXY > 1.005 * c.PriceUVXY:
            c.BoughtLongVIX = True
            TarPer(context, data, c.UVXY, c.VIX_HedgeLeverage)

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if (
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1])
        or
        (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1])
        or
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])
    ):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

I just joined the community recently and want to thank you guys for all these amazing links. There is so much material to learn from, it is going to be a loooong weekend lol

I posted my version of XIV Shotgun at https://www.quantopian.com/posts/xiv-shotgun-trading-inverse-vix-with-wvf

It includes TarPer function and margin. Hopefully it just works, so I don't have to fix it.

Tiny change. It does not automatically market sell other stocks in your portfolio. It still may sell them at limit price for profit though. Place your own manual limit sell orders to reject the algo placed trades.

I am live trading this at this moment. I have temporarily abandoned Robin Hood VIX Mix Extreme Vetting which is still a work in progress.

Clone Algorithm
314
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # ALPHA and BETA will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.Agree                 = False
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.PriceXIV = context.PriceUVXY = 0.00
    context.BoughtShortVIX = context.BoughtLongVIX = False
    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)
            c.BoughtShortVIX = False
            c.PriceXIV = 0.00

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 2000 # 200 Cushion plus cash for withdrawals
            RhMargin = 24000.00 # Set to 0 if you do not have Robinhood Gold
            MaxLeverage = 1.33 - .12 # Hard Limit for leverage minus seemingly necessary cushion
            MaxLeverage = min(MaxLeverage, max(MaxLeverage, context.account.leverage))
            RhMargin = min(RhMargin, PV * (MaxLeverage - 1))
            RhPV = PV + RhMargin - DoNotSpend  
            RhCash = RhPV - context.portfolio.positions_value
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue   = float(amount * price)
            TarValue   = float(RhPV * TargetPercent)
            DiffValue  = float(TarValue - PosValue)
            DiffValue  = min(DiffValue, RhCash)
            DiffAmount = int(DiffValue / price)
            DiffAmount = 0 if 0 > DiffAmount and 0 == amount else DiffAmount
            order(stock, DiffAmount)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c = context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # Buy XIV
    ##
    if c.ShortVIX and not c.BoughtShortVIX and not c.SellAndWait:
        PriceXIV = data.current(c.XIV, 'price')
        if not c.PriceXIV: c.PriceXIV = PriceXIV
        if PriceXIV < c.PriceXIV:
            c.PriceXIV = PriceXIV
        elif PriceXIV > 1.0025 * c.PriceXIV:
            c.BoughtShortVIX = True
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)

    ##
    # Buy UVXY
    ##
    if c.LongVIX and not c.BoughtLongVIX and not c.SellAndWait:
        PriceUVXY = data.current(c.UVXY, 'price')
        if not c.PriceUVXY: c.PriceUVXY = PriceUVXY
        if PriceUVXY < c.PriceUVXY:
            c.PriceUVXY = PriceUVXY
        elif PriceUVXY > 1.005 * c.PriceUVXY:
            c.BoughtLongVIX = True
            TarPer(context, data, c.UVXY, c.VIX_HedgeLeverage)

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if (
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1])
        or
        (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1])
        or
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])
    ):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

Do NOT get too excited. I am back live trading Robin Hood VIX Mix Extreme Vetting. It is still a work in progress. However, it does have a more reasonable feel to it.

You guys still on this algorithm or are you trading with a different one? I'm trying to figure out the signals between the switch from XIV and UVXY. The algo didn't seem to catch something and UVXY is up like 8% right now. It just sold XIV

I turned it off a few days ago when XIV started to get really stagnant, I'll turn it back on again in the future. If you look at the code, most of the signal checking and rebalancing is done in the morning, excepting some stop-loss code that might have just triggered when XIV dropped so much. It does this to take advantage of UVXY's frequent AM spikes.

I hopped out of it because it does tend to sometimes get creamed during crises, which are obviously hard to predict. I'm not really sure volatility can go lower, and I didn't want a sudden reversal to kill me if the algo was just holding XIV.

I'm still live trading as well. Same behavior as Matthew experienced. Hard to predict a day like this.

Trading as well it sold XIV on a stop loss due to the falling price of XIV. I bought myself UVXY manually but unfortunatley got it near top of today so i lost a few. Will see what happen soon tomm if it wasn't an ok choice. It basically spiked due to Trump North Korea comment. How that's be going rock go over tomm I'm not sure. I wonder I though if the also will buy UVXY tomm morning? Anyone know?

Commenting on my to phone sorry for the grammar it's autocorrecting

Nicholas, I think it will be I am trying not to touch it. I will let you know. When it sold XIV today it sold a portion of shares at a limit price 96.60 then the rest towards the end of the day for like 92 something. My question is how can we tell it to sell a slightly larger portion at the first limit price?

@Michael,
in def RogueTrader function:
RemainingFactor = 0.50 if stock is c.UVXY else 0.22
change 0.22 to a bigger number (not bigger than 1)

Going to bed now. Tomorrow should be interesting.

Macro Trader has a post for an algorithm that uses the new VNIM instead of XIV. It looks really good, DD is significantly lower but at the cost of lower returns. It's a bit hard to tell if its truly a safer option yet since it is so new but so far it looks promising. Overall I think we could learn from what he has and maybe use it here to help with those DDs. Maybe to use it during uncertainty periods like now.

Algo is here: https://www.quantopian.com/posts/another-vix-trading-algorithm-using-the-new-etf-vmin

For reference here is the latest version I am currently running with RH Gold off at the same time frame of his test.

Clone Algorithm
41
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv'
    nokoUrl = 'http://52.15.233.150/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.xiv = sid(40516)
    context.tqqq = sid(39214)
    context.uvxy = sid(41969)
    context.spyg = sid(22009)
    
    context.vix = -1
    context.xiv_day = 0
    set_benchmark(context.xiv) # ALPHA and BETA will be different
    set_slippage(slippage.VolumeShareSlippage(volume_limit=.20, price_impact=0.0))
    set_commission(commission.PerTrade(cost=0.00))
    
    context.SetAsideLeverageTotal = 0.20 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 1.00 # if certain conditions are met
    context.Agree                 = False
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.PriceXIV = context.PriceUVXY = 0.00
    context.BoughtShortVIX = context.BoughtLongVIX = False
    context.XIV       = symbol('XIV')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.XIV, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy XIV
    context.VXX = context.vxx = sid(38054) # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.XIV].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.XIV].amount > 0: 
            cancel_open_orders_for(context, data, c.XIV)
            TarPer(context, data, c.XIV, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
    else:
        SignalShortVIX = True

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False

    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.XIV in p or c.UVXY in p):
            TarPer(context, data, c.XIV, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.XIV not in p):
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)
            c.BoughtShortVIX = False
            c.PriceXIV = 0.00

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.XIV, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.XIVprice  = data.current(c.XIV, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(XIV  = c.XIVprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal / SyntheticVIX: {} / {}     VIX: {:.2f}     XIV: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.XIVprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 0 # 200 Cushion plus cash for withdrawals
            RhMargin = 0 # Set to 0 if you do not have Robinhood Gold
            MaxLeverage = 1.33 - .12 # Hard Limit for leverage minus seemingly necessary cushion
            MaxLeverage = min(MaxLeverage, max(MaxLeverage, context.account.leverage))
            RhMargin = min(RhMargin, PV * (MaxLeverage - 1))
            RhPV = PV + RhMargin - DoNotSpend  
            RhCash = RhPV - context.portfolio.positions_value
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue   = float(amount * price)
            TarValue   = float(RhPV * TargetPercent)
            DiffValue  = float(TarValue - PosValue)
            DiffValue  = min(DiffValue, RhCash)
            DiffAmount = int(DiffValue / price)
            DiffAmount = 0 if 0 > DiffAmount and 0 == amount else DiffAmount
            order(stock, DiffAmount)

        if stock.symbol in context.stops: del context.stops[stock.symbol]

def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
           
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    xiv_history = data.history(context.xiv, 'price', 2, '1d')  
    
    xiv_ratio = xiv_history[1]/xiv_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_xiv = -0.049          # 1
    threshold_vxv_xiv = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_xiv_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11

    # 0
    if last_vix < threshold_vix_too_low and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low: # if VIX is too low, invest in UVXY witht he hope of a spike
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_xiv: # if contango is high, invest in XIV to gain from decay
        target_sid = context.xiv
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_xiv: # if short term vol is low compared to mid term, invest in XIV to gain from decay
        target_sid = context.xiv

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # if backwardation is high, invest in UVXY to gain from decay
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # if short term vol is high compared to mid term, invest in UVXY to gain from growth
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # if VIX is too high, invest in XIV expecting that VIX will drop
        target_sid = context.xiv

    # 6
    elif vix_ratio < threshold_vc_low: # Vix down sharply, invest in XIV expecting that futures curve gets pulled down
        target_sid = context.xiv

    # 7
    elif vix_ratio > threshold_vc_high: # Vix up sharply, invest in UVXY expecting that futures curve gets pulled up
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #have to think
        target_sid = context.xiv

    # 9
    else:
        target_sid = context.uvxy

    # 10
    if (target_sid == context.xiv and xiv_ratio < threshold_xiv_ratio) : 
        # indicators say XIV but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and xiv_ratio > threshold_uvxy_ratio) :
        # indicators say UVXY but it just dropped overnight, so go for TQQQ
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.xiv  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 1,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 1,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # # #  End options  # # # # # # # # #  
            },  
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent  
            'cagr'       : 0,  
            'max_lvrg'   : 0,  
            'max_shrt'   : 0,  
            'risk_hi'    : 0,  
            'days'       : 0.0,  
            'date_prv'   : '',  
            'date_end'   : get_environment('end').date(),  
            'cash_low'   : manual_cash,  
            'cash'       : manual_cash,  
            'start'      : manual_cash,  
            'tz'         : time_zone,  
            'begin'      : time.time(),  # For run time  
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), datetime.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)  
        }  
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off  
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money  
        log.info(c.pvr['run_str'])  
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']  
    def _pvr(c):  
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1  
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'  
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))  
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['risk_hi'], '%.1f' % p['pvr']))  
        log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['risk_hi'], '%.0f' % p['max_shrt']))  
    def _minut():  
        dt = get_datetime().astimezone(timezone(p['tz']))  
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)  
    date = get_datetime().date()  
    if p['date_prv'] != date:  
        p['date_prv'] = date  
        p['days'] += 1.0  
    do_summary = 0  
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':  
        do_summary = 1              # Log summary every x days  
    if do_summary or date == p['date_end']:  
        p['cash'] = pf.cash  
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])  
    new_key_hi = 0                  # To trigger logging if on.  
    cash       = pf.cash  
    cash_dip   = int(max(0, p['start'] - cash))  
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.  
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))  
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low  
        new_key_hi = 1  
        p['cash_low'] = int(cash)             # Lowest cash level hit  
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:  
        new_key_hi = 1  
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage  
        if o['record_max_lvrg']: record(MaxLv   = p['max_lvrg'])

    if shorts < p['max_shrt']:  
        new_key_hi = 1  
        p['max_shrt'] = shorts                # Maximum shorts value  
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['risk_hi']:  
        new_key_hi = 1  
        p['risk_hi'] = risk                   # Highest risk overall  
        if o['record_risk_hi']:  record(RiskHi  = p['risk_hi'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short  
    if p['risk_hi'] != 0: # Avoid zero-divide  
        p['pvr'] = 100 * pnl / p['risk_hi']  
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'  
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve  
    if o['record_leverage']: record(Lvrg   = c.account.leverage) # Leverage  
    if o['record_cash']    : record(Cash   = cash)               # Cash  
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used  
    if o['record_q_return']: record(QRet   = 100 * pf.returns)  
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:  
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),  
            ' Lv '     + '%.1f' % c.account.leverage,  
            ' MxLv '   + '%.2f' % p['max_lvrg'],  
            ' QRet '   + '%.1f' % (100 * pf.returns),  
            ' PvR '    + '%.1f' % p['pvr'],  
            ' PnL '    + '%.0f' % pnl,  
            ' Cash '   + '%.0f' % cash,  
            ' CshLw '  + '%.0f' % p['cash_low'],  
            ' Shrt '   + '%.0f' % shorts,  
            ' MxShrt ' + '%.0f' % p['max_shrt'],  
            ' Risk '   + '%.0f' % risk,  
            ' RskHi '  + '%.0f' % p['risk_hi']  
        ))  
    if do_summary: _pvr(c)  
    if get_datetime() == get_environment('end'):   # Summary at end of run  
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes  
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))

def handle_data(context, data): 
    pvr(context, data)
    c = context
    if c.ShowMaxLev:
        if c.account.leverage > c.mx_lvrg:  
            c.mx_lvrg = c.account.leverage  
            record(mx_lvrg = c.mx_lvrg)    # Record maximum leverage encountered

    ##
    # Buy XIV
    ##
    if c.ShortVIX and not c.BoughtShortVIX and not c.SellAndWait:
        PriceXIV = data.current(c.XIV, 'price')
        if not c.PriceXIV: c.PriceXIV = PriceXIV
        if PriceXIV < c.PriceXIV:
            c.PriceXIV = PriceXIV
        elif PriceXIV > 1.0025 * c.PriceXIV:
            c.BoughtShortVIX = True
            TarPer(context, data, c.XIV, c.VIX_GrowthLeverage)

    ##
    # Buy UVXY
    ##
    if c.LongVIX and not c.BoughtLongVIX and not c.SellAndWait:
        PriceUVXY = data.current(c.UVXY, 'price')
        if not c.PriceUVXY: c.PriceUVXY = PriceUVXY
        if PriceUVXY < c.PriceUVXY:
            c.PriceUVXY = PriceUVXY
        elif PriceUVXY > 1.005 * c.PriceUVXY:
            c.BoughtLongVIX = True
            TarPer(context, data, c.UVXY, c.VIX_HedgeLeverage)

    ##
    # RogueTrader here and in RogueTrader scheduled function
    ##
    if c.RogueTrader and not c.SellAndWait:
        for position in c.portfolio.positions.itervalues():
            if position.amount == 0:
                if position.asset.symbol in c.stops: del c.stops[position.asset.symbol]
                continue
            elif position.asset.symbol not in c.stops:
                stoploss= c.stoploss if position.amount > 0 else -c.stoploss
                c.stops[position.asset.symbol]=position.last_sale_price*(1-stoploss)
                #log.info(' ! I have added '+str(position.asset.symbol)+' to Stops @ '+str((position.last_sale_price)*(1-stoploss)))
            elif c.stops[position.asset.symbol] < position.last_sale_price*(1- c.stoploss) and position.amount > 0:
                c.stops[position.asset.symbol]=position.last_sale_price*(1- c.stoploss)
                #log.info(' ! I have updated '+str(position.asset.symbol)+'- (Long) to stop @ '+str((position.last_sale_price)*(1- c.stoploss)))
            elif c.stops[position.asset.symbol] > position.last_sale_price and position.amount > 0:
                #sell
                log.info(' ! '+str(position.asset.symbol)+'- (Long) has hit stoploss @ '+str(position.last_sale_price))
                if get_open_orders(position.sid): cancel_open_orders_for(context, data, position.sid)
                c.stoppedout.append(position.asset.symbol)
                TarPer(context, data, position.sid, 0.00)
 
            ##
            # Sell and Wait
            ##
            if (
                    (position.sid is c.XIV and 0 < position.amount)
                    and
                    (10.76 < c.vix < 12) and (position.last_sale_price > 1.013 * c.XIVprice)
            ):
                c.SellAndWait = 1
            elif (
                    (position.sid is c.UVXY and 0 < position.amount)
                    and
                    (position.last_sale_price > 1.40 * c.UVXYprice)
            ):
                c.SellAndWait = 3

        ##
        # Sell and Wait
        ##
        if 0 < c.SellAndWait:
            for stock in c.portfolio.positions:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)

def CherryPickerClose(context,data):    
    
    c = context
    vxx_prices = data.history(c.VXX, "high", c.wvf_length*2, "1d")
    vxx_lows = data.history(c.VXX, "low", c.wvf_length*2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.wvf_length, center=False).max()
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100

    rsi = talib.RSI(vxx_prices, timeperiod=c.rsi_length)
    
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.ema1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.ema2)
    
    ## BUY RULES
    #if WVF crosses over smoothwvf1 and wvf < smoothwvf2
    if (
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] < c.SmoothedWVF2[-1])
        or
        (c.SmoothedWVF1[-2] < c.SmoothedWVF2[-2] and c.SmoothedWVF1[-1] > c.SmoothedWVF2[-1])
        or
        (WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-2] and WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-2])
    ):
        c.sell = False
        for stock in c.portfolio.positions:
            if stock is not c.XIV:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 0.00)
            else:
                cancel_open_orders_for(context, data, stock)
                TarPer(context, data, stock, 1.00)
      
    ## SELL RULES
    if c.portfolio.positions[c.XIV].amount > 0:
        #if rsi crosses over rsi_trigger
        if rsi[-2] < c.rsi_trigger and rsi[-1] > c.rsi_trigger:
            c.sell = True
            
        #if wvf crosses under smoothwvf2: sell
        elif WVF[-2] > c.SmoothedWVF2[-2] and WVF[-1] < c.SmoothedWVF2[-1]:
            c.sell = True

        else:
            c.sell = False
There was a runtime error.

How's everyone else doing? UVXY is up 30% over the last 2 days and the algo hasn't seen any flags for it. Keeps investing in XIV

Its a painful lesson I have learned few times with various algos. You have to go in manual override mode. You can't price in armageddon as they say.

It's simply that an algo can not cover all kinds of market scenarios.

Don't understand what everyone is upset about - the paper trading version sold XIV/TQQQ this morning without taking a loss.

Yes, mine is flat at this point, so did not get caught in the bulk of today's move lower.

Mine was down 5.88% before it got out

I managed to "manually" lock in 15% on UVXY today because of a stop loss buy order I forgot I'd set yesterday. Lucky. Helped offset some of my other losses ... a tiny bit.

Anyways, anybody else's paper trading graph showing a 100% flash crash in XIV (that did not actually happen)?
XIV Flash crash?

My graph shows such a 100% drop at 11:25 today, which prompted a sell off of VIX

If it's a bug that's triggering erroneous trading signals that's something Q might want to fix. I sent an email to support to let them know about it.

Hi Viridian, Nathan

During live trading, benchmark returns are calculated minutely using 'close' price. The way we are currently handling missing values (NaN) causes the benchmark to show losses of -%100 when we don't receive pricing data for a given minute bar. This blip in the graph usually fixes itself on subsequent days since benchmark returns during warmup are calculated using the 'price' field, which forward-fills for missing bars. We currently have an open report on this bug, pending review from our team.

However, this blip on benchmark returns would not cause signals in algorithms to be triggered. If your algorithm uses 'close' in its calculations, it could be mishandling missing data (NaN). In general, you should use 'price' instead to get forward-filled pricing for missing bars.

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Charles thanks for everything with the also I'm trying to work out ways the also would catch huge jumps like this anyway I've been looking into adding a threshold if SPY falls by .40% close if in XIV and tqqq and buy UVXY anyway you can add that and I can take a look?

For anyone live trading, a question: I'm running this algo in both RH and IB. Today in RH the algo went long UVXY, but in IB it went long XIV / TQQQ. Has anyone else experienced this behavior?

For me, today in RH, it went long XIV and TQQQ, then took some profit as a limit sell of a portion of XIV later in the day.

Interesting. In paper trading mine sold XIV yesterday at 15:50, got in UVXY at 13:38 today, and tried to sell half via a limit order at 14:30, but the limit order never filled. I think I'm running the Aug. 2nd version. Could it be that we're running different versions/revisions og the algo?

Strange. I'm running the same version (Aug 2) in both RH and IB. Also, looking at the algo logs from this morning, the RH version got a long VIX signal, while IB got a short VIX.

You're probably right, but I don't recall when I last updated it.

Theres got to be a way we can code something if VIX spikes mid day or something close all and go to UVXY, the algo is missing the big UVXY spikes recently, also if vix mid day sharp down if in UVXY close and go to XIV

Test change to the 3.0:

Code to capture profit on price jump - Quantopian
https://www.quantopian.com/posts/code-to-capture-profit-on-price-jump
Mar 1, 2017 - Looking for a price jump over 3.0x, adding to watch_list, and
processing ...

@Chris, Oh that's super weird! You're sure you're running the same version on both? If so I would guess the prices in live trading come from the corresponding broker and there's some inconsistency between them. That's super strange that the data would be different enough to trigger totally opposite signals, huh? I wonder if the data from Robinhood is worse. I assume your IB version made money today and the Robinhood one lost money? I'd be curious how future discrepancies play out on days where there's a clearer better trade -- today was more or less a wash, relatively speaking.

@Nicholas -- Of course you can, and I think the algorithm already does this, but I haven't studied it closely enough to see whether it's intraday or what. It's very difficult to differentiate between a zig zag, a spike, and a jump that continues in the same direction, and combinations thereof. If you can crack that puzzle you can be a gazillionaire. We also haven't had any massive spikes in Vix lately -- just little ones that might not have passed threshold anyway.

@Vhawk Yes, I ran a file compare to confirm. They're the same except that I changed the commission and margin to reflect that of my IB account. Shouldn't make a difference. As to the data quality, I thought that the signal data (or at least part of it) originates from the csv files that are loaded daily. If that's the case, there shouldn't be a difference between RH and IB data quality.

Correct, today IB made money and RH took a loss. I'll be running this algo on both accounts in parallel for a bit, so I'll let folks know if any see any future discrepancies.

Thanks Peter Bakker for resurrecting this algo! I really really really missed it! Only additional change I just now made was to change benchmark to SVXY so can compare to Aug 2015 and Feb 05, 2018 SVXY movements.
Refer to https://www.quantopian.com/posts/robinhood-vix-mix#5a812fb9fb806d0011faedd0

Clone Algorithm
88
Loading...
Backtest from to with initial capital
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 functools
import itertools
import math
import re
import talib
import time
import numpy as np
import pandas as pd
from datetime import datetime  
from pytz import timezone      
from scipy import stats
from zipline.utils import tradingcalendar

History = 128

def initialize(context):

    ##
    # CherryPicker
    ##

    context.rsi_length = 3
    context.rsi_trigger = 50
    context.wvf_length = 100
    context.ema1 = 10
    context.ema2 = 30
    context.sell = False

    ##
    # Not CherryPicker
    ##
    
    vixUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv'
    vxvUrl = 'http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vix3mdailyprices.csv'
    nokoUrl = 'http://173.212.203.121/noko.csv'  
    
    fetch_csv(nokoUrl, 
              symbol='v1', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX1, 
              post_func=shift_data)
    
    fetch_csv(nokoUrl, 
              symbol='v2', 
              date_column='Date', 
              date_format='%Y-%m-%d', 
              pre_func=addFieldsVX2, 
              post_func=shift_data)

    fetch_csv(vixUrl, 
              symbol='v', 
              skiprows=1,
              date_column='Date', 
              pre_func=addFieldsVIX,
              post_func=shift_data)

    fetch_csv(vxvUrl, 
              symbol='vxv', 
              skiprows=2,
              date_column='Date', 
              pre_func=addFieldsVXV,
              post_func=shift_data)

    context.SVXY = symbol('SVXY')
    context.tqqq = symbol('TQQQ')
    context.uvxy = symbol('UVXY')
    context.spyg = symbol('SPYG')
    
    context.vix = -1
    context.SVXY_day = 0
    set_benchmark(symbol('SVXY')) # ALPHA and BETA will be different
    
    context.SetAsideLeverageTotal = 0.666 # 100% allocate when BigSignalTQQQ
    context.VIX_GrowthLeverage    = 1 - context.SetAsideLeverageTotal
    context.VIX_MinHedgeLeverage  = context.VIX_HedgeLeverage  = 0.666
    context.VIX_MaxHedgeLeverage  = 0.666 # if certain conditions are met
    context.ShowMaxLev            = True

    # 3x NASDAQ nonfinancial, generally profitable
    context.SetAsideStocks = symbols('TQQQ')

    context.PriceSVXY = context.PriceUVXY = 0.00
    context.BoughtShortVIX = context.BoughtLongVIX = False
    context.SVXY       = symbol('SVXY')
    context.UVXY      = symbol('UVXY') # approx 8% weekly erosion, bigger spikes
    context.VIXstocks = (context.SVXY, context.UVXY)
    context.LongVIX  = False  # no decisions until one of three conditions exists
    context.ShortVIX = False  # no decisions until one of three conditions exists
    context.Agree     = False
    # not necessary to set BigSignal variables because they get set by MoreSignals function early every day
    
    # apparently highly successful VIX signals
    schedule_function(CherryPickerOpen, date_rules.every_day(), time_rules.market_open(), False)
    schedule_function(MoreSignals, date_rules.every_day(), time_rules.market_open(minutes = 2))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_open(minutes = 4))
    schedule_function(RogueTrader, date_rules.every_day(),time_rules.market_open(minutes = 60))
    schedule_function(cancel_open_orders, date_rules.every_day(), time_rules.market_close())    
    schedule_function(RecordVars, date_rules.every_day(), time_rules.market_close())    
    schedule_function(CherryPickerClose, date_rules.every_day(), time_rules.market_close(), False)

    #VXX used for strategy to buy SVXY
    context.VXX = context.vxx = symbol('VXX') # VXX

    #Editable values to tweak backtest
    context.AvgLength = 20
    context.LengthWVF = 100
    context.LengthEMA1 = 10
    context.LengthEMA2 = 30
    
    #internal variables to store data
    context.vxxAvg = 0 
    context.SmoothedWVF1 = 0
    context.SmoothedWVF2 = 0
    context.vxxLow = 0
    context.vxxHigh = 0
    
    #Internal stop variables
    # context.stoploss is set in RogueTrader
    context.stoploss = 0.50
    context.stops         = {}
    context.stoppedout    = []
    context.SellAndWait   = 0
    
def before_trading_start(context, data):

    context.RogueTrader = False # Not True until RogueTrader scheduled function runs

    context.mx_lvrg  = 0 # daily max leverage

    # To help determine previous signal when start algo in live trade Robinhood
    if not context.ShortVIX and not context.LongVIX: # Maybe we can figure it out
        context.ShortVIX = True if 0 < context.portfolio.positions[context.SVXY].amount  else False
        context.LongVIX  = True if 0 < context.portfolio.positions[context.UVXY].amount else False
        if context.ShortVIX and context.LongVIX: # Do no harm
            context.ShortVIX = context.LongVIX = False

def RecordVars(context, data):
    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass

def CherryPickerOpen(context,data):
    c = context
    if c.sell:
        c.sell = False
        if c.portfolio.positions[c.SVXY].amount > 0: 
            cancel_open_orders_for(context, data, c.SVXY)
            TarPer(context, data, c.SVXY, 0.00)
            
def RogueTrader(context, data):

    c = context
    c.RogueTrader = True # Not True until RogueTrader scheduled function runs

    cancel_open_orders(context, data)

    ##
    # Limit sell and Stop loss orders
    ##
    for stock in c.portfolio.positions:

        # Set stoploss
        c.stoploss = 0.21 if stock is c.UVXY else 0.035

        # Set LimitPrice for first order
        RemainingFactor  = 0.50 if stock is c.UVXY else 0.22
        MaxOrders        = 20   if stock is c.UVXY else 3
        if stock is c.UVXY:
            LimitPriceFactor = 1.06 if c.Agree else 1.05
        else:
            LimitPriceFactor = 1.04
        LimitPrice       = LimitPriceFactor * c.portfolio.positions[stock].cost_basis

        SharesRemaining = int(RemainingFactor * c.portfolio.positions[stock].amount)
        SharesPerOrder  = min(SharesRemaining, max(100, int(SharesRemaining / MaxOrders)))
        
        while 0 < SharesRemaining:

            order(stock, -SharesPerOrder, style=LimitOrder(LimitPrice))
            
            SharesRemaining -= SharesPerOrder
            SharesPerOrder = min(SharesRemaining, SharesPerOrder)
            LimitPrice *= LimitPriceFactor 
        if stock.symbol in c.stops: del c.stops[stock.symbol]
        
def Rebalance(context, data):
    c = context
    
    if 0 < c.SellAndWait:
        c.SellAndWait -= 1
        log.info('! Sell and wait')
        return

    cancel_open_orders(context, data) # avoid confusion

    ### Determine how much to risk in Long VIX ###
    # Load historical data for the stocks  
    hist = data.history(c.vxx, ['high', 'low', 'close'], 15, '1d')  
    # Calculate the ATR for the stock  
    atr_14 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=14)[-1]  
    atr_3 = talib.ATR(hist['high'],  
                    hist['low'],  
                    hist['close'],  
                    timeperiod=3)[-1]
    c.VIX_HedgeLeverage = c.VIX_MaxHedgeLeverage if atr_3 <= atr_14 else c.VIX_MinHedgeLeverage

    #Gets Moving Average of VXX
    price_hist = data.history(c.vxx, 'price', c.AvgLength, '1d')
    c.vxxAvg = price_hist.mean()
    
    #get data for calculations
    n = 200
    vxx_prices = data.history(c.vxx, "price", n + 2, "1d")
    vxx_lows = data.history(c.vxx, "low", n + 2, "1d")
    vxx_highest = vxx_prices.rolling(window = c.LengthWVF,center=False).max()
    
    #William's VIX Fix indicator a.k.a. the Synthetic VIX
    WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
    
    # calculated smoothed WVF
    c.SmoothedWVF1 = talib.EMA(WVF, timeperiod=c.LengthEMA1) 
    c.SmoothedWVF2 = talib.EMA(WVF, timeperiod=c.LengthEMA2)
    
    #Do some checks for cross overs. 
    if WVF[-1] > c.SmoothedWVF1[-1] and WVF[-2] < c.SmoothedWVF1[-1]:
        wvf_crossedSmoothedWVF1 = True
    else: 
        wvf_crossedSmoothedWVF1 = False
    #Same except for smoothed2 
    if WVF[-1] > c.SmoothedWVF2[-1] and WVF[-2] < c.SmoothedWVF2[-1]:
        wvf_crossedSmoothedWVF2 = True
    else: 
        wvf_crossedSmoothedWVF2 = False
    
    #Current price of vxx
    vxxPrice = data.current(c.vxx, 'price')
    
    #SignalShortVIX 
    if ( wvf_crossedSmoothedWVF1 and WVF[-1] < c.SmoothedWVF2[-1]) or (wvf_crossedSmoothedWVF2 and wvf_crossedSmoothedWVF1):
        SignalShortVIX = True
        log.info('! SignalShortVIX = True')
    elif ((vxxPrice > c.vxxAvg and vxx_prices[-2] < c.vxxAvg) or (WVF[-1] < c.SmoothedWVF2[-1] and WVF[-2] > c.SmoothedWVF2[-1])):
        SignalShortVIX = False
        log.info('! SignalShortVIX = False')
    else:
        SignalShortVIX = True
        log.info('! SignalShortVIX = True')

    if (c.BigSignalShortVIX and SignalShortVIX): # Agree ShortVIX
        log.info('! BigSignalShortVIX and SignalShortVIX = True : agreement Short VIX true')
        c.ShortVIX    = True
        c.LongVIX     = False
        c.Agree       = True

    elif (c.BigSignalLongVIX and not SignalShortVIX): # Agree LongVIX
        log.info('! BigSignalLongVIX and NOT SignalShortVIX = True : agreement Long VIX true')
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = True

    elif (c.BigSignalLongVIX and SignalShortVIX): # Not Agree LongVIX
        log.info('! BigSignalLongVIX and SignalShortVIX = True : NO agreement: -- what to do? Lets go for UVXY....')
        c.ShortVIX    = False
        c.LongVIX     = True
        c.Agree       = False
        
        
    for stock in c.portfolio.positions:
        if stock not in c.VIXstocks and stock not in c.SetAsideStocks:
            TarPer(context, data, stock, 0.00)

    ##
    # Rebalance only once until signal changes again
    # or
    # leverage drops too low
    ##

    l = c.account.leverage
    LevTooLow = True if (0.33 > l) or (0.90 < l < 1.05) else False
    p = c.portfolio.positions

    if c.BigSignalTQQQ:
        if LevTooLow or (c.SVXY in p or c.UVXY in p):
            TarPer(context, data, c.SVXY, 0.00)
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(1.00 / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)

    elif c.ShortVIX:
        if LevTooLow or (c.SVXY not in p):
            TarPer(context, data, c.UVXY, 0.00)
            SetAsideStocks = c.SetAsideStocks
            SetAsideLeveragePositions = len(SetAsideStocks)
            for stock in SetAsideStocks:
                if not DataCanTrade(context, data, stock):
                    SetAsideStocks.remove(stock)
                    SetAsideLeveragePositions -= 1
            SetAsideLeverage = float(c.SetAsideLeverageTotal / SetAsideLeveragePositions) if 0 < SetAsideLeveragePositions else 0.00
            for stock in SetAsideStocks:
                TarPer(context, data, stock, SetAsideLeverage)
            c.BoughtShortVIX = False
            c.PriceSVXY = 0.00

    elif c.LongVIX:
        if LevTooLow or (c.UVXY not in p):
            TarPer(context, data, c.SVXY, 0.00)
            for stock in c.SetAsideStocks:
                TarPer(context, data, stock, 0.00)
            c.BoughtLongVIX = False
            c.PriceUVXY = 0.00

    # Record / log stuff I want to know
    c.SVXYprice  = data.current(c.SVXY, 'price')
    c.UVXYprice = data.current(c.UVXY, 'price')
    TQQQprice = data.current(c.tqqq, 'price')
    #record(SVXY  = c.SVXYprice)
    #record(UVXY = c.UVXYprice)
    #record(TQQQ = TQQQprice)
    BigSignal = 'NoBigSignal'
    BigSignal = 'ShortVIX' if c.BigSignalShortVIX else BigSignal
    BigSignal = ' LongVIX' if c.BigSignalLongVIX  else BigSignal
    BigSignal = '    TQQQ' if c.BigSignalTQQQ     else BigSignal
    SyntheticVIX = 'NoSyntheticVIX'
    SyntheticVIX = 'ShortVIX' if c.ShortVIX else SyntheticVIX
    SyntheticVIX = ' LongVIX' if c.LongVIX  else SyntheticVIX
    log.info('BigSignal/SyntheticVIX: {} / {}     VIX: {:.2f}     SVXY: {:.2f}     UVXY: {:.2f}     TQQQ: {:.2f}'
        .format(BigSignal, SyntheticVIX, c.VIXprice, c.SVXYprice, c.UVXYprice, TQQQprice)
    )

def TarPer(context, data, stock, TargetPercent):

    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            order_target_percent(stock, 0.00)
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 200.00 # 200 Cushion plus cash for withdrawals
            RhMargin = 0.00 # Set to 0 if you do not have Robinhood Gold
            dispersion = data.current('v','20dstd')
            if dispersion>1.0: 
                MaxLeverage = 0.66 # Hard Limit for leverage minus seemingly necessary cushion
            else:
                MaxLeverage = 0.66
            MaxLeverage = min(MaxLeverage, max(MaxLeverage, context.account.leverage))
            RhMargin = min(RhMargin, PV * (MaxLeverage - 1))
            RhPV = PV + RhMargin - DoNotSpend  
            RhCash = RhPV - context.portfolio.positions_value
            amount = context.portfolio.positions[stock].amount
            price = data.current(stock, 'price')
            PosValue   = float(amount * price)
            TarValue   = float(RhPV * TargetPercent)
            DiffValue  = float(TarValue - PosValue)
            DiffValue  = min(DiffValue, RhCash)
            DiffAmount = int(DiffValue / price)
            DiffAmount = 0 if 0 > DiffAmount and 0 == amount else DiffAmount
            order(stock, DiffAmount)

        if stock.symbol in context.stops: del context.stops[stock.symbol]
            
            
def DataCanTrade(context, data, stock):

    try:
        if data.can_trade(stock):
            return True
        else:
            return False
    except:
        return False

def cancel_open_orders(context, data):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            cancel_order(order)
            #message = 'Canceling order of {amount} shares in {stock}'
            #log.info(message.format(amount=order.amount, stock=stock))
    
def cancel_open_orders_for(context, data, security):
    oo = get_open_orders()
    if len(oo) == 0:
        return
    for stock, orders in oo.iteritems():
        for order in orders:
            if stock is security:
                if order.amount < context.portfolio.positions[stock].amount:
                    cancel_order(order)
                    #message = 'Canceling order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                else: # Do NOT want to cancel stop loss order
                    #message = 'NOT Canceling stop loss order of {amount} shares in {stock}'
                    #log.info(message.format(amount=order.amount, stock=stock))
                    pass

def MoreSignals(context, data):  

    update_indices(context, data)     
    last_vix = context.VIXprice = data.current('v', 'Close')
    last_vx1 = data.current('v1','Close')  
    last_vx2 = data.current('v2','Close')      
    last_vxv = data.current('vxv', 'Close')
    last_vix_200ma_ratio = data.current('v', '200ma Ratio')
    dispersion = data.current('v','20dstd')
    
    record(dispersion=dispersion)
    record(last_vix=last_vix/10)
    # Calculating the gap between spot vix and the first month vix future
    last_ratio_v_v1 = last_vix/last_vx1

    # Calculating the contango ratio of the front and second month VIX Futures 
    last_ratio_v1_v2 = last_vx1/last_vx2

    # Blending the previous two ratios together using a weighted average
    ratio_weight = 0.7
    last_ratio = (ratio_weight*last_ratio_v_v1) + ((1-ratio_weight)*last_ratio_v1_v2) - 1
    
    vix_vxv_ratio = last_vix/last_vxv
    
    # Retrieve SPY prices for technical indicators
    prices = data.history(context.spyg, 'open', 40, '1d')
    
    # Retrieve SPY MACD data
    macda, signal, hist = talib.MACD(prices, fastperiod=12,slowperiod=26,signalperiod=9)
    macd = macda[-1] - signal[-1]
    
    # Calculate how much vix moved the previous day
    if (context.vix <> -1) : 
        vix_ratio = last_vix/context.vix -1
    else :
        vix_ratio = 0
    context.vix = last_vix
    
    SVXY_history = data.history(context.SVXY, 'price', 2, '1d')  
    
    SVXY_ratio = SVXY_history[1]/SVXY_history[0] - 1
    
    # Setting thresholds
    threshold_vix_too_low = 10.76   # 0 
    threshold_vix_200ma_ratio_low = 0.79  # 0 
    threshold_SVXY = -0.049          # 1
    threshold_vxv_SVXY = 0.87        # 2
    threshold_uvxy = 0.049          # 3
    threshold_macd = -0.55          # 3
    threshold_vxv_uvxy = 1.3        # 4
    threshold_vix_high = 19.9       # 5
    threshold_vc_low = -0.148       # 6
    threshold_vc_high = 0.046       # 8
    threshold_vc_high_2 = -0.06     # 8
    threshold_SVXY_ratio = -0.053    # 10
    threshold_uvxy_ratio = 0.08     # 11
    threshold_dispersion = 0.55
    # 0
    if last_vix < threshold_vix_too_low \
            and last_vix_200ma_ratio < threshold_vix_200ma_ratio_low :
        log.info('! if VIX is too low, invest in UVXY witht he hope of a spike')
        target_sid = context.uvxy
    # 1        
    elif last_ratio < threshold_SVXY: # 
        log.info('! if contango is high, invest in SVXY to gain from decay')
        target_sid = context.SVXY
    
    # 2
    elif vix_vxv_ratio < threshold_vxv_SVXY: # 
        log.info('! if short term vol is low compared to mid term, invest in SVXY to gain from decay')
        target_sid = context.SVXY

    # 3
    elif last_ratio > threshold_uvxy and macd > threshold_macd: # 
        log.info('! if backwardation is high, invest in UVXY to gain from decay')
        target_sid = context.uvxy

    # 4
    elif vix_vxv_ratio > threshold_vxv_uvxy: # 
        log.info('! if short term vol is high compared to mid term, invest in UVXY to gain from growth')
        target_sid = context.uvxy

    # 5
    elif last_vix > threshold_vix_high: # 
        log.info('! if VIX is too high, invest in SVXY expecting that VIX will drop')
        target_sid = context.SVXY

    # 6
    elif vix_ratio < threshold_vc_low: # 
        log.info('! Vix down sharply, invest in SVXY expecting that futures curve gets pulled down')
        target_sid = context.SVXY

    # 7
    elif vix_ratio > threshold_vc_high: # 
        log.info('! Vix up sharply, invest in UVXY expecting that futures curve gets pulled up')
        target_sid = context.uvxy

    # 8
    elif vix_ratio > threshold_vc_high_2: #
        log.info('! have to think')
        target_sid = context.SVXY

    # 9
    else:
        target_sid = context.uvxy
        log.info('! Going for UVXY...')

    # 10
    if (target_sid == context.SVXY and SVXY_ratio < threshold_SVXY_ratio) : 
        log.info('! indicators say SVXY but it just dropped overnight, so go for TQQQ')
        target_sid = context.tqqq 

    # 11
    elif (target_sid == context.uvxy and SVXY_ratio > threshold_uvxy_ratio) :
        log.info('! indicators say UVXY but it just dropped overnight, so go for TQQQ')
        target_sid = context.tqqq
    
    context.BigSignalShortVIX = True if context.SVXY  is target_sid else False
    context.BigSignalLongVIX  = True if context.uvxy is target_sid else False
    context.BigSignalTQQQ     = True if context.tqqq is target_sid else False
    
def update_indices(context, data):
    context.fetch_failed = False
    context.vix_vals = unpack_from_data(context, data, 'v')    
    context.vxv_vals = unpack_from_data(context, data, 'vxv')  
    context.vx1_vals = unpack_from_data(context, data, 'v1')
    context.vx2_vals = unpack_from_data(context, data, 'v2')

def fix_close(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%m/%d/%Y')))
    df = df.sort_values(by='Date', ascending=True)
    return df

def fix_closeVX(df,closeField):
    df = df.rename(columns={closeField:'Close'})
    # remove spurious asterisks
    df['Date'] = df['Date'].apply(lambda dt: re.sub('\*','',dt))
    # convert date column to timestamps
    df['Date'] = df['Date'].apply(lambda dt: pd.Timestamp(datetime.strptime(dt,'%Y-%m-%d')))
    df = df.sort_values(by='Date', ascending=True)
    return df


def subsequent_trading_date(date):
    tdays = tradingcalendar.trading_days
    last_date = pd.to_datetime(date)
    last_dt = tradingcalendar.canonicalize_datetime(last_date)
    next_dt = tdays[tdays.searchsorted(last_dt) + 1]
    return next_dt

def add_last_bar(df):
    last_date = df.index[-1]
    subsequent_date = subsequent_trading_date(last_date)
    blank_row = pd.Series({}, index=df.columns, name=subsequent_date)
    # add today, and shift all previous data up to today. This 
    # should result in the same data frames as in backtest
    df = df.append(blank_row).shift(1).dropna(how='all')
    return df

def shift_data(df):
    df = add_last_bar(df)
    df.fillna(method='ffill') 
    df['PrevCloses'] = my_rolling_apply_series(df['Close'], to_csv_str, History)
    dates = pd.Series(df.index)
    dates.index = df.index
    df['PrevDates'] = my_rolling_apply_series(dates, to_csv_str, History)
    return df

def unpack_from_data(context, data, sym):
    try:
        v = data.current(sym, 'PrevCloses')
        i = data.current(sym, 'PrevDates')
        return from_csv_strs(i,v,True).apply(float)
    except:
        log.warn("Unable to unpack historical {s} data.".format(s=sym))
        context.fetch_failed = True

def addFieldsVIX(df):
    df = fix_close(df,'VIX Close')
    df['200ma'] = df['Close'].rolling(200).mean()
    df['200ma Ratio'] = df['Close'] / df['200ma']
    df['20dstd'] =  df['Close'].rolling(20).std()

    return df

def addFieldsVXV(df):
    df.rename(columns={'Unnamed: 0': 'Date'}, inplace=True)
    df = fix_close(df,'CLOSE')
    return df

def addFieldsVX1(df):
    df = fix_closeVX(df,'F1')
    return df

def addFieldsVX2(df):
    df = fix_closeVX(df,'F2')
    return df

# convert a series of values to a comma-separated string of said values
def to_csv_str(s):
    return functools.reduce(lambda x,y: x+','+y, pd.Series(s).apply(str))

# a specific instance of rolling apply, for Series of any type (not just numeric,
# ala pandas.rolling_apply), where the index of the series is set to the indices
# of the last elements of each subset
def my_rolling_apply_series(s_in, f, n):
    s_out = pd.Series([f(s_in[i:i+n]) for i in range(0,len(s_in)-(n-1))]) 
    s_out.index = s_in.index[n-1:]
    return s_out

# reconstitutes a Series from two csv-encoded strings, one of the index, one of the values
def from_csv_strs(x, y, idx_is_date):
    s = pd.Series(y.split(','),index=x.split(','))
    if (idx_is_date):
        s.index = s.index.map(lambda x: pd.Timestamp(x))
    return s

def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information from https://www.quantopian.com/posts/pvr#569784bda73e9bf2b7000180
    '''  
    #import time  
    #from datetime import datetime  
    #from pytz import timezone      # Python will only do once, makes this portable.  
                                   #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]  
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals  
        manual_cash = c.portfolio.starting_cash  
        time_zone   = 'US/Central'   # Optionally change to your own time zone for wall clock time

        c.pvr = {  
            'options': {  
                # # # # # # # # # #  Options  # # # # # # # # # #  
                'logging'         : 0,    # Info to logging window with some new maximums  
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 0,    # Profit vs Risk returns (percentage)  
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value  
                'record_cash'     : 1,    # Cash available  
                'record_max_lvrg' : 0,    # Maximum leverage encountered  
                'record_risk_hi'  : 0,    # Highest risk overall  
                'record_shorting' : 0,    # Total value of any shorts  
                'record_max_shrt' : 0,    # Max value of shorting total  
                'record_cash_low' : 0,    # Any new lowest cash level  
                'record_q_return' : 0,    # Quantopian returns (percentage)  
                'record_pnl'      : 0,    # Profit-n-Loss  
                'record_risk'     : 1,    # Risked, max cash spent or shorts beyond longs+cash  
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)  
                # All records are end-of-day or the last data sent to chart during any day.  
                # The way the chart operates, only the last value of the day will be seen.  
                # # # # # # # #