This commit is contained in:
David Brazda
2024-10-21 20:15:58 +02:00
parent 39e4591d6a
commit 609a2846c2
4 changed files with 75 additions and 13 deletions

View File

@ -44,9 +44,10 @@ exits = exits | forced_exits_window
exits.tail(20) 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 # Indicators
Custom indicators in the `indicators` folder. Custom indicators in the `indicators` folder.

View File

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name='ttools', name='ttools',
version='0.3.4', version='0.3.5',
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=[
'vectorbtpro', 'vectorbtpro',

View File

@ -24,22 +24,28 @@ DIVERGENCE - of two time series, same like in v2realbot
@jit(nopython=True) @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 #div = a+b / a-b will give value between -1 and 1
if divtype == "reln": if divtype == "reln":
return (series1 - series2) / (series1 + series2) out = (series1 - series2) / (series1 + series2)
elif divtype == "rel": elif divtype == "rel":
return series1 - series2 out = series1 - series2
elif divtype == "abs": elif divtype == "abs":
return np.abs(series1 - series2) out = np.abs(series1 - series2)
elif divtype == "absn": elif divtype == "absn":
return np.abs(series1 - series2) / series1 out = np.abs(series1 - series2) / series1
elif divtype == "pctabs": elif divtype == "pctabs":
return np.abs(((series1 - series2) / series1) * 100) out = np.abs(((series1 - series2) / series1) * 100)
elif divtype == "pct": elif divtype == "pct":
return ((series1 - series2) / series1) * 100 out = ((series1 - series2) / series1) * 100
else: 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 Divergence indicator - various divergences between two series
@ -48,11 +54,12 @@ IND_DIVERGENCE = vbt.IF(
class_name='DIVERGENCE', class_name='DIVERGENCE',
module_name='ttools', module_name='ttools',
input_names=['series1', 'series2'], input_names=['series1', 'series2'],
param_names=["divtype"], param_names=["divtype", "round"],
output_names=['div'] output_names=['div']
).with_apply_func(divergence, ).with_apply_func(divergence,
takes_1d=True, takes_1d=True,
param_settings=dict( param_settings=dict(
), ),
divtype="reln" divtype="reln",
round=4
) )

View File

@ -4,6 +4,7 @@ import pandas_market_calendars as mcal
from typing import Any from typing import Any
import datetime import datetime
#TBD create NUMBA alternatives
def isrising(series: pd.Series, n: int) -> pd.Series: def isrising(series: pd.Series, n: int) -> pd.Series:
""" """
Checks if a series is rising over a given window size. 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) 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: def isfalling(series: pd.Series, n: int) -> pd.Series:
""" """
Checks if a series is falling over a given window size. 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) 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): 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 Accepts series and window range (number of minutes from market start) and returns boolean mask denoting