Skip to content

Commit

Permalink
fixing ease of movement implementation and adding test: bukosabino#58
Browse files Browse the repository at this point in the history
  • Loading branch information
bukosabino committed Nov 7, 2019
1 parent 37cb28d commit b6f71b2
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 13 deletions.
3 changes: 2 additions & 1 deletion ta/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
from ta.tests.trend import (TestADXIndicator, TestCCIIndicator,
TestVortexIndicator)
from ta.tests.utils import TestGeneral
from ta.tests.volume import TestOnBalanceVolumeIndicator
from ta.tests.volume import (TestEaseOfMovementIndicator,
TestOnBalanceVolumeIndicator)
31 changes: 31 additions & 0 deletions ta/tests/data/cs-easeofmovement.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Date,High,Low,Volume,EMV,SMA_EMV
18-Jun-12,63.740000000,62.630000000,32178836.000000000,,
19-Jun-12,64.510000000,63.850000000,36461672.000000000,1.801069353,
20-Jun-12,64.570000000,63.810000000,51372680.000000000,0.014793855,
21-Jun-12,64.310000000,62.620000000,42476356.000000000,-2.884545934,
22-Jun-12,63.430000000,62.730000000,29504176.000000000,-0.913430017,
25-Jun-12,62.850000000,61.950000000,33098600.000000000,-1.849020805,
26-Jun-12,62.700000000,62.060000000,30577960.000000000,-0.041860216,
27-Jun-12,63.180000000,62.690000000,35693928.000000000,0.761894292,
28-Jun-12,62.470000000,61.540000000,49768136.000000000,-1.737858938,
29-Jun-12,64.160000000,63.210000000,44759968.000000000,3.565686195,
2-Jul-12,64.380000000,63.870000000,33425504.000000000,0.671343654,
3-Jul-12,64.890000000,64.290000000,15895085.000000000,1.755259566,
5-Jul-12,65.250000000,64.480000000,37015388.000000000,0.572059382,
6-Jul-12,64.690000000,63.650000000,40672116.000000000,-1.777138913,
9-Jul-12,64.260000000,63.680000000,35627200.000000000,-0.325593928,-0.027667318
10-Jul-12,64.510000000,63.120000000,47337336.000000000,-0.455137568,-0.188824955
11-Jul-12,63.460000000,62.500000000,43373576.000000000,-1.848129838,-0.321890933
12-Jul-12,62.690000000,61.860000000,57651752.000000000,-1.014973491,-0.188350045
13-Jul-12,63.520000000,62.560000000,32357184.000000000,2.269665988,0.039013956
16-Jul-12,63.520000000,62.950000000,27620876.000000000,0.402413015,0.199830657
17-Jul-12,63.740000000,62.630000000,42467704.000000000,-0.130687546,0.193485848
18-Jul-12,64.580000000,63.390000000,44460240.000000000,2.141239004,0.292010470
19-Jul-12,65.310000000,64.720000000,52992592.000000000,1.146764061,0.498054970
20-Jul-12,65.100000000,64.210000000,40561552.000000000,-0.789910603,0.186940913
23-Jul-12,63.680000000,62.510000000,48636228.000000000,-3.752758129,-0.129066357
24-Jul-12,63.660000000,62.550000000,57230032.000000000,0.019395411,-0.253056654
25-Jul-12,62.950000000,62.150000000,46260856.000000000,-0.959774718,-0.362473375
26-Jul-12,63.730000000,62.970000000,41926492.000000000,1.450157099,-0.131952232
27-Jul-12,64.990000000,63.640000000,42620976.000000000,3.056593542,0.109632588
30-Jul-12,65.310000000,64.600000000,37809176.000000000,1.201824658,0.227987032
38 changes: 37 additions & 1 deletion ta/tests/volume.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import pandas as pd

from ta.tests.utils import TestIndicator
from ta.volume import OnBalanceVolumeIndicator, on_balance_volume
from ta.volume import (EaseOfMovementIndicator, OnBalanceVolumeIndicator,
ease_of_movement, on_balance_volume,
sma_ease_of_movement)


class TestOnBalanceVolumeIndicator(TestIndicator):
Expand All @@ -21,3 +23,37 @@ def test_obv2(self):
result = OnBalanceVolumeIndicator(
close=self._df['Close'], volume=self._df['Volume'], fillna=False).on_balance_volume()
pd.testing.assert_series_equal(self._df[target].tail(), result.tail(), check_names=False)


class TestEaseOfMovementIndicator(TestIndicator):
"""
https://school.stockcharts.com/doku.php?id=technical_indicators:ease_of_movement_emv
"""

_filename = 'ta/tests/data/cs-easeofmovement.csv'

def test_ease_of_movement(self):
target = 'EMV'
result = ease_of_movement(
high=self._df['High'], low=self._df['Low'], volume=self._df['Volume'], n=14, fillna=False)
pd.testing.assert_series_equal(self._df[target].tail(), result.tail(), check_names=False)

def test_ease_of_movement2(self):
target = 'EMV'
result = EaseOfMovementIndicator(
high=self._df['High'], low=self._df['Low'], volume=self._df['Volume'], n=14, fillna=False
).ease_of_movement()
pd.testing.assert_series_equal(self._df[target].tail(), result.tail(), check_names=False)

