diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 000000000..33d3885b2 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-01-30 - Hidden Complexity in Properties +**Learning:** Properties like `self.timeframes` in `freqtrade`'s Exchange class may regenerate lists/dicts on every access. Using them in tight loops causes significant overhead ($O(N)$ lookup + allocation) compared to cached variables. +**Action:** Always inspect property implementations before using them in loops. Hoist invariant property accesses out of loops and convert to sets for membership tests. diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ac19696fa..d236b18d3 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2732,6 +2732,10 @@ class Exchange: """ input_coroutines: list[Coroutine[Any, Any, OHLCVResponse]] = [] cached_pairs = [] + # optimization: cache timeframes as a set outside the loop to avoid repeated property access + # and linear search + available_timeframes = set(self.timeframes) + for pair, timeframe, candle_type in set(pair_list): if candle_type == CandleType.FUNDING_RATE and timeframe != ( ff_tf := self.get_option("funding_fee_timeframe") @@ -2743,7 +2747,7 @@ class Exchange: f"downloading {ff_tf} instead." ) timeframe = ff_tf - invalid_timeframe = timeframe not in self.timeframes and candle_type in ( + invalid_timeframe = timeframe not in available_timeframes and candle_type in ( CandleType.SPOT, CandleType.FUTURES, )