From: Jérôme Benoit Date: Mon, 27 Jul 2026 13:30:03 +0000 (+0200) Subject: fix(quickadapter): invalidate reversal caches together (#155) X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=7d524b246167209d329bee649ec88cf3d7b78a7b;p=freqai-strategies.git fix(quickadapter): invalidate reversal caches together (#155) Purge threshold and deviation entries for a pair before recording its new dataframe signature, and invalidate both caches when live pair-local label period or NATR multiplier state changes. --- diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index d68f2cb..9953570 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -14,7 +14,6 @@ from typing import ( Optional, Sequence, TypedDict, - TypeVar, ) import numpy as np @@ -97,7 +96,6 @@ CandleDeviationCacheKey = tuple[ str, DfSignature, float, float, int, InterpolationDirection, float ] CandleThresholdCacheKey = tuple[str, DfSignature, str, int, float, float] -_PairCacheT = TypeVar("_PairCacheT", bound=dict) class _TradeHistory(TypedDict): @@ -899,7 +897,13 @@ class QuickAdapterV3(IStrategy): def set_label_period_candles(self, pair: str, label_period_candles: Any) -> None: if is_finite_number(label_period_candles) and int(label_period_candles) > 0: - self._label_params[pair]["label_period_candles"] = int(label_period_candles) + label_period_candles = int(label_period_candles) + if ( + self._label_params[pair].get("label_period_candles") + != label_period_candles + ): + self._label_params[pair]["label_period_candles"] = label_period_candles + self._invalidate_pair_caches(pair) def get_label_horizon_candles(self, pair: str) -> int: period = self.get_label_period_candles(pair) @@ -936,9 +940,15 @@ class QuickAdapterV3(IStrategy): is_finite_number(label_natr_multiplier) and float(label_natr_multiplier) > 0.0 ): - self._label_params[pair]["label_natr_multiplier"] = float( - label_natr_multiplier - ) + label_natr_multiplier = float(label_natr_multiplier) + if ( + self._label_params[pair].get("label_natr_multiplier") + != label_natr_multiplier + ): + self._label_params[pair]["label_natr_multiplier"] = ( + label_natr_multiplier + ) + self._invalidate_pair_caches(pair) def get_label_natr_multiplier_fraction( self, @@ -1765,13 +1775,20 @@ class QuickAdapterV3(IStrategy): idx = length + idx return min(max(0, idx), length - 1) - def _invalidate_pair_cache( - self, cache: _PairCacheT, pair: str, df_signature: DfSignature - ) -> _PairCacheT: - if self._cached_df_signature.get(pair) != df_signature: - cache = type(cache)({k: v for k, v in cache.items() if k[0] != pair}) - self._cached_df_signature[pair] = df_signature - return cache + def _invalidate_pair_caches( + self, pair: str, df_signature: Optional[DfSignature] = None + ) -> None: + if df_signature is None or self._cached_df_signature.get(pair) != df_signature: + self._candle_deviation_cache = { + k: v for k, v in self._candle_deviation_cache.items() if k[0] != pair + } + self._candle_threshold_cache = { + k: v for k, v in self._candle_threshold_cache.items() if k[0] != pair + } + if df_signature is None: + self._cached_df_signature.pop(pair, None) + else: + self._cached_df_signature[pair] = df_signature def _calculate_candle_deviation( self, @@ -1784,9 +1801,7 @@ class QuickAdapterV3(IStrategy): quantile_exponent: float = 1.5, ) -> float: df_signature = QuickAdapterV3._df_signature(df) - self._candle_deviation_cache = self._invalidate_pair_cache( - self._candle_deviation_cache, pair, df_signature - ) + self._invalidate_pair_caches(pair, df_signature) cache_key: CandleDeviationCacheKey = ( pair, df_signature, @@ -1854,9 +1869,7 @@ class QuickAdapterV3(IStrategy): candle_idx: int = -1, ) -> float: df_signature = QuickAdapterV3._df_signature(df) - self._candle_threshold_cache = self._invalidate_pair_cache( - self._candle_threshold_cache, pair, df_signature - ) + self._invalidate_pair_caches(pair, df_signature) cache_key: CandleThresholdCacheKey = ( pair, df_signature,