ENH cfo indicator added MAINT linreg refactor

This commit is contained in:
Kevin Johnson
2020-09-16 09:39:10 -07:00
parent 56f5ed6110
commit 2116108b18
7 changed files with 58 additions and 39 deletions
+8 -6
View File
@@ -67,14 +67,15 @@ Thank you for your contribution!
* _Linear Decay_ (**linear_decay**): Renamed to _Decay_ (**decay**) and with the option for Exponential decay using ```mode="exp"```. See: ```help(ta.decay)```
## __New Indicators__
* _Squeeze_ (**squeeze**). A Momentum indicator. Both John Carter's TTM **and** Lazybear's TradingView versions are implemented. The default is John Carter's, or ```lazybear=False```. Set ```lazybear=True``` to enable Lazybear's.
* _TTM Trend_ (**ttm_trend**). A trend indicator inspired from John Carter's book "Mastering the Trade".
* _Chande Forecast Oscillator_ (**cfo**) It calculates the percentage difference between the actual price and the Time Series Forecast (the endpoint of a linear regression line).
* _Gann High-Low Activator_ (**hilo**) The Gann High Low Activator Indicator was created by Robert Krausz in a 1998.
* _Inside Bar_ (**cdl_inside**) An Inside Bar is a bar contained within it's previous bar's high and low See: ```help(ta.cdl_inside)```
* _SMI Ergodic_ (**smi**) Developed by William Blau, the SMI Ergodic Indicator is the same as the True Strength Index (TSI) except the SMI includes a signal line and oscillator.
* _Gann High-Low Activator_ (**hilo**) The Gann High Low Activator Indicator was created by Robert Krausz in a 1998
* _Squeeze_ (**squeeze**). A Momentum indicator. Both John Carter's TTM **and** Lazybear's TradingView versions are implemented. The default is John Carter's, or ```lazybear=False```. Set ```lazybear=True``` to enable Lazybear's.
* _Stochastic RSI_ (**stochrsi**) "Stochastic RSI and Dynamic Momentum Index" was created by Tushar Chande and Stanley Kroll. In line with Trading View's calculation. See: ```help(ta.stochrsi)```
* _TTM Trend_ (**ttm_trend**). A trend indicator inspired from John Carter's book "Mastering the Trade".
issue of Stocks & Commodities Magazine. It is a moving average based trend
indicator consisting of two different simple moving averages.
* _Stochastic RSI_ (**stochrsi**) "Stochastic RSI and Dynamic Momentum Index" was created by Tushar Chande and Stanley Kroll. In line with Trading View's calculation. See: ```help(ta.stochrsi)```
* _Inside Bar_ (**cdl_inside**) An Inside Bar is a bar contained within it's previous bar's high and low See: ```help(ta.cdl_inside)```
## __Updated Indicators__
* _Average True Range_ (**atr**): Added option to return **atr** as a percentage. See: ```help(ta.atr)```
@@ -365,7 +366,7 @@ print(bothhl2.name) # "pre_HL2_post"
* _Inside Bar_: **cdl_inside**
* _Heikin-Ashi_: **ha**
## _Momentum_ (33)
## _Momentum_ (34)
* _Awesome Oscillator_: **ao**
* _Absolute Price Oscillator_: **apo**
@@ -373,6 +374,7 @@ print(bothhl2.name) # "pre_HL2_post"
* _Balance of Power_: **bop**
* _BRAR_: **brar**
* _Commodity Channel Index_: **cci**
* _Chande Forecast Oscillator_: **cfo**
* _Center of Gravity_: **cg**
* _Chande Momentum Oscillator_: **cmo**
* _Coppock Curve_: **coppock**
+1 -1
View File
@@ -25,7 +25,7 @@ Category = {
"candles": ["cdl_doji", "cdl_inside", "ha"],
# Momentum
"momentum": ["ao", "apo", "bias", "bop", "brar", "cci", "cg", "cmo", "coppock", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd", "mom", "pgo", "ppo", "psl", "pvo", "roc", "rsi", "rvgi", "slope", "smi", "squeeze", "stoch", "stochrsi", "trix", "tsi", "uo", "willr"],
"momentum": ["ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo", "coppock", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd", "mom", "pgo", "ppo", "psl", "pvo", "roc", "rsi", "rvgi", "slope", "smi", "squeeze", "stoch", "stochrsi", "trix", "tsi", "uo", "willr"],
# Overlap
"overlap": ["dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku", "kama", "linreg", "midpoint", "midprice", "ohlc4", "pwma", "rma", "sinwma", "sma", "supertrend", "swma", "t3", "tema", "trima", "vwap", "vwma", "wcp", "wma", "zlma"],
+6 -1
View File
@@ -23,7 +23,7 @@ from pandas_ta.volatility import *
from pandas_ta.volume import *
from pandas_ta.utils import *
version = ".".join(("0", "2", "09b"))
version = ".".join(("0", "2", "10b"))
# Strategy DataClass
@@ -684,6 +684,11 @@ class AnalysisIndicators(BasePandasObject):
result = cci(high=high, low=low, close=close, length=length, c=c, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def cfo(self, length=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = cfo(close=close, length=length, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def cg(self, length=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = cg(close=close, length=length, offset=offset, **kwargs)
+14 -12
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from ..utils import get_drift, get_offset, verify_series
from ..overlap.linreg import linreg
from pandas_ta.overlap import linreg
from pandas_ta.utils import get_drift, get_offset, verify_series
def cfo(close, length=None, scalar=None, drift=None, offset=None, **kwargs):
"""Indicator: Chande Forcast Oscillator (CFO)"""
@@ -12,8 +12,9 @@ def cfo(close, length=None, scalar=None, drift=None, offset=None, **kwargs):
offset = get_offset(offset)
#Finding linear regression of Series
linreg_series = linreg(close,length=length)
cfo = ((close-linreg_series)/close *100)
cfo = scalar * (close - linreg(close, length=length, tsf=True))
cfo /= close
# Offset
if offset != 0:
cfo = cfo.shift(offset)
@@ -28,13 +29,13 @@ def cfo(close, length=None, scalar=None, drift=None, offset=None, **kwargs):
cfo.name = f"CFO_{length}"
cfo.category = "momentum"
return cmo
return cfo
cfo.__doc__ = \
"""Chande Forcast Oscillator (CFO)
The Forecast Oscillator calculates the percentage difference between the actual price
and the Time Series Forecast (the endpoint of a linear regression line).
The Forecast Oscillator calculates the percentage difference between the actual
price and the Time Series Forecast (the endpoint of a linear regression line).
Sources:
https://www.fmlabs.com/reference/default.htm?url=ForecastOscillator.htm
@@ -42,15 +43,16 @@ Sources:
Calculation:
Default Inputs:
length=9, drift=1, scalar=100
LINREG = Linear Regression
# Same Calculation as RSI except for this step
CFO = ( ( CLOSE- LINERREG ) / CLOSE * 100 )
CFO = scalar * (close - LINERREG(length, tdf=True)) / close
Args:
close (pd.Series): Series of 'close's
scalar (float): How much to magnify. Default: 100
drift (int): The short period. Default: 1
offset (int): How many periods to offset the result. Default: 0
length (int): The period. Default: 9
scalar (float): How much to magnify. Default: 100
drift (int): The short period. Default: 1
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+19 -19
View File
@@ -1,20 +1,20 @@
# -*- coding: utf-8 -*-
import math
from ..utils import get_offset, verify_series
from pandas_ta.utils import get_offset, verify_series
def linreg(close, length=None, offset=None, **kwargs):
"""Indicator: Linear Regression"""
# Validate arguments
close = verify_series(close)
length = int(length) if length and length > 0 else 14
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
offset = get_offset(offset)
angle = kwargs.pop('angle', False)
intercept = kwargs.pop('intercept', False)
degrees = kwargs.pop('degrees', False)
r = kwargs.pop('r', False)
slope = kwargs.pop('slope', False)
tsf = kwargs.pop('tsf', False)
angle = kwargs.pop("angle", False)
intercept = kwargs.pop("intercept", False)
degrees = kwargs.pop("degrees", False)
r = kwargs.pop("r", False)
slope = kwargs.pop("slope", False)
tsf = kwargs.pop("tsf", False)
# Calculate Result
x = range(1, length + 1) # [1, 2, ..., n] from 1 to n keeps Sum(xy) low
@@ -54,10 +54,10 @@ def linreg(close, length=None, offset=None, **kwargs):
linreg = linreg.shift(offset)
# Handle fills
if 'fillna' in kwargs:
linreg.fillna(kwargs['fillna'], inplace=True)
if 'fill_method' in kwargs:
linreg.fillna(method=kwargs['fill_method'], inplace=True)
if "fillna" in kwargs:
linreg.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
linreg.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
linreg.name = f"LR"
@@ -66,7 +66,7 @@ def linreg(close, length=None, offset=None, **kwargs):
if angle: linreg.name += "a"
if r: linreg.name += "r"
linreg.name += f"_{length}"
linreg.category = 'overlap'
linreg.category = "overlap"
return linreg
@@ -104,12 +104,12 @@ Args:
offset (int): How many periods to offset the result. Default: 0
Kwargs:
angle (bool, optional): Default: False. If True, returns the angle of the slope in radians
degrees (bool, optional): Default: False. If True, returns the angle of the slope in degrees
intercept (bool, optional): Default: False. If True, returns the angle of the slope in radians
r (bool, optional): Default: False. If True, returns it's correlation 'r'
slope (bool, optional): Default: False. If True, returns the slope
tsf (bool, optional): Default: False. If True, returns the Time Series Forecast value.
angle (bool, optional): Default: False. If True, returns the angle of the slope in radians
degrees (bool, optional): Default: False. If True, returns the angle of the slope in degrees
intercept (bool, optional): Default: False. If True, returns the angle of the slope in radians
r (bool, optional): Default: False. If True, returns it's correlation 'r'
slope (bool, optional): Default: False. If True, returns the slope
tsf (bool, optional): Default: False. If True, returns the Time Series Forecast value.
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
+5
View File
@@ -49,6 +49,11 @@ class TestMomentumExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "CCI_14_0.015")
def test_cfo_ext(self):
self.data.ta.cfo(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "CFO_9")
def test_cg_ext(self):
self.data.ta.cg(append=True)
self.assertIsInstance(self.data, DataFrame)
+5
View File
@@ -119,6 +119,11 @@ class TestMomentum(TestCase):
except Exception as ex:
error_analysis(result, CORRELATION, ex)
def test_cfo(self):
result = pandas_ta.cfo(self.close)
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "CFO_9")
def test_cg(self):
result = pandas_ta.cg(self.close)
self.assertIsInstance(result, Series)