Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Batch Transform help

Hey all, I'm just getting started here, I would really appreciate some help figuring out the batch_transform functionality.

My objective is to grab the last 21 days of a pair of stocks, then calculate the ratio of the pair, and then calculate a z-score (Current ratio - average ratio)/(Std of ratio). I'll eventually place an order in both names depending on the zscore, but I'm not worried about that right now. Here is the logic of what I want to do, but I'm not sure how to reference the columns in the dataframe. ratio = prices['19920']/prices['33217'] is wrong, not sure what the syntax is.

Thanks!

import numpy

def initialize(context):  
    context.qqq = sid(19920)  
    context.qqqx = sid(33217)  
    context.prices = []  


@batch_transform(window_length=21)  
def calc_zscore(datapanel, portfolio):  
    prices = datapanel['price']  
    ratio = prices['19920']/prices['33217']  
    zscore = (ratio[20]-ratio.mean())/numpy.std(ratio)  
    return zscore

def handle_data(context, data):  
    if calc_zscore(data, context.portfolio) > 1:  
        order(context.qqq,-100)  
        order(context.qqqx,100)  
7 responses

Mathew,

A little confused on what you're doing, do you want to take the ratio of each day in the last 21 days?

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.

Seong,
Yes, I want a new array of daily ratios, to then calculate average ratio, and standard deviation of the daily ratios. I don't know how to reference the dataframe columns

Thanks

Hi Matthew,

The appropriate syntax would be datapanel['price][sid(x)] and you can obtain an array of daily ratios using numpy.divide(x,y)

I've attached an example of what this would look like.

@batch_transform(window_length=21)  
def calc_zscore(datapanel, portfolio):  
    prices = datapanel['price']  
    # Reference the sid (e.g. sid(19920)) and use numpy.divide to get an array  
    ratio = numpy.divide(prices[sid(19920)],prices[sid(33217)])  
    log.debug(ratio)  
    zscore = (ratio[20]-ratio.mean())/numpy.std(ratio)  
    return zscore  

Let's me know if that's what you're looking for

This is exactly what I was looking for, thanks for the help!

Matthew,

It would be good to also have a check in your method because until the first window_length is met (21 days) calc_zscore can't return a zscore so here's a snippet with a check (if data is None: return)

@batch_transform(window_length=21)  
def calc_zscore(datapanel, portfolio):  


    if data is None: return


    prices = datapanel['price']  
    # Reference the sid (e.g. sid(19920)) and use numpy.divide to get an array  
    ratio = numpy.divide(prices[sid(19920)],prices[sid(33217)])  
    log.debug(ratio)  
    zscore = (ratio[20]-ratio.mean())/numpy.std(ratio)  
    return zscore  

Thanks for all the help guys, built a pretty basic version of the tool:

import numpy

def initialize(context):  
    context.pair1 = sid(19920)  
    context.pair2 = sid(33217)  
    context.invested = False  
    context.L = False  
    context.S = False  
@batch_transform(window_length=64)  
def calc_zscore(datapanel, portfolio, context):  
    prices = datapanel['price']  
    ratio = numpy.divide(prices[context.pair1],prices[context.pair2])  
    avg_ratio = numpy.average(ratio[:len(ratio)-1])  
    current_ratio = ratio[len(ratio)-1]  
    std_ratio = numpy.std(ratio[:len(ratio)-1])  
    zscore = (current_ratio - avg_ratio)/(std_ratio)  
    return zscore  
def handle_data(context, data):  
    zscore = calc_zscore(data, context.portfolio, context)  
    #So I don't trade in initial 63 day measurement period  
    if zscore is None:  
        zscore = 0  
    #Making positions approximately dollar-neutral  
    shares_pair1 = (context.portfolio.cash / 2) / data[context.pair1].price  
    shares_pair2 = (context.portfolio.cash / 2) / data[context.pair2].price  
    if not context.invested:  
        #If ratio is one standard deviation greater than average, short the numerator, buy the denominator  
        if (zscore > 1):  
            order(context.pair1,-shares_pair1)  
            order(context.pair2,shares_pair2)  
            context.invested = True  
            context.S = True  
        #If ratio is one standard deviation less than average, buy the numerator, short the denominator  
        elif (zscore < -1):  
            order(context.pair1,shares_pair1)  
            order(context.pair2,-shares_pair2)  
            context.invested = True  
            context.L = True  
    else:  
        #Exit short position if ratio returns to normal levels  
        if (zscore < 0.5 and context.S):  
            order(context.pair1,-context.portfolio.positions[context.pair1].amount)  
            order(context.pair2,-context.portfolio.positions[context.pair2].amount)  
            context.invested = False  
            context.S = False  
        #Exit long position if ratio returns to normal levels  
        elif (zscore > -0.5 and context.L):  
            order(context.pair1,-context.portfolio.positions[context.pair1].amount)  
            order(context.pair2,-context.portfolio.positions[context.pair2].amount)  
            context.invested = False  
            context.L = False