Compare commits

...

12 Commits

Author SHA1 Message Date
Matthias
bfe9aac16b fix(hyperliquid): exclude HIP3 pairs for now
part of #12558
2025-11-28 18:16:26 +01:00
Matthias
1770a68457 chore: fix wrongly worded exchange_response endpoint naming 2025-11-28 13:37:31 +01:00
Matthias
57fd455adf fix: Proper fix for plotscript
this time also saving the file ...

closes #12557
2025-11-28 13:29:50 +01:00
Matthias
61ecaa4c41 fix: ensure automatic exportfilename detection works
closes #12557
2025-11-28 13:13:57 +01:00
Matthias
682c4137b4 docs: improved wording 2025-11-27 20:16:38 +01:00
Matthias
759c18df3d docs: improve pre-commit docs wording 2025-11-27 07:04:00 +01:00
Matthias
04fda255de Merge pull request #12529 from mrpabloyeah/fix-backtesting-exception-when-no-data-is-available-for-a-pair
Fix backtesting exception when no data is available for a pair
2025-11-27 06:29:24 +01:00
Matthias
26f23c10b5 Merge pull request #12555 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2025-11-27 06:13:17 +01:00
Freqtrade Bot
001baa07b1 chore: update pre-commit hooks 2025-11-27 03:21:53 +00:00
mrpabloyeah
72724037af Replaced unsafe loop with a list comprehension & added docstring in refresh_pairlist() 2025-11-26 11:03:18 +01:00
mrpabloyeah
77e8a53572 Fix backtesting exception when no data is available for a pair (new approach) 2025-11-24 10:43:00 +01:00
mrpabloyeah
f98efdbe07 Fix backtesting exception when no data is available for a pair 2025-11-17 13:09:48 +01:00
10 changed files with 829 additions and 463 deletions

View File

@@ -675,7 +675,7 @@ Should you experience problems you suspect are caused by websockets, you can dis
Should you be required to use a proxy, please refer to the [proxy section](#using-a-proxy-with-freqtrade) for more information.
!!! Info "Rollout"
We're implementing this out slowly, ensuring stability of your bots.
We're rolling this out slowly, ensuring stability of your bots.
Currently, usage is limited to ohlcv data streams.
It's also limited to a few exchanges, with new exchanges being added on an ongoing basis.

View File

@@ -26,10 +26,19 @@ Alternatively (e.g. if your system is not supported by the setup.sh script), fol
This will install all required tools for development, including `pytest`, `ruff`, `mypy`, and `coveralls`.
Then install the git hook scripts by running `pre-commit install`, so your changes will be verified locally before committing.
This avoids a lot of waiting for CI already, as some basic formatting checks are done locally on your machine.
Run the following command to install the git hook scripts:
Before opening a pull request, please familiarize yourself with our [Contributing Guidelines](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md).
``` bash
pre-commit install
```
These pre-commit scripts check your changes automatically before each commit.
If any formatting issues are found, the commit will fail and will prompt for fixes.
This reduces unnecessary CI failures, reduces maintenance burden, and improves code quality.
You can run the checks manually when necessary with `pre-commit run -a`.
Before opening a pull request, please also familiarize yourself with our [Contributing Guidelines](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md).
### Devcontainer setup

File diff suppressed because it is too large Load Diff

View File

@@ -1769,7 +1769,7 @@ class Exchange:
balances.pop("total", None)
balances.pop("used", None)
self._log_exchange_response("fetch_balances", balances)
self._log_exchange_response("fetch_balance", balances)
return balances
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e

View File

@@ -3,6 +3,7 @@
import logging
from copy import deepcopy
from datetime import datetime
from typing import Any
from freqtrade.constants import BuySell
from freqtrade.enums import MarginMode, TradingMode
@@ -56,6 +57,13 @@ class Hyperliquid(Exchange):
config.update(super()._ccxt_config)
return config
def market_is_tradable(self, market: dict[str, Any]) -> bool:
parent_check = super().market_is_tradable(market)
# Exclude hip3 markets for now - which have the format XYZ:GOOGL/USDT:USDT -
# and XYZ:GOOGL as base
return parent_check and ":" not in market["base"]
def get_max_leverage(self, pair: str, stake_amount: float | None) -> float:
# There are no leverage tiers
if self.trading_mode == TradingMode.FUTURES:

View File

@@ -82,7 +82,7 @@ class Kraken(Exchange):
balances.pop("free", None)
balances.pop("total", None)
balances.pop("used", None)
self._log_exchange_response("fetch_balances", balances)
self._log_exchange_response("fetch_balance", balances)
# Consolidate balances
balances = self.consolidate_balances(balances)
@@ -104,7 +104,7 @@ class Kraken(Exchange):
balances[bal]["used"] = sum(order[1] for order in order_list if order[0] == bal)
balances[bal]["free"] = balances[bal]["total"] - balances[bal]["used"]
self._log_exchange_response("fetch_balances2", balances)
self._log_exchange_response("fetch_balance2", balances)
return balances
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e

View File

@@ -126,6 +126,7 @@ class Backtesting:
self.config["dry_run"] = True
self.price_pair_prec: dict[str, Series] = {}
self.available_pairs: list[str] = []
self.run_ids: dict[str, str] = {}
self.strategylist: list[IStrategy] = []
self.all_bt_content: dict[str, BacktestContentType] = {}
@@ -176,7 +177,8 @@ class Backtesting:
self._validate_pairlists_for_backtesting()
self.dataprovider.add_pairlisthandler(self.pairlists)
self.pairlists.refresh_pairlist()
self.dynamic_pairlist: bool = self.config.get("enable_dynamic_pairlist", False)
self.pairlists.refresh_pairlist(only_first=self.dynamic_pairlist)
if len(self.pairlists.whitelist) == 0:
raise OperationalException("No pair in whitelist.")
@@ -211,7 +213,6 @@ class Backtesting:
self._can_short = self.trading_mode != TradingMode.SPOT
self._position_stacking: bool = self.config.get("position_stacking", False)
self.enable_protections: bool = self.config.get("enable_protections", False)
self.dynamic_pairlist: bool = self.config.get("enable_dynamic_pairlist", False)
migrate_data(config, self.exchange)
self.init_backtest()
@@ -335,10 +336,12 @@ class Backtesting:
self.progress.set_new_value(1)
self._load_bt_data_detail()
self.price_pair_prec = {}
for pair in self.pairlists.whitelist:
if pair in data:
# Load price precision logic
self.price_pair_prec[pair] = get_tick_size_over_time(data[pair])
self.available_pairs.append(pair)
return data, self.timerange
def _load_bt_data_detail(self) -> None:
@@ -1587,7 +1590,7 @@ class Backtesting:
self.check_abort()
if self.dynamic_pairlist and self.pairlists:
self.pairlists.refresh_pairlist()
self.pairlists.refresh_pairlist(pairs=self.available_pairs)
pairs = self.pairlists.whitelist
# Reset open trade count for this candle

View File

@@ -75,11 +75,11 @@ def init_plotscript(config, markets: list, startup_candles: int = 0):
)
no_trades = False
filename = config.get("exportfilename")
filename = config.get("exportfilename") or config.get("exportdirectory")
if config.get("no_trades", False):
no_trades = True
elif config["trade_source"] == "file":
if not filename.is_dir() and not filename.is_file():
if not filename or (not filename.is_dir() and not filename.is_file()):
logger.warning("Backtest file is missing skipping trades.")
no_trades = True
try:

