diff --git a/tests/pipeline/test_technical.py b/tests/pipeline/test_technical.py index 7727431b9b..42b8853b83 100644 --- a/tests/pipeline/test_technical.py +++ b/tests/pipeline/test_technical.py @@ -20,6 +20,7 @@ MovingAverageConvergenceDivergenceSignal, AnnualizedVolatility, RSI, + FibonacciRetractment ) from zipline.testing import check_allclose, parameter_space from zipline.testing.fixtures import ZiplineTestCase @@ -663,3 +664,32 @@ def test_volatility(self): expected_vol, decimal=8 ) + + +class FibonacciRetractmentTestCase(ZiplineTestCase): + """ + Test FibonacciRetractment Calculation + """ + def test_fibo(self): + + fibo = FibonacciRetractment() + + nassets = 3 + today = np.datetime64(1, 'ns') + assets = np.arange(nassets) + + np.random.seed(100) # Seed so we get deterministic results. + test_data = np.abs(np.random.randn(15, nassets)) + + out = np.empty((6, nassets), dtype=float) + fibo.compute(today, assets, out, test_data) + + expected = np.empty((6, 3), dtype=float) + expected[0] = [1.74976547, 1.69061683, 1.61898166] + expected[1] = [1.08414923, 1.14101903, 1.0217989] + expected[2] = [0.87854002, 0.97124798, 0.83732884] + expected[3] = [0.67293081, 0.80147694, 0.65285877] + expected[4] = [0.41853298, 0.59142123, 0.42461615] + expected[5] = [0.00731456, 0.25187914, 0.05567601] + + np.testing.assert_almost_equal(out, expected, 7) diff --git a/zipline/pipeline/factors/__init__.py b/zipline/pipeline/factors/__init__.py index 65fadd80f6..98764f14bb 100644 --- a/zipline/pipeline/factors/__init__.py +++ b/zipline/pipeline/factors/__init__.py @@ -41,6 +41,7 @@ RateOfChangePercentage, RSI, TrueRange, + FibonacciRetractment, ) __all__ = [ @@ -58,6 +59,7 @@ 'ExponentialWeightedMovingStdDev', 'Factor', 'FastStochasticOscillator', + 'FibonacciRetractment', 'IchimokuKinkoHyo', 'Latest', 'LinearWeightedMovingAverage', diff --git a/zipline/pipeline/factors/technical.py b/zipline/pipeline/factors/technical.py index ad490ed2b5..2a0e53ca56 100644 --- a/zipline/pipeline/factors/technical.py +++ b/zipline/pipeline/factors/technical.py @@ -11,6 +11,7 @@ diff, dstack, inf, + apply_along_axis ) from numexpr import evaluate @@ -390,3 +391,36 @@ def compute(self, today, assets, out, close, fast_period, slow_period, # Convenience aliases. MACDSignal = MovingAverageConvergenceDivergenceSignal + + +class FibonacciRetractment(CustomFactor): + """ + FibonacciRetractment + + https://www.investopedia.com/ask/answers/05/fibonacciretracement.asp + + Given a period, 4 retractment levels are calculated between the peak + and the trough, where the ratio of each retractment level is based on + Fibonacci sequence. + + Parameters + ---------- + period : int > 0, optional + The window length for the Fibonacci retractment levels calculation. + Default is 0. + """ + + window_length = 15 + inputs = (EquityPricing.close,) + + def compute(self, today, assets, out, closes, period=0): + windowed_closes = closes[-period:] + trough = apply_along_axis(min, 0, windowed_closes) + peak = apply_along_axis(max, 0, windowed_closes) + range = peak - trough + out[0] = peak + out[1] = trough + 0.618 * range + out[2] = trough + 0.5 * range + out[3] = trough + 0.382 * range + out[4] = trough + 0.236 * range + out[5] = trough