Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
First Algorithm, CAPM Trading

Hi Everyone,

This is my first algorithm/post, so let me know what you think!

Basically how the algorithm works is it uses the CAPM model to predict what the asset's return should be (in this case, stock in Bank of America).

If today's returns did not quite meet the expectation of the CAPM model, hold a portfolio of only the stock. If the asset returns more than what the CAPM model predicted, then switch to holding a portfolio of only the safer market ETF. Lastly, if the CAPM is predicting negative returns, hedge your bets and hold half stock and half ETF.

The assumption is that the returns on the asset are likely to gravitate toward what the CAPM is predicting. So if it's coming up short, then there's still room to move upward, and vice-versa.

This definitely could use some work, but on the handful of stocks I ran backtests on, it seems to do a decent job.

Any and all tips would be appreciated!

5 responses

Thanks for sharing.
You'll want to watch that doubling up on order_target_percent(). If you go in 100% on one leg, you'd want to exit fully the other leg. Unless you're prepared to manage a leveraged account.
Also, you might use a fifo to only keep X numbers of returns against which you perform your linreg. See the code below.
Lastly, (and I'll have to dig into your use of the coeffs from polyfit) you'd want to employ a basket and then allocate your account out to those legs that were showing high beta (linreg slope).

import numpy  
import collections

Periods = 10

def initialize(context):  
    # Declare stock and market portfolio  
    context.security = symbol('BAC')  
    context.market = symbol('SPY')  
    # Lists for storing market data  
    context.X = collections.deque(maxlen=Periods)  
    context.Y = collections.deque(maxlen=Periods)  
def handle_data(context, data):  
    # Append returns to lists  
    context.X.append(data[context.market].returns())  
    context.Y.append(data[context.security].returns())  
    if len(context.Y) >= Periods and len(context.X) >= Periods:  
        x = numpy.array(context.X)  
        y = numpy.array(context.Y)  
        # Calculate CAPM  
        beta = numpy.polyfit(x, y, 1)  
        capm = beta[0] * context.X[-1] + beta[-1]  
        if capm > context.Y[-1]:  
            order_target_percent(context.security, 1)  
            order_target_percent(context.market, 0)  
        elif 0 < capm < context.Y[-1]:  
            order_target_percent(context.security, 0)  
            order_target_percent(context.market, 1)  
        elif capm < 0:  
            order_target_percent(context.security, 0.5)  
            order_target_percent(context.market, 0.5)  

Hi,

Thanks for the advice!

I had been meaning to employ a technique that would manage a portfolio of several stocks, and adjust itself based on which stocks where showing more dramatic CAPM predictions. However, I wasn't entirely sure about the most efficient approach. Any tips?

As for using fifo to estimate beta, do you think there's something to be said about the estimate's accuracy? Sure, using the last 10 periods would paint a much more honest picture of the current trend, but with such a small sample size, wouldn't you potentially risk having a less trustworthy model/estimate? I suppose you would have to strike a balance between sample size and relevance when choosing the number of periods.

The below might work for your multiple security technique. It doesn't rank them, but you could add that by using an relative number rather than 1.0, and then sort() 'ing them. There are various ranking posts here somewhere... As to the duration of returns for testing, maybe 21 days (a month) or 63 (a quarter)? or a combination of two or three?

[Edit: added exit collection to ensure no leverage]

import numpy  
import collections

Periods = 10

def initialize(context):  
    # Declare stock and market portfolio  
    context.securities = symbols('SPLV', 'XLB', 'XLU', 'XLY')  
    context.market = symbol('SPY')  
    # Lists for storing market data  
    context.Ys = {}  
    context.X = collections.deque(maxlen=Periods)  
    for security in context.securities:  
        context.Ys[security] = collections.deque(maxlen=Periods)  
def handle_data(context, data):  
    # Append returns to market list  
    context.X.append(data[context.market].returns())  
    x = numpy.array(context.X)  
    # Append returns to securities lists  
    eligibleSecurities = {}  
    inEligibleSecurities = {}  
    for security in context.securities:  
        if (data[security]):  
            context.Ys[security].append(data[security].returns())  
            if len(context.Ys[security]) >= Periods and len(context.X) >= Periods:  
                y = numpy.array(context.Ys[security])  
                # Calculate CAPM  
                beta = numpy.polyfit(x, y, 1)  
                capm = beta[0] * context.X[-1] + beta[-1]  
                if capm > context.Ys[security][-1]:  
                    eligibleSecurities[security] = 1.0  
                else:  
                    inEligibleSecurities[security] = 1.0  
    eligibleCount = float(len(eligibleSecurities))  
    totalEntryCount = float(len(context.securities))  
    # Buy each eligible security  
    for security in eligibleSecurities:  
        order_target_percent(security, eligibleSecurities[security] / totalEntryCount)  
    for security in inEligibleSecurities:  
        order_target_percent(security, 0)  
    # Buy the market for the remainder of the account  
    order_target_percent(context.market, (totalEntryCount - eligibleCount) / totalEntryCount)  

Thanks!

Hi Dan,

I was just wondering what assumptions you made to calculate CAPM in this way:

capm = beta[0]*context.X[-1] + beta[-1].

I would also like to know why you used polyfit to calculate the beta, I would have assumed it better to use the cov(x,y)/var(y) as per a least squares regression.

I love the idea of using CAPM to spot undervalued assets and your algo works great!
(I am a beginner so apologies if I have missed something obvious).

Regards,

George