Back to Community
Rebalance using a percentage hurdle

Hey everyone,

Could someone please point me to the exact code line to use in order to re-balance using a percentage hurdle. Let me give you a couple examples:
If my objective is 70% Equity and 30% FI. I would like to trigger the rebalancing only if the allocation deviate by more than X%.
Alternatively, at the quarterly rebalancing date, I would like to trigger the rebalancing only if the allocation deviate by more than X%.

Thanks for your kind help!
Cheers,
Bert

18 responses

@Bertrand B.,

If you outline your intended trading sequence there is probably already somebody's algo that nearly matches your request.

Day 1
Buy 70% of account in SPY, Buy 30% in Florins? Flintstone dollars? Retain, cash Float? Or 30% in Franks International?

Day 1 + X
Market rises 5% in first period.
Current balance: 73% SPY, 27% Franks
Sell 3% of SPY, Buy 3% of Franks

Day 1 + 2X
Market falls 10% in second period
Current balance: 58% SPY, 42% Franks
Buy 12% SPY, Sell 12% Franks

Is that about right?

My bad FI stands for Fixed Income to me.

Yes you are correct in the reasoning!

@Bertrand B., Fixed Income, of course - silly me.

Quantopian has made it easy for you then.

order_target_percent() will perform admirably for you.

Here's a strategy to get you started. It rebalances once a month, but there are no doubt strats here built by others that rebalance at different periodicities.

The log output looks like this (prior to rebalancing):

2011-09-01 PRINT Equities % of account 67.54  
2011-09-01 PRINT FixedInc % of account 32.41  
2011-10-03 PRINT Equities % of account 66.80  
2011-10-03 PRINT FixedInc % of account 32.85  
2011-11-01 PRINT Equities % of account 72.06  
2011-11-01 PRINT FixedInc % of account 27.94  
2011-12-01 PRINT Equities % of account 70.53  
2011-12-01 PRINT FixedInc % of account 29.46  
Clone Algorithm
41
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
PercentEquities = 70.0

def initialize(context):
    set_symbol_lookup_date('2015-01-01')
    context.Equities    = symbols("JKL","FAB","FVD","FTA","EZY","EPS","DEF","PRF","PWV","FDL","IWD")
    context.FixedIncome = symbols("TLO","VGSH","SCHO","PLW","EDV","GOVT","IEI","SHY","IEF","TLT")
    schedule_function(Rebalance, date_rule=date_rules.month_start())

def handle_data(context, data):  
    record(Leverage     = context.account.leverage)
    record(AccountValue = context.portfolio.positions_value + context.portfolio.cash)
    
def Rebalance(context, data):  
    accountValue = context.portfolio.positions_value + context.portfolio.cash
    
    pctEquities = PercentEquities / 100.0
    pctFixedInc = 1.0 - pctEquities
        
    #
    # Equities rebalance
    #    
    eligible = []
    accruedValue = 0.0
    for stock in context.Equities:    
        if (stock in data):
            eligible.append(stock)
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
            
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, pctEquities / eligibleCount)

    pctOfAccount = accruedValue / accountValue * 100.0
    record(EquityPct = pctOfAccount)
    print("Equities % of account {0:>5.2f}".format(pctOfAccount))
    
    #
    # Fixed Income rebalance
    #    
    eligible = []
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            eligible.append(stock)
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
            
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, pctFixedInc / eligibleCount)
        
    pctOfAccount = accruedValue / accountValue * 100.0
    record(FixedIncomePct = pctOfAccount)
    print("FixedInc % of account {0:>5.2f}".format(pctOfAccount))
There was a runtime error.

Thanks a lot for the details Market Tech! I will play around with it and do more backtesting!

It would be great to have an optimizer by either return or volatility for each variables such as here in our case the % hurdle for rebalancing.

@Bertrand B., I'm reworking it now to trigger a rebalance on when the %'s surpass some threshold. I'll post the new strat momentarily here.

Thanks a lot, I had a hard time getting it to work. As you can order to have X% yet trigger that order based on a delta to the original target is tricky.

Also I would very much appreciate if you had a beginner's guide to this coding language for me to familiarize myself. I got many ideas and would like to better understand the language to try them out!

Thanks again!

Version #2 with no date based rebalancing, only threshold rebalancing.

Logs look like this:

