Back to Community
Newbie Seeking Help With First Algo

Hi,

I'm trying to replicate a fairly simple ETF strategy that I've been successfully using for the past seven years. Here's the logic

  • From a basket of ETFs , buy any ETF that closed above it's 200 MA at the end of the month
    and sell any ETF that closed below it's 200 MA at the end of the month

fairly simple , profitable, and avoids bear markets like in 2008 . I need help with the following :

  1. schedule_function

I want the algorithm to run only once a month, and make all the necessary buy and sell on the 1st day of the month.
I copied and paste the Month Start function from the help page, but as you can see , the transactions are still taking place although the month.

  1. converting" Daily Moving Average to Monthly moving average

Ideally, I would like to use the 10 month MA rather than the 200 MA. Is there a way to do so ?

The end goal is to completely stop trading this strategy manually, transfer my accounts to IB and run it automatically with Quantopian.

Thank you in advance for your help.

Cheers

Lionel

Clone Algorithm
10
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
# STOCKS VS. MA Strategy
# STATUS: INcomplete


#initialize the strategy 
def initialize(context):

    
    
    # Portfolio
    context.stocks = [sid(21638), sid(21644), sid(21646), sid(21647), sid(21648), sid(21649), sid(21650), sid(21651), sid(21524)]

    context.price={}
    
    context.weights = 0.99/len(context.stocks)
    
    # Run on a monthly basis
    schedule_function(
        myfunc,
        date_rules.month_start(days_offset=1),
        time_rules.market_open(minutes=15)
        )

def myfunc(context,data):
    pass
    
       
     
    
def handle_data(context, data):
    
    
    for stock in context.stocks:
        price = data[stock].price
        slowMA = data[stock].mavg(200)
    
   
  
        # LONG 
      
        if  price > slowMA   :
            order_target_percent(stock,context.weights) 
            
       
        # SELL   
        
        elif price < slowMA :
      
            order(stock,-(context.portfolio.positions[stock].amount)) 
            
            
        
             
          

          
          
          
There was a runtime error.
5 responses

Built a variation.
Just add more symbols to the "symbols("SPY")" call.
And you'll want to remove the "record(ClosePrice... " and "record(TenMonthMA..." lines at the bottom.

This uses a collection eligibleStocks to determine how many total to be in at one time. And then uses this to determine the divisor for full account leverage.

Clone Algorithm
38
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
import talib

def initialize(context):
    symbols("SPY")
    schedule_function(func=CalcIndicators, date_rule=date_rules.month_start())
    schedule_function(func=HandleExits,    date_rule=date_rules.month_start())
    schedule_function(func=HandleEntries,  date_rule=date_rules.month_start())
    
def handle_data(context, data):
    record(PositionCount = len([1 for stock in context.portfolio.positions if context.portfolio.positions[stock].amount > 0] ))
    record(SettledCash   = context.account.settled_cash)

def HandleEntries(context, data):
    eligibleStocks = []
    for stock in data:
        if (data[stock].close_price > data[stock].tenMonthMA):
            eligibleStocks.append(stock)

    eligibleStocksCount = float(len(eligibleStocks))                
    for stock in eligibleStocks:
        order_target_percent(stock, 1.0 / eligibleStocksCount)

def HandleExits(context, data):
    inEligibleStocks = []
    for stock in data:
        if (data[stock].close_price < data[stock].tenMonthMA):
            inEligibleStocks.append(stock)

    for stock in inEligibleStocks:
        order_target_percent(stock, 0.0)
        
def CalcIndicators(context, data):
    closes = history(200, "1d", "close_price").resample("1m")
    closes.dropna()
    tenMonthMA = closes.apply(talib.MA, timeperiod=10)
    
    for stock in data:
        data[stock].tenMonthMA = tenMonthMA[stock][-1]
        record(TenMonthMA   = data[stock].tenMonthMA)
        record(ClosePrice   = data[stock].close_price)        
There was a runtime error.

Hi Market Tech

Thank you so much for your help and quick response. This is exactly what i was looking for. I really appreciate this.

Cheers

Lionel

Compared to all this qualified covariance quant quaffing recently debuting here simplistic technical balancing strats just don't hold a statistically significant candle. However, they are tractable for the layman (me), and are entertaining to toy with. To wit, this variation uses a technique by which when 59% of the instruments in the group have failed to make their monthly filter -- the whole group gets out. That is, when 41% show a waning market, it's time to exit, stage right.
Also note the rebalance period is 7 days before the EOM. As previously been discussed here, the last week of the month is the critical month in which to perform one's examinations and reassessments.
You'll also note the multiple calls to symbols(). Works.
Also added is a Trailing stop.

Note: By posting the code in the body it become searchable. Code in the attached strats is not.

import talib

