Back to Community
3 X ETF TMF/UPRO rebalanced every 90 days....

Simple strategy using UPRO ( 3 X S&P 500) and TMF ( 3 X 18 -20 yrs Treasury). Simply hold both securities( 50 /50) and re-balance every quarter. This was created with the " Quantopian Algo builder" - https://www.quantopian.com/algobuilder

If possible, can some add a rule where this system trades only when the SPY is above its 200 MA ?

Thanks

L.

Clone Algorithm
421
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
''' 
    This algorithm defines a target long-only diversified portfolio and rebalances 
    it at a user-specified frequency.
'''

import datetime
import pytz
import pandas as pd
from zipline.utils.tradingcalendar import get_early_closes

def initialize(context):
    # Define the instruments in the portfolio:
    context.sids = {
        sid(38533): 0.5,
        sid(38294): 0.5,
    }
    
    # Define the benchmark (used to get early close dates for reference).
    context.spy           = sid(8554)
    
    start_date = context.spy.security_start_date
    end_date   = context.spy.security_end_date
    
    # Initialize context variables the define rebalance logic:
    context.rebalance_date = None
    context.next_rebalance_Date = None
    context.rebalance_days = 90
    
    # Get the dates when the market closes early:
    context.early_closes = get_early_closes(start_date, end_date).date

    set_commission(commission.PerTrade(cost=1))

def handle_data(context, data):
    # Get the current exchange time, in local timezone: 
    exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # If it is rebalance day, rebalance:
    if context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
       # If we are in rebalance window but there are open orders, wait til next minute
       if has_open_orders(data,context) == True:
            log.info('Has open orders, not rebalancing.')
       else:
           # If there are no open orders we can rebalance.
           rebalance(context, data, exchange_time) 
           log.info('Rebalanced portfolio to target weights at %s' % exchange_time)

           # Update the current and next rebalance dates
           context.rebalance_date = exchange_time 
           context.next_rebalance_date = context.rebalance_date + datetime.timedelta(days=context.rebalance_days)      
           print(context.next_rebalance_date)

def rebalance(context,data,exchange_time):    
    for sid in context.sids:
        if sid in data:
            order_target_percent(sid, context.sids[sid])

def has_open_orders(data,context):               
    # Only rebalance when we have zero pending orders.
    has_orders = False
    for stk in data:
        orders = get_open_orders(stk)
        if orders:
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=stk)  
            has_orders = True
    return has_orders           
There was a runtime error.
15 responses

Will this algo breakdown once the Fed begins to raise interest prices?

Hi Eric ,

Perhaps that will be the case. One thing I forgot to mention was that these two ETFs were first available mid 2009, start of the biggest rally in recenthistory, so the results are obviously very bias. I would like to see hoe these will perform in a bear market.

Why did you pick these ETFs for this strategy? They both work of course:) but I wonder what other pairs of ETFs/asset classes could be paired together in order produce returns.

I want a balanced " equity/bonds" portfolio , hence those two ETFs. Also surprisingly enough, these two ETFs offer quarterly distribution, very low , but still something. This could work with any S&P 500 / Treasury ETFs ( SPXL/TMF , SPY/TLT [1X])

I think you should check Clifford Asness paper : Value and Momentum everywhere http://papers.ssrn.com/sol3/papers.cfm?abstract_id=1363476 , his dollar neutral long-short value and momentum strategy is similar to yours.

Hi Adityo

Thank you for sharing this with me, I appreciate it :)

Here's the backtest above, but with UPRO as a benchmark. This way, one can see the presumed smoothing effect of TMF, and the associated reduction in return for the recent run up of the S&P 500.

I've poked around a bit regarding these NX ETFs (N = 2 or 3). Taking UPRO as an example, the first thing I note is that it is primarily an investment in a derivative called an S&P 500 Index Swap (see http://finance.yahoo.com/q/hl?s=UPRO+Holdings), which, by a miracle of modern finance, returns 3X the S&P 500. So what's the catch? How can the average long-term earnings growth of the S&P 500 constituents be amplified by 3X indefinitely? If we never see a bear market again, would this scheme work?

The other thing I've noticed is that the market cap of leveraged ETFs is quite small compared to direct ETFs. For example, UPRO is $768.22M, whereas SPY is $180.38B. Also, if one compares the expense ratios, they are 0.95% versus 0.09%, respectively. Long-term (e.g. decades), I'm wondering if you just end up with the same average return as a buy and hold balanced ETF/mutual fund, but you get the privilege of paying ~1% more in expenses (not to mention broker commissions).

