Revision b39a5c0c90f81590878221338e41a18ab9983041 authored by Dario Lopez Padial on 22 October 2019, 07:42:45 UTC, committed by Dario Lopez Padial on 22 October 2019, 07:42:45 UTC
1 parent e38add5
volume.py
# -*- coding: utf-8 -*-
"""
.. module:: volume
:synopsis: Volume Indicators.
.. moduleauthor:: Dario Lopez Padial (Bukosabino)
"""
import numpy as np
import pandas as pd
def acc_dist_index(high, low, close, volume, fillna=False):
"""Accumulation/Distribution Index (ADI)
Acting as leading indicator of price movements.
https://en.wikipedia.org/wiki/Accumulation/distribution_index
Args:
high(pandas.Series): dataset 'High' column.
low(pandas.Series): dataset 'Low' column.
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
clv = ((close - low) - (high - close)) / (high - low)
clv = clv.fillna(0.0) # float division by zero
ad = clv * volume
ad = ad + ad.shift(1, fill_value=ad.mean())
if fillna:
ad = ad.replace([np.inf, -np.inf], np.nan).fillna(0)
return pd.Series(ad, name='adi')
def on_balance_volume(close, volume, fillna=False):
"""On-balance volume (OBV)
It relates price and volume in the stock market. OBV is based on a
cumulative total volume.
https://en.wikipedia.org/wiki/On-balance_volume
Args:
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
df = pd.DataFrame([close, volume]).transpose()
df['OBV'] = np.nan
c1 = close < close.shift(1)
c2 = close > close.shift(1)
if c1.any():
df.loc[c1, 'OBV'] = - volume
if c2.any():
df.loc[c2, 'OBV'] = volume
obv = df['OBV'].cumsum()
if fillna:
obv = obv.replace([np.inf, -np.inf], np.nan).fillna(0)
return pd.Series(obv, name='obv')
def chaikin_money_flow(high, low, close, volume, n=20, fillna=False):
"""Chaikin Money Flow (CMF)
It measures the amount of Money Flow Volume over a specific period.
http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_money_flow_cmf
Args:
high(pandas.Series): dataset 'High' column.
low(pandas.Series): dataset 'Low' column.
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
n(int): n period.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
mfv = ((close - low) - (high - close)) / (high - low)
mfv = mfv.fillna(0.0) # float division by zero
mfv *= volume
cmf = (mfv.rolling(n, min_periods=0).sum()
/ volume.rolling(n, min_periods=0).sum())
if fillna:
cmf = cmf.replace([np.inf, -np.inf], np.nan).fillna(0)
return pd.Series(cmf, name='cmf')
def force_index(close, volume, n=2, fillna=False):
"""Force Index (FI)
It illustrates how strong the actual buying or selling pressure is. High
positive values mean there is a strong rising trend, and low values signify
a strong downward trend.
http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:force_index
Args:
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
n(int): n period.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
fi = close.diff(n) * volume.diff(n)
if fillna:
fi = fi.replace([np.inf, -np.inf], np.nan).fillna(0)
return pd.Series(fi, name='fi_'+str(n))
def ease_of_movement(high, low, close, volume, n=20, fillna=False):
"""Ease of movement (EoM, EMV)
It relate an asset's price change to its volume and is particularly useful
for assessing the strength of a trend.
https://en.wikipedia.org/wiki/Ease_of_movement
Args:
high(pandas.Series): dataset 'High' column.
low(pandas.Series): dataset 'Low' column.
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
n(int): n period.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
emv = (high.diff(1) + low.diff(1)) * (high - low) / (2 * volume)
emv = emv.rolling(n, min_periods=0).mean()
if fillna:
emv = emv.replace([np.inf, -np.inf], np.nan).fillna(0)
return pd.Series(emv, name='eom_' + str(n))
def volume_price_trend(close, volume, fillna=False):
"""Volume-price trend (VPT)
Is based on a running cumulative volume that adds or substracts a multiple
of the percentage change in share price trend and current volume, depending
upon the investment's upward or downward movements.
https://en.wikipedia.org/wiki/Volume%E2%80%93price_trend
Args:
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
n(int): n period.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
vpt = volume * ((close - close.shift(1, fill_value=close.mean())) / close.shift(1, fill_value=close.mean()))
vpt = vpt.shift(1, fill_value=vpt.mean()) + vpt
if fillna:
vpt = vpt.replace([np.inf, -np.inf], np.nan).fillna(0)
return pd.Series(vpt, name='vpt')
def negative_volume_index(close, volume, fillna=False):
"""Negative Volume Index (NVI)
http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:negative_volume_inde
The Negative Volume Index (NVI) is a cumulative indicator that uses the
change in volume to decide when the smart money is active. Paul Dysart
first developed this indicator in the 1930s. [...] Dysart's Negative Volume
Index works under the assumption that the smart money is active on days
when volume decreases and the not-so-smart money is active on days when
volume increases.
The cumulative NVI line was unchanged when volume increased from one
period to the other. In other words, nothing was done. Norman Fosback, of
Stock Market Logic, adjusted the indicator by substituting the percentage
price change for Net Advances.
This implementation is the Fosback version.
If today's volume is less than yesterday's volume then:
nvi(t) = nvi(t-1) * ( 1 + (close(t) - close(t-1)) / close(t-1) )
Else
nvi(t) = nvi(t-1)
Please note: the "stockcharts.com" example calculation just adds the
percentange change of price to previous NVI when volumes decline; other
sources indicate that the same percentage of the previous NVI value should
be added, which is what is implemented here.
Args:
close(pandas.Series): dataset 'Close' column.
volume(pandas.Series): dataset 'Volume' column.
fillna(bool): if True, fill nan values with 1000.
Returns:
pandas.Series: New feature generated.
See also:
https://en.wikipedia.org/wiki/Negative_volume_index
"""
price_change = close.pct_change()
vol_decrease = (volume.shift(1) > volume)
nvi = pd.Series(
data=np.nan, index=close.index, dtype='float64', name='nvi')
nvi.iloc[0] = 1000
for i in range(1, len(nvi)):
if vol_decrease.iloc[i]:
nvi.iloc[i] = nvi.iloc[i - 1] * (1.0 + price_change.iloc[i])
else:
nvi.iloc[i] = nvi.iloc[i - 1]
if fillna:
# IDEA: There shouldn't be any na; might be better to throw exception
nvi = nvi.replace([np.inf, -np.inf], np.nan).fillna(1000)
return pd.Series(nvi, name='nvi')
# TODO
def put_call_ratio():
# will need options volumes for this put/call ratio
"""Put/Call ratio (PCR)
https://en.wikipedia.org/wiki/Put/call_ratio
"""
# TODO
return
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...