'Calculating stocks's On Balance Volume (OBV) in python

I am doing my first project in python. I have a pandas dataframe called df with two columns "close" and "volume". I want to calculate/obtain OBV column based on first two columns. The formula is given below;

If the close is above the prior close then: Current OBC = Previous OBC + 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)

close volume  OBC
30    2500    nan
32    3000    5500
25    2700    2800
35    4000    6800
20    1000    5800

I am using this code:

for i in df.close[1:]:
    if i > df.close.shift(-1):
        df["OBC"] = df.volume + df.OBC.shift(-1) 
    elif i < df.close.shift(-1):
        df["OBC"] = df.OBC.shift(-1) - df.volume
    else:
        df["OBC"] = df.OBC

and i get this error:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

I have looked at this question but didn't get any help. Truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()

Beyond this error, I feel that code may run into trouble in calculating correct OBV. Any help will be of great value.



Solution 1:[1]

I dont know why you are getting the error but here is a solution to get OBV:

np.where(df['close'] > df['close'].shift(1), df['volume'], 
np.where(df['close'] < df['close'].shift(1), -df['volume'], 0)).cumsum()

It is also faster, which is good if you are gonna do many iterations!

Solution 2:[2]

I'm basing this on Olli's answer, but I think it's a slightly cleaner solution:

obv = (np.sign(df['close'].diff()) * df['volume']).fillna(0).cumsum()

Solution 3:[3]

this is another answer maybe help someone:

obv = (df.volume * (~df.close.diff().le(0) * 2 - 1)).cumsum()

Solution 4:[4]

Usually, it is not recommended to iterate through all your rows. You could do column operation as below:

# Creating "Vol+-" is a temporary column where,
# Vol is positive for Close > Previous Close
# Vol is negative for Close < Previous Close
# Zero if Close == Previous Close

df.loc[df["Close"] > df["Close"].shift(1), "Vol+-"] = df["Volume"]  
df.loc[df["Close"] < df["Close"].shift(1), "Vol+-"] = df["Volume"] * (-1)  
df.loc[df["Close"] == df["Close"].shift(1), "Vol+-"] = 0  

 
df["OBV"] = self._indicators_df["Vol+-"].cumsum()  
df.drop(["Vol+-"], axis=1, inplace=True)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Olli
Solution 2 SuperCodeBrah
Solution 3 moraei
Solution 4 Raphael G