2011-12-05 PRINT Rebalancing ...  
2011-12-05 PRINT Equities % of account 72.01  
2011-12-05 PRINT FixedInc % of account 28.03  
2012-03-16 PRINT Rebalancing ...  
2012-03-16 PRINT Equities % of account 72.11  
2012-03-16 PRINT FixedInc % of account 27.20  
2012-05-14 PRINT Rebalancing ...  
2012-05-14 PRINT Equities % of account 67.86  
2012-05-14 PRINT FixedInc % of account 31.83  

Per learning the platform/language -- you would best be served reading the other posts that also ask questions about starting out. One I see is just now being discussed...

Clone Algorithm
41
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
PercentEquities  = 70.0
PercentThreshold = 2.0


def initialize(context):
    set_symbol_lookup_date('2015-01-01')
    context.Equities    = symbols("JKL","FAB","FVD","FTA","EZY","EPS","DEF","PRF","PWV","FDL","IWD")
    context.FixedIncome = symbols("TLO","VGSH","SCHO","PLW","EDV","GOVT","IEI","SHY","IEF","TLT")
    schedule_function(RecordPercentages)
    schedule_function(Rebalance)

def handle_data(context, data):  
    record(Leverage     = context.account.leverage)
    record(AccountValue = context.portfolio.positions_value + context.portfolio.cash)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Rebalance(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
    pctEquities = PercentEquities / 100.0
    pctFixedInc = 1.0 - pctEquities
        
    #
    # Equities rebalance
    #    
    eligible = []
    accruedValue = 0.0
    for stock in context.Equities:    
        if (stock in data):
            eligible.append(stock)
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
    
    #
    # Test for rebalance trigger, if less than threshold - exit
    #
    pctOfAccount = accruedValue / accountValue * 100.0
    if (abs(PercentEquities - pctOfAccount) < PercentThreshold):
        return
    
    print("Rebalancing ...")
    
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, pctEquities / eligibleCount)
    
    print("Equities % of account {0:>5.2f}".format(pctOfAccount))
    
    #
    # Fixed Income rebalance
    #    
    eligible = []
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            eligible.append(stock)
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
        
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, pctFixedInc / eligibleCount)
    
    pctOfAccount = accruedValue / accountValue * 100.0
    print("FixedInc % of account {0:>5.2f}".format(pctOfAccount))
    
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def RecordPercentages(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
        
    # Equities 
    accruedValue = 0.0
    for stock in context.Equities:    
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
    
    pctOfAccount = accruedValue / accountValue * 100.0
    record(EquityPct = pctOfAccount)
    
    # Fixed Income
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
        
    pctOfAccount = accruedValue / accountValue * 100.0
    record(FixedIncomePct = pctOfAccount)
There was a runtime error.

Thanks a lot for your help! I managed to increase by 14% my P&L over the 6 year period.
All other metrics like sharp/sortino and DD are better too!

Vladimir Y. shows how easy the Q makes such techniques. Proper use of order_target_percent is a super 4th level language encapsulator that alleviates tons of extra code. I imagine Captain Picard saying "Make it so" and order_target_percent does the work.

Here'a a variation of the above. It dynamically shifts the 70/30 depending on the rebalance drift.

Clone Algorithm
41
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
PercentEquities   = 70.0
PercentThreshold  = 2.0
PercentAdjustment = 5.0


def initialize(context):
    set_symbol_lookup_date('2015-01-01')
    context.Equities    = symbols("JKL","FAB","FVD","FTA","EZY","EPS","DEF","PRF","PWV","FDL","IWD")
    context.FixedIncome = symbols("TLO","VGSH","SCHO","PLW","EDV","GOVT","IEI","SHY","IEF","TLT")
    schedule_function(RecordPercentages)
    schedule_function(Rebalance)
    context.CurrentPercentEquities = PercentEquities

def handle_data(context, data):  
    record(Leverage     = context.account.leverage)
    record(AccountValue = context.portfolio.positions_value + context.portfolio.cash)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Rebalance(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
    pctEquities = context.CurrentPercentEquities / 100.0
    pctFixedInc = 1.0 - pctEquities
        
    #
    # Equities analysis
    #    
    eligible = []
    accruedValue = 0.0
    for stock in context.Equities:    
        if (stock in data):
            eligible.append(stock)
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
    
    #
    # Test for rebalance trigger, if less than threshold - exit
    #
    pctOfAccount = accruedValue / accountValue * 100.0
    if (abs(context.CurrentPercentEquities - pctOfAccount) < PercentThreshold):
        return
    
    #
    # Dynamically adjust the percentages based on which direction the market recently went
    #
    if (pctOfAccount == 0.0): 
        pass
    elif (pctOfAccount < context.CurrentPercentEquities):
        context.CurrentPercentEquities += PercentAdjustment
        if (context.CurrentPercentEquities > 90):
            context.CurrentPercentEquities = 90
    else:
        context.CurrentPercentEquities -= PercentAdjustment
        if (context.CurrentPercentEquities < 50):
            context.CurrentPercentEquities = 50
            
    # Resample %'s as they may have changed
    pctEquities = context.CurrentPercentEquities / 100.0
    pctFixedInc = 1.0 - pctEquities
    
    print("Rebalancing... Current % of equities {0:>5.2f}".format(context.CurrentPercentEquities))
    
    #
    # Equities rebalance
    #        
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, pctEquities / eligibleCount)
    
    print("Equities % of account {0:>5.2f}".format(pctOfAccount))
    
    #
    # Fixed Income rebalance
    #    
    eligible = []
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            eligible.append(stock)
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
        
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, pctFixedInc / eligibleCount)
    
    pctOfAccount = accruedValue / accountValue * 100.0
    print("FixedInc % of account {0:>5.2f}".format(pctOfAccount))
    
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def RecordPercentages(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
        
    # Equities 
    accruedValue = 0.0
    for stock in context.Equities:    
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
    
    pctOfAccount = accruedValue / accountValue * 100.0
    record(EquityPct = pctOfAccount)
    
    # Fixed Income
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
        
    pctOfAccount = accruedValue / accountValue * 100.0
    record(FixedIncomePct = pctOfAccount)
There was a runtime error.

Thanks everyone for the help to get the re-balancing right. I would now like to add a second rule to this algorithm.

Re-balancing is great to tremendously increase our risk numbers yet there is one I would like to control better and that is my drawdown. It is currently 19% which isn't that much from an absolute point of view but I believe this could be controlled. Moreover the ETF I use are quite recent and are only tested in a very bullish market.

Any idea how I could now use a 200 period MA to exit the market (but only for the fixed income or the equity portion reaching the 200MA).
So it would be like this:
Re-balance Equity/Fixed Income at X:X ratio when it deviate by more than X%
Close the position (either Equity only, Fixed Income only or both) if it reaches its 200MA. Buyback when it comes back at the 200MA level.

I am worried of intensive trading there (buying and selling for a while when the stock is around its 200MA). Any experience on that? suggestions?

Thanks again!
Bert

Bertrand B., You may want to provide specific conditions or additional constraints as to how to use your MA filter. See the new code in this strategy. I've added more and more code, but I'd rather segment a strategy into chunks of obvious functionality than to string it all together. I don't think Vladimir will be very happy with the additions.

One way to reduce whipsaws using moving averages as filters or signals generators is to use two different periodicities. Use the longer one to enter a long position, and a shorter (marginally shorter) to trigger exits. For instance:

The black arrow is an exit on a faster MA. The red arrow is a false entry on that same fast MA. But if you only enter when price closes above the longer MA then you'd avoid that poor entry. And there's another avoided false entry later on too.

False entry image

Clone Algorithm
41
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
import zipline

PercentEquities   = 70.0
PercentThreshold  = 2.0
LongMAPeriods     = 200

def initialize(context):
    set_symbol_lookup_date('2015-01-01')
    context.equitiesList    = symbols("SPY","DIA","QQQ","IWM")
    context.fixedIncomeList = symbols("TLT","IEI","SHY","IEF")
    context.Equities        = {}
    context.FixedIncome     = {}

    schedule_function(CalculateLongMA)
    schedule_function(HandleInterExits)
    schedule_function(RecordPercentages)
    schedule_function(Rebalance)

def handle_data(context, data):
    record(Leverage     = context.account.leverage)
    record(AccountValue = context.portfolio.positions_value + context.portfolio.cash)
    
    #
    # Reconcile securities to make sure, when the come online, that they're in the collections
    #
    for stock in data:
        if (stock in context.equitiesList and stock not in context.Equities):
            context.Equities[stock] = zipline.protocol.SIDData(stock)
            continue
        if (stock in context.fixedIncomeList and stock not in context.FixedIncome):
            context.FixedIncome[stock] = zipline.protocol.SIDData(stock)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Rebalance(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
    pctEquities  = PercentEquities / 100.0
    pctFixedInc  = 1.0 - pctEquities
        
    eligibleEquities  = [];  eligibleFixed  = []
    accruedEquities   = 0.0; accruedFixed   = 0.0
    pctOfAcctEquities = 0.0; pctOfAcctFixed = 0.0
    
    #
    # Equities and Fixed analysis
    #    
    for stock in context.Equities:
        if (stock not in data):
            continue
        accruedEquities += context.portfolio.positions[stock].amount * data[stock].close_price
        eligibleEquities.append(stock)

    for stock in context.FixedIncome:
        if (stock not in data):
            continue
        accruedFixed += context.portfolio.positions[stock].amount * data[stock].close_price       
        eligibleFixed.append(stock)
    
    pctOfAcctEquities = accruedEquities / accountValue * 100.0
    pctOfAcctFixed    = accruedFixed    / accountValue * 100.0

    #
    # Test for rebalance trigger, if less than threshold - exit
    #
    if (abs(PercentEquities - pctOfAcctEquities) < PercentThreshold):
        return
    
    print("Rebalancing... ")
    print("Equities % of account {0:>5.2f}".format(pctOfAcctEquities))
    print("FixedInc % of account {0:>5.2f}".format(pctOfAcctFixed))
       
    #
    # If not above the 200 period SMA, do not include in rebalance
    #
    for stock in eligibleEquities:
        if (not context.Equities[stock].AboveLongMA):
            eligibleEquities.remove(stock)
            print("Equity long MA filtered {0}".format(stock.symbol))
    #
    # If not above the 200 period SMA, do not include in rebalance
    #
    for stock in eligibleFixed:        
        if (not context.FixedIncome[stock].AboveLongMA):
            eligibleFixed.remove(stock)
            print("FixedInc long MA filtered {0}".format(stock.symbol))
        
    #
    # Equities rebalance
    #        
    eligibleCount = float(len(eligibleEquities))
    for stock in eligibleEquities:
        order_target_percent(stock, pctEquities / eligibleCount)
    #
    # Fixed Income rebalance
    # 
    eligibleCount = float(len(eligibleFixed))
    for stock in eligibleFixed:
        order_target_percent(stock, pctFixedInc / eligibleCount)
    
    
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def RecordPercentages(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
        
    # Equities 
    accruedValue = 0.0
    for stock in context.Equities:    
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
    
    pctOfAccount = accruedValue / accountValue * 100.0
    record(EquityPct = pctOfAccount)
    
    # Fixed Income
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
        
    pctOfAccount = accruedValue / accountValue * 100.0
    record(FixedIncomePct = pctOfAccount)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def HandleInterExits(context, data):
    #
    # Early exits for those securities which fall below the 200 period SMA
    #
    for stock in context.Equities:
        if (context.Equities[stock].AboveLongMA):
            continue
        if (context.portfolio.positions[stock].amount):
            order_target_percent(stock, 0.0)
            print("Equity inter balance exit   {0}".format(stock.symbol))
    for stock in context.FixedIncome:
        if (context.FixedIncome[stock].AboveLongMA):
            continue
        if (context.portfolio.positions[stock].amount):
            order_target_percent(stock, 0.0)
            print("FixedInc inter balance exit {0}".format(stock.symbol))
    
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def CalculateLongMA(context, data):
    closeDeck = history(LongMAPeriods, "1d", "close_price").dropna(axis=1)
    closeDeck = closeDeck[[sid for sid in closeDeck if sid in data]]
    sma       = closeDeck.apply(talib.MA, timeperiod = LongMAPeriods, matype = MAType.SMA).dropna()
    for stock in context.Equities:
        context.Equities[stock].AboveLongMA = False
        if (stock in sma):
            context.Equities[stock].AboveLongMA    = True if data[stock].close_price > sma[stock][-1] else False
    for stock in context.FixedIncome:
        context.FixedIncome[stock].AboveLongMA = False
        if (stock in sma):
            context.FixedIncome[stock].AboveLongMA = True if data[stock].close_price > sma[stock][-1] else False

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class MAType():
    SMA   = 0; EMA   = 1; WMA   = 2; DEMA  = 3; TEMA  = 4;    
    TRIMA = 5; KAMA  = 6; MAMA  = 7; T3    = 8
There was a runtime error.

Great idea about using two different MA! I will look at your code more in depth also and try to understand why is happening.

As a side note. I would like to know if this is possible to have monthly contribution rather than a fixed initial capital (like in your example). As I am looking to find an optimal strategy to use in a pension plan like system (constant contribution). I believe than understanding the strategy from a contribution point of view is simply a game changer. Any drawdown are great opportunities to buy-in at cheaper prices. If this is possible then I believe that we wouldn't need any "safety net" like the two MAs.

Looking forward hearing back from you about this point.

Thanks for all the contribution!
Bert

@Bertrand B., Periodic contributions is a great topic. You should raise it as a new forum post. I have not read anything about this ability but you're right, it's a natural one that should be allowable (by injecting X% or Y$,000 into an account's cash). One could model a variation by using a leverage factor. Let's say every January 1st, you "inject" 5% leverage on a $100k account ($5 grand). What'd you'd get was extra working capital just like you were contributing yearly to your IRA. The mechanism is actually quite easy to implement.

In this code you'd add a context.Leverage factor which would start out at 1.0:

    #  
    # Equities rebalance  
    #  
    eligibleCount = float(len(eligibleEquities))  
    for stock in eligibleEquities:  
        order_target_percent(stock, (pctEquities / eligibleCount) * context.Leverage)  
    #  
    # Fixed Income rebalance  
    #  
    eligibleCount = float(len(eligibleFixed))  
    for stock in eligibleFixed:  
        order_target_percent(stock, (pctFixedInc / eligibleCount) * context.Leverage)  

And once a year (watching the year of the date change) boost the leverage:

    context.Leverage += .05  

Do you believe we could make it monthly instead of yearly? I am sure that the contribution frequency will make a significant difference here.

Bertrand B., As you can imagine, the periodicity could be of any time scale. But using this leverage trick is only a hack (although the end results may be the same). You should ask in another post about periodic contribution injection modeling.

@Bertramd B., Here's a variation on your technique. Instead of a fixed 70/30 I use a dynamic calculator using the Aroon indicator (highest high period within X periods, lowest low period within X periods). The total of these are assembled and then -- inverted -- this means that as equities climb in value, a smaller percentage is allotted to them. As they drop, a larger percentage.

I also included the leverage trick to simulate contributions. 1/2 percent per month. And the rebalance only occurs if the equity % is outside 70% -- this causes too much rebalancing, and so a modification will be required to only perform a more accurate test. Nice return though. Stable with a limited drawdown during 08. Adding a few more securities and cash equivalents might smooth it even more. I think one could trade this right now and be fairly confident it will return some decent results.

Clone Algorithm
132
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
import zipline

MaxPercentEquities = 70.0
MinPercentEquities = 30.0
PercentThreshold   = 10.0
AroonPeriods       = 7
MontlyContribution = .005 # 1/2 a percent / month

def initialize(context):
    set_symbol_lookup_date('2015-01-01')
    context.equitiesList    = symbols("SPY","QQQ")
    context.fixedIncomeList = symbols("TLT","LQD")
    context.Equities        = {}
    context.FixedIncome     = {}
    context.Leverage        = 1.0    
    context.PctEquitiesMultiplier = 1.0
    
    schedule_function(RecordPercentages)
    schedule_function(CalculatePortions, date_rule=date_rules.month_start())
    schedule_function(Rebalance, date_rule=date_rules.month_start())

def handle_data(context, data):
    record(Leverage     = context.account.leverage)
    record(AccountValue = context.portfolio.positions_value + context.portfolio.cash)
    
    #
    # Reconcile securities to make sure, when the come online, that they're in the collections
    #
    for stock in data:
        if (stock in context.equitiesList and stock not in context.Equities):
            context.Equities[stock] = zipline.protocol.SIDData(stock)
            continue
        if (stock in context.fixedIncomeList and stock not in context.FixedIncome):
            context.FixedIncome[stock] = zipline.protocol.SIDData(stock)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Rebalance(context, data):  
    if (get_open_orders()):
        return
    
    accountValue = context.portfolio.positions_value + context.portfolio.cash
    #
    # Adjust the % equities by this dynamic, Aroon based multiplier
    #
    targetPctEquities  = (MaxPercentEquities * context.PctEquitiesMultiplier) / 100.0
    targetPctFixedInc  = 1.0 - targetPctEquities
        
    eligibleEquities  = [];  eligibleFixed  = []
    accruedEquities   = 0.0; accruedFixed   = 0.0
    actualPctEquities = 0.0; actualPctFixed = 0.0
    
    #
    # Equities and Fixed analysis
    #    
    for stock in context.Equities:
        if (stock not in data):
            continue
        accruedEquities += context.portfolio.positions[stock].amount * data[stock].close_price
        eligibleEquities.append(stock)

    for stock in context.FixedIncome:
        if (stock not in data):
            continue
        accruedFixed += context.portfolio.positions[stock].amount * data[stock].close_price       
        eligibleFixed.append(stock)
    
    actualPctEquities = accruedEquities / accountValue * 100.0
    actualPctFixed    = accruedFixed    / accountValue * 100.0

    #
    # Test for rebalance trigger, if less than threshold - exit
    # Note: this needs work to follow the actual % changes and only react on X% change
    #
    if (abs(MaxPercentEquities - actualPctEquities) < PercentThreshold):
        return
    
    print("Rebalancing... ")
    print("Equities % of account {0:>5.2f}".format(actualPctEquities))
    print("FixedInc % of account {0:>5.2f}".format(actualPctFixed))
        
    #
    # Equities rebalance
    #        
    eligibleCount = float(len(eligibleEquities))
    if (targetPctEquities == targetPctEquities):
        for stock in eligibleEquities:
            order_target_percent(stock, (targetPctEquities / eligibleCount) * context.Leverage)
    #
    # Fixed Income rebalance
    # 
    eligibleCount = float(len(eligibleFixed))
    if (targetPctFixedInc == targetPctFixedInc):
        for stock in eligibleFixed:
            order_target_percent(stock, (targetPctFixedInc / eligibleCount) * context.Leverage)
    
    #
    # Simulate a fractional account contribution
    #
    context.Leverage += MontlyContribution

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def RecordPercentages(context, data):  
    if (get_open_orders()):
        return

    accountValue = context.portfolio.positions_value + context.portfolio.cash
        
    # Equities 
    accruedValue = 0.0
    for stock in context.Equities:
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
    
    pctOfAccount = accruedValue / accountValue * 100.0
    record(EquityPct = pctOfAccount)
    
    # Fixed Income
    accruedValue = 0.0
    for stock in context.FixedIncome:
        if (stock in data):
            accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price
        
    pctOfAccount = accruedValue / accountValue * 100.0
    record(FixedIncomePct = pctOfAccount)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def CalculatePortions(context, data):
    lowDeck  = history(AroonPeriods + 1, '1d', 'low')
    highDeck = history(AroonPeriods + 1, '1d', 'high')
    portionForEquities = 0; portionForFixed = 0
    
    for stock in context.Equities:
        aroon = talib.AROON(highDeck[stock], lowDeck[stock], timeperiod = AroonPeriods)
        portionForEquities += (aroon[1][-1] - aroon[0][-1])
    portionForEquities /= len(context.Equities)
    
    for stock in context.FixedIncome:
        aroon = talib.AROON(highDeck[stock], lowDeck[stock], timeperiod = AroonPeriods)
        portionForFixed += (aroon[1][-1] - aroon[0][-1])
    portionForFixed /= len(context.FixedIncome)
    
    #
    # Build a 0.0 to 1.0 multiplier against which the percent of equities will be modified
    #
    context.PctEquitiesMultiplier = (((200 - (portionForEquities - portionForFixed)) / 4.0) / 200.0)
    #
    # Force a minimum of equity participation
    #    
    context.PctEquitiesMultiplier += (MinPercentEquities / 100.0)
    record(PctEquitiesMultiplier = context.PctEquitiesMultiplier)
    
There was a runtime error.

Market Tech,
Have you created an updated version of your Aroon based algorithm? I am going to try to update its depreciation issues, but thought I'd first check to see if you'd already done so.

I get these emails on threads I've replied to, but I'm no longer active. Good luck, you'll need it. MT