View File

@@ -134,8 +134,20 @@ class PairListManager(LoggingMixin):
def _get_cached_tickers(self) -> Tickers:
return self._exchange.get_tickers()
def refresh_pairlist(self) -> None:
"""Run pairlist through all configured Pairlist Handlers."""
def refresh_pairlist(self, only_first: bool = False, pairs: list[str] | None = None) -> None:
"""
Run pairlist through all configured Pairlist Handlers.
:param only_first: If True, only run the first PairList handler (the generator)
and skip all subsequent filters. Used during backtesting startup to ensure
historic data is loaded for the complete universe of pairs that the
generator can produce (even if later filters would reduce the list size).
Prevents missing data when a filter returns a variable number of pairs
across refresh cycles.
:param pairs: Optional list of pairs to intersect with the generated pairlist.
Only pairs present both in the generated list and this parameter are kept.
Used in backtesting to filter out pairs with no available data.
"""
# Tickers should be cached to avoid calling the exchange on each call.
tickers: dict = {}
if self._tickers_needed:
@@ -144,6 +156,11 @@ class PairListManager(LoggingMixin):
# Generate the pairlist with first Pairlist Handler in the chain
pairlist = self._pairlist_handlers[0].gen_pairlist(tickers)
# Optional intersection with an explicit list of pairs (used in backtesting)
if pairs is not None:
pairlist = [p for p in pairlist if p in pairs]
if not only_first:
# Process all Pairlist Handlers in the chain
# except for the first one, which is the generator.
for pairlist_handler in self._pairlist_handlers[1:]:

View File

@@ -2772,7 +2772,7 @@ def test_time_pair_generator_open_trades_first(mocker, default_conf, dynamic_pai
dummy_row = (end_date, 1.0, 1.1, 0.9, 1.0, 0, 0, 0, 0, None, None)
data = {pair: [dummy_row] for pair in pairs}
def mock_refresh(self):
def mock_refresh(self, **kwargs):
# Simulate shuffle
self._whitelist = pairs[::-1] # ['ETH/BTC', 'NEO/BTC', 'LTC/BTC', 'XRP/BTC']