Back to Community
For Robinhood trading

Perhaps someone might find this algo useful and/or be willing to improve it ...

Clone Algorithm
309
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

def initialize(context):
    
    # Robinhood
    set_long_only()
    set_commission(commission.PerTrade(cost=0.0))
    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744)
    context.f = sid(22887)
    context.x = sid(40516)
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    if get_open_orders():
        return    
    if context.m not in data:
        return
    if context.f not in data:
        return
    if context.x not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    signal = (x[context.m] > 1) and (x[context.x] > 1)
    
    if signal:    
        order_target_percent(context.x, 0.3)
        order_target_percent(context.m, 0.7)
        order_target(context.f, 0)
    else:
        order_target(context.x, 0)
        order_target_percent(context.f, 1.0)
        order_target(context.m, 0)
    
    record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    record(leverage = context.account.leverage, 
           exposure = context.account.net_leverage)

There was a runtime error.
117 responses

Looks really interesting but I'm getting a runtime error from the Rebalance function when I try to backtest it myself... I don't think I've made any changes to the algo.

What's the error that you are getting?

Backtests have worked for me but i didn't run them too far in the past. Might be due to a symbol not being available within a selected time frame.

@Tim - Can you elaborate on what decisions this algo is making? I like the performance but did not completely understand what its actually doing.

It looks too good to be true. It's such a short backtest history.

@Minh Ngo: Well, I suppose you are not going to use it then, right?

@Brandon: Yes, this is the earliest the backtest can be started because some of the symbols do not go further back.

Here's the same algorithm without the VIX ETF component (XIV), which makes it possible to run the backtest from 2004.

The EDV bond has also been replaced by the TLT for the same reason, but this is not very important.

The XIV spices things up, though, and provides for additional returns.

Clone Algorithm
103
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

def initialize(context):
    
    # Robinhood
    set_long_only()
    set_commission(commission.PerTrade(cost=0.0))
    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(23921) # TLT
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    if get_open_orders():
        return    
    if context.m not in data:
        return
    if context.f not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    signal = (x[context.m] > 1) 
    
    if signal:    
        order_target_percent(context.m, 1.0)
        order_target(context.f, 0)
    else:
        order_target_percent(context.f, 1.0)
        order_target(context.m, 0)
    
    record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    record(leverage = context.account.leverage, 
           exposure = context.account.net_leverage)

There was a runtime error.

@Brandon: The algorithm essentially uses a cross-over signal from a short and a long moving average of a S&P 500 ETF in order to enter and exit positions in this same index and in a government bond.

The VIX ETF (XIV) is essentially assumed to move in the same direction as the market, since a bullish market is linked to decreasing volatility and the other way around.

@Tim thanks for the elaboration.

I have experimented with your Algo and made a few modifications that might help the Robinhood implementation a bit. In addition I added the signal indicator to the chart to help view the progress of that a bit. With @Tim's addition of TLT, I made a modification to do 50% TLT and 50% EDV instead of 100% of one or the other

One more note: Since Robinhood clears their sales after 3 days you cannot use proceeds from a sale to purchase again on the same day. So I build a variable in that limits the Algo. The context.value variable should be not more than half of the cash balance in the account to allow for the sell and purchase on the same day.

EDIT: Forgot to attach the Algo...see below

Here are the results

Clone Algorithm
30
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

def initialize(context):
    # Robinhood
    set_long_only()
    set_commission(commission.PerTrade(cost=0.0))
    
    """ 
    NOTE: context.value = the amount of money you want to limit this Algo with. Since Robinhood has a T3 model you cannot buy and sell the same day unless the cash is available so if you sell $500 of XYZ and buy $500 of XYZ you must have the $500 in cash on Robinhood. So this variable should be half of the cash value in your account.
    """
    context.value = 500
    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.g = sid(23921) # TLT
    context.x = sid(40516) # XIV
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    
def rebalance(context, data):
    
    if get_open_orders():
        return    
    if context.m not in data:
        return
    if context.f not in data:
        return
    if context.x not in data:
        return
    if context.g not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    log.info(x)
    
    signal = (x[context.m] > 1) 
    record(Indicator = (x[context.m]))
    
    if signal:    
        xv = (context.value * 0.3) / context.portfolio.portfolio_value
        mv = (context.value * 0.7) / context.portfolio.portfolio_value
        fv = (context.value * 0) / context.portfolio.portfolio_value
        gv = (context.value * 0) / context.portfolio.portfolio_value

        order_target_percent(context.x, xv)
        order_target_percent(context.m, mv)
        order_target(context.f, 0)
        order_target(context.g, 0)
    else:
        xv = (context.value * 0) / context.portfolio.portfolio_value
        mv = (context.value * 0) / context.portfolio.portfolio_value
        fv = (context.value * 0.5) / context.portfolio.portfolio_value
        gv = (context.value * 0.5) / context.portfolio.portfolio_value
        order_target(context.x, 0)
        order_target(context.m, 0)
        order_target_percent(context.f, fv)
        order_target_percent(context.g, gv)
    
    record(signal = signal)
    log.warn(context.portfolio.portfolio_value)
    log.info("RSP Percent = %s" % (mv * 100))
    log.info("EDV Percent = %s" % (fv * 100))
    log.info("TLT Percent = %s" % (gv * 100))
    log.info("XIV Percent = %s" % (xv * 100))
    log.warn("M Shares = %s" % context.portfolio.positions[symbol('RSP')].amount)
    log.warn("F Shares = %s" % context.portfolio.positions[symbol('EDV')].amount)
    log.warn("G Shares = %s" % context.portfolio.positions[symbol('TLT')].amount)
    log.warn("X Shares = %s" % context.portfolio.positions[symbol('XIV')].amount)
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    record(leverage = context.account.leverage)#, 
           #exposure = context.account.net_leverage)

There was a runtime error.

Thanks Brandon, I did not realize that proceeds from a sale cannot be used immediately.

Your welcome, I put this to the true test by going live with it yesterday. It was a down day, but the loss was less than SPY. So far the Algo has performed as expected.

Brandon, please be very careful !

My original algo, at least, goes into negative cash from time to time. I only discovered this yesterday. If you are trading with a non-margin account, such us RobinHood (I believe), this could trigger a margin call !

If you are using RobinHood, I would strongly suggest that you record (plot) the context.portfolio.cash variable in your backtest to see whether it goes negative.

I am very sorry about this development, but I was totally unaware about this problem until very recently when one of our fellow Quantopians turned my attention it.

Thanks Tim. Hopfully having the context.value variable should help limit my exposure because it should limit the purchases, so far it is working as expected.

I will backtest to confirm the fuctionality as you noted.

Just ran a few backtests and they responded as expected.

FYI, in the past I have noticed that with Robinhood and live trades they will not allow you to go negative with cash. Any orders that will cause a negative cash balance are automatically denied.

Many thanks for coming back with the results so quickly, Brandon, and good luck with your live trade!

For those interested, here is a version of my original algo with improved cash management -- thanks to @garyha and his PrV routine !

It still goes into negative cash sometimes, but to a negligible extent.

It checks for positive cash before placing any orders. This should in principle take care of the three-day delay in the a availability of sale proceeds as well, but the only way to know and to see what side effects this may have would probably be to go live with it, unfortunately. Perhaps I should put the money where the mouth is and cough up some cash ...

The volatility is very high and the drawdown periods protracted, but the profits are not bad and the Sharpe ratio is more or less acceptable, I suppose.

Below I am also attaching a Pyfolio tear sheet of the backtest for your peruse.

Clone Algorithm
155
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

