Back to Community
Robin Hood VIX Sliding Pendulum

Not as good as my Robin Hood VIX Asylum. However, this has very few moving parts and should be simple to emulate on my laptop with live broker data that is not 15 minutes old.

Clone Algorithm
144
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
#Imports
from datetime import datetime  
from pytz import timezone      
from zipline.utils import tradingcalendar
from quantopian.pipeline.data.builtin import USEquityPricing
import statsmodels.api as sm 
import numpy as np
import pandas as pd
import functools
import quantopian.pipeline.data 
import re
import scipy
import talib
import time

History = 128

def initialize(context):
    c = context

    c.RobinHoodMargin = 0 # I prefer to not use margin.
    
    set_benchmark(symbol('SVXY'))
    c.SVXY = symbol('SVXY')
    c.TQQQ = symbol('TQQQ')
    c.UVXY = symbol('UVXY')
    c.SPYG = symbol('SPYG')

    c.ShowMaxLev = True

    c.Profit   = 0.03
    c.Loss     = -0.025
    c.StopLoss = -0.015

    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 15))
    
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    #Set commission and slippage
    set_commission(commission.PerShare(cost=0.000, min_trade_cost=0.0)) 
    set_slippage(slippage.FixedSlippage(spread=0.01))
    
def before_trading_start(context, data):
    c = context
    c.OkToAct = c.TooLateToAct = False
    c.mx_lvrg  = 0 # daily max leverage

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

    cancel_open_orders_for(context, data, stock)
    if DataCanTrade(context, data, stock):

        if 0 == TargetPercent:
            amount = context.portfolio.positions[stock].amount
            amount = 0 if 0 > amount else amount
            price = data.current(stock, 'price')
            order_target_percent(stock, 0.00)
            log.info('SOLD ALL {} {} Shares @ MarketPrice {:.2f}'\
            .format(stock.symbol, amount, price))
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 0 # 0 is ok if MaxLeverage is less than 0.50
            RhMargin = context.RobinHoodMargin # Set to 0 if you do not have Robinhood Gold or if you do not use the margin money
            MaxLeverage = 0.99
            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
            Action = "Bought" if 0 < DiffAmount else "Sold"
            if stock is context.UVXY:
                LimitPrice = price * 1.00
                order(stock, DiffAmount, style=LimitOrder(LimitPrice))
                log.info('{} {} {} Shares @ LimitPrice {:.2f}'\
                .format(Action, stock.symbol, DiffAmount, LimitPrice))
            else:
                order(stock, DiffAmount)
                log.info('{} {} {} Shares @ MarketPrice {:.2f}'\
                .format(Action, stock.symbol, DiffAmount, price))

def Rebalance(context,data):
    c = context
    cancel_open_orders(context, data)

    # Reset with 1 share at beginning or after sold all shares
    if 0 == c.portfolio.positions[c.SVXY].amount: order(c.SVXY, 1)

    for position in c.portfolio.positions.itervalues():

        PercProfit = position.last_sale_price / position.cost_basis - 1
        if position.sid is c.SVXY:
            if 1 == position.amount:
                if 1.33 * c.Profit < PercProfit:
                    TarPer(c, data, c.SVXY, 0.99)
                elif c.Loss > PercProfit:
                    TarPer(c, data, c.SVXY, 0.99)
            elif 1 < position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, c.SVXY, 0.00)

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

    for position in c.portfolio.positions.itervalues():

        PercProfit = position.last_sale_price / position.cost_basis - 1
        if position.sid is c.SVXY and 1 < position.amount:
            if c.StopLoss > PercProfit:
                TarPer(c, data, c.SVXY, 0.00)

def my_record_vars(context, data):    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass
    
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 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.  
                # # # # # # # # #  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)))
There was a runtime error.
12 responses

Includes trailing stop loss and UVXY after big SVXY gains anticipating whiplash. Still appears that it will be easy to implement using live data on my laptop.

Clone Algorithm
144
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
#Imports
from datetime import datetime  
from pytz import timezone      
from zipline.utils import tradingcalendar
from quantopian.pipeline.data.builtin import USEquityPricing
import statsmodels.api as sm 
import numpy as np
import pandas as pd
import functools
import quantopian.pipeline.data 
import re
import scipy
import talib
import time

History = 128

def initialize(context):
    c = context

    c.RobinHoodMargin = 0 # I prefer to not use margin.

    set_benchmark(symbol('SVXY'))
    c.One   = symbol('SVXY')
    c.Two   = symbol('UVXY')
    c.Three = symbol('RWR')

    c.ShowMaxLev = True

    #Buy Triggers
    c.Profit   = 0.08
    c.Loss     = -0.04 

    #Trailing Stop Loss happens in handle_data
    c.StopLoss = -0.03
    c.stop_pct = 1 + c.StopLoss
    c.stops = pd.Series({})

    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 120))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 60))
    
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    #Set commission and slippage
    set_commission(commission.PerShare(cost=0.000, min_trade_cost=0.0)) 
    set_slippage(slippage.FixedSlippage(spread=0.01))
    
