Back to Community
Futures trend reversion algo

This algo uses recently released Futures API to trade various futures contracts on different asset classes. The logic is relatively simple, and please see the comments in code. It trades intermittently and as a result the average leverage is low, and the maximum gross leverage is currently set to 2 through Optimizer API constraint .

Clone Algorithm
1120
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
####################################################################
# Futures momentum reversion trade algorithm
# By Naoki Nagai, May 2017
#####################################################################
import numpy as np
import scipy as sp
from quantopian.algorithm import order_optimal_portfolio
import quantopian.experimental.optimize as opt

def initialize(context):
    # Futures to be traded by the algorithm, by asset class
    context.futures_by_assetclass = {
        'equities' : [
            'SP',    # S&P 500 Futures (US large cap)
            'ER',    # Russell 2000 Mini (US smalll cap)
            'NK',    # Nikkei 225 Futures (Japan)
            'EI',    # MSCI Emerging Markets Mini (Emerging)
       ],
        'fixedincome': [
            'TU',    # 2yr Tbill
            'TY',    # TNote 10 yr
            'US',    # TBond 30 yr
            'ED',    # Eurodollar
         ],
        'currencies' : [
            'EC',    # Euro
            'JE',    # Japanese YEN
            'SF',    # Swiss franc
            'ME',    # Mexican Peso
        ],     
        'commodities' :[
            'CL',    # Light Sweet Crude Oil
            'GC',    # Gold
            'NG',    # Natural gas
            'CN',    # Corn
        ],
    }
    
    # This holds all continuous future objects as an array
    context.futures = []
    for assetclass in context.futures_by_assetclass:
        for future in context.futures_by_assetclass[assetclass]:
             context.futures.append(continuous_future(future))
    
    # Window length for the trend. The algo checks the trend in this interval    
    context.window = 63    # 63 days = one quarter
    
    # This arbitrary value determines the weight of futurues long and short
    context.multiplier = 2250.
    
    # Max leverage the algo can take
    context.maxleverage = 2.0
    
    # How certain you want to be the trend is there.  Null hypothesis probability
    context.pvalue = 0.15
    
    # Rebalance every day, 30 minutes after market open
    schedule_function(func=rebalance, 
                      date_rule=date_rules.every_day(), 
                      time_rule=time_rules.market_open(minutes=30))
    
    # Record exposure by asset class everyday
    schedule_function(record_exposure, 
                      date_rules.every_day(), 
                      time_rules.market_close())

def rebalance(context, data):
    # Calculate slopes for each futures
    prediction = calc_slopes(context, data)
    
    # Get target weights to futures contracts based on slopes
    target_weights = get_target_weights(context, data, prediction)
    
    # Exposure is noted for logging and record() plotting
    context.exposure = {}
    text = ''
    for contract in target_weights:
        context.exposure[contract.root_symbol] = target_weights[contract]
        if target_weights[contract] != 0:
            text += "\n%+3.1f%% \t%s \t(%s)" % (target_weights[contract]*100, contract.symbol, contract.asset_name)
    if text == '':
        text = '\nNo positions to take'
    log.info('Target position of today:' + text)
    
    # Rebalance portfolio using optimaize API
    order_optimal_portfolio(
        opt.TargetPortfolioWeights(target_weights),
        constraints=[opt.MaxGrossLeverage(context.maxleverage),],
        universe=target_weights
    )

def calc_slopes(context, data):
    # Initialize output
    prediction = {}
    
    # Get pricing data of continuous futures
    all_prices = data.history(context.futures, 'price', context.window + 1, '1d')
    
    # Calculate daily returns for each continuous futures
    all_returns = all_prices.pct_change()[1:]
    
    # for each future, run regression to underestand the trend of price movement
    for future in context.futures:
        
        # Y-axis is the daily return
        Y = np.array(all_returns[future])

        # X-axis is -3, -2, -1, 0...
        X = np.array(range(-len(Y)+1,1))

        # Then, we get a and b where Y = a X + b
        coef = sp.stats.linregress(X, Y)
        
        # Initialize
        prediction[future] = 0
        
        # Return trend exists i.e. price momentum is accelerating with high probability
        if (coef.pvalue < context.pvalue):
            
            # Price momentumm is clear. Speed and acceleration is in same direction
            if (coef.slope * coef.intercept > 0.):
                
                # Then, predict the price trend should reverse
                prediction[future] = -coef.slope * context.multiplier
            
    return prediction

