diff --git a/freqtrade/util/formatters.py b/freqtrade/util/formatters.py index fa656f52b..caac5a68b 100644 --- a/freqtrade/util/formatters.py +++ b/freqtrade/util/formatters.py @@ -78,5 +78,5 @@ def format_duration(td: timedelta) -> str: """ d = td.days h, r = divmod(td.seconds, 3600) - m, s = divmod(r, 60) + m, _ = divmod(r, 60) return f"{d}d {h:02d}:{m:02d}" diff --git a/requirements-dev.txt b/requirements-dev.txt index eead94387..b8c97c881 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ -r requirements-freqai-rl.txt -r docs/requirements-docs.txt -ruff==0.12.12 +ruff==0.13.0 mypy==1.18.1 pre-commit==4.3.0 pytest==8.4.2 diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 3e8dd6c60..5231d45a4 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -658,7 +658,9 @@ def test_start_new_strategy_no_arg(): args = [ "new-strategy", ] - with pytest.raises(OperationalException, match="`new-strategy` requires --strategy to be set."): + with pytest.raises( + OperationalException, match=r"`new-strategy` requires --strategy to be set\." + ): start_new_strategy(get_args(args)) @@ -803,7 +805,7 @@ def test_get_ui_download_url_direct(mocker): assert last_version == "0.0.1" assert x == "http://download1.zip" - with pytest.raises(ValueError, match="UI-Version not found."): + with pytest.raises(ValueError, match=r"UI-Version not found\."): x, last_version = get_ui_download_url("0.0.3", False) @@ -1650,7 +1652,7 @@ def test_hyperopt_show(mocker, capsys): pargs = get_args(args) pargs["config"] = None with pytest.raises( - OperationalException, match="The index of the epoch to show should be greater than -4." + OperationalException, match=r"The index of the epoch to show should be greater than -4\." ): start_hyperopt_show(pargs) @@ -1658,7 +1660,7 @@ def test_hyperopt_show(mocker, capsys): pargs = get_args(args) pargs["config"] = None with pytest.raises( - OperationalException, match="The index of the epoch to show should be less than 4." + OperationalException, match=r"The index of the epoch to show should be less than 4\." ): start_hyperopt_show(pargs) @@ -2032,5 +2034,7 @@ def test_start_edge(): ] pargs = get_args(args) - with pytest.raises(OperationalException, match="The Edge module has been deprecated in 2023.9"): + with pytest.raises( + OperationalException, match=r"The Edge module has been deprecated in 2023\.9" + ): start_edge(pargs) diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index dd85fb8bb..e7c914040 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -75,7 +75,7 @@ def test_get_latest_hyperopt_file(testdatadir): # Test with absolute path with pytest.raises( OperationalException, - match="--hyperopt-filename expects only the filename, not an absolute path.", + match=r"--hyperopt-filename expects only the filename, not an absolute path\.", ): get_latest_hyperopt_file(str(testdatadir.parent), str(testdatadir.parent)) @@ -344,7 +344,7 @@ def test_create_cum_profit1(testdatadir): assert cum_profits.iloc[0]["cum_profits"] == 0 assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 - with pytest.raises(ValueError, match="Trade dataframe empty."): + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): create_cum_profit( df.set_index("date"), bt_data[bt_data["pair"] == "NOTAPAIR"], @@ -369,10 +369,10 @@ def test_calculate_max_drawdown(testdatadir): underwater = calculate_underwater(bt_data) assert isinstance(underwater, DataFrame) - with pytest.raises(ValueError, match="Trade dataframe empty."): + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): calculate_max_drawdown(DataFrame()) - with pytest.raises(ValueError, match="Trade dataframe empty."): + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): calculate_underwater(DataFrame()) @@ -391,7 +391,7 @@ def test_calculate_csum(testdatadir): assert csum_min1 == csum_min + 5 assert csum_max1 == csum_max + 5 - with pytest.raises(ValueError, match="Trade dataframe empty."): + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): csum_min, csum_max = calculate_csum(DataFrame()) diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index 3d6c9adc2..4cf3ed23f 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -49,7 +49,7 @@ def test_ohlcv_to_dataframe(ohlcv_history_list, caplog): def test_trades_to_ohlcv(trades_history_df, caplog): caplog.set_level(logging.DEBUG) - with pytest.raises(ValueError, match="Trade-list empty."): + with pytest.raises(ValueError, match=r"Trade-list empty\."): trades_to_ohlcv(pd.DataFrame(columns=trades_history_df.columns), "1m") df = trades_to_ohlcv(trades_history_df, "1m") diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 6e07e5025..1550d93e6 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -859,7 +859,7 @@ def test_validate_pricing(default_conf, mocker): default_conf["exchange"]["name"] = "binance" ExchangeResolver.load_exchange(default_conf) has.update({"fetchTicker": False}) - with pytest.raises(OperationalException, match="Ticker pricing not available for .*"): + with pytest.raises(OperationalException, match=r"Ticker pricing not available for .*"): ExchangeResolver.load_exchange(default_conf) has.update({"fetchTicker": True}) @@ -868,7 +868,7 @@ def test_validate_pricing(default_conf, mocker): ExchangeResolver.load_exchange(default_conf) has.update({"fetchL2OrderBook": False}) - with pytest.raises(OperationalException, match="Orderbook not available for .*"): + with pytest.raises(OperationalException, match=r"Orderbook not available for .*"): ExchangeResolver.load_exchange(default_conf) has.update({"fetchL2OrderBook": True}) @@ -877,7 +877,7 @@ def test_validate_pricing(default_conf, mocker): default_conf["trading_mode"] = TradingMode.FUTURES default_conf["margin_mode"] = MarginMode.ISOLATED - with pytest.raises(OperationalException, match="Ticker pricing not available for .*"): + with pytest.raises(OperationalException, match=r"Ticker pricing not available for .*"): ExchangeResolver.load_exchange(default_conf) @@ -3556,7 +3556,7 @@ def test_get_historic_trades_notsupported( pair = "ETH/BTC" with pytest.raises( - OperationalException, match="This exchange does not support downloading Trades." + OperationalException, match=r"This exchange does not support downloading Trades\." ): exchange.get_historic_trades(pair, since=trades_history[0][0], until=trades_history[-1][0]) @@ -4442,7 +4442,7 @@ def test_get_markets( def test_get_markets_error(default_conf, mocker): ex = get_patched_exchange(mocker, default_conf) mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=None)) - with pytest.raises(OperationalException, match="Markets were not loaded."): + with pytest.raises(OperationalException, match=r"Markets were not loaded\."): ex.get_markets("LTC", "USDT", True, False) @@ -5243,7 +5243,7 @@ def test__fetch_and_calculate_funding_fees( # Return empty "refresh_latest" mocker.patch(f"{EXMS}.refresh_latest_ohlcv", return_value={}) ex = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange) - with pytest.raises(ExchangeError, match="Could not find funding rates."): + with pytest.raises(ExchangeError, match=r"Could not find funding rates\."): ex._fetch_and_calculate_funding_fees( pair="ADA/USDT:USDT", amount=amount, is_short=False, open_date=d1, close_date=d2 ) diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 16def3d19..1304ca9ba 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -986,7 +986,7 @@ def test_execute_entry( # Fail to get price... mocker.patch(f"{EXMS}.get_rate", MagicMock(return_value=0.0)) - with pytest.raises(PricingError, match="Could not determine entry price."): + with pytest.raises(PricingError, match=r"Could not determine entry price\."): freqtrade.execute_entry(pair, stake_amount, is_short=is_short) # In case of custom entry price diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 292ee92aa..adf88cb25 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -357,7 +357,7 @@ def test_get_pair_precision_bt(default_conf, mocker) -> None: pair = "UNITTEST/BTC" backtesting.pairlists._whitelist = [pair] ex_mock = mocker.patch(f"{EXMS}.get_precision_price", return_value=1e-5) - data, timerange = backtesting.load_bt_data() + data, _timerange = backtesting.load_bt_data() assert data assert backtesting.get_pair_precision(pair, dt_utc(2018, 1, 1)) == (1e-8, TICK_SIZE) @@ -444,7 +444,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) -> backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) - with pytest.raises(OperationalException, match="No data found. Terminating."): + with pytest.raises(OperationalException, match=r"No data found. Terminating\."): backtesting.start() @@ -465,7 +465,7 @@ def test_backtesting_no_pair_left(default_conf, mocker) -> None: default_conf["export"] = "none" default_conf["timerange"] = "20180101-20180102" - with pytest.raises(OperationalException, match="No pair in whitelist."): + with pytest.raises(OperationalException, match=r"No pair in whitelist\."): Backtesting(default_conf) default_conf.update( @@ -476,7 +476,7 @@ def test_backtesting_no_pair_left(default_conf, mocker) -> None: ) with pytest.raises( - OperationalException, match="Detail timeframe must be smaller than strategy timeframe." + OperationalException, match=r"Detail timeframe must be smaller than strategy timeframe\." ): Backtesting(default_conf) @@ -517,7 +517,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, tickers) -> None: default_conf["strategy_list"] = [CURRENT_TEST_STRATEGY, "StrategyTestV2"] with pytest.raises( OperationalException, - match="PrecisionFilter not allowed for backtesting multiple strategies.", + match=r"PrecisionFilter not allowed for backtesting multiple strategies\.", ): Backtesting(default_conf) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 4d9d68eae..21f011a9b 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -280,7 +280,7 @@ def test_start_no_data(mocker, hyperopt_conf, tmp_path) -> None: "5", ] pargs = get_args(args) - with pytest.raises(OperationalException, match="No data found. Terminating."): + with pytest.raises(OperationalException, match=r"No data found. Terminating\."): start_hyperopt(pargs) # Cleanup since that failed hyperopt start leaves a lockfile. @@ -1127,7 +1127,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None assert opt.backtesting.strategy.max_open_trades != 1 opt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: "ET1" - with pytest.raises(OperationalException, match="Optuna Sampler ET1 not supported."): + with pytest.raises(OperationalException, match=r"Optuna Sampler ET1 not supported\."): opt.get_optimizer(42) diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index 6d3110509..8fd47a8e6 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -11,8 +11,8 @@ from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver def test_hyperoptlossresolver_noname(default_conf): with pytest.raises( OperationalException, - match="No Hyperopt loss set. Please use `--hyperopt-loss` to specify " - "the Hyperopt-Loss class to use.", + match=r"No Hyperopt loss set. Please use `--hyperopt-loss` to specify " + r"the Hyperopt-Loss class to use\.", ): HyperOptLossResolver.load_hyperoptloss(default_conf) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index f2c3c69e3..f12bc31ec 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -1669,7 +1669,7 @@ def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers): with pytest.raises( OperationalException, - match="RangeStabilityFilter requires sort_direction to be either None.*", + match=r"RangeStabilityFilter requires sort_direction to be either None\.*", ): get_patched_freqtradebot(mocker, default_conf) @@ -2526,7 +2526,7 @@ def test_MarketCapPairList_exceptions(mocker, default_conf_usdt, caplog): } ] with pytest.raises( - OperationalException, match="Category layer250 not in coingecko category list." + OperationalException, match=r"Category layer250 not in coingecko category list\." ): PairListManager(exchange, default_conf_usdt) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index ed2cd8ac4..2c1e80bf4 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -82,7 +82,7 @@ def test_fetch_pairlist_mock_response_html(mocker, rpl_config): exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0 ) - with pytest.raises(OperationalException, match="RemotePairList is not of type JSON."): + with pytest.raises(OperationalException, match=r"RemotePairList is not of type JSON\."): remote_pairlist.fetch_pairlist() diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 3c8490d44..03cdaa0b6 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -393,7 +393,7 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short): freqtradebot.strategy.order_types["stoploss_on_exchange"] = True create_mock_trades(fee, is_short) rpc = RPC(freqtradebot) - with pytest.raises(RPCException, match="Trade with id '200' not found."): + with pytest.raises(RPCException, match=r"Trade with id '200' not found\."): rpc._rpc_delete("200") trades = Trade.session.scalars(select(Trade)).all() @@ -1204,7 +1204,7 @@ def test_rpc_force_entry(mocker, default_conf, ticker, fee, limit_buy_order_open patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = "ETH/BTC" - with pytest.raises(RPCException, match="Maximum number of trades is reached."): + with pytest.raises(RPCException, match=r"Maximum number of trades is reached\."): rpc._rpc_force_entry(pair, None) freqtradebot.config["max_open_trades"] = 5 @@ -1286,7 +1286,7 @@ def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None: patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = "ETH/BTC" - with pytest.raises(RPCException, match="Can't go short on Spot markets."): + with pytest.raises(RPCException, match=r"Can't go short on Spot markets\."): rpc._rpc_force_entry(pair, None, order_side=SignalDirection.SHORT) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 56068e6fe..88437fd03 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -355,7 +355,7 @@ def test_api__init__(default_conf, mocker): apiserver = ApiServer(default_conf) apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) assert apiserver._config == default_conf - with pytest.raises(OperationalException, match="RPC Handler already attached."): + with pytest.raises(OperationalException, match=r"RPC Handler already attached\."): apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) apiserver.cleanup() @@ -534,7 +534,7 @@ def test_api_reloadconf(botclient): def test_api_pause(botclient): - ftbot, client = botclient + _ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/pause") assert_response(rc) @@ -3281,7 +3281,7 @@ def test_api_download_data(botclient, mocker, tmp_path): def test_api_markets_live(botclient): - ftbot, client = botclient + _ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/markets") assert_response(rc, 200) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 97983ecd8..e9e2ee65a 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -984,7 +984,7 @@ async def test_telegram_profit_long_short_handle( mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch.multiple(EXMS, fetch_ticker=ticker_usdt, get_fee=fee) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) + telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) # When there are no trades await telegram._profit_long(update=update, context=MagicMock()) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index f06248166..3d6f5c752 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -988,7 +988,7 @@ def test_auto_hyperopt_interface_loadparams(default_conf, mocker, caplog): } mocker.patch("freqtrade.strategy.hyper.HyperoptTools.load_params", return_value=expected_result) - with pytest.raises(OperationalException, match="Invalid parameter file provided."): + with pytest.raises(OperationalException, match=r"Invalid parameter file provided\."): StrategyResolver.load_strategy(default_conf) mocker.patch( diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index 61d5605fb..923f3635b 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -111,7 +111,7 @@ def test_load_strategy_noname(default_conf): default_conf["strategy"] = "" with pytest.raises( OperationalException, - match="No strategy set. Please use `--strategy` to specify the strategy class to use.", + match=r"No strategy set. Please use `--strategy` to specify the strategy class to use\.", ): StrategyResolver.load_strategy(default_conf) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 66d039845..ba54e7b52 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -250,7 +250,7 @@ def test_from_recursive_files(testdatadir) -> None: assert "test_pricing2_conf.json" in conf["config_files"][3] files = testdatadir / "testconfigs/recursive.json" - with pytest.raises(OperationalException, match="Config loop detected."): + with pytest.raises(OperationalException, match=r"Config loop detected\."): load_from_files([files]) @@ -672,7 +672,7 @@ def test_validate_max_open_trades(default_conf): default_conf["stake_amount"] = "unlimited" with pytest.raises( OperationalException, - match="`max_open_trades` and `stake_amount` cannot both be unlimited.", + match=r"`max_open_trades` and `stake_amount` cannot both be unlimited\.", ): validate_config_consistency(default_conf) @@ -691,14 +691,15 @@ def test_validate_price_side(default_conf): conf["order_types"]["entry"] = "market" with pytest.raises( OperationalException, - match='Market entry orders require entry_pricing.price_side = "other".', + match=r'Market entry orders require entry_pricing.price_side = "other"\.', ): validate_config_consistency(conf) conf = deepcopy(default_conf) conf["order_types"]["exit"] = "market" with pytest.raises( - OperationalException, match='Market exit orders require exit_pricing.price_side = "other".' + OperationalException, + match=r'Market exit orders require exit_pricing.price_side = "other"\.', ): validate_config_consistency(conf) @@ -716,8 +717,8 @@ def test_validate_tsl(default_conf): default_conf["stoploss"] = 0.0 with pytest.raises( OperationalException, - match="The config stoploss needs to be different " - "from 0 to avoid problems with sell orders.", + match=r"The config stoploss needs to be different " + r"from 0 to avoid problems with sell orders\.", ): validate_config_consistency(default_conf) default_conf["stoploss"] = -0.10 @@ -767,7 +768,7 @@ def test_validate_whitelist(default_conf): del conf["exchange"]["pair_whitelist"] # Test error case with pytest.raises( - OperationalException, match="StaticPairList requires pair_whitelist to be set." + OperationalException, match=r"StaticPairList requires pair_whitelist to be set\." ): validate_config_consistency(conf) @@ -969,7 +970,7 @@ def test__validate_consumers(default_conf, caplog) -> None: conf = deepcopy(default_conf) conf.update({"external_message_consumer": {"enabled": True, "producers": []}}) with pytest.raises( - OperationalException, match="You must specify at least 1 Producer to connect to." + OperationalException, match=r"You must specify at least 1 Producer to connect to\." ): validate_config_consistency(conf) @@ -996,7 +997,7 @@ def test__validate_consumers(default_conf, caplog) -> None: } ) with pytest.raises( - OperationalException, match="Producer names must be unique. Duplicate: default" + OperationalException, match=r"Producer names must be unique\. Duplicate: default" ): validate_config_consistency(conf) @@ -1026,7 +1027,7 @@ def test__validate_orderflow(default_conf) -> None: conf["exchange"]["use_public_trades"] = True with pytest.raises( ConfigurationError, - match="Orderflow is a required configuration key when using public trades.", + match=r"Orderflow is a required configuration key when using public trades\.", ): validate_config_consistency(conf) @@ -1050,7 +1051,7 @@ def test_validate_edge_removal(default_conf): } with pytest.raises( ConfigurationError, - match="Edge is no longer supported and has been removed from Freqtrade with 2025.6.", + match=r"Edge is no longer supported and has been removed from Freqtrade with 2025\.6\.", ): validate_config_consistency(default_conf)