4

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.

Bill P
  • 3,622
  • 10
  • 20
  • 32
user113321
  • 41
  • 1
  • 4
  • The statement `i > df.close.shift(-1)` is comparing a number to a series, the result is a series of booleans, that's why it doesn't now what to do. – Yuca Oct 05 '18 at 19:04
  • Any idea how to deal with it? – user113321 Oct 06 '18 at 05:58
  • Look for solutions [like this](https://github.com/voice32/stock_market_indicators/blob/master/indicators.py). – TravelTrader Apr 01 '19 at 02:03
  • Where you able to find a solution to this ,Did you verify the output with TV chart outputs. search github on balance volume there are plenty of working code.However this is not giving the same output as tradingivew. – Marx Babu Jun 03 '19 at 07:14

4 Answers4

6

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!

Olli
  • 61
  • 1
  • 2
6

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()
SuperCodeBrah
  • 2,874
  • 2
  • 19
  • 33
1

this is another answer maybe help someone:

obv = (df.volume * (~df.close.diff().le(0) * 2 - 1)).cumsum()
moraei
  • 1,443
  • 1
  • 16
  • 15
0

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)
Raphael G
  • 9
  • 3