So rather than buying UPRO, one thought is that there may be useful information in the degree to which UPRO tracks SPY. Basically, use UPRO's algorithm as an indicator.

Grant

Clone Algorithm
65
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
''' 
    This algorithm defines a target long-only diversified portfolio and rebalances 
    it at a user-specified frequency.
'''

import datetime
import pytz
import pandas as pd
from zipline.utils.tradingcalendar import get_early_closes

def initialize(context):
    # Define the instruments in the portfolio:
    context.sids = {
        sid(38533): 0.5,
        sid(38294): 0.5,
    }
    
    # Define the benchmark (used to get early close dates for reference).
    context.spy           = sid(8554)
    
    start_date = context.spy.security_start_date
    end_date   = context.spy.security_end_date
    
    # Initialize context variables the define rebalance logic:
    context.rebalance_date = None
    context.next_rebalance_Date = None
    context.rebalance_days = 90
    
    # Get the dates when the market closes early:
    context.early_closes = get_early_closes(start_date, end_date).date

    set_commission(commission.PerTrade(cost=1))
    set_benchmark(sid(38533)) # UPRO

def handle_data(context, data):
    # Get the current exchange time, in local timezone: 
    exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # If it is rebalance day, rebalance:
    if context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
       # If we are in rebalance window but there are open orders, wait til next minute
       if has_open_orders(data,context) == True:
            log.info('Has open orders, not rebalancing.')
       else:
           # If there are no open orders we can rebalance.
           rebalance(context, data, exchange_time) 
           log.info('Rebalanced portfolio to target weights at %s' % exchange_time)

           # Update the current and next rebalance dates
           context.rebalance_date = exchange_time 
           context.next_rebalance_date = context.rebalance_date + datetime.timedelta(days=context.rebalance_days)      
           print(context.next_rebalance_date)

def rebalance(context,data,exchange_time):    
    for sid in context.sids:
        if sid in data:
            order_target_percent(sid, context.sids[sid])

def has_open_orders(data,context):               
    # Only rebalance when we have zero pending orders.
    has_orders = False
    for stk in data:
        orders = get_open_orders(stk)
        if orders:
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=stk)  
            has_orders = True
    return has_orders           
There was a runtime error.

Found way to beat UPRO using another strategy, but need help programing it .......

Lionel, why not post your strategy or difficulty, and I'm sure myself or some could help.

My fear with leveraged ETFs is getting wiped out during the first day(s) of a bear market. The rebalancing is a good way to mitigate some of those losses, I would just make sure the rebalancing favors movement from your risky stocks towards bonds and doesn't let your stock portion drain your bonds dry. Also, I wouldn't go with TMF. They offer no yields and they are extremely volatile, both of which are the exact opposite reason people buy bonds in the first place. Let's say the Fed raises interest rates, this could easily shock your bond ETF enough to completely wipe it out, and then the fear in the markets could completely wipe out UPRO, leaving you with nothing after just 2-3 bad days, even if the markets completely recover.

I still have faith in a plan like this though. As long as you realize how reckless it is.

If your risk appetite is lower than other , then I recommend that you use SPY / TLT instead, no swap , no craziness. Cheers

Clone Algorithm
52
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
''' 
    This algorithm defines a target long-only diversified portfolio and rebalances 
    it at a user-specified frequency.
'''

import datetime
import pytz
import pandas as pd
from zipline.utils.tradingcalendar import get_early_closes

def initialize(context):
    # Define the instruments in the portfolio:
    context.sids = {
        sid(8554): 0.5,
        sid(23921): 0.5,
    }
    
    # Define the benchmark (used to get early close dates for reference).
    context.spy           = sid(8554)
    
    start_date = context.spy.security_start_date
    end_date   = context.spy.security_end_date
    
    # Initialize context variables the define rebalance logic:
    context.rebalance_date = None
    context.next_rebalance_Date = None
    context.rebalance_days = 90
    
    # Get the dates when the market closes early:
    context.early_closes = get_early_closes(start_date, end_date).date

    set_commission(commission.PerTrade(cost=1))