def get_target_weights(context, data, prediction):
    
    # Target weights per contract 
    target_weights = {}
    
    total = 0.
    for future in context.futures:
        total += prediction[future]
        
    # Target weight for the most traded actual futures contract
    for future in context.futures:
        
        # Get the contract from the continuous futures object
        contract = data.current(future, 'contract')
        
        # If contract is tradable, assign weight
        if contract and data.can_trade(contract):
            target_weights[contract] = prediction[future] / max(total,1.0)
    
    return target_weights

def record_exposure(context, data):
    # Record net exposure to different asset classes for tracking
    for assetclass in context.futures_by_assetclass:
        
        # We add weights by asset class
        asset_weight = 0.
        for future in context.exposure:
            if future in context.futures_by_assetclass[assetclass]:
                asset_weight += context.exposure[future]
        
        # Plot exposure in asset class
        record(assetclass, asset_weight)        
    
    # Record gross leverage
    record(leverage = context.account.leverage)

    
    
There was a runtime error.
16 responses

Awesome, great example of using futures plus the optimize API.

Disclaimer

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

Cloned. This will save time. Thank you for sharing Naoki!

Why can't this be used in the paper trading on Quantopian? Keep getting the message: There was a problem deploying your algorithm to live trading: Futures not allowed in live trading. The changes I made worked fine throughout the backtests..

Thanks for your help!

Hi Brandon,

Futures trading, while available in research and backtesting, is currently not available for live trading. We're working on enabling a futures based contest and being able to make allocations to futures based strategies. We're also looking into adding futures functionality to Pyfolio.

Thanks for the quick reply!
Do you guys have a timeline on this? Or is it still quite far away?

We currently do not have a timeline for release of live trading for futures. Sorry I can't provide more info at this time. We prefer not to overpromise anything too early, so our next steps will be to complete the features I mentioned earlier and then determine what is the next most important addition.

When running backtest on 2017 data, there is an error:

KeyError: 1074201706  
There was a runtime error on line 69.  

Any tip how to resolve it?

Thank you Naoki for sharing.

I can't really understand what is context.multiplier = 2250. I saw this numer is multiply by the slope to get the weight but why did you choose 2250?

Thanks for your help

Hi Peter, have you had any luck in understanding the error? I get the same but not sure what it refers too

Hello Peter & Matteo,

We identified an issue with ER pricing data that causes the KeyError you mentioned. This is currently in our queue, but unfortunately I cannot offer a timeline for a resolution.

In the meantime, you can avoid this error by removing ER from your universe of tradable futures.

Apologies for any inconvenience.

Disclaimer

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

The Error may have been caused by the fact that the Russell 2000 returned to CME Group July 10th, 2017.
The ticker on many platforms changed from /TF to /RTY

What would it take to get this in plain language "trading rules"?

I can't follow all this complicated code...

The OOS performance of this strategy is flat.

Matt,

I still getting:

There was a runtime error.
KeyError:1074201706

Line: 97 incalc_slopes

    all_prices = data.history(context.futures, 'price', context.window + 1, '1d')  

Any tip how you resolve it?

Vladimir - just drop ER from the futures' list.

hi

many thanks for providing this code!

I ran it and noticed that almost every day it tries to short the JPY futures and it fails to do so:
2007-01-18 23:00 WARN Your order for -26 shares of JEH07 has been partially filled. 1 shares were successfully sold. 25 shares were not filled by the end
2007-01-19 23:00 WARN Your order for -25 shares of JEH07 failed to fill by the end of day and was canceled.

why is that?
is it not liquid enough? for 26 contracts?

and again same problem in 2015
2015-12-01 23:00 WARN Your order for -10 shares of JEZ15 failed to fill by the end of day and was canceled.
2015-12-02 23:00 WARN Your order for -10 shares of JEZ15 failed to fill by the end of day and was canceled.