'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 |