DayPeriods = 170  
MonthPeriods = 8  
TrailingStopPct = 10

def initialize(context):  
    set_symbol_lookup_date("2015-01-01")  
    #symbols("IDU","IYC","IYE","IYG","IYH","IYJ","IYK","IYM","IWY","PXI")  
    symbols("SPY","DIA","QQQ")  
    symbols("XLP","XLU","XLV","XLY","AGG","XLB","XLE","XLF","XLI","XLK")  
    symbols("DVY","VIS","VDC","VXF","IYT")  
    context.hedge = symbol("TLT")  
    schedule_function(func=CalcIndicators, date_rule=date_rules.month_end(days_offset=7))  
    schedule_function(func=HandleExits,    date_rule=date_rules.month_end(days_offset=7))  
    schedule_function(func=HandleEntries,  date_rule=date_rules.month_end(days_offset=7))  
    schedule_function(TrailingStop,        date_rule=date_rules.every_day())

def handle_data(context, data):  
    record(PositionCount = len([1 for stock in context.portfolio.positions if context.portfolio.positions[stock].amount > 0] ))  
    record(SettledCash   = context.account.settled_cash)

def HandleEntries(context, data):  
    eligibleStocks = []  
    for stock in data:  
        if (stock == context.hedge): continue  
        if (data[stock].close_price > data[stock].tenMonthMA):  
            eligibleStocks.append(stock)

    eligibleStocksCount = float(len(eligibleStocks))  
    if (len(eligibleStocks) <= len(data) * .6):  
        order_target_percent(context.hedge, .5)  
        print(">>> EnterHedge {0} @ {1:<7.2f}".format(context.hedge.symbol, data[context.hedge].close_price))  
        return

    for stock in eligibleStocks:  
        order_target_percent(stock, 1.0 / eligibleStocksCount)  
        print("HandleEntries  {0} @ {1:<7.2f}".format(stock.symbol, data[stock].close_price))

    ### To here means we've re-entered an up trend, get rid of the hedge  
    if (context.portfolio.positions[context.hedge].amount):  
        order_target_percent(context.hedge, 0.0)  
        print("<<< ExitHedge  {0} @ {1:<7.2f}".format(context.hedge.symbol, data[context.hedge].close_price))

def HandleExits(context, data):  
    inEligibleStocks = []  
    for stock in data:  
        if (stock == context.hedge): continue  
        if (data[stock].close_price < data[stock].tenMonthMA):  
            inEligibleStocks.append(stock)

    if (len(inEligibleStocks) > len(data) * .4):  
        for stock in data:  
            if (stock == context.hedge): continue  
            if (context.portfolio.positions[stock].amount):  
                order_target_percent(stock, 0.0)  
                print("HandleExits    {0} @ {1:<7.2f}".format(stock.symbol, data[stock].close_price))

def CalcIndicators(context, data):  
    closes = history(DayPeriods, "1d", "close_price").resample("1m")  
    closes = closes.dropna()  
    tenMonthMA = closes.apply(talib.MA, timeperiod=MonthPeriods)

    for stock in data:  
        data[stock].tenMonthMA = tenMonthMA[stock][-1]  
        record(TenMonthMA   = data[stock].tenMonthMA)  
        record(ClosePrice   = data[stock].close_price)        

def TrailingStop(context, data):  
    for stock in data:  
        if context.portfolio.positions[stock].amount > 0:  
            offset = (TrailingStopPct * data[stock].close_price) / 100.0  
            if ('trailingStop' in data[stock]):  
                if (data[stock].close_price < data[stock].trailingStop):  
                    order_target_percent(stock, 0.0)  
                    print("TrailingStop   {0} @ {1:<7.2f}".format(stock.symbol, data[stock].close_price))  
                    del data[stock].trailingStop  
                    continue  
                else:  
                    data[stock].trailingStop = max(data[stock].trailingStop, data[stock].low - offset)  
            else:  
                data[stock].trailingStop = data[stock].low - offset  

Updated above.

From Florent's C.'s use of the treasury security TLT, (thank you Florent), I figured that when this strategy was all out, that would be a good time to be all in (or at least 1/2 in) for this treasury ETF.

And it turns out it's a great hedge. So, everytime this strategy get fully out, we place a 1/2 position trade into TLT. We let this ride until either it gets trail stopped out, or we get back in with 60%+ with the other ETFs. Now, due to the daily trading scheme and positions overlapping, at times, this strat leverages a bit, but not much. And if run in Minutely mode, would eliminate any hedge by ensuring full exit before full entry (hedge to group or group to hedge).

Lionel, thanks for the impetus. Looks like a promising strategy. I hope it fairs well for you.

Hi Market Tech

Thank you for all your help with this, and will update this thread with the real time results.

Cheers