Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
All Season Fund - Scotts Investments. Daily rebalancing with fixed target weights

http://www.scottsinvestments.com/all-season-etf/

Anyone can give me tips to change it to quarterly/yearly rebalancing?

12 responses

Just noticed a mistake in the rebalancing part. Here are the fixed results:

For periodic rebalancing you can use something along the lines of

def rebalance(data,context,target_weight,sid):  
    if pd.datetools.isBMonthEnd(date):  
        Your rebalance code  

This will give you monthly rebalance, of course you need to either set a global date variable in handle data or pass an additional arg to rebalance, which is stored in the bar data

Hi Bernd,

A simple way to rebalance monthly would be to rebalance the first of the month when

context.month != get_datetime().month  

I've attached a backtest that shows you it in action, I've also put all the weights into a Python dictionary to make it a bit easier to call the rebalance() function

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.

Thanks Seong, your ideas helped me very much. They helped me setting up the closed form minimum correlation variant. Will have to take a look into the scipy optimizers to form one using weight constraints so I can get a long only variant as well.

Heres how you use Scipy to optimize:

import scipy.optimize as opt

def cost_func(weights):  
    your function to minimize here (example)  
    return -1 * (mu/ sigma) 

syms = [list of your trading universe]  
bounds = [(0, 1) for x in syms] #0 to 1 bounds to prevent leverage or shorts  
constraint = ({'type':'eq', 'fun': lambda weights: weights.sum() - 1}) #constrain weights such that they sum to 1.00

fmin = opt.minimize(cost_func, weights, method = 'SLSQP', bounds=bounds, constraints = c) #You have to use SLSQP for bound-constrained optimization  
w = Series(fmin.x, index= syms) #create series with percentage weights indexed by sym

for sym in w.dropna().index: #iterate through and buy positions  
    target_percent(data[sym].sid, w[sym]) #Not sure if this function carries over from zipline, however this orders the percentage given by fmin  

Clearly, you will need to modify this to suit your needs but this is the general workflow. Just be careful in designing your cost function such that there exists a solution. Also more stringent constraints will make it harder to find a solution. You should also raise a Value Error if the optimization doesn't work, as the above simply ignores it and will continue on.

Thanks, I will play around with that. When I tried to use it yesterday I had some problems with passing on the covariance matrix as extra args because it interpreted every row/colums (not sure which one) as a single argument.

Are you trying to use the minimum variance portfolio?

Yes, tried something like this:

def solve_weights(C):  
        def fitness(W, C):  
            return (dot(dot(W,C),W)  
        n = len(C)  
        W = ones([n])/n  
        b_ = [(0.,1.) for i in range(n)]  
        c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. })  
        optimized = scipy.optimize.minimize(fitness, W, args = (C), method='SLSQP', constraints=c_, bounds=b_)  
        if not optimized.success:  
                raise BaseException(optimized.message)  
        return optimized.x  

But the fitness function wouldn't accept my covariance matrix (C). Still kinda new to numpy/scipy. Usually use matlab for that kinda stuff.

Well for the global minimum variance portfolio there is a numerical solution, that will be far faster and therefore Scipy isn't necessary. However should you choose otherwise, there really is no need to pass covariance, as it isn't changing between calls to fitness. if your fitness function is inside handle_data, your covariance matrix will still be in the scope.

your solve weights function really isn't necessary;

def fitness(W):  
     return (dot(dot(W,C),W)  

n = len(C)  
W = ones([n])/n  
b_ = [(0.,1.) for i in range(n)]  
c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. })  
optimized = scipy.optimize.minimize(fitness, W, method='SLSQP', constraints=c_, bounds=b_)  
if not optimized.success:  
    raise ValueError(optimized.message)  
return optimized.x  

The numerical solution is

in pseudo code
inv(cov).dot(1) / 1T.dot(inv(cov).dot(1))

I know, I already used that. But the closed from solution doesn't allow for weight constraints which it what I want to accomplish using the optimizer.

Ah I see, I guess I briefly lost sight of the issue at hand. But hopefully the optimization routine works for you

Bernd - Did you ever get the long-only variant coded?