def before_trading_start(context, data):
    c = context
    c.mx_lvrg  = 0 # daily max leverage

def set_trailing_stop(context, data):
    c = context

    for p in c.portfolio.positions.itervalues():

        if 1 < p.amount and not get_open_orders(p):
            """PercProfit = p.last_sale_price / p.cost_basis - 1
            if c.StopLoss > PercProfit:
                TarPer(c, data, p.sid, 0.00)"""
            prc = p.last_sale_price
            if p in c.stops:
                if prc < c.stops[p]:
                    stp = c.stops[p]
                    log.info("Stop Loss'd {} at prc {}, {}% of stp prc {}".format(
                            p.sid, '%.2f' % prc, '%.1f' % (100 * prc / stp), '%.2f' % stp))
                    TarPer(c, data, p.sid, 0.00)
                    del c.stops[p]
                else: c.stops[p] = max(c.stops[p], c.stop_pct * prc)
            else:
                c.stops[p] = c.stop_pct * prc

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

    cancel_open_orders_for(context, data, stock)
    if DataCanTrade(context, data, stock):

        if stock in context.stops:
            del context.stops[stock]
        if 0 == TargetPercent:
            amount = context.portfolio.positions[stock].amount
            amount = 0 if 0 > amount else amount
            price = data.current(stock, 'price')
            order_target_percent(stock, 0.00)
            log.info('SOLD ALL {} {} Shares @ MarketPrice {:.2f}'\
            .format(stock.symbol, amount, price))
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 0 # 0 is ok if MaxLeverage is less than 0.50
            RhMargin = context.RobinHoodMargin # Set to 0 if you do not have Robinhood Gold or if you do not use the margin money
            MaxLeverage = 0.99
            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
            Action = "Bought" if 0 < DiffAmount else "Sold"
            order(stock, DiffAmount)
            log.info('{} {} {} Shares @ MarketPrice {:.2f}'\
                     .format(Action, stock.symbol, DiffAmount, price))

def Rebalance(context,data):
    c = context
    cancel_open_orders(context, data)

    # Reset with 1 share at beginning or after sold all shares
    if 0 == c.portfolio.positions[c.One].amount: order(c.One, 1)

    for position in c.portfolio.positions.itervalues():

        PercProfit = position.last_sale_price / position.cost_basis - 1
        if (position.sid is c.One):
            if 1 == position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, position.sid, 0.99)
                elif c.Loss > PercProfit:
                    TarPer(c, data, position.sid, 0.99)
            elif 1 < position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, position.sid, 0.00)
                    TarPer(c, data, c.Two, 0.99)

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

    set_trailing_stop(context, data)

def my_record_vars(context, data):    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass
    
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 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.  
                # # # # # # # # #  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)))
There was a runtime error.

Unfortunately the exposure of SVXY (and UVXY) changed in early March 2018. So this algo reflects parameters that do better with the new SVXY. I changed the settings to:
Buy Triggers: Profit 0.02 Loss -0.01
Trailing Stop Loss is still -0.03
These are the settings I am using in live trading on my laptop trying to copy the behavior of this algorithm.

Clone Algorithm
144
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
#Imports
from datetime import datetime  
from pytz import timezone      
from zipline.utils import tradingcalendar
from quantopian.pipeline.data.builtin import USEquityPricing
import statsmodels.api as sm 
import numpy as np
import pandas as pd
import functools
import quantopian.pipeline.data 
import re
import scipy
import talib
import time

History = 128

def initialize(context):
    c = context

    c.RobinHoodMargin = 0 # I prefer to not use margin.

    set_benchmark(symbol('SVXY'))
    c.One   = symbol('SVXY')
    c.Two   = symbol('UVXY')
    c.Three = symbol('RWR')

    c.ShowMaxLev = True

    #Buy Triggers
    c.Profit   = 0.02
    c.Loss     = -0.01

    #Trailing Stop Loss happens in handle_data
    c.StopLoss = -0.03
    c.stop_pct = 1 + c.StopLoss
    c.stops = pd.Series({})

    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 120))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 60))
    
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    #Set commission and slippage
    set_commission(commission.PerShare(cost=0.000, min_trade_cost=0.0)) 
    set_slippage(slippage.FixedSlippage(spread=0.01))
    
def before_trading_start(context, data):
    c = context
    c.mx_lvrg  = 0 # daily max leverage