def initialize(context):
    
    # Robinhood
    set_long_only()
    set_commission(commission.PerTrade(cost=0.0))
    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    c = context
    
    if get_open_orders():
        return    
    if c.m not in data:
        return
    if c.f not in data:
        return
    if c.x not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash > 0:
            order(c.x, cash*0.3 // data[c.x].price,)
            order(c.m, cash*0.7 // data[c.m].price)
    else:
        order_target(c.x, 0)
        order_target(c.m, 0)
        if cash > 0:
            order(c.f, cash // data[c.f].price)
    
    # record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    
    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1


    
There was a runtime error.

The Pyfolio sheet.

Loading notebook preview...
Notebook previews are currently unavailable.

Hi Tim,

You might also consider what I posted here:

https://www.quantopian.com/posts/minimum-variance-w-slash-constraint

Grant

The first algo: Profited 22829 on 39165 activated/transacted for PvR of 58.3%   0.0505 %/day  
Most recent:    Profited 20194 on 10095 activated/transacted for PvR of 200.0%  0.1766 %/day  

blue pill (to forget about The Matrix and continue to live in the world of illusion) or a red pill (to enter the [...] world of reality). -- Morpheus, Wikipedia

Tim chose the red pill, PvR. Result, a dramatic increase ... 58.3% ==> 200.0%

In 60 algos that I ran this week with PvR, there is only one higher in PvR per day (that could not yet be eliminated as overfitting)
  than this one by Tim Vidmar, at 0.1766 %/day.

I will share privately with anyone demonstrating that they are working with PvR and understand it, which algo that is.

(The code above by GK ranks 8th even with the negative cash).

Hi Grant,

Many thanks for drawing my attention to your optimization algorithm. Brilliant job! I can only wish I knew that much Python ...

I do believe, however, that the issue of negative cash, as pointed out by garyha, is an important one. Have you tried running your algorithm with his PvR metrics?

Regards,

Tim

One final version of the algo on my part. Negative cash is now completely avoided, so the algo should work for live trading with Robinhood. Also added is a simulation of the 3-day delay in the availability of sales' proceeds. Please comment out the related parts only suitable for backtesting when going live and the other way around.

To stay on the safe side, only 95% of the available cash is invested at any time. Since the assumption is that Robinhood charges no commission, trading is now carried out daily, rather than weekly., to catch market downturns earlier. The result of all these measures is that the profit is much reduced with regard to the more risky version, but so are the volatility and the drawdown.

The main advantage of this algo as compared to simply investing into an index ETF is that it should be able to avoid a major crash in a serious crisis, like the one in 2008.

Clone Algorithm
155
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 math as m


def initialize(context):
    
    # Robinhood
    set_long_only()
    set_commission(commission.PerTrade(cost=0.0))
    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.last_sale = None
    context.trading_days = 0
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.every_day(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    c = context
    
    # For live trading only
    # if do_unsettled_funds_exist(context):
    #     return

    # Only for backtesting purposes!
    if cash_settlement_date(context):
        return
    
    if get_open_orders():
        return    
    if c.m not in data:
        return
    if c.f not in data:
        return
    if c.x not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash > 0:
            w = int(m.floor(cash*0.25 / data[c.x].price))
            if w >= 0:
                order_target(c.x, w)
            w = int(m.floor(cash*0.7 / data[c.m].price))
            if w >= 0:
                order_target(c.m, w)
    else:
        order_target(c.x, 0)
        order_target(c.m, 0)
        if cash > 0:
            w = int(m.floor(cash*0.95 / data[c.f].price))
            if w >= 0:
                order_target(c.f, w)
                
    # Backetes only
    check_last_sale(context)
    
    # record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    

def do_unsettled_funds_exist(context):

    # To only be used for live trading!
    if context.portfolio.cash != context.account.settled_cash:
        return True

def check_last_sale(context):

    #To only be used for backtesting!
 
    open_orders = get_open_orders()
    most_recent_trade = []
 
    if open_orders:
        for sec, order in open_orders.iteritems():
            for oo in order:
                if oo.amount < 0:
                    most_recent_trade.append(oo.created)
    if len(most_recent_trade) > 0:
        context.last_sale = max(most_recent_trade)

        
def cash_settlement_date(context):

    # To only be used for backtesting!
    if context.last_sale and (get_datetime() - context.last_sale).days < 3:
        return True

    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1


    
There was a runtime error.

Regarding cash going slightly negative, I hadn't considered how that would play out with Robinhood. The obvious solution is that the sum of the allocations needs to be less than 1, to compensate (i.e. create a small cash buffer). But what should the factor be? I suppose one approach is to run successive backtests, tweaking the leverage until it always stays below 1.0, but that wouldn't necessarily guard against bad things from happening live on Robinhood.

He meant your own -$22,362 (started with $10k), 120% negative cash.
Repeating his question, have you tried yours, GK, with PvR?
Maybe a similar increase in fully accounted-for profitability could come out of that.

Hi Gary,

I'm not sure what you mean. If the leverage is around 1, how could the algo have borrowed -$22,362?

Grant

Because record() only records the last value seen in a day, explained further at Max Intraday Leverage

@Tim, great additions I plan on migrating my live trading to this now.

Thanks again for your trust, Brandon, all the best with your trades!

Please let me know if anything seems suspicious or goes wrong.

The only thing going wrong today is the market direction! ;-)

@Brandon: Sorry to hear that, if the trend continues for a few days, the code should realize that and take action, i.e., switch the investement to bonds.

+0.54% today, i'd say it worked. Once it went live the Algo bought in then decided to sell in the afternoon.

A note...I am getting this during live trading with this code. I don't understand what is causing it to drop into the IF statement.

def do_unsettled_funds_exist(context):  
    """  
    For Robinhood users. In order to prevent you from attempting  
    to trade on unsettled cash (settlement dates are T+3) from  
    sale of proceeds. You can use this snippet of code which  
    checks for whether or not you currently have unsettled funds  
    To only be used for live trading!  
    """  
    if context.portfolio.cash != context.account.settled_cash:  
        log.info("Unsettled Funds EXIST")  
        log.info("Portfolio Cash = '%s'" % (context.portfolio.cash))  
        log.info("Settled Cash = '%s'" % (context.account.settled_cash))  
        return True

2016-02-16 14:32 do_unsettled_funds_exist:115 INFO Settled Cash = '416.56'
2016-02-16 14:32 do_unsettled_funds_exist:114 INFO Portfolio Cash = '416.56'
2016-02-16 14:32 do_unsettled_funds_exist:113 INFO Unsettled Funds EXIST

Brandon,

I believe the message is related to the T+3 settlement rule and is simply informing you that due to it, less funds (cash) are actually available than one might expect. The algorithm takes this into account and waits for the fund to be settled, i.e., on the days when you get the message, the algo does not (should not) trade.

Unfortunately I do not think that's the cause. As you can see from the live log message above, it is actually determining that context.portfolio.cash and context.account.settled_cash do not equal (lines 113,114,115) in the algo.

To troubleshoot I added the two lines to print the values and they match.

if context.portfolio.cash != context.account.settled_cash:  
        log.info("Unsettled Funds EXIST")  
        log.info("Portfolio Cash = '%s'" % (context.portfolio.cash))  
        log.info("Settled Cash = '%s'" % (context.account.settled_cash))  
        return True  

It appears that the algo is somehow determining that 416.56 != 416.56.. which is wrong and should not run any of the if statement
if context.portfolio.cash != context.account.settled_cash:

I think something is not functioning properly with quantopian's implementation of robinhood algo's.

Here is the complete code with the modifications...I can't figure out why its thinking the two equal values of context.portfolio.cash and context.account.settled_cash and not equal.

Clone Algorithm
15
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 math as m


def initialize(context):
    
    # Robinhood
    set_long_only()
    set_commission(commission.PerTrade(cost=0.0))
    
    """ 
    NOTE: context.value = the amount of money you want to limit this Algo with. Since Robinhood has a T3 model you cannot buy and sell the same day unless the cash is available so if you sell $500 of XYZ and buy $500 of XYZ you must have the $500 in cash on Robinhood. So this variable should be half of the cash value in your account.
    """
    context.value = 900
    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.last_sale = None
    context.trading_days = 0
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.every_day(),
                      time_rule = time_rules.market_close(minutes = 60),
                      half_days = True)
    
def rebalance(context, data):
    log.info("Rebalancing...")
    c = context
    
    # For live trading only
    if do_unsettled_funds_exist(context):
        return

    # Only for backtesting purposes!
    #if cash_settlement_date(context):
    #    return
    
    if get_open_orders():
        return    
    if c.m not in data:
        return
    if c.f not in data:
        return
    if c.x not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    record(SigRSP = (x[c.m]))
    record(SigXIV = (x[c.x]))
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash >= c.value:
            w = int(m.floor(c.value*0.25 / data[c.x].price))
            if w >= 0:
                order_target(c.x, w)
                log.info("Ordered '%s' shares of '%s'" % (w, c.x)) 
                w = 0
            w = int(m.floor(c.value*0.7 / data[c.m].price))
            if w >= 0:
                order_target(c.m, w)
                log.info("Ordered '%s' shares of '%s'" % (w, c.m)) 
    else:
        order_target(c.x, 0)
        order_target(c.m, 0)
        if cash >= c.value:
            w = int(m.floor(c.value*0.95 / data[c.f].price))
            if w >= 0:
                order_target(c.f, w)
                log.info("Ordered '%s' shares of '%s'" % (w, c.f)) 
                
    # Backetes only
    #check_last_sale(context)
    
    record(signal = signal)
    
def handle_data(context, data):
    
    if context.start:
        log.info("Running Context Start")
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    

def do_unsettled_funds_exist(context):
    """
    For Robinhood users. In order to prevent you from attempting
    to trade on unsettled cash (settlement dates are T+3) from
    sale of proceeds. You can use this snippet of code which
    checks for whether or not you currently have unsettled funds
    
    To only be used for live trading!
    """
    if context.portfolio.cash != context.account.settled_cash:
        log.info("Unsettled Funds EXIST")
        log.info("Portfolio Cash = '%s'" % (context.portfolio.cash))
        log.info("Settled Cash = '%s'" % (context.account.settled_cash)) 
        return True

def check_last_sale(context):

    #To only be used for backtesting!
    log.info("Checking Last Sale")
    open_orders = get_open_orders()
    most_recent_trade = []
 
    if open_orders:
        for sec, order in open_orders.iteritems():
            for oo in order:
                if oo.amount < 0:
                    most_recent_trade.append(oo.created)
    if len(most_recent_trade) > 0:
        context.last_sale = max(most_recent_trade)

        
def cash_settlement_date(context):

    # To only be used for backtesting!
    log.info("Getting settlement date")
    if context.last_sale and (get_datetime() - context.last_sale).days < 3:
        return True

    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 0         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 0         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 0         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 0         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1
There was a runtime error.

Brandon,

I think waht we have here is a typical example of the not very savvy programming practice of directly comparing two real numbers for equality .

Even a slightest difference in some decimal places, which can be caused by rounding errors, will make them appear different.

I think one could try the following few lines of code instead:

if np.abs(context.portfolio.cash / context.account.settled_cash - 1) > 0.001:
log.info("Unsettled Funds EXIST")
log.info("Portfolio Cash = '%s'" % (context.portfolio.cash))
log.info("Settled Cash = '%s'" % (context.account.settled_cash))
return True

Variation on the theme, for those with a margin account (IB ?) and a stomach for large volatility and drawdowns.

Advantage: exceedingly simple, could even be executed manually.

See also http://seekingalpha.com/article/3924056-xiv-svxy-go?ifp=0 .

Clone Algorithm
76
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

def initialize(context):    
    
    set_long_only()    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    c = context
    
    if get_open_orders():
        return    
    if c.m not in data:
        return
    if c.f not in data:
        return
    if c.x not in data:
        return
    
    P = history(100, '1d', 'price').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash > 0:
            order(c.x, cash // data[c.x].price,)
    else:
        order_target(c.x, 0)
        if cash > 0:
            order(c.f, cash // data[c.f].price)
    
    # record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    
    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1


    
There was a runtime error.

Tear sheet for the algo above.

Loading notebook preview...
Notebook previews are currently unavailable.

Thanks for the great discussion here, everyone. For those trading the algo since this discussion, how has it performed? I just backtested the most recent Robinhood-compatable version from Tim from 02/04 to 03/27 and it returns about 10% lower than SPY. Is that expected variance, or is the algo not working out of sample?

Hey @Tim,

Thanks for the algo. I'm new to this and trying to learn more about it. I figure an interesting way, besides tutorials and books, is to look at other people's codes and dissect them to learn what each component is doing. Upon running some back tests of your second iteration posted in this thread from 3/24/15 to 3/24/16, it seems like the algo only purchased 3 stocks total. They were RSP, XIV, and EDV. It purchased and held most for a long period of time. Is this the correct implementation of the algo? For the heck of it, I hit run Live Yesterday just to see how Live Trading worked. It ended up buying RSP and XIV.

Thanks!
Zach

Hi Zach,

Sincerest thanks for your interest in this algorithm and for your comments.

You have analyzed the algo's behaviour absolutely correctly, it invests into an equal-weights S&P 500 ETF (RSP), plus an inverse volatility index (XIV), which is highly correlated with the RSP and spices things up -- in the sense of higher returns, but also higher risk -- except during the periods of profound market draw-downs, which are detected through a simple crossing of a long and a short moving averages of the RSP and the VIX, when it goes 100% into a government bond ETF (EDV), until the market rises again. The idea is thus to follow the market, as it is often recommended to retail investors, while avoiding as much as possible the highly bearish periods (i.e., the 2008 crisis). Quite simplistic, therefore, but not entirely un-effective.

Good luck with the algo if you intend to trade trade it live. Should you have any suggestions for improvement, please feel free to implement them and/or let me know.

Regards,

Tim

Hey @Steve,

According to the Pyfolio tear sheet analysis (the various versions of) this algo indeed perform worse out of sample then otherwise, but the average yearly return should still be around 10%.

That being said, the algo can indeed experience long periods of drawdown or very low growth. It is also not really clear how is it going to perform in this era of extremely low interest rates.

Please be careful if you are trading it live. If there are people out here who do, I would very much like to hear about your experience.

Regards,

Tim

Thanks @Tim - I think it's worth emphasizing what you've already said on this thread: the main value of this algo is to protect against severe market downturns, rather than to earn abnormal returns regardless of market conditions.

Thanks Tim!

As soon as I sent it and went back in to deconstruct it, I figured out that may be the context.m etc. may be the 3 stocks. Low and behold, I just didn't read the comments next to them saying which stocks there were... I really appreciate the context of the algo; it'll help me better understand the code and math when trying to piece it together. I'm coming from a Matlab background so I have an idea of the syntax but need to do a lot more studying a practice!

As a side note, up 1% so far today with my chump change investment :D !!

Thanks,
Zach

Here is the latest incarnation of the algo, with garyha's PvR routine, and Luca's cash management. Thanks for your brilliant code, guys!

It no longer obeys the T+3 rule, since it has been apparently abandoned by Robinhood in the meantime, and it stays in positive cash all the time.

It needs a warm-up period, though, to come to full leverage (1.0).

Clone Algorithm
86
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

class ExposureMngr(object):
    
    def __init__(self, target_leverage = 1.0, target_long_exposure_perc = 0.50, target_short_exposure_perc = 0.50):   
        self.target_leverage            = target_leverage
        self.target_long_exposure_perc  = target_long_exposure_perc              
        self.target_short_exposure_perc = target_short_exposure_perc           
        self.short_exposure             = 0.0
        self.long_exposure              = 0.0
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
      
    def get_current_leverage(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure
        curr_leverage = (context.portfolio.portfolio_value - curr_cash) / context.portfolio.portfolio_value
        return curr_leverage

    def get_exposure(self, context, consider_open_orders = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)
        return long_exposure + short_exposure
    
    def get_long_short_exposure(self, context, consider_open_orders = True):
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure     
        return (long_exposure, short_exposure)
    
    def get_long_short_exposure_pct(self, context, consider_open_orders = True, consider_unused_cash = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)        
        total_cash = long_exposure + short_exposure
        if consider_unused_cash:
            total_cash += self.get_available_cash(context, consider_open_orders)
        long_exposure_pct   = long_exposure  / total_cash if total_cash > 0 else 0
        short_exposure_pct  = short_exposure / total_cash if total_cash > 0 else 0
        return (long_exposure_pct, short_exposure_pct)
    
    def get_available_cash(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure            
        leverage_cash = context.portfolio.portfolio_value * (self.target_leverage - 1.0)
        return curr_cash + leverage_cash
          
    def get_available_cash_long_short(self, context, consider_open_orders = True):
        total_available_cash  = self.get_available_cash(context, consider_open_orders)
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure
        current_exposure       = long_exposure + short_exposure + total_available_cash
        target_long_exposure  = current_exposure * self.target_long_exposure_perc
        target_short_exposure = current_exposure * self.target_short_exposure_perc        
        long_available_cash   = target_long_exposure  - long_exposure 
        short_available_cash  = target_short_exposure - short_exposure
        return (long_available_cash, short_available_cash)
    
    def update(self, context, data):
    
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
        for stock, orders in  get_open_orders().iteritems():
            if stock not in data:
                continue
            price = data[stock].price
            amount = 0 if stock not in context.portfolio.positions else context.portfolio.positions[stock].amount
            for oo in orders:
                order_amount = oo.amount - oo.filled
                if order_amount < 0 and amount <= 0:
                    self.open_order_short_exposure += (price * -order_amount)
                elif order_amount > 0 and amount >= 0:
                    self.open_order_long_exposure  += (price * order_amount)
        
        self.short_exposure = 0.0
        self.long_exposure  = 0.0
        for stock in context.portfolio.positions:
            amount = context.portfolio.positions[stock].amount
            last_sale_price = context.portfolio.positions[stock].last_sale_price
            if amount < 0:
                self.short_exposure += (last_sale_price * -amount)
            elif amount > 0:
                self.long_exposure  += (last_sale_price * amount)
        

def initialize(context):
        
    context.exposure = ExposureMngr(target_leverage = 1.0,
                                    target_long_exposure_perc = 1.0,
                                    target_short_exposure_perc = 0.0)

    schedule_function(rebalance, 
                      date_rules.every_day(),
                      time_rules.market_close(minutes = 30))  

    context.m = sid(24744)
    context.x = sid(40516)
    context.f = sid(22887) 

def rebalance(context,data):

    P = history(100, '1d', 'price').dropna(axis = 1)
    x = (P.tail(10).median() / P.median() - 1).dropna()

    if context.m not in x.index:
        return
    if context.f not in x.index:
        return
    if context.x not in x.index:
        return   
    
    if context.m not in data:
        return
    if context.f not in data:
        return
    if context.x not in data:
        return  
    
    if 'price' not in data[context.m]:
        return
    if 'price' not in data[context.f]:
        return
    if 'price' not in data[context.x]:
        return 
    
    signal = (x > 0)

    if context.m not in signal.index:
        return
    if context.f not in signal.index:
        return
    if context.x not in signal.index:
        return
    
    if get_open_orders():
        return
    
    go = signal[context.m] and signal[context.x]
    
    bet = context.portfolio.portfolio_value / 5
    
    context.exposure.update(context, data)
    
    long_cash, short_cash = \
    context.exposure.get_available_cash_long_short(context)
        
    if go:
        if long_cash > bet:
            order_value(context.m, bet * 0.7)
            order_value(context.x, bet * 0.3)
        order_target(context.f, 0.0)              
    else:
        if long_cash > bet:
            order_value(context.f, bet)
        order_target(context.m, 0.0)                                          
        order_target(context.x, 0.0)
    
    #record(go = go)
    
    pvr(context, data)


def handle_data(context,data):    

    pass


def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    def _pvr_():  
            log.info('PvR {} %/day     {}'.format(  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi \
          or log_method == 'daily' and c.pvr['date_prv'] != date \
          or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    if c.pvr['days'] % 130 == 0 and _minute() == '100': _pvr_()  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            _pvr_()  
            c.pvr_summary_done = 1

    

    
    
    
    
    
There was a runtime error.

Thanks @Tim! I'll see if this is the one that I've implemented. And one last dumb question, but how long is the warm-up period? I figure the code will go back in time and start using previous data to calculate market averages and cross over. Or does it use the starting data for that as the point at which the code was initiated?

Thanks again!
Zach

@Zach, the algo takes about a week to "warm up" because when switching between the investment into any of the three different assets it deals with it only invests 1/5 of the allocated cash into the asses that is to be made part of the current portfolio at each time step in order to prevent over-leveraging and to smoothen its overall performance, so that in the beginning the leverage only gradually builds up to unity from zero.

Hi @Zhang,

My reasoning / numerical experimentation went approximately like this:

-- XIV is by definition inversely related to market volatility;
-- Market rallies are linked to periods of low volatility and turmoils to increased volatility;
-- XIV should therefore be fairly well correlated with the performance of the market itself;
-- I use SPY as a market proxy and later came accross the article that indeed demonstrated the existence of high correlation between XIV and SPY;
-- My entry / exit signal is thus based on both SPY and XIV and it looks for a simple crossover of two MAs -- this latter finding is purely empirical.

Hope this makes sense.

Sorry, should have addressed you as Meng, I suppose, my apologies. I assumed Zhang was the family name and I believe it is Chinese tradition to refer to persons by their family name. Please forgive me if I got it wrong.

@Zhang, thank you for sharing the graph of live trading with the XIV. I assume you programmed the same signal that my algo is using in in the environment of a live-trading platform? Was this easily done?

Hi Zhang,

Good luck with your trades, thanks for the trust and please let me know if anything goes wrong -- or if you have great success, of course ! :-)

Hey Tim, thanks for all your help on this! is your algo from 3/29 ready for live trade?

And what do you mean by warm up period.... does the algo already account for this or does it mean i should just put in a small amount, then dump in the rest a week later?

Any ideas on how to get Robinhood instant? Its apparently got a wait queue.

edit:After review of the algo i see it 'warms up' on its own.

Hi Corey,

I have to point out that I am actually not able to say whether the algo is ready for Robinhood trading or not, because I have never traded it live myself. It is my understanding that only US citizens can currently open a Robinhood account and I am unfortunately not one of them. :-)

That being said, it would certainly be a good idea to incorporate into the algo all the special elements linked to Robinhood trading, as they are presented in a dedicated framework sample on the Quantopian help pages:

https://www.quantopian.com/help#sample-robinhood

And yes, you are absolutely right, you can invest the entire sum that you intend to from the very start, the "warm up" is automatic.

I made some coding changes that have no immediate impact to the Algo that Tim last posted as a learning tool for myself. I basically changed some literals to variables, so that I could do iterative backtesting a little easier. The sections modified are initialize and rebalance. You can get interesting results by changing the values for betFuturesPercent and betEquityPercent to say .5 the returns go > 340% over the period.

My purpose as I mentioned is to learn. I was hoping someone could point me to some documentation that would help me understand the following lines;

    P = history(100, '1d', 'price').dropna(axis = 1)  
    x = (P.tail(10).median() / P.median() - 1).dropna()

    go = signal[context.m] and signal[context.x]

I have never worked in python before, though I have worked in BASIC (of a number of flavors, including .NET) emacs languages including awk and actionScript as well as languages used when dinosaurs roamed the earth.

Clone Algorithm
105
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
class ExposureMngr(object):
    
    def __init__(self, target_leverage = 1.0, target_long_exposure_perc = 0.50, target_short_exposure_perc = 0.50):   
        self.target_leverage            = target_leverage
        self.target_long_exposure_perc  = target_long_exposure_perc              
        self.target_short_exposure_perc = target_short_exposure_perc           
        self.short_exposure             = 0.0
        self.long_exposure              = 0.0
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
      
    def get_current_leverage(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure
        curr_leverage = (context.portfolio.portfolio_value - curr_cash) / context.portfolio.portfolio_value
        return curr_leverage

    def get_exposure(self, context, consider_open_orders = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)
        return long_exposure + short_exposure
    
    def get_long_short_exposure(self, context, consider_open_orders = True):
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure     
        return (long_exposure, short_exposure)
    
    def get_long_short_exposure_pct(self, context, consider_open_orders = True, consider_unused_cash = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)        
        total_cash = long_exposure + short_exposure
        if consider_unused_cash:
            total_cash += self.get_available_cash(context, consider_open_orders)
        long_exposure_pct   = long_exposure  / total_cash if total_cash > 0 else 0
        short_exposure_pct  = short_exposure / total_cash if total_cash > 0 else 0
        return (long_exposure_pct, short_exposure_pct)
    
    def get_available_cash(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure            
        leverage_cash = context.portfolio.portfolio_value * (self.target_leverage - 1.0)
        return curr_cash + leverage_cash
          
    def get_available_cash_long_short(self, context, consider_open_orders = True):
        total_available_cash  = self.get_available_cash(context, consider_open_orders)
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure
        current_exposure       = long_exposure + short_exposure + total_available_cash
        target_long_exposure  = current_exposure * self.target_long_exposure_perc
        target_short_exposure = current_exposure * self.target_short_exposure_perc        
        long_available_cash   = target_long_exposure  - long_exposure 
        short_available_cash  = target_short_exposure - short_exposure
        return (long_available_cash, short_available_cash)
    
    def update(self, context, data):
    
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
        for stock, orders in  get_open_orders().iteritems():
            if stock not in data:
                continue
            price = data[stock].price
            amount = 0 if stock not in context.portfolio.positions else context.portfolio.positions[stock].amount
            for oo in orders:
                order_amount = oo.amount - oo.filled
                if order_amount < 0 and amount <= 0:
                    self.open_order_short_exposure += (price * -order_amount)
                elif order_amount > 0 and amount >= 0:
                    self.open_order_long_exposure  += (price * order_amount)
        
        self.short_exposure = 0.0
        self.long_exposure  = 0.0
        for stock in context.portfolio.positions:
            amount = context.portfolio.positions[stock].amount
            last_sale_price = context.portfolio.positions[stock].last_sale_price
            if amount < 0:
                self.short_exposure += (last_sale_price * -amount)
            elif amount > 0:
                self.long_exposure  += (last_sale_price * amount)
        

def initialize(context):
        
    context.exposure = ExposureMngr(target_leverage = 1.0,
                                    target_long_exposure_perc = 1.0,
                                    target_short_exposure_perc = 0.0)

    schedule_function(rebalance, 
                      date_rules.every_day(),
                      time_rules.market_close(minutes = 30))  


    equitySymbol = symbol('RSP')
    futuresSymbol = symbol('XIV')
    treasurySymbol = symbol('EDV')
    
    context.m = equitySymbol
    context.x = futuresSymbol
    context.f = treasurySymbol
    context.betTotalPercent = 0.20000
    context.betEquityPercent = 0.7
    context.betFuturesPercent = 0.3
    context.buyTrx = 0
    context.sellTrx = 0

def rebalance(context,data):

    P = history(100, '1d', 'price').dropna(axis = 1)
    x = (P.tail(10).median() / P.median() - 1).dropna()

    if context.m not in x.index:
        return
    if context.f not in x.index:
        return
    if context.x not in x.index:
        return   
    
    if context.m not in data:
        return
    if context.f not in data:
        return
    if context.x not in data:
        return  
    
    if 'price' not in data[context.m]:
        return
    if 'price' not in data[context.f]:
        return
    if 'price' not in data[context.x]:
        return 
    
    signal = (x > 0)

    if context.m not in signal.index:
        return
    if context.f not in signal.index:
        return
    if context.x not in signal.index:
        return
    
    if get_open_orders():
        return
    
    go = signal[context.m] and signal[context.x]
    
    bet = context.portfolio.portfolio_value * context.betTotalPercent
    
    context.exposure.update(context, data)
    
    long_cash, short_cash = \
    context.exposure.get_available_cash_long_short(context)

    if go:
        if long_cash > bet:
            order_value(context.x, bet * context.betFuturesPercent)
            context.buyTrx += 1
            order_value(context.m, bet * context.betEquityPercent)
            context.buyTrx += 1
        order_target(context.f, 0.0)              
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
    else:
        if long_cash > bet:
            order_value(context.f, bet)
            context.buyTrx += 1
        order_target(context.m, 0.0)                                          
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.x, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
       
    #record(go = go)
    
    pvr(context, data)


def handle_data(context,data):    

    pass


def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    def _pvr_():  
            log.info('PvR {} %/day     {}'.format(  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi \
          or log_method == 'daily' and c.pvr['date_prv'] != date \
          or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    if c.pvr['days'] % 130 == 0 and _minute() == '100': _pvr_()  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        log.info(' Buys {} Sells {}'.format('%.0f' % context.buyTrx, '%.0f' % context.sellTrx))
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            _pvr_()  
            c.pvr_summary_done = 1

    

    
    
    
    
    
There was a runtime error.

I decided to take a stab at documenting the code I wasn't clear on.

If there is a kind soul that will look over my comments and provide knowledgeable insight I would appreciate it;

#   Get last hundred days worth of prices for universe, removing rows where the price is NaN.  
    P = history(100, '1d', 'price').dropna(axis = 1)  
    x = (P.tail(10).median() / P.median() - 1).dropna()  
#   Get 1 value for each member of the universe.  
#   The value will be the median of the last 10 days divided by the median of the entire set.

#   Check if the datatable x contains the members of our universe.  
    if context.m not in x.index:  
        return  
    if context.f not in x.index:  
        return  
    if context.x not in x.index:  
        return  
#   Check if the datatable data contains the members of our universe.  
    if context.m not in data:  
        return  
    if context.f not in data:  
        return  
    if context.x not in data:  
        return  
#   Check if the datatable data contains prices for the members of our universe.  
    if 'price' not in data[context.m]:  
        return  
    if 'price' not in data[context.f]:  
        return  
    if 'price' not in data[context.x]:  
        return  
#   create a datatable signal with boolean values that indicate whether the last 10 days median > last 100 days median.  
    signal = (x > 0)

#   Check if the datatable signal contains prices for the members of our universe.  
    if context.m not in signal.index:  
        return  
    if context.f not in signal.index:  
        return  
    if context.x not in signal.index:  
        return

Well if you've got the stomach for it, here is a version that adds another position to the mix, and fiddles with how much to bet on the various positions.

Pointers on max draw down reduction without hammering the return might be good.

Clone Algorithm
105
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
class ExposureMngr(object):
    
    def __init__(self, target_leverage = 1.0, target_long_exposure_perc = 0.50, target_short_exposure_perc = 0.50):   
        self.target_leverage            = target_leverage
        self.target_long_exposure_perc  = target_long_exposure_perc              
        self.target_short_exposure_perc = target_short_exposure_perc           
        self.short_exposure             = 0.0
        self.long_exposure              = 0.0
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
      
    def get_current_leverage(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure
        curr_leverage = (context.portfolio.portfolio_value - curr_cash) / context.portfolio.portfolio_value
        return curr_leverage

    def get_exposure(self, context, consider_open_orders = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)
        return long_exposure + short_exposure
    
    def get_long_short_exposure(self, context, consider_open_orders = True):
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure     
        return (long_exposure, short_exposure)
    
    def get_long_short_exposure_pct(self, context, consider_open_orders = True, consider_unused_cash = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)        
        total_cash = long_exposure + short_exposure
        if consider_unused_cash:
            total_cash += self.get_available_cash(context, consider_open_orders)
        long_exposure_pct   = long_exposure  / total_cash if total_cash > 0 else 0
        short_exposure_pct  = short_exposure / total_cash if total_cash > 0 else 0
        return (long_exposure_pct, short_exposure_pct)
    
    def get_available_cash(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure            
        leverage_cash = context.portfolio.portfolio_value * (self.target_leverage - 1.0)
        return curr_cash + leverage_cash
          
    def get_available_cash_long_short(self, context, consider_open_orders = True):
        total_available_cash  = self.get_available_cash(context, consider_open_orders)
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure
        current_exposure       = long_exposure + short_exposure + total_available_cash
        target_long_exposure  = current_exposure * self.target_long_exposure_perc
        target_short_exposure = current_exposure * self.target_short_exposure_perc        
        long_available_cash   = target_long_exposure  - long_exposure 
        short_available_cash  = target_short_exposure - short_exposure
        return (long_available_cash, short_available_cash)
    
    def update(self, context, data):
    
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
        for stock, orders in  get_open_orders().iteritems():
            if stock not in data:
                continue
            price = data[stock].price
            amount = 0 if stock not in context.portfolio.positions else context.portfolio.positions[stock].amount
            for oo in orders:
                order_amount = oo.amount - oo.filled
                if order_amount < 0 and amount <= 0:
                    self.open_order_short_exposure += (price * -order_amount)
                elif order_amount > 0 and amount >= 0:
                    self.open_order_long_exposure  += (price * order_amount)
        
        self.short_exposure = 0.0
        self.long_exposure  = 0.0
        for stock in context.portfolio.positions:
            amount = context.portfolio.positions[stock].amount
            last_sale_price = context.portfolio.positions[stock].last_sale_price
            if amount < 0:
                self.short_exposure += (last_sale_price * -amount)
            elif amount > 0:
                self.long_exposure  += (last_sale_price * amount)
        

def initialize(context):
        
    context.exposure = ExposureMngr(target_leverage = .99,
                                    target_long_exposure_perc = .99,
                                    target_short_exposure_perc = 0.0)

    schedule_function(rebalance, 
                      date_rules.every_day(),
                      time_rules.market_close(hours = 2))  


    equitySymbol = symbol('RSP')
#    equitySymbol = symbol('RSP')
    equityFuturesSymbol = symbol('XIV')
#    futuresSymbol = symbol('XIV')
    treasurySymbol = symbol('EDV')
    treasuryFuturesSymbol = symbol('TMF')
#    treasurySymbol = symbol('EDV')
    
    context.e = equitySymbol
    context.ef = equityFuturesSymbol
    context.t = treasurySymbol
    context.tf = treasuryFuturesSymbol
    context.betTotalPercent = 0.30000
    context.betLowPercent = 0.8
    context.betHighPercent = 0.2
    context.historyDays = 100
    context.buyTrx = 0
    context.sellTrx = 0

def rebalance(context,data):
#   Get last hundred days worth of prices for universe, removing rows where the price is NaN.
    P = history(context.historyDays, '1d', 'price').dropna(axis = 1) 
    x = (P.tail(context.historyDays/10).median() / P.median() - 1).dropna() 
#   Get 1 value for each member of the universe. 
#   The value will be the median of the last 10 days divided by the median of the entire set.

#   Check if the datatable x contains the members of our universe.
    if context.e not in x.index:
        return
    if context.t not in x.index:
        return
    if context.tf not in x.index:
        return
    if context.ef not in x.index:
        return   
    
#   Check if the datatable data contains the members of our universe.
    if context.e not in data:
        return
    if context.t not in data:
        return
    if context.tf not in data:
        return
    if context.ef not in data:
        return  
    
#   Check if the datatable data contains prices for the members of our universe.
    if 'price' not in data[context.e]:
        return
    if 'price' not in data[context.t]:
        return
    if 'price' not in data[context.tf]:
        return
    if 'price' not in data[context.ef]:
        return 
#   create a datatable signal with boolean values that indicate whether the last 10 days median > last 100 days median.    
    signal = (x > 0)

#   Check if the datatable signal contains prices for the members of our universe.
    if context.e not in signal.index:
        return
    if context.t not in signal.index:
        return
    if context.tf not in signal.index:
        return
    if context.ef not in signal.index:
        return
    
    if get_open_orders():
        return

#   Set boolean to true if both equitySymbol and futuresSymbol 10 day median prices are > 100 day median prices
    go = (signal[context.e] and signal[context.ef]) and ((x[context.e] + x[context.ef]) * 3 > (x[context.t] + x[context.tf]))
    
    bet = context.portfolio.portfolio_value * context.betTotalPercent
    
    context.exposure.update(context, data)
    
    long_cash, short_cash = \
    context.exposure.get_available_cash_long_short(context)

    if go:
        if long_cash > bet:
            if x[context.ef] > x[context.e]:
                betFuturesPercent = context.betLowPercent
                betEquityPercent = context.betHighPercent
            else:
                betFuturesPercent = context.betHighPercent
                betEquityPercent = context.betLowPercent
            order_value(context.ef, bet * betFuturesPercent)
            context.buyTrx += 1
            order_value(context.e, bet * betEquityPercent)
            context.buyTrx += 1
        order_target(context.t, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.tf, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
    else:
        if long_cash > bet:
            if x[context.t] > x[context.tf]:
                betTreasuryPercent = context.betLowPercent
                betTreasuryFuturesPercent = context.betHighPercent
            else:
                betTreasuryPercent = context.betHighPercent
                betTreasuryFuturesPercent = context.betLowPercent
            order_value(context.t,  bet * betTreasuryPercent)
            context.buyTrx += 1
            order_value(context.tf,  bet * betTreasuryFuturesPercent)
            context.buyTrx += 1
        order_target(context.e, 0.0)                                          
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.ef, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1

       
    #record(go = go)
    
    pvr(context, data)


def handle_data(context,data):    

    pass


def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data[p].price))  
        if shrs > 0:  
            longs  += int(shrs * data[p].price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    def _pvr_():  
            log.info('PvR {} %/day     {}'.format(  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi \
          or log_method == 'daily' and c.pvr['date_prv'] != date \
          or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    if c.pvr['days'] % 130 == 0 and _minute() == '100': _pvr_()  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        log.info(' Buys {} Sells {}'.format('%.0f' % context.buyTrx, '%.0f' % context.sellTrx))
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            _pvr_()  
            c.pvr_summary_done = 1

    

    
    
    
    
    
We have migrated this algorithm to work with a new version of the Quantopian API. The code is different than the original version, but the investment rationale of the algorithm has not changed. We've put everything you need to know here on one page.
There was a runtime error.

Hi Paul,

Thanks for adding the comments, as far as I am concerned they are spot on -- or at least what you describe is what I intended to achieve with the code. :-)

The code itself is probably awful from an experienced Python programmer's point of view. My apologies, my "primary" language is Fortran.

Hi again Paul,

Your latest version of the algo really achieves some exciting returns. May I trouble you for an explanation of its logic?

Paul,

You have quite obviously sucessfuly figured it out by yourself by now, but just for the sake of completeness, the explantion of the lines in the algo that you quote:

P = history(100, '1d', 'price').dropna(axis = 1)
x = (P.tail(10).median() / P.median() - 1).dropna()

The history command fetches the last one hundred days of daily close price values for all the equities used in the algo and stores them as a Pandas DataFrame into the variable P. Then, a simple way of estimating their momentum follows by taking the ratio of the median value of the last ten days of P and the full 100 days.

go = signal[context.m] and signal[context.x]

This is just a simple assignment to a logical variable that combines the values of the logical "array" signal (actually , a Pandas Series object) for two different equities.

I hope this helps.

Thanks for the compliments Tim. I dabbled in Fortran a long time ago.

The first thing that I did was to change some of the literals to variables so that I could easily change the values and run different values. Other things I did was;

Added a second symbol, TMF to the treasury side, so that I had two symbols on either side to buy/sell. I then added some logic to decide which of the two symbols on either side might be best at the moment (lines 184 and 202). I also changed the split of how much to bet from 70%/30% to 80%/20%, I also changed how much to bet on a single day from 20% to 30%.

One thing I read which I need to be careful of is fitting the algo to match the backtested data. It seems as though if you tweak an algo too much it performs well on backtested data but sucks when put into production. I believe that max drawdown is supposed to help you avoid that by showing you cases where your algo bites it.

I am not overly risk averse, and my next step is to try live trading, if I can figure out how to put the latest iteration into production.

Thanks for the explanation of gist of your algo, Paul.

Overfitting can indeed be dangerous, but as long as an algorithm performs reasonably well once the original basic idea behind it has been implemented, some amount of optimization should not be a problem, I suppose.

All the best with the live trade!

Paul, have you or anyone else been trading this algorithm live? Also, has anyone tried this on Quantopian 2 yet? I am no expert and have been really just reading the code to understand what it does. I tried some tweaks, but none of them have ran without any errors. Thx all and nice work.

I plan to try it as soon as my robinhood account gets 'instant' but I will need someone help setting it up.

@corey windsor what kind of help do you need? You can check out the Live Trading Section in the FAQ on how to setup your account and integrate it with Quantopian. There's also the Algorithm Framework for Robinhood for implementing the proper code for live trading.

Let me know if that helps at all.

I have taken the step of paper trading $10,000 on an "enhanced" version of my (Tim's) algorithm. Didn't start until about 11:00 AM and it booked $19.04 profit in paper money.

I started moving money around so that I can fund my newly created Robinhood account. I will probably fire it up live on Monday feeding it $10,000 assuming nothing goes haywire between now and then.

Thanks for the feedback Paul, I hope you can find time to keep us updated on how it goes and whether you decide to tweak it. Have you tested it on Q2? Currious, do you know of any algos that trade based on trading pattens?

Paul

Did you have to update any code before going live with your Robinhood account for the latest algorithm that you posted? I'm thinking about putting a couple thousand in live to see how it does.

Steven, this is still a work in progress. I did add a line of code that I hope will keep me from over spending when live;

    long_cash = min(long_cash, context.account.settled_cash, context.account.available_funds)

I am still paper trading the algo, and so far since 11:00 AM on Wednesday the version I am running is up about $83, based on $10,000 start.

Having said all of that I am waffling between IB & Robinhood. As I mentioned in a separate thread I am concerned about Robinhood from a "ready for primetime" perspective, and IB because of costs. I have created accounts on both, but as of yet they are unfunded.

Where did you add that line of code?

I also ran a back-test in the new version of Q2. Results are the same, back-testing is insanely quick but I'll work on upgrading the below things.

Line 122: The `history` method is deprecated. Use `data.history` instead.  
Line 138: Checking whether an asset is in data is deprecated.  
Line 140: Checking whether an asset is in data is deprecated.  
Line 142: Checking whether an asset is in data is deprecated.  
Line 144: Checking whether an asset is in data is deprecated.  
Line 148: `data[sid(N)]` is deprecated. Use `data.current`.  
Line 150: `data[sid(N)]` is deprecated. Use `data.current`.  
Line 152: `data[sid(N)]` is deprecated. Use `data.current`.  
Line 154: `data[sid(N)]` is deprecated. Use `data.current`.  
Line 301: `data[sid(N)]` is deprecated. Use `data.current`.  

Actually I wasn't looking at the right backtest. It's actually a difference of 100% or so.

Here is the link to a thread that has the backtest on Q2.
enter link description here

So I went ahead and put 1k in Robinhood and am running Pauls latest version to see how things go. If there are any new changes you want to share please let me know.

I'll update in a couple days to let everyone know how it's running.

Hi Steven, thanks for your input. Did you change the deprecated coding? I have been "Live Testing" the algo through Quantopian for a few days now. It is currently up over 8%. I would love to know if you or anyone else is seeing any discrepancies with the Real Time/Real Money trading vs. Quantopian's live trading, and if the algo is trading as expected in regards to the coding.

I suspect the markets may shift a little soon and we should see some pullback off these high levels from the major indexes. I am anxious to see how the algo will treat any change in market conditions.

Thanks

Hi Sid

I haven't changed it yet. I plan to work on that some tonight. As of right now i'm up 4.52%. It has only bought XIV so far. I'm using the last version that Paul put up. Are you using that same version? Did you make any changes to it?

I'm also using Robinhood, did you change anything in the code that relates to that before going live? I didn't see anything in it having to do with 3 day settlement which i'm not to worried about since I'm hoping to get Robinhood instant soon but just wondering if you messed with any of that.

Steve, I havn't had a chance to look at the deprecated code or new documentation. I will hopefully have a chance soon. I will keep,you posted.

All - I have an updated the algorithm for Q2, which is based on Paul Stearns' 4/6/2016 post. I have been trading live with it since that time (minus a few Robinhood crashes). So far up 5.2% on 1k initial investment. Waiting for a switch from equity to treasury before I start investing more. Very thankful for this group. Learning something new every day.

Clone Algorithm
96
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
#https://www.quantopian.com/posts/for-robinhood-trading 
#Tim Vidmar  3/29/2016 original
#Here is the latest incarnation of the algo, with garyha's PvR routine, and Luca's cash management. Thanks for your brilliant code, guys!

#It no longer obeys the T+3 rule, since it has been apparently abandoned by Robinhood in the meantime, and it stays in positive cash all the time.

#It needs a warm-up period, though, to come to full leverage (1.0).

#Paul Stearns  4/6/2016
#Well if you've got the stomach for it, here is a version that adds another position to the mix, and fiddles with how much to bet on the various positions.

#Pointers on max draw down reduction without hammering the return might be good.

class ExposureMngr(object):
    
    def __init__(self, target_leverage = 1.0, target_long_exposure_perc = 0.50, target_short_exposure_perc = 0.50):   
        self.target_leverage            = target_leverage
        self.target_long_exposure_perc  = target_long_exposure_perc              
        self.target_short_exposure_perc = target_short_exposure_perc           
        self.short_exposure             = 0.0
        self.long_exposure              = 0.0
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
      
    def get_current_leverage(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure
        curr_leverage = (context.portfolio.portfolio_value - curr_cash) / context.portfolio.portfolio_value
        return curr_leverage

    def get_exposure(self, context, consider_open_orders = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)
        return long_exposure + short_exposure
    
    def get_long_short_exposure(self, context, consider_open_orders = True):
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure     
        return (long_exposure, short_exposure)
    
    def get_long_short_exposure_pct(self, context, consider_open_orders = True, consider_unused_cash = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)        
        total_cash = long_exposure + short_exposure
        if consider_unused_cash:
            total_cash += self.get_available_cash(context, consider_open_orders)
        long_exposure_pct   = long_exposure  / total_cash if total_cash > 0 else 0
        short_exposure_pct  = short_exposure / total_cash if total_cash > 0 else 0
        return (long_exposure_pct, short_exposure_pct)
    
    def get_available_cash(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure            
        leverage_cash = context.portfolio.portfolio_value * (self.target_leverage - 1.0)
        return curr_cash + leverage_cash
          
    def get_available_cash_long_short(self, context, consider_open_orders = True):
        total_available_cash  = self.get_available_cash(context, consider_open_orders)
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure
        current_exposure       = long_exposure + short_exposure + total_available_cash
        target_long_exposure  = current_exposure * self.target_long_exposure_perc
        target_short_exposure = current_exposure * self.target_short_exposure_perc        
        long_available_cash   = target_long_exposure  - long_exposure 
        short_available_cash  = target_short_exposure - short_exposure
        return (long_available_cash, short_available_cash)
    
    def update(self, context, data):
    
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
        for stock, orders in  get_open_orders().iteritems():
            if stock not in data:
                continue
            price = data[stock].price
            amount = 0 if stock not in context.portfolio.positions else context.portfolio.positions[stock].amount
            for oo in orders:
                order_amount = oo.amount - oo.filled
                if order_amount < 0 and amount <= 0:
                    self.open_order_short_exposure += (price * -order_amount)
                elif order_amount > 0 and amount >= 0:
                    self.open_order_long_exposure  += (price * order_amount)
        
        self.short_exposure = 0.0
        self.long_exposure  = 0.0
        for stock in context.portfolio.positions:
            amount = context.portfolio.positions[stock].amount
            last_sale_price = context.portfolio.positions[stock].last_sale_price
            if amount < 0:
                self.short_exposure += (last_sale_price * -amount)
            elif amount > 0:
                self.long_exposure  += (last_sale_price * amount)
        

def initialize(context):
        
    context.exposure = ExposureMngr(target_leverage = .99,
                                    target_long_exposure_perc = .99,
                                    target_short_exposure_perc = 0.0)

    schedule_function(rebalance, 
                      date_rules.every_day(),
                      time_rules.market_close(hours = 2))  



    equitySymbol = sid(24744)
#    equitySymbol = symbol('RSP')
    equityFuturesSymbol = sid(40516)
#    futuresSymbol = symbol('XIV')
    treasurySymbol = sid(22887)
#    treasurySymbol = symbol('EDV')
    treasuryFuturesSymbol = sid(38294)
#    treasuryFuturesSymbol = symbol('TMF')

    context.assets = [sid(24744), sid(40516), sid(22887), sid(38294)]
    
    context.e = equitySymbol
    context.ef = equityFuturesSymbol
    context.t = treasurySymbol
    context.tf = treasuryFuturesSymbol
    context.betTotalPercent = 0.30000
    context.betLowPercent = 0.8
    context.betHighPercent = 0.2
    context.historyDays = 100
    context.buyTrx = 0
    context.sellTrx = 0

def rebalance(context,data):
#   Get last hundred days worth of prices for universe, removing rows where the price is NaN.
    P = data.history(context.assets,'price',context.historyDays, '1d').dropna(axis = 1) 
    x = (P.tail(context.historyDays/10).median() / P.median() - 1).dropna() 
#   Get 1 value for each member of the universe. 
#   The value will be the median of the last 10 days divided by the median of the entire set.

#   Check if the datatable x contains the members of our universe.
    if context.e not in x.index:
        return
    if context.t not in x.index:
        return
    if context.tf not in x.index:
        return
    if context.ef not in x.index:
        return   
    
#   Check if the asset has a known last price and is currently listed on a supported exchange. 
    if not data.can_trade(context.e):
       return
    if not data.can_trade(context.t):
       return
    if not data.can_trade(context.tf):
       return
    if not data.can_trade(context.ef):
       return      

#   create a datatable signal with boolean values that indicate whether the last 10 days median > last 100 days median.    
    signal = (x > 0)

#   Check if the datatable signal contains prices for the members of our universe.
    if context.e not in signal.index:
        return
    if context.t not in signal.index:
        return
    if context.tf not in signal.index:
        return
    if context.ef not in signal.index:
        return
    
    if get_open_orders():
        return

#   Set boolean to true if both equitySymbol and futuresSymbol 10 day median prices are > 100 day median prices
    go = (signal[context.e] and signal[context.ef]) and ((x[context.e] + x[context.ef]) * 3 > (x[context.t] + x[context.tf]))
    
    bet = context.portfolio.portfolio_value * context.betTotalPercent
    
    context.exposure.update(context, data)
    
    long_cash, short_cash = \
    context.exposure.get_available_cash_long_short(context)

    if go:
        if long_cash > bet:
            if x[context.ef] > x[context.e]:
                betFuturesPercent = context.betLowPercent
                betEquityPercent = context.betHighPercent
            else:
                betFuturesPercent = context.betHighPercent
                betEquityPercent = context.betLowPercent
            order_value(context.ef, bet * betFuturesPercent)
            context.buyTrx += 1
            order_value(context.e, bet * betEquityPercent)
            context.buyTrx += 1
        order_target(context.t, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.tf, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
    else:
        if long_cash > bet:
            if x[context.t] > x[context.tf]:
                betTreasuryPercent = context.betLowPercent
                betTreasuryFuturesPercent = context.betHighPercent
            else:
                betTreasuryPercent = context.betHighPercent
                betTreasuryFuturesPercent = context.betLowPercent
            order_value(context.t,  bet * betTreasuryPercent)
            context.buyTrx += 1
            order_value(context.tf,  bet * betTreasuryFuturesPercent)
            context.buyTrx += 1
        order_target(context.e, 0.0)                                          
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.ef, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1

       
    #record(go = go)
    
    pvr(context, data)


def handle_data(context,data):    

    pass


def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount
        shrs_price = data.current(p, 'price')     
        if shrs < 0:  
            shorts += int(abs(shrs * shrs_price))  
        if shrs > 0:  
            longs  += int(shrs * shrs_price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    def _pvr_():  
            log.info('PvR {} %/day     {}'.format(  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi \
          or log_method == 'daily' and c.pvr['date_prv'] != date \
          or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    if c.pvr['days'] % 130 == 0 and _minute() == '100': _pvr_()  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        log.info(' Buys {} Sells {}'.format('%.0f' % context.buyTrx, '%.0f' % context.sellTrx))
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            _pvr_()  
            c.pvr_summary_done = 1

    

    
    
    
    
There was a runtime error.

Thanks Caleb

Ignore previous post, backtests match up perfect. I had a different starting balance. Great work!!!

Clone Algorithm
103
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
#https://www.quantopian.com/posts/for-robinhood-trading 
#Tim Vidmar  3/29/2016 original
#Here is the latest incarnation of the algo, with garyha's PvR routine, and Luca's cash management. Thanks for your brilliant code, guys!

#It no longer obeys the T+3 rule, since it has been apparently abandoned by Robinhood in the meantime, and it stays in positive cash all the time.

#It needs a warm-up period, though, to come to full leverage (1.0).

#Paul Stearns  4/6/2016
#Well if you've got the stomach for it, here is a version that adds another position to the mix, and fiddles with how much to bet on the various positions.

#Pointers on max draw down reduction without hammering the return might be good.

class ExposureMngr(object):
    
    def __init__(self, target_leverage = 1.0, target_long_exposure_perc = 0.50, target_short_exposure_perc = 0.50):   
        self.target_leverage            = target_leverage
        self.target_long_exposure_perc  = target_long_exposure_perc              
        self.target_short_exposure_perc = target_short_exposure_perc           
        self.short_exposure             = 0.0
        self.long_exposure              = 0.0
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
      
    def get_current_leverage(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure
        curr_leverage = (context.portfolio.portfolio_value - curr_cash) / context.portfolio.portfolio_value
        return curr_leverage

    def get_exposure(self, context, consider_open_orders = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)
        return long_exposure + short_exposure
    
    def get_long_short_exposure(self, context, consider_open_orders = True):
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure     
        return (long_exposure, short_exposure)
    
    def get_long_short_exposure_pct(self, context, consider_open_orders = True, consider_unused_cash = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)        
        total_cash = long_exposure + short_exposure
        if consider_unused_cash:
            total_cash += self.get_available_cash(context, consider_open_orders)
        long_exposure_pct   = long_exposure  / total_cash if total_cash > 0 else 0
        short_exposure_pct  = short_exposure / total_cash if total_cash > 0 else 0
        return (long_exposure_pct, short_exposure_pct)
    
    def get_available_cash(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure            
        leverage_cash = context.portfolio.portfolio_value * (self.target_leverage - 1.0)
        return curr_cash + leverage_cash
          
    def get_available_cash_long_short(self, context, consider_open_orders = True):
        total_available_cash  = self.get_available_cash(context, consider_open_orders)
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure
        current_exposure       = long_exposure + short_exposure + total_available_cash
        target_long_exposure  = current_exposure * self.target_long_exposure_perc
        target_short_exposure = current_exposure * self.target_short_exposure_perc        
        long_available_cash   = target_long_exposure  - long_exposure 
        short_available_cash  = target_short_exposure - short_exposure
        return (long_available_cash, short_available_cash)
    
    def update(self, context, data):
    
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
        for stock, orders in  get_open_orders().iteritems():
            if stock not in data:
                continue
            price = data[stock].price
            amount = 0 if stock not in context.portfolio.positions else context.portfolio.positions[stock].amount
            for oo in orders:
                order_amount = oo.amount - oo.filled
                if order_amount < 0 and amount <= 0:
                    self.open_order_short_exposure += (price * -order_amount)
                elif order_amount > 0 and amount >= 0:
                    self.open_order_long_exposure  += (price * order_amount)
        
        self.short_exposure = 0.0
        self.long_exposure  = 0.0
        for stock in context.portfolio.positions:
            amount = context.portfolio.positions[stock].amount
            last_sale_price = context.portfolio.positions[stock].last_sale_price
            if amount < 0:
                self.short_exposure += (last_sale_price * -amount)
            elif amount > 0:
                self.long_exposure  += (last_sale_price * amount)
        

def initialize(context):
        
    context.exposure = ExposureMngr(target_leverage = .99,
                                    target_long_exposure_perc = .99,
                                    target_short_exposure_perc = 0.0)

    schedule_function(rebalance, 
                      date_rules.every_day(),
                      time_rules.market_close(hours = 2))  



    equitySymbol = sid(24744)
#    equitySymbol = symbol('RSP')
    equityFuturesSymbol = sid(40516)
#    futuresSymbol = symbol('XIV')
    treasurySymbol = sid(22887)
#    treasurySymbol = symbol('EDV')
    treasuryFuturesSymbol = sid(38294)
#    treasuryFuturesSymbol = symbol('TMF')

    context.assets = [sid(24744), sid(40516), sid(22887), sid(38294)]
    
    context.e = equitySymbol
    context.ef = equityFuturesSymbol
    context.t = treasurySymbol
    context.tf = treasuryFuturesSymbol
    context.betTotalPercent = 0.30000
    context.betLowPercent = 0.8
    context.betHighPercent = 0.2
    context.historyDays = 100
    context.buyTrx = 0
    context.sellTrx = 0

def rebalance(context,data):
#   Get last hundred days worth of prices for universe, removing rows where the price is NaN.
    P = data.history(context.assets,'price',context.historyDays, '1d').dropna(axis = 1) 
    x = (P.tail(context.historyDays/10).median() / P.median() - 1).dropna() 
#   Get 1 value for each member of the universe. 
#   The value will be the median of the last 10 days divided by the median of the entire set.

#   Check if the datatable x contains the members of our universe.
    if context.e not in x.index:
        return
    if context.t not in x.index:
        return
    if context.tf not in x.index:
        return
    if context.ef not in x.index:
        return   
    
#   Check if the asset has a known last price and is currently listed on a supported exchange. 
    if not data.can_trade(context.e):
       return
    if not data.can_trade(context.t):
       return
    if not data.can_trade(context.tf):
       return
    if not data.can_trade(context.ef):
       return      

#   create a datatable signal with boolean values that indicate whether the last 10 days median > last 100 days median.    
    signal = (x > 0)

#   Check if the datatable signal contains prices for the members of our universe.
    if context.e not in signal.index:
        return
    if context.t not in signal.index:
        return
    if context.tf not in signal.index:
        return
    if context.ef not in signal.index:
        return
    
    if get_open_orders():
        return

#   Set boolean to true if both equitySymbol and futuresSymbol 10 day median prices are > 100 day median prices
    go = (signal[context.e] and signal[context.ef]) and ((x[context.e] + x[context.ef]) * 3 > (x[context.t] + x[context.tf]))
    
    bet = context.portfolio.portfolio_value * context.betTotalPercent
    
    context.exposure.update(context, data)
    
    long_cash, short_cash = \
    context.exposure.get_available_cash_long_short(context)

    if go:
        if long_cash > bet:
            if x[context.ef] > x[context.e]:
                betFuturesPercent = context.betLowPercent
                betEquityPercent = context.betHighPercent
            else:
                betFuturesPercent = context.betHighPercent
                betEquityPercent = context.betLowPercent
            order_value(context.ef, bet * betFuturesPercent)
            context.buyTrx += 1
            order_value(context.e, bet * betEquityPercent)
            context.buyTrx += 1
        order_target(context.t, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.tf, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
    else:
        if long_cash > bet:
            if x[context.t] > x[context.tf]:
                betTreasuryPercent = context.betLowPercent
                betTreasuryFuturesPercent = context.betHighPercent
            else:
                betTreasuryPercent = context.betHighPercent
                betTreasuryFuturesPercent = context.betLowPercent
            order_value(context.t,  bet * betTreasuryPercent)
            context.buyTrx += 1
            order_value(context.tf,  bet * betTreasuryFuturesPercent)
            context.buyTrx += 1
        order_target(context.e, 0.0)                                          
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.ef, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1

       
    #record(go = go)
    
    pvr(context, data)


def handle_data(context,data):    

    pass


def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount
        shrs_price = data.current(p, 'price')     
        if shrs < 0:  
            shorts += int(abs(shrs * shrs_price))  
        if shrs > 0:  
            longs  += int(shrs * shrs_price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    def _pvr_():  
            log.info('PvR {} %/day     {}'.format(  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi \
          or log_method == 'daily' and c.pvr['date_prv'] != date \
          or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    if c.pvr['days'] % 130 == 0 and _minute() == '100': _pvr_()  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        log.info(' Buys {} Sells {}'.format('%.0f' % context.buyTrx, '%.0f' % context.sellTrx))
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            _pvr_()  
            c.pvr_summary_done = 1

    

    
    
    
    
There was a runtime error.

Would it be possible to incorporate some of the things in the following back-test that I found here: https://www.quantopian.com/posts/universal-portfolios

It would be interesting to use the logic that determines if the market is in a downturn. If you see attached it held cash throughout the 2008 crash. I think this might be a great addition if someone knows how to implement it into the latest one above.

Clone Algorithm
18
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
'''
---------------------------------------
UNIVERSAL PORTFOLIOS
---------------------------------------
Implementation of strategy inspired by the paper by Thomas M. Cover, Information Theorist
Implementation authored by Naoki Nagai, 2015

Description:

This algo is a Quantopian python implematation of Universal Portfolios, described in paper by Professor Thomas M. Cover from Stanford.  Universal Portfolio is mathematically proved to achieve the return close to to the optimal constantly rebalanced portfolio in hindsight.

Methodology:

Let us construct regularly rebalanced portfolios with fixed weights given to each security (e.g. 40% Equity, 40% Bond, 20% Gold).  What would be the optimal weight given to each asset?  In this methodology, we evaluate every portfolio with every possible combination of weights, and calculate the return for each.  Then, our Universal Porfolio will be the weighted average of the all of these possible portfolio, weighted by the performance of each.  We don't make any kind of statistical assumptions about the underlying distribution of prices.  It's purely based on historical pricing data.

Proof:

Professor Cover's paper shows that return generated from this methodology S^ approaches  S*.  S* is the return of the regularly rebalanced portfolio with the optimal constant weight, which was selected in hindsight.  Even though we select the universal portfolio before knowing how it turns out, it approaches the optimal portfolio that was selected after the performance is known.
(for proof, see paper http://www-isl.stanford.edu/~cover/papers/paper93.pdf)

Analogy:

Algo works kind of like this.  We have tens of thousands of porfolio managers who decides their own allocations.  Then looking at the their performance for the 1 past year, we allocate our investment fudns proportional to the past 1 year return.  You can imagine this probably works.

Implication:

Perhaps Q fund could allocate their entire fund to each algo using this methodology. 
'''

import numpy as np

def initialize(context):
    set_symbol_lookup_date('2015-01-01')
    context.equities = symbols(
        # Equity
        'VV',     # US large cap
        'VO',     # US mid cap
        'VB',     # US small cap
    )
    context.fixedincome = symbols(
        # Fixed income
        'TLT',    # Long-term government bond
        'IEF',    # Mid-term government bond
        'LQD',    # Corporate bond
    )
    context.realasset = symbols(
        # Commodity and REIT
        'GLD',    # Gold
        'VNQ',    # US REIT
    )
    context.securities = context.equities + context.fixedincome + context.realasset
    
    context.period = 252                   # One year to evaluate past performance
    context.lever = 0.95                    # Leverage 
    context.allocation_intervals = 10      # Allocation intervals (100% / 10 = 10% increment) 
    context.weights = dict.fromkeys(context.securities, 0)
    context.shares = dict.fromkeys(context.securities, 0)

    # analyze function determine the target weights for the week
    schedule_function(analyze, date_rules.week_start(days_offset = 2),   time_rules.market_close())

    # rebalance function determine the target shares for the day
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(minutes=60))
    
    # Run execute every 15 minutes and in the first and last bars of each day
    for i in range(1,390):
        if i == 1 or i == 389 or i%15 == 0:
            schedule_function(execute, date_rules.every_day(), time_rules.market_open(minutes=i))
    
def analyze(context, data):    
    print 'here'
    # History of return prices
    prices = data.history(context.securities, 'price', context.period+1, '1d')
    
    # Returns is daily change, remove empty security, remove the last day NaN
    returns = prices.pct_change().dropna(how='all',axis=1)[1:]
    
    # Change the data to Numpy for faster calculation
    X = np.array(returns)
    X[np.isnan(X)] = 0
    
    # Transpose and add 1 (i.e. change -0.01 -> 0.99)
    X = X.transpose() + 1.0
    (n, m) = X.shape
    
    # In theory, we are supposed to calculate the integral of wealth over all portfolio
    # You cannot do that in practice, so we approximate by doing it descreetly.
    # We are going to vary weight by 10% due to memory constraint.
    B = binnings(context.allocation_intervals, n) / context.allocation_intervals
    
    # B is a matrix containing weights for every possible portfolio.  
    # We try every combination of weights for m securities
    # There are precision C m combinations for such portfolio = 19,448 portfolios for 8 securities 
    log.info('--- Universal Portfolio: evaluated %d possible portfolio for %d assets' % B.shape)

    # S is wealth vector corresponding to each portfolio. It is calculated as below
    # - B contains vectors of weights for all portfolio, X contains daily return for each asset
    # - By matrix algebra, BX will calculate daily returns for each porfolio for the past yaer
    # - Product of BX along the axis 1 (time) is the annual return for each portfolio.
    S = np.prod(np.dot(B,X), axis=1) - 1
    
    # Finally weight is calculated by weighted average of all portfolios possible, 
    # using the past 1 year of past return as the weight.  We can do this by SB/|S|
    W = np.dot(S,B)/sum(abs(S))
   
    # Store the weight in context variable.  We calculate this weekly. 
    # Actually ordering of shares is peformed in rebalance function
    i = 0
    for sec in returns:
        log.info('%4s: % 2.1f (%s)' % (sec.symbol, W[i] *100, sec.security_name))
        if data.can_trade(sec):
            # We set the weight to long-only.
            # After we calculate the weight average of the all portfolio returns,
            # it could happen that weighted average ends up being negative.
            # It needs to be verified but it does not mean we should short it.
            # It means it needs not to be invested in those securities with negative weight
            context.weights[sec] = max(0,W[i])
        i = i + 1
     
        
# From the target weight, calculate how many shares we should be owning
def rebalance(context, data):
    print 'here2'
    # Take averages of 3 days to avoid over-reacting to daily price fluctuation
    prices = data.history(context.securities, 'price', 3, '1d').mean()
    
    for sec in context.weights:
        
        # Target weight for this asset
        target_weight = context.weights[sec] * context.lever

        # How many shares are we trading?
        target_share = context.portfolio.portfolio_value * target_weight / prices[sec]
        
        # Record target shares
        context.shares[sec] = target_share
        
def execute(context, data):
    # Average trading volume per hour
    tradingvolume = data.history(context.securities, 'volume', 3, '1d').mean()
    
    # Open Orders
    open_orders = get_open_orders()
    
    for sec in context.shares:
        # If share has no data, skip
        if not data.can_trade(sec) or sec in open_orders:
            continue
        
        # How many shares are we trading?
        target_share = context.shares[sec]
        
        # How many shares do we have now?
        current_share = context.portfolio.positions[sec].amount
        
        # Trading shares is the gap between the current and target shares
        trade_share = target_share - current_share
        
        # volume of share trade cannot exceed the trading volume of the last bar
        trade_share = min(trade_share,  tradingvolume[sec]/390/5)    # for buying shares
        trade_share = max(trade_share, -tradingvolume[sec]/390/5)    # for selling shares
        
        # Don't trade less than $1000 to save comission
        if abs(trade_share * data.current(sec, 'price')) < 1000:
            continue
        
        # Make the order 
        order_target(sec, current_share + trade_share)
    
def record_vars(context, data):
    w = context.weights
    record(equities =    sum(w[s] for s in context.equities    if w[s] > 0))
    record(fixedincome = sum(w[s] for s in context.fixedincome if w[s] > 0))
    record(realassets =  sum(w[s] for s in context.realasset   if w[s] > 0))
    record(cash = max(0,context.portfolio.cash) / context.portfolio.portfolio_value)
   
# Thanks to smart implementaion by the user 'bar' from stackoverflow
# http://stackoverflow.com/questions/6750298/efficient-item-binning-algorithm-itertools-numpy
def binnings(n, k, cache={}):
    if n == 0:
        return np.zeros((1, k))
    if k == 0:
        return np.empty((0, 0))
    args = (n, k)
    if args in cache:
        return cache[args]
    a = binnings(n - 1, k, cache)
    a1 = a + (np.arange(k) == 0)
    b = binnings(n, k - 1, cache)
    b1 = np.hstack((np.zeros((b.shape[0], 1)), b))
    b1 = np.hstack((np.zeros((b.shape[0], 1)), b))
    result = np.vstack((a1, b1))
    cache[args] = result
    return result
There was a runtime error.

Steven and Caleb,
Happy to hear your algo's are performing well on Robinhood. Just to clarify, are you two trading under the T+3 restrictions with Robinhood (aka you don't have Robinhood instant yet)? I'm wondering because this code doesn't follow the T+3 restrictions anymore, but I would assume the algo would still perform as long as there is enough settled cash when making an order.

Anyone using Robinhood with Robinhood Instant? Would love to compare insights.

I am using Robinhood with Robinhood Instant. What would you like to know?

Hey Banana Bread,

So I just got Instant and I am trading with a dollar amount below the PDT rules. The algo buys and sells only one security each day. Seems like I am getting PDT rejections on the sell side, but not the buy side. As a result, the algo started building up a position and holding overnight. Have you written any code that accounts for T+3 but adjusts for the $1K that will automatically turnover from Robin Instant? (I am going to work on that soon, but looking for a shortcut). Also, I am wondering if you have noticed any period where Instant does not immediately turn over the $1K? I had a situation where the algo was supposed to sell one minute and buy the next minute. It missed the buy, but not because of PDT rejection, rather just a lack of cash. Wondering if you have seen anything similar?

Thanks

Thanks Caleb for posting the update. I am glad to hear it is working out well for you. I'm waiting to see how the algo exits it's current positions before gong live. Unfortunately, what Frank posted about the PDT issues has me concerned since I am hoping to start well below the PDT requirement level.

Frank, are you referring to this particular algo that Caleb posted or another algo you are using with Robinhood? As far as I can tell the algo Caleb and the others posted does not necessarily conduct day trades.

Thanks.

Hey Frank,

Yes PDT restrictions only applies to selling the stock not buying it. If you have RobinHood instant you get all your money instantly when you sell a position. The $1K you are referring to only applies to recurring deposits. I haven't written any code for the T+3 rule because it's unnecessary as an RH instant user. There can be periods where trades may take longer than a minute to process but that usually happens in low liquidity stocks and I know RH has in the past month have had issues where orders wouldn't go through. What I would do is check every minute for rejected trades and reopen them if they had been rejected, if your sell order goes through eventually your buy order will go through too because you will have enough cash. Let me know if you have any additional questions.

Jake - I am trading under Robinhood classic (T+3) and am currently in line for Robinhood Instant. Although, I'm not sure I will sign up for Robinhood Instant once I'm offered the chance. Overall, there just seems to be more restrictions with Robinhood Instant. I would rather have the flexibility of being a PDT if the algorithm calls for it. I'm already trading above the $1k max deposit for instant, and will now be subject to the 4-5 day deposit/withdrawal waiting period going forward...although is it $1k total or up to $1k/deposit, but at what frequency??? Low dollar value stocks and leveraged ETFs have different rules under Robinhood Instant, so that will be another thing to account for in the algorithm.

Sid - From the backtest and what I can tell, the algorithm does not fall under the PDT rules. It does not buy and sell the same stock within the same day. It also only performs trades once a day.

@ Banana Bread - Thanks for the input. Very informative.

@ Sid - My apologies. I am not referring to the algo in this thread. Sorry if I caused confusion.

Apparently we have unknowingly created a strategy very similar to this one,

http://seekingalpha.com/article/3967731-hack-market-math-part-2?ifp=0 ,

except that we use the long equivalents of the short positions they take and that we have a filter, i.e., a way of gauging the direction the market as a whole is presumably heading for.

Actually, the the guys who created the strategy that I mentioned in the previous post also seem to have a few long-only versions:

http://seekingalpha.com/article/3165846-the-zomma-strategy-index-master-sheet .

I have been using Pauls latest version of this Algo in live trading with 1k. It has only bought XIV. I thought it was going to be buying more than 1 stock at a time. Is that not correct.

What determines if it buys RSP, EVD and TMF? Is this functioning how it should. I have around 223 available cash right now. Haven't seen any additional buys. Have not seen it sell anything yet either.

Has anyone been able to update Tim's algo from Feb 25, 2016 that only trades XIV/EDV for Q2? The move to Q2 has deprecated multiple lines of code.

The algo is the simplest I have found and can manually trade on my retirement account.

Hey Tyler,

Here's a backtest of a Q2-adapted version of the algo you mention.

Please be careful trading it, the volatility and drawdowns are huge.

The algo also goes slightly into negative cash at some point, so the associates trades would be rejected by Robinhood, I understand, but it probably only happens once or twice.

Clone Algorithm
195
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
def initialize(context):    
    
    set_long_only()    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.secs = [context.m, context.f, context.x]
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.every_day(),
                      time_rule = time_rules.market_open(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    c = context
    
    if get_open_orders():
        return    

    if not data.can_trade(c.m):
        return
    if not data.can_trade(c.f):
        return
    if not data.can_trade(c.x):
        return
    
    P = data.history(c.secs,'price', 100, '1d').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    if not c.m in x.index:
        return
    if not c.f in x.index:
        return
    if not c.x in x.index:
        return
        
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash > 0:
            order(c.x, cash // data.current(c.x, 'price'))
    else:
        order_target(c.x, 0)
        if cash > 0:
            order(c.f, cash // data.current(c.f, 'price'))
    
    # record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    
    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data.current(p, 'price')))  
        if shrs > 0:  
            longs  += int(shrs * data.current(p, 'price'))

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1


    
There was a runtime error.

Forgot to mention that I changed the trading frequency in the above-mentioned algo to daily. Here's the original approach with weekly trades. For some reason it performs quite a bit worse.

In both cases the trading period in the day has been changed from market_close - 30 min to market_open + 30 minutes, to assure enough time for the trades to be filled.

Clone Algorithm
195
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
def initialize(context):    
    
    set_long_only()    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.secs = [context.m, context.f, context.x]
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_open(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    c = context
    
    if get_open_orders():
        return    

    if not data.can_trade(c.m):
        return
    if not data.can_trade(c.f):
        return
    if not data.can_trade(c.x):
        return
    
    P = data.history(c.secs,'price', 100, '1d').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    if not c.m in x.index:
        return
    if not c.f in x.index:
        return
    if not c.x in x.index:
        return
        
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash > 0:
            order(c.x, cash // data.current(c.x, 'price'))
    else:
        order_target(c.x, 0)
        if cash > 0:
            order(c.f, cash // data.current(c.f, 'price'))
    
    # record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    
    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data.current(p, 'price')))  
        if shrs > 0:  
            longs  += int(shrs * data.current(p, 'price'))

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1


    
There was a runtime error.

Why is there a difference between the updated Q2 algo and the one from Feb 25, 2016? The older one was traded weekly and was up to 600% before the switch over to Q2?

@Tyler,

Here a Q2-adapted version of the original algo: like that one it trades weekly, on market-close minus 30 min.

The difference in the performance must stem from the changes introduced with Q2.

I think this also indicates that the algo is highly volatile, sensitive and in a way unstable. I do not recommend trading it.

Clone Algorithm
195
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
def initialize(context):    
    
    set_long_only()    
    set_do_not_order_list(security_lists.leveraged_etf_list)
    
    context.m = sid(24744) # RSP
    context.f = sid(22887) # EDV
    context.x = sid(40516) # XIV
    
    context.secs = [context.m, context.f, context.x]
    
    context.start = True
    
    schedule_function(func = rebalance,
                      date_rule = date_rules.week_start(),
                      time_rule = time_rules.market_close(minutes = 30),
                      half_days = True)
    

def rebalance(context, data):
    
    c = context
    
    if get_open_orders():
        return    

    if not data.can_trade(c.m):
        return
    if not data.can_trade(c.f):
        return
    if not data.can_trade(c.x):
        return
    
    P = data.history(c.secs,'price', 100, '1d').dropna(axis=1)
    x = P.tail(10).median() / P.median()
    
    if not c.m in x.index:
        return
    if not c.f in x.index:
        return
    if not c.x in x.index:
        return
        
    signal = (x[c.m] > 1) and (x[c.x] > 1)
    cash = c.portfolio.cash    
    
    if signal:    
        order_target(c.f, 0)
        if cash > 0:
            order(c.x, cash // data.current(c.x, 'price'))
    else:
        order_target(c.x, 0)
        if cash > 0:
            order(c.f, cash // data.current(c.f, 'price'))
    
    # record(signal = signal)
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        context.start = False
            
    # record(leverage = context.account.leverage, 
    #        exposure = context.account.net_leverage)

    # record(cash = context.portfolio.cash)
 
    pvr(context, data)
    
    
def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:  
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * data.current(p, 'price')))  
        if shrs > 0:  
            longs  += int(shrs * data.current(p, 'price'))

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi or log_method == 'daily' and c.pvr['date_prv'] != date or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            log.info('PvR {}%  {} %/day     {}'.format('%.1f' % pvr_rtrn,  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))  
            c.pvr_summary_done = 1


    
There was a runtime error.

I fear it's too overfitted. I can't play with volatile ETFs knowing that your half of your money can disappear in a month. It's psychologically demanding.

Why does the algo buy and sell in multiple transactions? I see 10+ sell orders of the same security minutes apart.

3:31 RSP SELL
3:33 RSP SELL
3:35 RSP SELL
3:36 RSP SELL

@Corey: Because not all orders can be filled immediately.

So if I have robinhood but no instant, which version should I use?

Are there any updates from those live-trading this? XIV has taken a large downturn lately, very curious as to how it fared.

I'm down 13.2%. :'(
I' m trading SVXY since they won't let me trade ETN due to being under 21 :(

The last version has a DD of 29%.... If you cannot stomach that type of DD (Whihc I can't) then don't trade it. If a DD is in the Backtest, assume it will happen again

I have been trading a modified version of this Algo on IB for about a month. It has been up as high as 7%, and is currently down 1.3%. The modifications I made include;

1) Added trailing stops on individual stocks based on the stock's volatility.
2) Added total portfolio trailing stop based on fixed loss (15%).
3) Added skim profit % weekly. Removes % of profit from available cash at the beginning of every week.
4) Trade RSP & XIV vs EDV & TMF.

Total initial investment is $10,000.

I have been using this algo on Quantopian's live trading environment and I was up over 20% at one time. Right now, I noticed this is the second time I've watched a downturn occur and the algo does nothing about it (sell or take profits, leverage itself, etc.). This down turn has been fast and hard so I sit at about 4% on the upside from over %20. Since this is a long term strategy, I am still interested in Seeing what the algo does at this point. I suspect another day or two of downside pressure before a bounce from oversold territory. Being that the algo has the account fully invested, It would have been great to see some profit taking and then some more buying at the dips. I guess I will wait and see. Interestingly enough, since this algo trades only a few stocks, it is easy to use as a guide and manually trade referencing it. I will report more.

Paul is your modified version using the trailing stops effectively? Are the trailing stops based on percentage, moving averages or somethng else? TIA

Paul

I'm interested to see how your handling trailing stops and skimming profit. Thankfully I stopped the Algo 2 weeks ago and bought VXX which I sold yesterday, and have been buying and selling manually for VXX and XIV so I'm up almost 20%. My worry was I wanted to capture 5% gains and lock that profit in then either buy XIV again or VXX depending on how the markets moved, I had to hold VXX for 2 weeks but was worth it. I would be interested to see an algo that using trailing limits and stops on XIV to buy at low point and sell right when it turns and maybe even add VXX in it some. I may try to work on that some.

For anyone who lost money I wouldn't be that worried since I'm sure you will make it back in a week or two.

Here are some log entries from live trading, that explain the trailing stop a bit;

2016-06-13 09:31 trailingStop:511 INFO asset:Equity(40516 [XIV]) cost basis:27.6978740157 current price:29.734 high price:34.0334516129
2016-06-13 09:31 trailingStop:509 INFO Trailing stop sold all:Equity(40516 [XIV]) shares:127 stop:10.93%
2016-05-19 15:59 trailingStart:528 INFO Equity(40516 [XIV]) rehabilitated - current:27.5659354839 low:26.3741290323 threshold:27.54
2016-05-19 10:32 trailingStop:511 INFO asset:Equity(40516 [XIV]) cost basis:28.9441756303 current price:26.693516129 high price:29.6287741935
2016-05-19 10:32 trailingStop:509 INFO Trailing stop sold all:Equity(40516 [XIV]) shares:119 stop:8.87%

To calculate the trailing stop percent threshold I take the high price - low price / average price for the last 42 days, with a maximum of 15%. I also set a flag so I don't buy the stock again until it has been rehabilitated, which I define as inverse of a trailing stop, in other words it must bounce off a low by its volatility * .5 (1/2 its volatility factor).

My profit skimmer takes the current portfolio value, if it is greater than the portfolio value from last week, I calculate the profit, multiply by .4 and add it to "cash reserve" which is an amount of cash I do not trade with. The next time I rebalance the portfolio, I sell enough to cover cash reserve.

Paul,

I would love to see the code behind that since it's still kind of going over my head.

Ouch to whoever shorted volatility. Yet another reason to treat these dangerous instruments with respect.

Well we bounced a little, then Brexit happened. The latest code version, the one without the stop loss, never exited XIV or RSP. It basically gave up all its gains (over 20%, I think 24%). That's a big move, BUT I noticed it is not down, or at a significant loss. At this point, I would definitely look to improve the exiting strategy. Paul, I would love to know how your latest version is trading. Have you thought about using SMA or EMA crossunders?

It looks as though I sold and it rehabilitated. At this moment I am up .1%

2016-06-24 10:30 trailingStart:528 INFO Equity(40516 [XIV]) rehabilitated - current:25.9231935484 low:24.1366774194 threshold:25.6  
2016-06-24 10:01 trailingStop:511 INFO asset:Equity(40516 [XIV]) cost basis:24.7971740157 current price:25.4919354839 high price:29.7525483871  
2016-06-24 10:01 trailingStop:509 INFO Trailing stop sold all:Equity(40516 [XIV]) shares:127 stop:12.14%

Paul can you share your trailing stop/rehabilitation?

How can the algo Caleb Mellbom posted on Apr 22, 2016 be modified to ensure all cash is used? There is always a lump of cash sitting on the sidelines - want to try and push the leverage to 1 as much as possible.

Is there an updated algorithm? I tried to update the Algorithm with newer version, but its not working.

Is this algo safe for smaller accounts (less than $25,000). Will it trigger the "Pattern Day trader" rule (by executing four roundtrip daytrades within 5 days)?

Seconding Yogi's question. I'm assuming the Pattern Day Trader rule shouldn't be a problem, but with the most recent updated version of this algo, would it be safe/make sense for a smaller account?

I did a forward test and it looks like it still works. Got a popup saying that the code was automatically updated as well.

Clone Algorithm
48
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
class ExposureMngr(object):
    
    def __init__(self, target_leverage = 1.0, target_long_exposure_perc = 0.50, target_short_exposure_perc = 0.50):   
        self.target_leverage            = target_leverage
        self.target_long_exposure_perc  = target_long_exposure_perc              
        self.target_short_exposure_perc = target_short_exposure_perc           
        self.short_exposure             = 0.0
        self.long_exposure              = 0.0
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
      
    def get_current_leverage(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure
        curr_leverage = (context.portfolio.portfolio_value - curr_cash) / context.portfolio.portfolio_value
        return curr_leverage

    def get_exposure(self, context, consider_open_orders = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)
        return long_exposure + short_exposure
    
    def get_long_short_exposure(self, context, consider_open_orders = True):
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure     
        return (long_exposure, short_exposure)
    
    def get_long_short_exposure_pct(self, context, consider_open_orders = True, consider_unused_cash = True):
        long_exposure, short_exposure = self.get_long_short_exposure(context, consider_open_orders)        
        total_cash = long_exposure + short_exposure
        if consider_unused_cash:
            total_cash += self.get_available_cash(context, consider_open_orders)
        long_exposure_pct   = long_exposure  / total_cash if total_cash > 0 else 0
        short_exposure_pct  = short_exposure / total_cash if total_cash > 0 else 0
        return (long_exposure_pct, short_exposure_pct)
    
    def get_available_cash(self, context, consider_open_orders = True):
        curr_cash = context.portfolio.cash - (self.short_exposure * 2)
        if consider_open_orders:
            curr_cash -= self.open_order_short_exposure
            curr_cash -= self.open_order_long_exposure            
        leverage_cash = context.portfolio.portfolio_value * (self.target_leverage - 1.0)
        return curr_cash + leverage_cash
          
    def get_available_cash_long_short(self, context, consider_open_orders = True):
        total_available_cash  = self.get_available_cash(context, consider_open_orders)
        long_exposure         = self.long_exposure
        short_exposure        = self.short_exposure
        if consider_open_orders:
            long_exposure  += self.open_order_long_exposure
            short_exposure += self.open_order_short_exposure
        current_exposure       = long_exposure + short_exposure + total_available_cash
        target_long_exposure  = current_exposure * self.target_long_exposure_perc
        target_short_exposure = current_exposure * self.target_short_exposure_perc        
        long_available_cash   = target_long_exposure  - long_exposure 
        short_available_cash  = target_short_exposure - short_exposure
        return (long_available_cash, short_available_cash)
    
    def update(self, context, data):
    
        self.open_order_short_exposure  = 0.0
        self.open_order_long_exposure   = 0.0
        for stock, orders in  get_open_orders().iteritems():
            if not data.can_trade(stock):
                continue
            price = data.current(stock, 'price')
            amount = 0 if stock not in context.portfolio.positions else context.portfolio.positions[stock].amount
            for oo in orders:
                order_amount = oo.amount - oo.filled
                if order_amount < 0 and amount <= 0:
                    self.open_order_short_exposure += (price * -order_amount)
                elif order_amount > 0 and amount >= 0:
                    self.open_order_long_exposure  += (price * order_amount)
        
        self.short_exposure = 0.0
        self.long_exposure  = 0.0
        for stock in context.portfolio.positions:
            amount = context.portfolio.positions[stock].amount
            last_sale_price = context.portfolio.positions[stock].last_sale_price
            if amount < 0:
                self.short_exposure += (last_sale_price * -amount)
            elif amount > 0:
                self.long_exposure  += (last_sale_price * amount)
        

def initialize(context):
        
    context.exposure = ExposureMngr(target_leverage = .99,
                                    target_long_exposure_perc = .99,
                                    target_short_exposure_perc = 0.0)

    schedule_function(rebalance, 
                      date_rules.every_day(),
                      time_rules.market_close(hours = 2))  


    # RSP
    equitySymbol = sid(24744)
    # XIV
    equityFuturesSymbol = sid(40516)
    # EDV
    treasurySymbol = sid(22887)
    # TMF
    treasuryFuturesSymbol = sid(38294)
    
    context.e = equitySymbol
    context.ef = equityFuturesSymbol
    context.t = treasurySymbol
    context.tf = treasuryFuturesSymbol
    
    context.securities = [equitySymbol, equityFuturesSymbol, 
                          treasurySymbol, treasuryFuturesSymbol]
    
    context.betTotalPercent = 0.30000
    context.betLowPercent = 0.8
    context.betHighPercent = 0.2
    context.historyDays = 100
    context.buyTrx = 0
    context.sellTrx = 0

def rebalance(context,data):
#   Get last hundred days worth of prices for universe, removing rows where the price is NaN.
    P = data.history(context.securities, 'price', context.historyDays, '1d').dropna(axis = 1) 
    x = (P.tail(context.historyDays/10).median() / P.median() - 1).dropna() 
#   Get 1 value for each member of the universe. 
#   The value will be the median of the last 10 days divided by the median of the entire set.

#   Check if the datatable x contains the members of our universe.
    if context.e not in x.index:
        return
    if context.t not in x.index:
        return
    if context.tf not in x.index:
        return
    if context.ef not in x.index:
        return   
    
#   Check if the datatable data contains the members of our universe.
    if not data.can_trade(context.securities).all():
        return

#   create a datatable signal with boolean values that indicate whether the last 10 days median > last 100 days median.    
    signal = (x > 0)

#   Check if the datatable signal contains prices for the members of our universe.
    if context.e not in signal.index:
        return
    if context.t not in signal.index:
        return
    if context.tf not in signal.index:
        return
    if context.ef not in signal.index:
        return
    
    if get_open_orders():
        return

#   Set boolean to true if both equitySymbol and futuresSymbol 10 day median prices are > 100 day median prices
    go = (signal[context.e] and signal[context.ef]) and ((x[context.e] + x[context.ef]) * 3 > (x[context.t] + x[context.tf]))
    
    bet = context.portfolio.portfolio_value * context.betTotalPercent
    
    context.exposure.update(context, data)
    
    long_cash, short_cash = \
    context.exposure.get_available_cash_long_short(context)

    if go:
        if long_cash > bet:
            if x[context.ef] > x[context.e]:
                betFuturesPercent = context.betLowPercent
                betEquityPercent = context.betHighPercent
            else:
                betFuturesPercent = context.betHighPercent
                betEquityPercent = context.betLowPercent
            order_value(context.ef, bet * betFuturesPercent)
            context.buyTrx += 1
            order_value(context.e, bet * betEquityPercent)
            context.buyTrx += 1
        order_target(context.t, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.tf, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
    else:
        if long_cash > bet:
            if x[context.t] > x[context.tf]:
                betTreasuryPercent = context.betLowPercent
                betTreasuryFuturesPercent = context.betHighPercent
            else:
                betTreasuryPercent = context.betHighPercent
                betTreasuryFuturesPercent = context.betLowPercent
            order_value(context.t,  bet * betTreasuryPercent)
            context.buyTrx += 1
            order_value(context.tf,  bet * betTreasuryFuturesPercent)
            context.buyTrx += 1
        order_target(context.e, 0.0)                                          
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1
        order_target(context.ef, 0.0)
        if context.buyTrx > context.sellTrx:
            context.sellTrx += 1

       
    #record(go = go)
    
    pvr(context, data)


def pvr(context, data):  
    ''' Custom chart and/or log of profit_vs_risk returns and related information  
    '''  
    # # # # # # # # # #  Options  # # # # # # # # # #  
    record_max_lvrg = 1         # Maximum leverage encountered  
    record_leverage = 0         # Leverage (context.account.leverage)  
    record_q_return = 0         # Quantopian returns (percentage)  
    record_pvr      = 1         # Profit vs Risk returns (percentage)  
    record_pnl      = 0         # Profit-n-Loss  
    record_shorting = 1         # Total value of any shorts  
    record_overshrt = 0         # Shorts beyond longs+cash  
    record_risk     = 0         # Risked, max cash spent or shorts beyond longs+cash  
    record_risk_hi  = 1         # Highest risk overall  
    record_cash     = 0         # Cash available  
    record_cash_low = 1         # Any new lowest cash level  
    logging         = 1         # Also to logging window conditionally (1) or not (0)  
    log_method      = 'risk_hi' # 'daily' or 'risk_hi'

    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 efficiency, readability]  
    if 'pvr' not in c:  
        date_strt = get_environment('start').date()  
        date_end  = get_environment('end').date()  
        cash_low  = c.portfolio.starting_cash  
        mode      = get_environment('data_frequency')  
        c.pvr = {  
            'max_lvrg': 0,  
            'risk_hi' : 0,  
            'days'    : 0.0,  
            'date_prv': '',  
            'cash_low': cash_low,  
            'date_end': date_end,  
            'mode'    : mode,  
            'run_str' : '{} to {}  {}  {}'.format(date_strt,date_end,int(cash_low),mode)  
        }  
        log.info(c.pvr['run_str'])  
    pvr_rtrn     = 0            # Profit vs Risk returns based on maximum spent  
    profit_loss  = 0            # Profit-n-loss  
    shorts       = 0            # Shorts value  
    longs        = 0            # Longs  value  
    overshorts   = 0            # Shorts value beyond longs plus cash  
    new_risk_hi  = 0  
    new_cash_low = 0                           # To trigger logging in cash_low case  
    lvrg         = c.account.leverage          # Standard leverage, in-house  
    date         = get_datetime().date()       # To trigger logging in daily case  
    cash         = c.portfolio.cash  
    start        = c.portfolio.starting_cash  
    cash_dip     = int(max(0, start - cash))  
    q_rtrn       = 100 * (c.portfolio.portfolio_value - start) / start

    if int(cash) < c.pvr['cash_low']:                # New cash low  
        new_cash_low = 1  
        c.pvr['cash_low']   = int(cash)  
        if record_cash_low:  
            record(CashLow = int(c.pvr['cash_low'])) # Lowest cash level hit

    if record_max_lvrg:  
        if c.account.leverage > c.pvr['max_lvrg']:  
            c.pvr['max_lvrg'] = c.account.leverage  
            record(MaxLv = c.pvr['max_lvrg'])        # Maximum leverage

    if record_pnl:  
        profit_loss = c.portfolio.pnl  
        record(PnL = profit_loss)                    # "Profit and Loss" in dollars

    for p in c.portfolio.positions:
        price = data.current(p, 'price')
        shrs = c.portfolio.positions[p].amount  
        if shrs < 0:  
            shorts += int(abs(shrs * price))  
        if shrs > 0:  
            longs  += int(shrs * price)

    if shorts > longs + cash: overshorts = shorts             # Shorts when too high  
    if record_shorting: record(Shorts  = shorts)              # Shorts value as a positve  
    if record_overshrt: record(OvrShrt = overshorts)          # Shorts value as a positve  
    if record_cash:     record(Cash = int(c.portfolio.cash))  # Cash  
    if record_leverage: record(Lvrg = c.account.leverage)     # Leverage

    risk = int(max(cash_dip, shorts))  
    if record_risk: record(Risk = risk)       # Amount in play, maximum of shorts or cash used

    if risk > c.pvr['risk_hi']:  
        c.pvr['risk_hi'] = risk  
        new_risk_hi = 1

        if record_risk_hi:  
            record(RiskHi = c.pvr['risk_hi']) # Highest risk overall

    if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)  
        if c.pvr['risk_hi'] != 0:     # Avoid zero-divide  
            pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.pvr['risk_hi']  
            record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

    if record_q_return:  
        record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

    def _minute():   # To preface each line with minute of the day.  
        if get_environment('data_frequency') == 'minute':  
            bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
            minute = (bar_dt.hour * 60) + bar_dt.minute - 570  # (-570 = 9:31a)  
            return str(minute).rjust(3)  
        return ''    # Daily mode, just leave it out.

    def _pvr_():  
            log.info('PvR {} %/day     {}'.format(  
                '%.4f' % (pvr_rtrn / c.pvr['days']), c.pvr['run_str']))  
            log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format(  
                '%.0f' % (c.portfolio.portfolio_value - start), '%.0f' % c.pvr['risk_hi'],  
                '%.1f' % pvr_rtrn))  
            log.info('  QRet {} PvR {} CshLw {} MxLv {} RskHi {} Shrts {}'.format(  
                '%.2f' % q_rtrn, '%.2f' % pvr_rtrn, '%.0f' % c.pvr['cash_low'],  
                '%.2f' % c.pvr['max_lvrg'], '%.0f' % c.pvr['risk_hi'], '%.0f' % shorts))

    if logging:  
        if log_method == 'risk_hi' and new_risk_hi \
          or log_method == 'daily' and c.pvr['date_prv'] != date \
          or new_cash_low:  
            qret    = ' QRet '   + '%.1f' % q_rtrn  
            lv      = ' Lv '     + '%.1f' % lvrg              if record_leverage else ''  
            pvr     = ' PvR '    + '%.1f' % pvr_rtrn          if record_pvr      else ''  
            pnl     = ' PnL '    + '%.0f' % profit_loss       if record_pnl      else ''  
            csh     = ' Cash '   + '%.0f' % cash              if record_cash     else ''  
            shrt    = ' Shrt '   + '%.0f' % shorts            if record_shorting else ''  
            ovrshrt = ' Shrt '   + '%.0f' % overshorts        if record_overshrt else ''  
            risk    = ' Risk '   + '%.0f' % risk              if record_risk     else ''  
            mxlv    = ' MaxLv '  + '%.2f' % c.pvr['max_lvrg'] if record_max_lvrg else ''  
            csh_lw  = ' CshLw '  + '%.0f' % c.pvr['cash_low'] if record_cash_low else ''  
            rsk_hi  = ' RskHi '  + '%.0f' % c.pvr['risk_hi']  if record_risk_hi  else ''  
            log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minute(),  
               lv, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, ovrshrt, risk, rsk_hi))  
    if c.pvr['date_prv'] != date: c.pvr['days'] += 1.0  
    if c.pvr['days'] % 130 == 0 and _minute() == '100': _pvr_()  
    c.pvr['date_prv'] = date  
    if c.pvr['date_end'] == date:  
        # Summary on last minute of last day.  
        # If using schedule_function(), backtest last day/time may need to match for this to execute.  
        log.info(' Buys {} Sells {}'.format('%.0f' % context.buyTrx, '%.0f' % context.sellTrx))
        if 'pvr_summary_done' not in c: c.pvr_summary_done = 0  
        log_summary = 0  
        if c.pvr['mode'] == 'daily' and get_datetime().date() == c.pvr['date_end']:  
            log_summary = 1  
        elif c.pvr['mode'] == 'minute' and get_datetime() == get_environment('end'):  
            log_summary = 1  
        if log_summary and not c.pvr_summary_done:  
            _pvr_()  
            c.pvr_summary_done = 1
There was a runtime error.

Hey all, is this still a recommended robinhood-friendly algo to start with?

@anyone interested

After trying to study the history of this thread:
It started out with XIV, but then for a little while in the beginning, the algo was changed to trading RSP vs. TLT based on a crossover. That was the highlight. Unfortunately, after they reduced the balancing from weekly to daily, the statistically significant advantage disappeared. But even the original wasn't trustworthy because of trading rarely. Also, as far as I can tell from research and study, all of general US indices are - for the most part - systemically similar (if someone knows otherwise, please enlighten me), so I avoid using indices other than the S&P500 to avoid overfitting. Also, TLT, bonds, and assets in general have been inflated (Or more specifically future values have been pulled towards us in time. Also literal inflation.) because of QE from the fed and the bank of China, so returns from long bonds can't be expected to continue any more unless you know what the central banks are going to do.

Speaking of that, I've heard that there are reasons to be concerned about a possible junk bond bubble. I don't know the technicals of that, yet, but be cautious if someone does invest in high-yield-bonds (relevant ETFs being JNK, HYG, and ANGL).

As far as the rest of the thread, I think the other algos might just be taking their profit from XIV, and looked more complicated. If you don't know, XIV was a trainwreck from the beginning, which crashed 6 months ago. XIV was terminated, thank god. If there was ever anything good in it, you can just buy/sell the VIX futures. But VIX performance is meaningless anyway without an extreme degree of understanding and studying 100-year or ideally 1000-year old volatility events.