Back to Community
Experiments with GEM and Dual Momemtum

Ok, here is my back test of Gary Antonucci's GEM strategy. I selected EEM SPY EFA and SHY ETFS due to long histories.
It is not exactly beating the SPY long term but it has dodged some nasty drawdowns.

I borrowed the code from another user, who sells everything every month and often ends up buying the same etf back. I not sure how to fix that with this object model. I am a python newbie.

Clone Algorithm
130
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
# For this example, we're going to write a simple momentum script.  
# When the stock goes up quickly, we're going to buy; 
# when it goes down we're going to sell.  
# Hopefully we'll ride the waves.

# To run an algorithm in Quantopian, you need two functions: 
# initialize and handle_data
from operator import itemgetter

def initialize(context):
    context.topMom = 2
    context.rebal_int = 3
    context.lookback = 250
    set_symbol_lookup_date('2002-08-01')
    #context.stocks = symbols('SPY', 'VOO', 'IVV')
    #context.stocks = symbols('XLF', 'XLE', 'XLU', 'XLK', 'XLB', 'XLP', 'XLY', 'XLI', 'XLV', 'BIL')
    #context.stocks = symbols('SPY', 'VEA', 'BIL')
    #context.stocks = symbols('SPY', 'EFA', 'BND', 'VNQ', 'GSG', 'BIL')
    # wealthfront
    #context.stocks = symbols('VTI','VEA','VWO','VIG','VNQ','LQD','EMB','XLE')
    context.stocks = symbols('SPY','EFA','EEM','SHY')
    #context.stocks = symbols('DDM', 'MVV', 'QLD', 'SSO', 'UWM', 'SAA', 'UYM', 'UGE', 'UCC', 'UYG', 'RXL', 'UXI', 'DIG', 'URE', 'ROM', 'UPW', 'BIL')
 
    schedule_function(rebalance,
                      date_rule=date_rules.month_start(),
                      time_rule=time_rules.market_open())
    

def rebalance(context, data):
    #Create stock dictionary of momentum
    MomList = GenerateMomentumList(context, data)
    
    #sell all positions
    for stock in context.portfolio.positions:
        order_target(stock, 0)
    #create % weight
    
    #if data[spy].price < data[spy].mavg(200):
    #    order_target_percent(symbol('IEF'), 1)
    #    return
    
    weight = 0.95/context.topMom

    #buy
    for l in MomList:
        stock = l[0]
        if stock in data and data[stock].close_price > data[stock].mavg(200):
            order_percent(stock, weight)
    pass

    
def GenerateMomentumList(context, data):
    
    MomList = []
    price_history = history(bar_count=context.lookback, frequency="1d", field='price')
    
    for stock in context.stocks:
        now = price_history[stock].ix[-1]
        old = price_history[stock].ix[0]
        pct_change = (now - old) / old
        #if now > data[stock].mavg(200):
        MomList.append([stock, pct_change, price_history[stock].ix[0]])
       

    #sort in descending order, the price change (%)
        
    MomList = sorted(MomList, key=itemgetter(1), reverse=True)
    #return only the top "topMom" number of securities
    MomList = MomList[0:context.topMom]
    print (MomList[0][0].symbol)

    return MomList


def change(one, two):
    return(( two - one)/one)
    
def handle_data(context, data):
    pass
There was a runtime error.
3 responses

Sorry, my code was really bad. I've learned a lot since. For consolation, here is more efficient code. Believe it or not, it holds the same portfolio. Q has data starting 2002, so any code that looks back a year should start at 2003. If you start my new code and your code at 2003, they are equivalent.

Again, I apologize for my poor initial code. I have no doubt that I will regret publishing my newest code in the future as I learn more about Python and finance :P

I hope this algorithm was a useful learning experience for you though!

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

# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    set_symbol_lookup_date('2002-08-01')
    context.stocks = symbols('SPY','EFA','EEM','SHY')
    context.formation = 250
    context.port_size = 2
    
    context.month_count = 0
    
    schedule_function(
    rebalance,
    date_rules.month_end(days_offset=0),
    time_rules.market_close(minutes=5)
    )
    pass

def rebalance(context, data):
    #get daily historical prices
    h = history(context.formation, '1d', 'price')[context.stocks]
    #use dataframe resample to get monthly values
    h = h.resample('M', how='last')
    print h
    #first, get the pct_change() from the first date (0) in the dataframe and today (-1). Then splice the last row because the first row is all NaN. Drop all stocks with NaN then turn the dataframe into series. Series make it easier to sort.
    mom = h.iloc[[0,-1]].pct_change().tail(1).dropna(axis=1).squeeze()
    #order by return percentage, and take only the top N
    mom = mom.order().index[-context.port_size:]
    print mom
    #liquidate positions that are not in the buy list
    for s in context.portfolio.positions:
        if s not in mom:
            order_target_percent(s, 0)
    
    #get weight based on how many stocks are selected
    weight = 0.95/len(mom)
    
    #purchase stocks in the buy list
    for s in mom:
        if s in data:
            order_target_percent(s, weight)
    
# Will be called on every trade event for the securities you specify. 
def handle_data(context, data):
    record(leverage = context.account.leverage)
    pass
There was a runtime error.

thanks sharing, I did experiments, just to replace trading security .

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

# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    #set_symbol_lookup_date('2002-08-01')
    context.stocks = symbols('SPY','QQQ','EFA','IEV','TLT','GLD')
    context.formation = 250#30#250
    context.port_size = 2
    
    context.month_count = 0
    
    schedule_function(
    rebalance,
    date_rules.month_end(days_offset=0),
    time_rules.market_close(minutes=5)
    )
    pass

def rebalance(context, data):
    #get daily historical prices
    h = history(context.formation, '1d', 'price')[context.stocks]
    #use dataframe resample to get monthly values
    h = h.resample('M', how='last')
    print h
    #first, get the pct_change() from the first date (0) in the dataframe and today (-1). Then splice the last row because the first row is all NaN. Drop all stocks with NaN then turn the dataframe into series. Series make it easier to sort.
    mom = h.iloc[[0,-1]].pct_change().tail(1).dropna(axis=1).squeeze()
    #order by return percentage, and take only the top N
    mom = mom.order().index[-context.port_size:]
    print mom
    #liquidate positions that are not in the buy list
    for s in context.portfolio.positions:
        if s not in mom:
            order_target_percent(s, 0)
    
    #get weight based on how many stocks are selected
    weight = 0.95/len(mom)
    
    #purchase stocks in the buy list
    for s in mom:
        if s in data:
            order_target_percent(s, weight)
    
# Will be called on every trade event for the securities you specify. 
def handle_data(context, data):
    record(leverage = context.account.leverage)
    pass
There was a runtime error.

@Novice TAI Thanks for the code. Just wondering. Looks like it is possible to buy a stock with negative change over last year. i am correct? Also, i suggest we get away from bonds as they have had wonderful non repeatable alpha. We should go to cash if nothing has positive returns. Trouble is, i can't yet figure out how to do this in python. Any suggestion?