def set_trailing_stop(context, data):
    c = context

    for p in c.portfolio.positions.itervalues():

        if 1 < p.amount and not get_open_orders(p):
            """PercProfit = p.last_sale_price / p.cost_basis - 1
            if c.StopLoss > PercProfit:
                TarPer(c, data, p.sid, 0.00)"""
            prc = p.last_sale_price
            if p in c.stops:
                if prc < c.stops[p]:
                    stp = c.stops[p]
                    log.info("Stop Loss'd {} at prc {}, {}% of stp prc {}".format(
                            p.sid, '%.2f' % prc, '%.1f' % (100 * prc / stp), '%.2f' % stp))
                    TarPer(c, data, p.sid, 0.00)
                    del c.stops[p]
                else: c.stops[p] = max(c.stops[p], c.stop_pct * prc)
            else:
                c.stops[p] = c.stop_pct * prc

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

    cancel_open_orders_for(context, data, stock)
    if DataCanTrade(context, data, stock):

        if stock in context.stops:
            del context.stops[stock]
        if 0 == TargetPercent:
            amount = context.portfolio.positions[stock].amount
            amount = 0 if 0 > amount else amount
            price = data.current(stock, 'price')
            order_target_percent(stock, 0.00)
            log.info('SOLD ALL {} {} Shares @ MarketPrice {:.2f}'\
            .format(stock.symbol, amount, price))
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 0 # 0 is ok if MaxLeverage is less than 0.50
            RhMargin = context.RobinHoodMargin # Set to 0 if you do not have Robinhood Gold or if you do not use the margin money
            MaxLeverage = 0.99
            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
            Action = "Bought" if 0 < DiffAmount else "Sold"
            order(stock, DiffAmount)
            log.info('{} {} {} Shares @ MarketPrice {:.2f}'\
                     .format(Action, stock.symbol, DiffAmount, price))

def Rebalance(context,data):
    c = context
    cancel_open_orders(context, data)

    # Reset with 1 share at beginning or after sold all shares
    if 0 == c.portfolio.positions[c.One].amount: order(c.One, 1)

    for position in c.portfolio.positions.itervalues():

        PercProfit = position.last_sale_price / position.cost_basis - 1
        if (position.sid is c.One):
            if 1 == position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, position.sid, 0.99)
                elif c.Loss > PercProfit:
                    TarPer(c, data, position.sid, 0.99)
            elif 1 < position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, position.sid, 0.00)
                    TarPer(c, data, c.Two, 0.99)

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

    set_trailing_stop(context, data)

def my_record_vars(context, data):    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass
    
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 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.  
                # # # # # # # # #  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)))
There was a runtime error.

This is closer to my original intent.
c.Profit = 0.01925 c.Loss = -0.01125
DelayedTarPer for UVXY

Clone Algorithm
144
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
#Imports
from datetime import datetime  
from pytz import timezone      
from zipline.utils import tradingcalendar
from quantopian.pipeline.data.builtin import USEquityPricing
import statsmodels.api as sm 
import numpy as np
import pandas as pd
import functools
import quantopian.pipeline.data 
import re
import scipy
import talib
import time

History = 128

def initialize(context):
    c = context

    c.RobinHoodMargin = 0 # I prefer to not use margin.

    set_benchmark(symbol('SVXY'))
    c.One   = symbol('SVXY')
    c.Two   = symbol('UVXY')
    c.Three = symbol('RWR')

    c.ShowMaxLev = True

    #Buy Triggers
    c.Profit   = 0.01925
    c.Loss     = -0.01125

    #Trailing Stop Loss happens in handle_data
    c.StopLoss = -0.03
    c.stop_pct = 1 + c.StopLoss
    c.stops = pd.Series({})
    c.DelayedTrade = False
    c.DelayedStock = c.Two
    c.DelayedTargetPercent = 0.99

    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 120))
    schedule_function(Rebalance, date_rules.every_day(), time_rules.market_close(minutes = 60))
    
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    #Set commission and slippage
    set_commission(commission.PerShare(cost=0.000, min_trade_cost=0.0)) 
    set_slippage(slippage.FixedSlippage(spread=0.01))
    
def before_trading_start(context, data):
    c = context
    c.mx_lvrg  = 0 # daily max leverage

