ENH #342 jma indicator MAINT jma ext TST DOC

This commit is contained in:
Kevin Johnson
2021-07-25 15:31:32 -07:00
parent aaa4054e62
commit cefa083ed9
8 changed files with 91 additions and 57 deletions
+6 -4
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -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"],
+5
View File
@@ -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)
+1
View File
@@ -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
+65 -49
View File
@@ -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:
+1 -1
View File
@@ -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",
+5
View File
@@ -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)
+5
View File
@@ -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)