Back to Community
XIV strategy that survived 2008 (don't miss this one!)

This simple strategy is inspired by Gary Antonacci's GEM strategy. The strategy uses a dual momentum model with a risk-on securities basket containing SPY (US equities), EFA (international developed equities), EEM (emerging market equities) and XIV (inverse volatility). AGG (bonds) is used as the absolute momentum filter and TLT (long-term treasuries) is used as the out-of-market asset. The strategy has 4 holding slots of 25% each and can be filled by any of the securities mentioned above.

One small difference: this strategy uses a 1-month look-back period and only trades quarterly vs. Antonacci's 12-month look-back period and monthly trading. The reasoning is explained in this post.

Here's the link to this strategy on Portfolio Visualizer for confirmation: http://bit.ly/2w0dqog

Below is a pre-2008 backtest of the strategy on Portfolio Visualizer using synthetic XIV data found here. Read this for the caveats of using synthetic XIV/VXX data. Note that the stats are based on monthly return data.

Enjoy! Please help me improve it if you can. If anything is off or weird, please let me know as well! For any collaboration request or general consultation, feel free to email: [email protected]

Clone Algorithm
764
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
#DUAL MOMENTUM QUARTERLY ROTATION
#Developers: Kory Hoang & Mohammed Khalfan
#For collaboration requests, please email: [email protected]

def initialize(context):
    context.currently_holding = []
    schedule_function(clear, date_rules.month_end(), time_rules.market_open())
    schedule_function(rebalance, date_rules.month_end(), time_rules.market_close(minutes=5))
    schedule_function(record_vars, date_rules.every_day(), time_rules.market_close())
    set_benchmark(symbol('SPY'))
    
def clear(context, data):

    today = get_datetime()
    if not (today.month == 12 or today.month == 3 or today.month == 6 or today.month == 9):
        return
    print("1" + str(context.currently_holding))
    for s in context.currently_holding:
        order_target_percent(s, 0)
    context.currently_holding = []   

def rebalance(context, data):
    today = get_datetime()
    if not (today.month == 12 or today.month == 3 or today.month == 6 or today.month == 9):
        return
    
    assets = symbols('XIV','SPY','EEM','EFA')
    safe = symbol('AGG')
    tlt = symbol('TLT')
    
    mom_period = 22 #22 days = 1 month 

    hist = data.history(assets, 'price', mom_period + 1, '1d')  
    mom = (hist.iloc[-1]/hist.iloc[0]) - 1.0  
    mom = mom.dropna()  
    top_assets = mom.sort_values().index[-4:]
    top_assets_values = mom.sort_values()[-4:]
    
    safe_percent = 0
    tlt_percent = 0
    
    for x in range(len(top_assets)):
        if top_assets_values[x] > 0:
            if data.can_trade(top_assets[x]):  
                order_target_percent(top_assets[x], 0.25)
                context.currently_holding.append(top_assets[x])
        else:
            hist = data.history(safe, 'price', mom_period + 1, '1d')  
            mom = (hist[-1]/hist[0]) - 1.0
            if mom > 0:
                safe_percent += 0.25
                if safe not in context.currently_holding: context.currently_holding.append(safe)
            else:
                tlt_percent += 0.25
                if tlt not in context.currently_holding: context.currently_holding.append(tlt)
                    
    top_up = 4 - len(top_assets)
    if top_up > 0:
        for x in range(top_up):
            hist = data.history(safe, 'price', mom_period + 1, '1d')  
            mom = (hist[-1]/hist[0]) - 1.0
            if mom > 0:
                safe_percent += 0.25
                if safe not in context.currently_holding: context.currently_holding.append(safe)
            else:
                tlt_percent += 0.25
                if tlt not in context.currently_holding: context.currently_holding.append(tlt)
    print(safe_percent, tlt_percent)
    if safe_percent > 0.0 and data.can_trade(safe):  
        order_target_percent(safe, safe_percent)                        
                        
    if tlt_percent > 0.0 and data.can_trade(tlt):  
        order_target_percent(tlt, tlt_percent)
        
    print("2" + str(context.currently_holding)  )

    
def record_vars(context, data):
    record(leverage = context.account.leverage)
There was a runtime error.
7 responses

indeed it's a low risk one. A lot improvement on performance can be made.

This one was created with institutional level scalability in mind, hence its low trading frequency

Hi Kory,
Very nice to look at your algos. Maybe a newbie question, but is there a way to see the total commissions paid per year of quarter? Also, is there a way to make the same algo without leverage to see the adjusted performance? I wanna know the applicability for a retail investor paying high commissions and no access to low interest leverage (or not interested in it anyway).

Thanks,
JD

Hi JD,

The current algo uses default commission and slippage. There is also no leverage used. To see the full effect of what commissions can do, use the below codes. I have 2 commission rates coded (one is commented out) that you can play with. One is for Interactive brokers and one is for TradeStation, both of which I have accounts with. You can set commissions to 0 and then compare it to other rates to see what the difference in performance would be.

The total amount of $ for variable commission rates like Interactive Brokers depends on the number of shares. For fixed rates like TradeStation, it's easier to figure out. Below pic shows how many trades the algo made in the backtest. Total TradeStation commissions for 2016 would have been about $5 x 27 trades = $135

Clone Algorithm
99
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
#DUAL MOMENTUM QUARTERLY ROTATION
#Developers: Kory Hoang & Mohammed Khalfan
#For collaboration requests, please email: [email protected]

def initialize(context):
    context.currently_holding = []
    schedule_function(clear, date_rules.month_end(), time_rules.market_open())
    schedule_function(rebalance, date_rules.month_end(), time_rules.market_close(minutes=5))
    schedule_function(record_vars, date_rules.every_day(), time_rules.market_close())
    set_benchmark(symbol('SPY'))
    
    
    set_commission(commission.PerShare(cost=0.005, min_trade_cost=1.00)) #Interactive Brokers' rate
    
    #set_commission(commission.PerTrade(cost=5)) #TradeStation's rate
 


def clear(context, data):

    today = get_datetime()
    if not (today.month == 12 or today.month == 3 or today.month == 6 or today.month == 9):
        return
    print("1" + str(context.currently_holding))
    for s in context.currently_holding:
        order_target_percent(s, 0)
    context.currently_holding = []   

def rebalance(context, data):
    today = get_datetime()
    if not (today.month == 12 or today.month == 3 or today.month == 6 or today.month == 9):
        return
    
    assets = symbols('XIV','SPY','EEM','EFA')
    safe = symbol('AGG')
    tlt = symbol('TLT')
    
    mom_period = 22 #22 days = 1 month 

    hist = data.history(assets, 'price', mom_period + 1, '1d')  
    mom = (hist.iloc[-1]/hist.iloc[0]) - 1.0  
    mom = mom.dropna()  
    top_assets = mom.sort_values().index[-4:]
    top_assets_values = mom.sort_values()[-4:]
    
    safe_percent = 0
    tlt_percent = 0
    
    for x in range(len(top_assets)):
        if top_assets_values[x] > 0:
            if data.can_trade(top_assets[x]):  
                order_target_percent(top_assets[x], 0.25)
                context.currently_holding.append(top_assets[x])
        else:
            hist = data.history(safe, 'price', mom_period + 1, '1d')  
            mom = (hist[-1]/hist[0]) - 1.0
            if mom > 0:
                safe_percent += 0.25
                if safe not in context.currently_holding: context.currently_holding.append(safe)
            else:
                tlt_percent += 0.25
                if tlt not in context.currently_holding: context.currently_holding.append(tlt)
                    
    top_up = 4 - len(top_assets)
    if top_up > 0:
        for x in range(top_up):
            hist = data.history(safe, 'price', mom_period + 1, '1d')  
            mom = (hist[-1]/hist[0]) - 1.0
            if mom > 0:
                safe_percent += 0.25
                if safe not in context.currently_holding: context.currently_holding.append(safe)
            else:
                tlt_percent += 0.25
                if tlt not in context.currently_holding: context.currently_holding.append(tlt)
    print(safe_percent, tlt_percent)
    if safe_percent > 0.0 and data.can_trade(safe):  
        order_target_percent(safe, safe_percent)                        
                        
    if tlt_percent > 0.0 and data.can_trade(tlt):  
        order_target_percent(tlt, tlt_percent)
        
    print("2" + str(context.currently_holding)  )

    
def record_vars(context, data):
    record(leverage = context.account.leverage)
There was a runtime error.

Thank you for posting your idea Kory. I know that you had posted a similar algo, using GLD instead of XIV.
Can you comment on making such change?
Thank you.

Also I observed that trading monthly / bimonthly makes the algo far less robust. Any insight behind this to share?

Thank you.

Obviously, this article didn't see the fateful day when Credit Swiss's XIV ETF blow up. I lost a lot of money and I had to put an investment policy unto myself no more leverage (if using derivative only notional principals can be traded).
If you want to know what I do. I do very simple things, invest prudently. www.uakdiversified.com