def handle_data(context, data):
    # Get the current exchange time, in local timezone: 
    exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # If it is rebalance day, rebalance:
    if context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
       # If we are in rebalance window but there are open orders, wait til next minute
       if has_open_orders(data,context) == True:
            log.info('Has open orders, not rebalancing.')
       else:
           # If there are no open orders we can rebalance.
           rebalance(context, data, exchange_time) 
           log.info('Rebalanced portfolio to target weights at %s' % exchange_time)

           # Update the current and next rebalance dates
           context.rebalance_date = exchange_time 
           context.next_rebalance_date = context.rebalance_date + datetime.timedelta(days=context.rebalance_days)      
           print(context.next_rebalance_date)

def rebalance(context,data,exchange_time):    
    for sid in context.sids:
        if sid in data:
            order_target_percent(sid, context.sids[sid])

def has_open_orders(data,context):               
    # Only rebalance when we have zero pending orders.
    has_orders = False
    for stk in data:
        orders = get_open_orders(stk)
        if orders:
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=stk)  
            has_orders = True
    return has_orders           
There was a runtime error.

Hi guys,

I've stumbled upon this post and I have to say it looks very interesting. I've been investing in UPRO and while I've made some handsome returns, I am still looking for a good hedge strategy for if things take a turn for the worst. Can anyone comment on their view regarding how this algorithm will be affected if te fed keeps raising interest rates? Thanks!

Hey Rokus,

I am similar thoughts about TQQQ (3X NADAQ). The strategy I am considering is using rolling call options 6 weeks out. Purchase them in the money (about $20 bucks below current price. ( ie I bought a March 16 160 call last week for $23.07 , TQQQ was at $174ish). These options track TQQQ pretty well (delta = 0.85+) while giving you some downside protection (12%). The idea is to have about 10-15% invested in the option and the rest cash.

I figure this is a good strategy because on a major market correction you will only lose your option. While still having almost full benefit of the shares moving up. Best part is after a setback you still have money to reinvest. I started considering this strategy when I read that simulating the TQQQ back to event like 2008 it would have lost 93% of its value.

SPY MA200 Check added to the original algo:

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
''' 
    This algorithm defines a target long-only diversified portfolio and rebalances 
    it at a user-specified frequency.
'''

import datetime
import pytz
import pandas as pd
import numpy as np
from zipline.utils.tradingcalendar import get_early_closes

def initialize(context):
    # Define the instruments in the portfolio:
    context.sids = {
        sid(38533): 0.5,
        sid(38294): 0.5,
    }
    
    # Define the benchmark (used to get early close dates for reference).
    context.spy           = sid(8554)
    
    start_date = context.spy.security_start_date
    end_date   = context.spy.security_end_date
    
    # Initialize context variables the define rebalance logic:
    context.rebalance_date = None
    context.next_rebalance_Date = None
    context.rebalance_days = 90
    
    # Get the dates when the market closes early:
    context.early_closes = get_early_closes(start_date, end_date).date

    set_commission(commission.PerTrade(cost=1))

def handle_data(context, data):
    # Get the current exchange time, in local timezone: 
    exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # If it is rebalance day, rebalance:
    if context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
       # If we are in rebalance window but there are open orders, wait til next minute
       if has_open_orders(data,context) == True:
            log.info('Has open orders, not rebalancing.')
       else:
           # If there are no open orders we can rebalance.
           rebalance(context, data, exchange_time) 
           log.info('Rebalanced portfolio to target weights at %s' % exchange_time)

           # Update the current and next rebalance dates
           context.rebalance_date = exchange_time 
           context.next_rebalance_date = context.rebalance_date + datetime.timedelta(days=context.rebalance_days)      
           print(context.next_rebalance_date)

def rebalance(context,data,exchange_time):
    h200 = data.history(context.spy, "close", 200, "1d")
    ma200 = np.mean(h200)
    price = data.current(context.spy, 'price')
    
    for sid in context.sids:
        if sid in data:
            if price > ma200:
                order_target_percent(sid, context.sids[sid])
            else:
                order_target_percent(sid, 0)

def has_open_orders(data,context):               
    # Only rebalance when we have zero pending orders.
    has_orders = False
    for stk in data:
        orders = get_open_orders(stk)
        if orders:
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=stk)  
            has_orders = True
    return has_orders
There was a runtime error.

Hi,
Nice strategy here.
What abou using EMA200 on S&P500 to know when to get in or out in UPRO ?