From 609a2846c299644a6b846dea5cba0a1c60c75b93 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Mon, 21 Oct 2024 20:15:58 +0200 Subject: [PATCH] update --- README.md | 5 +-- setup.py | 2 +- ttools/indicators/DIVERGENCE.py | 27 +++++++++++------ ttools/vbtutils.py | 54 +++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a743ca4..72c4eb4 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,10 @@ exits = exits | forced_exits_window exits.tail(20) ``` +## is rising/is falling +`isrising(series,n)`,`isfalling(series, n)` - returns mask where the condition is satisfied of rising or falling elements including equal values -`isrising(series,n)`,`isfalling(series, n)` - returns mask where the condition is satisfied of consecutive rising or falling elements - +`isrisingc(series,n)`,`isfallingc(series, n)` - same as above but scritly rising/fallinf (no equal values) # Indicators Custom indicators in the `indicators` folder. diff --git a/setup.py b/setup.py index 135fef4..4f8efbf 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='ttools', - version='0.3.4', + version='0.3.5', packages=find_packages(), install_requires=[ 'vectorbtpro', diff --git a/ttools/indicators/DIVERGENCE.py b/ttools/indicators/DIVERGENCE.py index 13df904..6f9a604 100644 --- a/ttools/indicators/DIVERGENCE.py +++ b/ttools/indicators/DIVERGENCE.py @@ -24,22 +24,28 @@ DIVERGENCE - of two time series, same like in v2realbot @jit(nopython=True) -def divergence(series1, series2, divtype): +def divergence(series1, series2, divtype, round): #div = a+b / a-b will give value between -1 and 1 if divtype == "reln": - return (series1 - series2) / (series1 + series2) + out = (series1 - series2) / (series1 + series2) elif divtype == "rel": - return series1 - series2 + out = series1 - series2 elif divtype == "abs": - return np.abs(series1 - series2) + out = np.abs(series1 - series2) elif divtype == "absn": - return np.abs(series1 - series2) / series1 + out = np.abs(series1 - series2) / series1 elif divtype == "pctabs": - return np.abs(((series1 - series2) / series1) * 100) + out = np.abs(((series1 - series2) / series1) * 100) elif divtype == "pct": - return ((series1 - series2) / series1) * 100 + out = ((series1 - series2) / series1) * 100 else: - return np.full_like(series1, np.nan) + out = np.full_like(series1, np.nan) + + for i in range(out.shape[0]): + if not np.isnan(out[i]): + out[i] = np.round(out[i], round) + + return out """ Divergence indicator - various divergences between two series @@ -48,11 +54,12 @@ IND_DIVERGENCE = vbt.IF( class_name='DIVERGENCE', module_name='ttools', input_names=['series1', 'series2'], - param_names=["divtype"], + param_names=["divtype", "round"], output_names=['div'] ).with_apply_func(divergence, takes_1d=True, param_settings=dict( ), - divtype="reln" + divtype="reln", + round=4 ) \ No newline at end of file diff --git a/ttools/vbtutils.py b/ttools/vbtutils.py index 0d49ed3..7eda9c6 100644 --- a/ttools/vbtutils.py +++ b/ttools/vbtutils.py @@ -4,6 +4,7 @@ import pandas_market_calendars as mcal from typing import Any import datetime +#TBD create NUMBA alternatives def isrising(series: pd.Series, n: int) -> pd.Series: """ Checks if a series is rising over a given window size. @@ -23,6 +24,32 @@ def isrising(series: pd.Series, n: int) -> pd.Series: """ return series.rolling(n).apply(lambda x: (x == sorted(x, reverse=False)).all(), raw=False).fillna(False).astype(bool) +def isrisingc(series: pd.Series, n: int) -> pd.Series: + """ + Checks if a series is strictly rising over a given window size. + Returns True for windows where values are strictly increasing. + + Parameters + ---------- + series : pd.Series + Input series + n : int + Window size + + Returns + ------- + pd.Series + Boolean mask indicating when the series is strictly rising + """ + # Calculate the difference between consecutive values + diffs = series.diff() + + # We check if all values in the window are negative (falling) + result = diffs.rolling(n-1).apply(lambda x: (x > 0).all(), raw=True) + + # Fill the first n-1 values with False and return the boolean mask + return result.fillna(False).astype(bool) + def isfalling(series: pd.Series, n: int) -> pd.Series: """ Checks if a series is falling over a given window size. @@ -41,6 +68,33 @@ def isfalling(series: pd.Series, n: int) -> pd.Series: """ return series.rolling(n).apply(lambda x: (x == sorted(x, reverse=True)).all(), raw=False).fillna(False).astype(bool) +def isfallingc(series: pd.Series, n: int) -> pd.Series: + """ + Checks if a series is strictly falling over a given window size. + Returns True for windows where values are strictly decreasing. + + Parameters + ---------- + series : pd.Series + Input series + n : int + Window size + + Returns + ------- + pd.Series + Boolean mask indicating when the series is strictly falling + """ + # Calculate the difference between consecutive values + diffs = series.diff() + + # We check if all values in the window are negative (falling) + result = diffs.rolling(n-1).apply(lambda x: (x < 0).all(), raw=True) + + # Fill the first n-1 values with False and return the boolean mask + return result.fillna(False).astype(bool) + + def create_mask_from_window(series: Any, entry_window_opens:int, entry_window_closes:int, use_cal: bool = True): """ Accepts series and window range (number of minutes from market start) and returns boolean mask denoting