diff --git a/tests/pipeline/test_technical.py b/tests/pipeline/test_technical.py index bcf233fc..a34d88e3 100644 --- a/tests/pipeline/test_technical.py +++ b/tests/pipeline/test_technical.py @@ -1,6 +1,7 @@ from __future__ import division from nose_parameterized import parameterized +from six.moves import range import numpy as np import pandas as pd import talib @@ -15,6 +16,7 @@ from zipline.pipeline.factors import ( Aroon, FastStochasticOscillator, IchimokuKinkoHyo, + RateOfChangePercentage, ) from zipline.testing import ExplodingObject, parameter_space from zipline.testing.fixtures import WithAssetFinder, ZiplineTestCase @@ -354,3 +356,27 @@ class IchimokuKinkoHyoTestCase(ZiplineTestCase): str(e.exception), '%s must be <= the window_length: 53 > 52' % arg, ) + + +class TestRateOfChangePercentage(ZiplineTestCase): + @parameterized.expand([ + ('constant', [2.] * 10, 0.0), + ('step', [2.] + [1.] * 9, -50.0), + ('linear', [2. + x for x in range(10)], 450.0), + ('quadratic', [2. + x**2 for x in range(10)], 4050.0), + ]) + def test_rate_of_change_percentage(self, test_name, data, expected): + window_length = len(data) + + rocp = RateOfChangePercentage( + inputs=(USEquityPricing.close,), + window_length=window_length, + ) + today = pd.Timestamp('2014') + assets = np.arange(5, dtype=np.int64) + # broadcast data across assets + data = np.array(data)[:, np.newaxis] * np.ones(len(assets)) + + out = np.zeros(len(assets)) + rocp.compute(today, assets, out, data) + assert_equal(out, np.full((len(assets),), expected)) diff --git a/zipline/pipeline/factors/__init__.py b/zipline/pipeline/factors/__init__.py index e32dac95..422cf750 100644 --- a/zipline/pipeline/factors/__init__.py +++ b/zipline/pipeline/factors/__init__.py @@ -24,6 +24,7 @@ from .technical import ( FastStochasticOscillator, IchimokuKinkoHyo, MaxDrawdown, + RateOfChangePercentage, Returns, RSI, SimpleMovingAverage, @@ -47,6 +48,7 @@ __all__ = [ 'IchimokuKinkoHyo', 'Latest', 'MaxDrawdown', + 'RateOfChangePercentage', 'RecarrayField', 'Returns', 'RollingLinearRegressionOfReturns', diff --git a/zipline/pipeline/factors/technical.py b/zipline/pipeline/factors/technical.py index 8344130a..5802fff3 100644 --- a/zipline/pipeline/factors/technical.py +++ b/zipline/pipeline/factors/technical.py @@ -590,3 +590,26 @@ class IchimokuKinkoHyo(CustomFactor): out.senkou_span_a = (tenkan_sen + kijun_sen) / 2 out.senkou_span_b = (high.max(axis=0) + low.min(axis=0)) / 2 out.chikou_span = close[chikou_span_length] + + +class RateOfChangePercentage(CustomFactor): + """ + Rate of change Percentage + ROC measures the percentage change in price from one period to the next. + The ROC calculation compares the current price with the price `n` + periods ago. + Formula for calculation: ((price - prevPrice) / prevPrice) * 100 + price - the current price + prevPrice - the price n days ago, equals window length + """ + def compute(self, today, assets, out, close): + today_close = close[-1] + prev_close = close[0] + evaluate('((tc - pc) / pc) * 100', + local_dict={ + 'tc': today_close, + 'pc': prev_close + }, + global_dict={}, + out=out, + )