Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
OBV custom factor

Hi,

I am trying to implement an OBV custom factor by using a global context variable to accumulate the obv each day as it isn't possible to reference earlier values of factors in the pipeline as an input in the same way as the standard data such as close price, volume, etc. Note: the obv is a running total of the volume traded where the volume is added to the previous value if the price increased and subtracted if it decreased - see obv.

Unfortunately I am getting an error:
There was a runtime error.
ValueError: operands could not be broadcast together with shapes (7824,) (7826,) (7824,)
... USER ALGORITHM:259, in compute
context.obv += obv[-1]

Which looks like is because of trying add different arrays of different lengths into the accumulation variable context.obv (I guess this is because there are different stocks each day when the pipeline computes the values as some may cease being traded or new companies might be added etc). Is there a way to accumulate a value in a custom factor that depends on previous values each time the pipeline is called to compute a new value reliably? Also what if you want a running moving average on the accumulated value?

Thanks.

2 responses

Hi all, I thought could share this implementation in case is helpful for anyone (much credit to quantopian support)... A context variable accumulate an obv value is defined in the initialise method and then is referenced in the obv custom factor to get the obv each day as it depends on the previous result:



def initialize(context):  
    """  
    Called once at the start of the algorithm.  
    """  
    set_benchmark(sid(33579))  
    context.security_list = sid(33579)  
    context.obv = None  
    context.running_obv = None  
...


def OBV(context):  
    """  
    # If the closing price is above the prior close price then:  
    # Current OBV = Previous OBV + Current Volume  
    # If the closing price is below the prior close price then:  
    # Current OBV = Previous OBV  -  Current Volume  
    # If the closing prices equals the prior close price then:  
    # Current OBV = Previous OBV (no change)

    Parameters  
    ----------

    Returns  
    -------  
    zipline.CustomFactor : daily On Balance Volume (OBV) based on the price and volume of the previous day


    """  
    class OBV_(CustomFactor):

        inputs = [USEquityPricing.close, USEquityPricing.volume]  
        window_length = 2  
        def compute(self, today, assets, out, close, vol):  
            diff = close[-1] - close[-2]  
            diff_abs = np.absolute(diff)  
            price_dir = diff/diff_abs  
            obv = pd.Series((price_dir * vol)[-1], index=assets)  
            if(context.obv is None):  
                context.obv = obv  
                context.running_obv = obv  
            else:  
                context.obv = context.obv.add(obv, fill_value=0)  
                context.running_obv.append(obv)  
            out[:] = context.obv[assets]  
    return OBV_()  

I'm not sure if there is a straight forward way to calculate a running average on this factor without using another context or global variable(?) I was going to use another Panda variable to store the value of the obv each day and then set the moving average in another custom factor but this doesn't seem like a very good way to code it (e.g. the result would always require calling the obv custom factor before calling a running average on obv factor which seems a bit prone to errors)... Anyway hope this is useful for someone.
Cheers

Thanks Adrian, this helped me a lot - I ended up taking this approach in the attached notebook.

I'm using a CustomFactor with 2 outputs - First one is the 'original' calculation, second one is its average over the last 5 days.

(Am not using context at the moment, as I'm only playing in Research for now)

Cheers,
Chris