Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Pairs trading Implementation

Hello,

I met some problems when implementing a simple pairs trading algorithm, wondered if anyone could assist me with some of my codes. The algorithm is easy, I have detected sid(27809) and sid(28145) were high correlated for the former half year(Jan 1 - July 31). What I want to do contains 3 steps:

Step 1: normalized each stock's price, by dividing each day's price by the first day(Jan 1)'s price.

Step 2: get difference of these two stock's price difference, like diff(stock1 - stock2), let's call this line by diff-line

Step 3: get two Bollinger bands (one with 1 times variation, the other with 2 times variation) of the diff-line, when diff-line gets above the 2 times upper band, long stock2 short stock1 by same amount of money, and when abs-line touch the 1 times upper bank, end the investment. And use the same way when diff-line gets lower bands.

I have tried to implemented as follows, according to a former pairs trading strategy on this website, but unfortunately unfamiliar with the library in Quantopian. Hope someone could help me, thanks in advance.

def initialize(context):  
    context.stock1 = sid(27809)  
    context.stock2 = sid(28145)

# Will be called on every trade event for the securities you specify.  
def handle_data(context, data):  
    diff = data[context.stock1]/data[context.stock1][0] - data[context.stock2]/data[context.stock2][0]  
    deviation = diff.stddev(5)  
    if(deviation != None):  
        mean = diff.mavg(5)  
        zscore = (diff.price - mean) / deviation  
        if(zscore > 2.0):  
            order(context.stock1, -100)  
            order(context.stock2, 200)  
        elif(zscore < -2.0):  
            order(context.stock1, 100)  
            order(context.stock2, -200)  
        elif(zscore > -1.0 or zscore < 1.0):  
            reduce_position(context.stock1, context.portfolio, 100)  
            reduce_position(context.stock1, context.portfolio, 200)  
            log.info("Positions reduced")

def reduce_position(stock, portfolio, abs_quantity):  
    """  
    decrease exposure, regardless of position long/short.  
    buy for a short position, sell for a long.  
    """  
    pos_amount = portfolio.positions[stock].amount  
    if pos_amount > 0:  
        order(stock, -1 * abs_quantity)  
    elif pos_amount < 0:  
        order(stock, abs_quantity)

3 responses

I've created a similar pairs trading strategy in link below.

The only difference I made is that, I looked at a 20-day moving average period to calculate mean and standard deviation. I also set close trades at the time of purchase. So, for instance, if I bought stock1 and sold stock2 with a ratio of 0.5, I would decide them on the close trade for when the ratio crossed some threshold (for example, 0.6).

I think you may be able to use this as a base.

https://www.quantopian.com/posts/coke-vs-pepsi-an-integration-trade

Thanks Branko, it's really kind of you, I will try that.

Hi Weiyi,

I think it makes more sense to consider a ratio of the two stock prices as Branko did, instead of looking at the absolute difference in the two prices. However, for future reference, here is one way you could record the price of a security at the start of trading:

def initialize(context):  
    context.stock1 = sid(27809)  
    context.stock2 = sid(28145)  
    context.stock1_start_price = None  
    context.stock2_start_price = None   

def handle_data(context, data):  
    if not context.stock1_start_price:  
        context.stock1_start_price = data[context.stock1].price  
    if not context.stock2_start_price:  
        context.stock2_start_price = data[context.stock2].price  

Also, check out this portion of Branko's code (it's very cool):

@batch_transform(refresh_period=1, window_length=20)  
def avgstd(datapanel,context):  
    """ returns mean and standard devaition of KO/PEP over the last 20 days"""  
    a_price = np.array(datapanel['price'][context.stocks[0]])  
    b_price = np.array(datapanel['price'][context.stocks[1]])  
    if a_price is not None and b_price is not None:  
        spread = a_price/b_price  
        return (spread.mean(), spread.std())  
    else:  
        return None  

The batch transform is a great tool in Quantopian. It's a decorator (which is a concept I found a bit confusing the first time I saw one, so if my explanation is totally unneeded, forgive me). Basically what decorators do is take a function as an argument and return a new function. So on Quantopian, the batch transform decorator lets you really easily define the data to be for a certain time window, instead of a shorter time period. When you're working with Bollinger Bands, this is very handy!