def test_sma_ease_of_movement(self):
target = 'SMA_EMV'
result = sma_ease_of_movement(
high=self._df['High'], low=self._df['Low'], volume=self._df['Volume'], n=14, fillna=False)
pd.testing.assert_series_equal(self._df[target].tail(), result.tail(), check_names=False)

def test_sma_ease_of_movement2(self):
target = 'SMA_EMV'
result = EaseOfMovementIndicator(
high=self._df['High'], low=self._df['Low'], volume=self._df['Volume'], n=14, fillna=False
).sma_ease_of_movement()
pd.testing.assert_series_equal(self._df[target].tail(), result.tail(), check_names=False)
41 changes: 32 additions & 9 deletions ta/volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,33 +159,35 @@ class EaseOfMovementIndicator(IndicatorMixin):
https://en.wikipedia.org/wiki/Ease_of_movement
"""

def __init__(self, high: pd.Series, low: pd.Series, close: pd.Series,
volume: pd.Series, n: int = 20, fillna: bool = False):
def __init__(self, high: pd.Series, low: pd.Series, volume: pd.Series, n: int = 14, fillna: bool = False):
"""
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.
"""
self._high = high
self._low = low
self._close = close
self._volume = volume
self._n = n
self._fillna = fillna
self._run()

def _run(self):
emv = (self._high.diff(1) + self._low.diff(1)) * (self._high - self._low) / (2 * self._volume)
self._emv = emv.rolling(self._n, min_periods=0).mean()
self._emv = (self._high.diff(1) + self._low.diff(1)) * (self._high - self._low) / (2 * self._volume)
self._emv *= 100000000

def ease_of_movement(self) -> pd.Series:
emv = self.check_fillna(self._emv, value=0)
return pd.Series(emv, name=f'eom_{self._n}')

def sma_ease_of_movement(self) -> pd.Series:
emv = self._emv.rolling(self._n, min_periods=0).mean()
emv = self.check_fillna(emv, value=0)
return pd.Series(emv, name=f'sma_eom_{self._n}')


class VolumePriceTrendIndicator(IndicatorMixin):
"""Volume-price trend (VPT)
Expand Down Expand Up @@ -336,7 +338,29 @@ def force_index(close, volume, n=2, fillna=False):
return ForceIndexIndicator(close=close, volume=volume, n=n, fillna=fillna).force_index()


def ease_of_movement(high, low, close, volume, n=20, fillna=False):
def ease_of_movement(high, low, volume, n=14, 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.
volume(pandas.Series): dataset 'Volume' column.
n(int): n period.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
return EaseOfMovementIndicator(
high=high, low=low, volume=volume, n=n, fillna=fillna).ease_of_movement()


def sma_ease_of_movement(high, low, volume, n=14, fillna=False):
"""Ease of movement (EoM, EMV)
It relate an asset's price change to its volume and is particularly useful
Expand All @@ -347,7 +371,6 @@ def ease_of_movement(high, low, close, volume, n=20, fillna=False):
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.
Expand All @@ -356,7 +379,7 @@ def ease_of_movement(high, low, close, volume, n=20, fillna=False):
pandas.Series: New feature generated.
"""
return EaseOfMovementIndicator(
high=high, low=low, close=close, volume=volume, n=n, fillna=fillna).ease_of_movement()
high=high, low=low, volume=volume, n=n, fillna=fillna).sma_ease_of_movement()


def volume_price_trend(close, volume, fillna=False):
Expand Down
20 changes: 18 additions & 2 deletions ta/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,36 @@ def add_volume_ta(df: pd.DataFrame, high: str, low: str, close: str, volume: str
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""

# Accumulation Distribution Index
df[f'{colprefix}volume_adi'] = AccDistIndexIndicator(
high=df[high], low=df[low], close=df[close], volume=df[volume], fillna=fillna).acc_dist_index()

# On Balance Volume
df[f'{colprefix}volume_obv'] = OnBalanceVolumeIndicator(
close=df[close], volume=df[volume], fillna=fillna).on_balance_volume()

# Chaikin Money Flow
df[f'{colprefix}volume_cmf'] = ChaikinMoneyFlowIndicator(
high=df[high], low=df[low], close=df[close], volume=df[volume], fillna=fillna).chaikin_money_flow()

# Force Index
df[f'{colprefix}volume_fi'] = ForceIndexIndicator(
close=df[close], volume=df[volume], fillna=fillna).force_index()
df[f'{colprefix}volume_em'] = EaseOfMovementIndicator(
high=df[high], low=df[low], close=df[close], volume=df[volume], n=14, fillna=fillna).ease_of_movement()

# Ease of Movement
indicator = EaseOfMovementIndicator(high=df[high], low=df[low], volume=df[volume], n=14, fillna=fillna)
df[f'{colprefix}volume_em'] = indicator.ease_of_movement()
df[f'{colprefix}volume_sma_em'] = indicator.sma_ease_of_movement()

# Volume Price Trend
df[f'{colprefix}volume_vpt'] = VolumePriceTrendIndicator(
close=df[close], volume=df[volume], fillna=fillna).volume_price_trend()

# Negative Volume Index
df[f'{colprefix}volume_nvi'] = NegativeVolumeIndexIndicator(
close=df[close], volume=df[volume], fillna=fillna).negative_volume_index()

return df


Expand Down

0 comments on commit b6f71b2

Please sign in to comment.