From 9c60eaff7a5b5d6f551539d5dcddec8e402aca90 Mon Sep 17 00:00:00 2001 From: Lluis Date: Fri, 22 May 2020 14:29:43 +0200 Subject: [PATCH] Add macd signals --- pandas_ta/__init__.py | 3 +- pandas_ta/core.py | 10 ++++- pandas_ta/signals/README.md | 4 +- pandas_ta/signals/macd_signals.py | 75 +++++++++++++++++++++++++++++++ pandas_ta/signals/rsi_signals.py | 2 +- 5 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 pandas_ta/signals/macd_signals.py diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py index 5cfefd6..2b5bc4a 100644 --- a/pandas_ta/__init__.py +++ b/pandas_ta/__init__.py @@ -130,8 +130,9 @@ from .volume.pvol import pvol from .volume.pvt import pvt from .volume.vp import vp -# Event +# Signals from .signals.rsi_signals import rsi_signals +from .signals.macd_signals import macd_signals # DataFrame Extension from .core import * diff --git a/pandas_ta/core.py b/pandas_ta/core.py index 980737f..6defdf7 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -1233,7 +1233,7 @@ class AnalysisIndicators(BasePandasObject): - # Events indicators + # Signals indicators def rsi_signals(self, close=None, above_val=None, below_val=None, length=None, drift=None, offset=None, **kwargs): close = self._get_column(close, 'close') from pandas_ta.signals.rsi_signals import rsi_signals @@ -1241,3 +1241,11 @@ class AnalysisIndicators(BasePandasObject): self._add_prefix_suffix(result, **kwargs) self._append(result, **kwargs) return result + + def macd_signals(self, close=None, fast=None, slow=None, signal=None, offset=None, **kwargs): + close = self._get_column(close, 'close') + from pandas_ta.signals.macd_signals import macd_signals + result = macd_signals(close=close, fast=fast, slow=slow, signal=signal, offset=offset, **kwargs) + self._add_prefix_suffix(result, **kwargs) + self._append(result, **kwargs) + return result diff --git a/pandas_ta/signals/README.md b/pandas_ta/signals/README.md index 78f630e..78bed0c 100644 --- a/pandas_ta/signals/README.md +++ b/pandas_ta/signals/README.md @@ -13,9 +13,9 @@ - [ ] Extended RSI (Overbought/oversold 80/20 and positive trend when $RSI_t$ > $RSI_{t-1}$) - [ ] Crossing EMA(50) and EMA(200) (long term bearish/bullish signal) - [ ] Crossing EMA(12) and EMA(26) (short term bearish/bullish signal) -- [ ] Crossing MACD(26,12, 9) with 0 line (bearish/bullish signal) +- [x] Crossing MACD(26,12, 9) with 0 line (bearish/bullish signal) - [ ] Stochastic oscillator (Overbough/oversold 80/20) - [ ] Crossing Bollinger bands with close price - [ ] Average directional index (ADX) cross with 20 or 25 (trend/drift and 40 for strong trend) - [ ] Extend ADX with DI+ and DI- (strong trend/drift confirming uptrend or downtrend) -- [ ] Aroon osicillator around 100 +- [ ] Aroon osicillator around 100 diff --git a/pandas_ta/signals/macd_signals.py b/pandas_ta/signals/macd_signals.py new file mode 100644 index 0000000..6c51341 --- /dev/null +++ b/pandas_ta/signals/macd_signals.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +from pandas import DataFrame +from ..momentum.macd import macd +from ..utils import above_value, below_value, cross_value + +def macd_signals(close, fast=None, slow=None, signal=None, offset=None, **kwargs): + """Indicator: Signals based on Moving Average, Convergence/Divergence (MACD)""" + macd_df = macd(close, fast=fast, slow=slow, signal=signal, offset=offset, **kwargs) + histogram_col = macd_df.columns.values[macd_df.columns.str.startswith('MACDH')][0] + histogram_serie = macd_df[histogram_col] + macd_col = macd_df.columns.values[macd_df.columns.str.startswith('MACD_')][0] + macd_serie = macd_df[macd_col] + + signals_above = cross_value(histogram_serie, 0, above=True, asint=True, **kwargs) + signals_below = cross_value(histogram_serie, 0, above=False, asint=True, **kwargs) + bull = above_value(histogram_serie, 0, asint=True, **kwargs) + bear = below_value(histogram_serie, 0, asint=True, **kwargs) + + # Name and Categorize it + # Not needed because above_value/below_value is already naming + signals_above.name = f"{macd_serie.name}_X_BULL" + signals_below.name = f"{macd_serie.name}_X_BEAR" + bull.name = f"{macd_serie.name}_BULL" + bear.name = f"{macd_serie.name}_BEAR" + signals_above.category = signals_below.category = bull.category = bear.category = 'signals' + + # Prepare DataFrame to return + data = { + signals_above.name: signals_above, + signals_below.name: signals_below, + bull.name: bull, + bear.name: bear + } + + macddf = DataFrame(data) + macddf.name = f"{macd_serie.name}_signals" + macddf.category = 'signals' + + return macddf + + + +macd.__doc__ = \ +"""Moving Average Convergence Divergence (MACD) + +The MACD is a popular indicator to that is used to identify a security's trend. +While APO and MACD are the same calculation, MACD also returns two more series +called Signal and Histogram. The Signal is an EMA of MACD and the Histogram is +the difference of MACD and Signal. + +Sources: + https://www.tradingview.com/wiki/MACD_(Moving_Average_Convergence/Divergence) + +Calculation: + Default Inputs: + fast=12, slow=26, signal=9 + EMA = Exponential Moving Average + MACD = EMA(close, fast) - EMA(close, slow) + Signal = EMA(MACD, signal) + Histogram = MACD - Signal + +Args: + close (pd.Series): Series of 'close's + fast (int): The short period. Default: 12 + slow (int): The long period. Default: 26 + signal (int): The signal period. Default: 9 + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: + fillna (value, optional): pd.DataFrame.fillna(value) + fill_method (value, optional): Type of fill method + +Returns: + pd.DataFrame: macd, histogram, signal columns. +""" \ No newline at end of file diff --git a/pandas_ta/signals/rsi_signals.py b/pandas_ta/signals/rsi_signals.py index 6db5ebc..d8af594 100644 --- a/pandas_ta/signals/rsi_signals.py +++ b/pandas_ta/signals/rsi_signals.py @@ -5,7 +5,7 @@ from ..utils import above_value, below_value, cross_value def rsi_signals(close, above_val=None, below_val=None, length=None, drift=None, offset=None, crossing=False, **kwargs): """Indicator: Signals based on Relative Strength Index (RSI)""" - rsi_series = rsi(close, length=None, drift=None, offset=None, **kwargs) + rsi_series = rsi(close, length=length, drift=drift, offset=offset, **kwargs) above_val = int(above_val) if above_val and above_val > 0 else 80 below_val = int(below_val) if below_val and below_val > 0 else 20