From 540fc5f35dee423207ca7cf6d005159807ddb301 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 25 Mar 2025 15:45:29 +0100 Subject: [PATCH] feat(qav3): add zero lag support to FRAMA MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- quickadapter/user_data/strategies/Utils.py | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index efaa78f..7b48dd0 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -121,8 +121,9 @@ def vwapb(dataframe: pd.DataFrame, window=20, num_of_std=1) -> tuple: return vwap_low, vwap, vwap_high -def zero_lag_series(series: pd.Series, timeperiod: int) -> pd.Series: - lag = int(0.5 * (timeperiod - 1)) +def zero_lag_series(series: pd.Series, period: int) -> pd.Series: + """Applies a zero lag filter to reduce MA lag.""" + lag = int(0.5 * (period - 1)) return 2 * series - series.shift(lag) @@ -167,9 +168,9 @@ def _fractal_dimension(high: np.ndarray, low: np.ndarray, period: int) -> float: return np.clip(D, 1.0, 2.0) -def frama(df: pd.DataFrame, period: int = 16) -> pd.Series: +def frama(df: pd.DataFrame, period: int = 16, zero_lag=False) -> pd.Series: """ - Original FRAMA implementation per Ehlers' paper. + Original FRAMA implementation per Ehlers' paper with optional zero lag. """ if period % 2 != 0: raise ValueError("period must be even") @@ -178,18 +179,18 @@ def frama(df: pd.DataFrame, period: int = 16) -> pd.Series: low = df["low"] close = df["close"] + if zero_lag: + high = zero_lag_series(high, period=period) + low = zero_lag_series(low, period=period) + close = zero_lag_series(close, period=period) + fd = pd.Series(np.nan, index=close.index) for i in range(period, len(close)): window_high = high.iloc[i - period : i] window_low = low.iloc[i - period : i] - - if len(window_high) != period: - continue - fd.iloc[i] = _fractal_dimension(window_high.values, window_low.values, period) - alpha = np.exp(-4.6 * (fd - 1)) - alpha = np.clip(alpha, 0.01, 1) + alpha = np.exp(-4.6 * (fd - 1)).clip(0.01, 1) frama = pd.Series(np.nan, index=close.index) frama.iloc[period - 1] = close.iloc[:period].mean() @@ -215,7 +216,7 @@ def smma(series: pd.Series, period: int, zero_lag=False, offset=0) -> pd.Series: return pd.Series(index=series.index, dtype=float) if zero_lag: - series = zero_lag_series(series, timeperiod=period) + series = zero_lag_series(series, period=period) smma = pd.Series(np.nan, index=series.index) smma.iloc[period - 1] = series.iloc[:period].mean() @@ -253,8 +254,8 @@ def ewo( price_series = get_price_fn(pricemode)(dataframe) if zero_lag: - price_series_ma1 = zero_lag_series(price_series, timeperiod=ma1_length) - price_series_ma2 = zero_lag_series(price_series, timeperiod=ma2_length) + price_series_ma1 = zero_lag_series(price_series, period=ma1_length) + price_series_ma2 = zero_lag_series(price_series, period=ma2_length) else: price_series_ma1 = price_series price_series_ma2 = price_series -- 2.43.0