def set_trailing_stop(context, data):
    c = context

    for p in c.portfolio.positions.itervalues():

        if 1 < p.amount and not get_open_orders(p):
            """PercProfit = p.last_sale_price / p.cost_basis - 1
            if c.StopLoss > PercProfit:
                TarPer(c, data, p.sid, 0.00)"""
            prc = p.last_sale_price
            if p in c.stops:
                if prc < c.stops[p]:
                    stp = c.stops[p]
                    log.info("Stop Loss'd {} at prc {}, {}% of stp prc {}".format(
                            p.sid, '%.2f' % prc, '%.1f' % (100 * prc / stp), '%.2f' % stp))
                    TarPer(c, data, p.sid, 0.00)
                    del c.stops[p]
                else: c.stops[p] = max(c.stops[p], c.stop_pct * prc)
            else:
                c.stops[p] = c.stop_pct * prc

def DelayedTarPer(context, data):
    c = context

    if c.DelayedTrade:
        if not get_open_orders():
            c.DelayedTrade = False
            TarPer(context, data, c.DelayedStock, c.DelayedTargetPercent)

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

    cancel_open_orders_for(context, data, stock)
    if DataCanTrade(context, data, stock):

        if stock in context.stops:
            del context.stops[stock]
        if 0 == TargetPercent:
            amount = context.portfolio.positions[stock].amount
            amount = 0 if 0 > amount else amount
            price = data.current(stock, 'price')
            order_target_percent(stock, 0.00)
            log.info('SOLD ALL {} {} Shares @ MarketPrice {:.2f}'\
            .format(stock.symbol, amount, price))
        else:
            # Always want money available to withdraw 
            # and also try to prevent margin related order rejections
            PV = context.portfolio.portfolio_value
            DoNotSpend = 0 # 0 is ok if MaxLeverage is less than 0.50
            RhMargin = context.RobinHoodMargin # Set to 0 if you do not have Robinhood Gold or if you do not use the margin money
            MaxLeverage = 0.99
            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
            Action = "Bought" if 0 < DiffAmount else "Sold"
            order(stock, DiffAmount)
            log.info('{} {} {} Shares @ MarketPrice {:.2f}'\
                     .format(Action, stock.symbol, DiffAmount, price))

def Rebalance(context,data):
    c = context
    cancel_open_orders(context, data)

    # Reset with 1 share at beginning or after sold all shares
    if 0 == c.portfolio.positions[c.One].amount: order(c.One, 1)

    for position in c.portfolio.positions.itervalues():

        PercProfit = position.last_sale_price / position.cost_basis - 1
        if (position.sid is c.One):
            if 1 == position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, position.sid, 0.99)
                elif c.Loss > PercProfit:
                    TarPer(c, data, position.sid, 0.99)
            elif 1 < position.amount:
                if c.Profit < PercProfit:
                    TarPer(c, data, position.sid, 0.00)
                    c.DelayedTrade = True
                    c.DelayedStock = c.Two
                    c.DelayedTargetPercent = 0.99

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

    set_trailing_stop(context, data)
    DelayedTarPer(context, data)

def my_record_vars(context, data):    
    # handling this in handle_data now to show mx_lvrg
    if not context.ShowMaxLev:
        record(Leverage=context.account.leverage)
        pass
    
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 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.  
                # # # # # # # # #  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)))
There was a runtime error.

Hey Charles how are you implementing this live? Would you mind giving a summary?

Slight update I changed StopLoss = -0.03 to .StopLoss = -0.0275.

@Nicholas,
I built a custom python framework on my laptop that tries to behave the same as this algorithm. I am using Robinhood command line API at https://github.com/sanko/Robinhood/blob/master/Order.md#place-an-order . I also am using TCL / Expect for some of the command line automation. I also am trading ETHereum in the same algo by automatically interacting with the Robinhood website at https://robinhood.com/crypto/ETH . A couple of Python modules are helpful in doing all this. https://pyautogui.readthedocs.io/en/latest/keyboard.html and https://pypi.org/project/clipboard/0.0.4/ .

Thanks Charles can you explain more on your buy signals? I’m a little rusty on my python at the moment I see the .02 are you buying when the symbol goes up 2%?

Thanks for all your help

@Zenothestoic, did you replace your XIV with SVXY? What kind of rolling positions do you use? anything that can be codified here?

@zenotastic, I made an algo where you can test options in Quantopian.

What type of strangle do you enter each month? Based on the steepness of the curve or just around the spot?

Yeah, it seems that a strangle on VXX (or SVXY) is relatively "expensive"....and really only has a good "chance" of making money on the VIX spikes. I can see where it would have done good semi recently (with good timing on the spikes) but if VIX continues to stabilize I would think the # of profitable strangles will drop over time....Dont get me wrong: I'm very interested in the concept, but never quite found the data to help prove there was a solid timing/rollover/price points would consistently profit.

Can anyone provide any insight on the buy triggers on this algo? When does it buy?

I have a fully automated system up and looking to try this in it but confused on the logic

Thanks

SVXY and UVXY no longer supported on robinhood FYI

Well maybe it still is looks like a split coming tomorrow