diff --git a/README.md b/README.md
index 459cca5..50b2f69 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Candles](#candles-64)
* [Cycles](#cycles-1)
* [Momentum](#momentum-41)
- * [Overlap](#overlap-32)
+ * [Overlap](#overlap-33)
* [Performance](#performance-3)
* [Statistics](#statistics-11)
* [Trend](#trend-18)
@@ -112,7 +112,7 @@ $ pip install pandas_ta
Latest Version
--------------
-Best choice! Version: *0.3.10b*
+Best choice! Version: *0.3.11b*
* Includes all fixes and updates between **pypi** and what is covered in this README.
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
@@ -214,7 +214,7 @@ Thanks for using **Pandas TA**!
_Thank you for your contributions!_
-
+
@@ -723,7 +723,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
-### **Overlap** (32)
+### **Overlap** (33)
* _Arnaud Legoux Moving Average_: **alma**
* _Double Exponential Moving Average_: **dema**
@@ -738,6 +738,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
* _Ichimoku Kinkō Hyō_: **ichimoku**
* Returns two DataFrames. For more information: ```help(ta.ichimoku)```.
* ```lookahead=False``` drops the Chikou Span Column to prevent potential data leak.
+* _Jurik Moving Average_: **jma**
* _Kaufman's Adaptive Moving Average_: **kama**
* _Linear Regression_: **linreg**
* _McGinley Dynamic_: **mcgd**
@@ -968,6 +969,7 @@ trading account, or fund. See ```help(ta.drawdown)```
* _Cross Signals_ (**xsignals**) was created by Kevin Johnson. It is a wrapper of Trade Signals that returns Trends, Trades, Entries and Exits. Cross Signals are commonly used for **bbands**, **rsi**, **zscore** crossing some value either above or below two values at different times. See ```help(ta.xsignals)```
* _Directional Movement_ (**dm**) developed by J. Welles Wilder in 1978 attempts to determine which direction the price of an asset is moving. See ```help(ta.dm)```
* _Even Better Sinewave_ (**ebsw**) measures market cycles and uses a low pass filter to remove noise. See: ```help(ta.ebsw)```
+* _Jurik Moving Average_ (**jma**) attempts to eliminate noise to see the "true" underlying activity.. See: ```help(ta.jma)```
* _Klinger Volume Oscillator_ (**kvo**) was developed by Stephen J. Klinger. It is designed to predict price reversals in a market by comparing volume to price.. See ```help(ta.kvo)```
* _Schaff Trend Cycle_ (**stc**) is an evolution of the popular MACD incorportating two cascaded stochastic calculations with additional smoothing. See ```help(ta.stc)```
* _Squeeze Pro_ (**squeeze_pro**) is an extended version of "TTM Squeeze" from John Carter. See ```help(ta.squeeze_pro)```
diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py
index d3a5d31..0e740b8 100644
--- a/pandas_ta/__init__.py
+++ b/pandas_ta/__init__.py
@@ -55,9 +55,9 @@ Category = {
# Overlap
"overlap": [
"alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku",
- "kama", "linreg", "mcgd", "midpoint", "midprice", "ohlc4", "pwma", "rma",
- "sinwma", "sma", "ssf", "supertrend", "swma", "t3", "tema", "trima",
- "vidya", "vwap", "vwma", "wcp", "wma", "zlma"
+ "jma", "kama", "linreg", "mcgd", "midpoint", "midprice", "ohlc4",
+ "pwma", "rma", "sinwma", "sma", "ssf", "supertrend", "swma", "t3",
+ "tema", "trima", "vidya", "vwap", "vwma", "wcp", "wma", "zlma"
],
# Performance
"performance": ["log_return", "percent_return"],
diff --git a/pandas_ta/core.py b/pandas_ta/core.py
index 82eb52c..fdee64a 100644
--- a/pandas_ta/core.py
+++ b/pandas_ta/core.py
@@ -1194,6 +1194,11 @@ class AnalysisIndicators(BasePandasObject):
result = hwma(close=close, na=na, nb=nb, nc=nc, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
+ def jma(self, length=None, phase=None, offset=None, **kwargs):
+ close = self._get_column(kwargs.pop("close", "close"))
+ result = jma(close=close, length=length, phase=phase, offset=offset, **kwargs)
+ return self._post_process(result, **kwargs)
+
def kama(self, length=None, fast=None, slow=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = kama(close=close, length=length, fast=fast, slow=slow, offset=offset, **kwargs)
diff --git a/pandas_ta/overlap/__init__.py b/pandas_ta/overlap/__init__.py
index 4c098ff..7a0f5ec 100644
--- a/pandas_ta/overlap/__init__.py
+++ b/pandas_ta/overlap/__init__.py
@@ -9,6 +9,7 @@ from .hlc3 import hlc3
from .hma import hma
from .hwma import hwma
from .ichimoku import ichimoku
+from .jma import jma
from .kama import kama
from .linreg import linreg
from .ma import ma
diff --git a/pandas_ta/overlap/jma.py b/pandas_ta/overlap/jma.py
index 3fd8be1..be116e3 100644
--- a/pandas_ta/overlap/jma.py
+++ b/pandas_ta/overlap/jma.py
@@ -1,67 +1,79 @@
# -*- coding: utf-8 -*-
-from pandas_ta.utils import get_offset, verify_series
-from pandas import Series
-import numpy as np
+from numpy import average as npAverage
from numpy import nan as npNaN
-import math
+from numpy import log as npLog
+from numpy import power as npPower
+from numpy import sqrt as npSqrt
+from numpy import zeros_like as npZeroslike
+from pandas import Series
+from pandas_ta.utils import get_offset, verify_series
-def jma(close, length=None, phase=0, offset=None, **kwargs):
- """
- Indicator: Jurik Moving Average (JMA)
- Implementation of: https://c.mql5.com/forextsd/forum/164/jurik_1.pdf
- Jurik Volty from: https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/
- """
+
+def jma(close, length=None, phase=None, offset=None, **kwargs):
+ """Indicator: Jurik Moving Average (JMA)"""
# Validate Arguments
- length = int(length) if length and length > 0 else 7
- close = verify_series(close, length)
+ _length = int(length) if length and length > 0 else 7
+ phase = float(phase) if phase and phase != 0 else 0
+ close = verify_series(close, _length)
offset = get_offset(offset)
if close is None: return
# Define base variables
- jma = np.zeros_like(close)
- Volty = np.zeros_like(close)
- vSum = np.zeros_like(close)
- Kv = det0 = det1 = ma2 = 0.0
+ jma = npZeroslike(close)
+ volty = npZeroslike(close)
+ v_sum = npZeroslike(close)
+
+ kv = det0 = det1 = ma2 = 0.0
jma[0] = ma1 = uBand = lBand = close[0]
+
# Static variables
- SumLen = 10
- len = 0.5*(length-1)
- PR = 0.5 if phase<-100 else 2.5 if phase>100 else phase*0.01+1.5
- len1 = max((math.log(math.sqrt(len))/math.log(2.0))+2.0, 0)
- pow1 = max(len1-2.0, 0.5)
- len2 = math.sqrt(len)*len1
- bet = len2/(len2+1)
- beta = 0.45*(length-1)/(0.45*(length-1)+2.0)
- for i in range(1, close.shape[0]):
+ sum_length = 10
+ length = 0.5 * (_length - 1)
+ pr = 0.5 if phase < -100 else 2.5 if phase > 100 else 1.5 + phase * 0.01
+ length1 = max((npLog(npSqrt(length)) / npLog(2.0)) + 2.0, 0)
+ pow1 = max(length1 - 2.0, 0.5)
+ length2 = length1 * npSqrt(length)
+ bet = length2 / (length2 + 1)
+ beta = 0.45 * (length - 1) / (0.45 * (length - 1) + 2.0)
+
+ m = close.shape[0]
+ for i in range(1, m):
price = close[i]
+
# Price volatility
- del1 = price-uBand
- del2 = price-lBand
- Volty[i] = max(abs(del1),abs(del2)) if abs(del1)!=abs(del2) else 0
+ del1 = price - uBand
+ del2 = price - lBand
+ volty[i] = max(abs(del1),abs(del2)) if abs(del1)!=abs(del2) else 0
+
# Relative price volatility factor
- vSum[i] = vSum[i-1] + (Volty[i]-Volty[max(i-SumLen,0)])/SumLen
- avgVolty = np.average(vSum[max(i-65,0):i+1])
- dVolty = 0 if avgVolty==0 else Volty[i]/avgVolty
- rVolty = max(1.0, min(math.pow(len1, 1/pow1), dVolty))
+ v_sum[i] = v_sum[i - 1] + (volty[i] - volty[max(i - sum_length, 0)]) / sum_length
+ avg_volty = npAverage(v_sum[max(i - 65, 0):i + 1])
+ d_volty = 0 if avg_volty ==0 else volty[i] / avg_volty
+ r_volty = max(1.0, min(npPower(length1, 1 / pow1), d_volty))
+
# Jurik volatility bands
- pow2 = math.pow(rVolty, pow1)
- Kv = math.pow(bet, math.sqrt(pow2))
- uBand = price if (del1 > 0) else price - (Kv*del1)
- lBand = price if (del2 < 0) else price - (Kv*del2)
+ pow2 = npPower(r_volty, pow1)
+ kv = npPower(bet, npSqrt(pow2))
+ uBand = price if (del1 > 0) else price - (kv * del1)
+ lBand = price if (del2 < 0) else price - (kv * del2)
+
# Jurik Dynamic Factor
- power = math.pow(rVolty, pow1)
- alpha = math.pow(beta, power)
+ power = npPower(r_volty, pow1)
+ alpha = npPower(beta, power)
+
# 1st stage - prelimimary smoothing by adaptive EMA
- ma1 = ((1-alpha)*price)+(alpha*ma1) #
+ ma1 = ((1 - alpha) * price) + (alpha * ma1)
+
# 2nd stage - one more prelimimary smoothing by Kalman filter
- det0 = ((price-ma1)*(1-beta))+(beta*det0)
- ma2 = ma1+PR*det0
+ det0 = ((price - ma1) * (1 - beta)) + (beta * det0)
+ ma2 = ma1 + pr * det0
+
# 3rd stage - final smoothing by unique Jurik adaptive filter
- det1 = ((ma2-jma[i-1])*(1-alpha)*(1-alpha))+(alpha*alpha*det1)
+ det1 = ((ma2 - jma[i - 1]) * (1 - alpha) * (1 - alpha)) + (alpha * alpha * det1)
jma[i] = jma[i-1] + det1
# Remove initial lookback data and convert to pandas frame
- jma[0:length-1] = npNaN
+ jma[0:_length - 1] = npNaN
jma = Series(jma, index=close.index)
# Offset
@@ -75,27 +87,31 @@ def jma(close, length=None, phase=0, offset=None, **kwargs):
jma.fillna(method=kwargs["fill_method"], inplace=True)
# Name & Category
- jma.name = f"JMA_{length}"
+ jma.name = f"JMA_{_length}_{phase}"
jma.category = "overlap"
return jma
jma.__doc__ = \
-""" Jurik Moving Average Average (JMA)
+"""Jurik Moving Average Average (JMA)
+
+Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the "true"
+underlying activity. It has extremely low lag, is very smooth and is responsive
+to market gaps.
Sources:
- Implementation of: https://c.mql5.com/forextsd/forum/164/jurik_1.pdf
+ https://c.mql5.com/forextsd/forum/164/jurik_1.pdf
+ https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/
Calculation:
Default Inputs:
- length=7
- phase=0
+ length=7, phase=0
Args:
close (pd.Series): Series of 'close's
length (int): Period of calculation. Default: 7
- phase (float): how heavy/light the average is [-100, 100] Default: 0
+ phase (float): How heavy/light the average is [-100, 100]. Default: 0
offset (int): How many lengths to offset the result. Default: 0
Kwargs:
diff --git a/setup.py b/setup.py
index a9a9e78..590d956 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
- version=".".join(("0", "3", "10b")),
+ version=".".join(("0", "3", "11b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
diff --git a/tests/test_ext_indicator_overlap_ext.py b/tests/test_ext_indicator_overlap_ext.py
index f56ba41..e6bdebc 100644
--- a/tests/test_ext_indicator_overlap_ext.py
+++ b/tests/test_ext_indicator_overlap_ext.py
@@ -63,6 +63,11 @@ class TestOverlapExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "HWMA_0.2_0.1_0.1")
+ def test_jma_ext(self):
+ self.data.ta.jma(append=True)
+ self.assertIsInstance(self.data, DataFrame)
+ self.assertEqual(self.data.columns[-1], "JMA_7_0")
+
def test_kama_ext(self):
self.data.ta.kama(append=True)
self.assertIsInstance(self.data, DataFrame)
diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py
index 726449b..5848969 100644
--- a/tests/test_indicator_overlap.py
+++ b/tests/test_indicator_overlap.py
@@ -139,6 +139,11 @@ class TestOverlap(TestCase):
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "KAMA_10_2_30")
+ def test_jma(self):
+ result = pandas_ta.jma(self.close)
+ self.assertIsInstance(result, Series)
+ self.assertEqual(result.name, "JMA_7_0")
+
def test_ichimoku(self):
ichimoku, span = pandas_ta.ichimoku(self.high, self.low, self.close)
self.assertIsInstance(ichimoku, DataFrame)