Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] develop from freqtrade:develop #404

Merged
merged 5 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ repos:

- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.8.6'
rev: 'v0.9.1'
hooks:
- id: ruff
- id: ruff-format
Expand Down
5 changes: 2 additions & 3 deletions freqtrade/commands/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ def __init__(self, *args, **kwargs):
help="Specify the class name of the hyperopt loss function class (IHyperOptLoss). "
"Different functions can generate completely different results, "
"since the target for optimization is different. Built-in Hyperopt-loss-functions are: "
f'{", ".join(HYPEROPT_LOSS_BUILTIN)}',
f"{', '.join(HYPEROPT_LOSS_BUILTIN)}",
metavar="NAME",
),
"hyperoptexportfilename": Arg(
Expand Down Expand Up @@ -663,8 +663,7 @@ def __init__(self, *args, **kwargs):
"--ignore-missing-spaces",
"--ignore-unparameterized-spaces",
help=(
"Suppress errors for any requested Hyperopt spaces "
"that do not contain any parameters."
"Suppress errors for any requested Hyperopt spaces that do not contain any parameters."
),
action="store_true",
),
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/commands/data_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
def _check_data_config_download_sanity(config: Config) -> None:
if "days" in config and "timerange" in config:
raise ConfigurationError(
"--days and --timerange are mutually exclusive. "
"You can only specify one or the other."
"--days and --timerange are mutually exclusive. You can only specify one or the other."
)

if "pairs" not in config:
Expand Down
4 changes: 2 additions & 2 deletions freqtrade/data/history/history_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ def _download_pair_history(
logger.info(
f'Download history data for "{pair}", {timeframe}, '
f"{candle_type} and store in {datadir}. "
f'From {format_ms_time(since_ms) if since_ms else "start"} to '
f'{format_ms_time(until_ms) if until_ms else "now"}'
f"From {format_ms_time(since_ms) if since_ms else 'start'} to "
f"{format_ms_time(until_ms) if until_ms else 'now'}"
)

logger.debug(
Expand Down
8 changes: 4 additions & 4 deletions freqtrade/exchange/check_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,29 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool:
f"This command requires a configured exchange. You should either use "
f"`--exchange <exchange_name>` or specify a configuration file via `--config`.\n"
f"The following exchanges are available for Freqtrade: "
f'{", ".join(available_exchanges())}'
f"{', '.join(available_exchanges())}"
)

if not is_exchange_known_ccxt(exchange):
raise OperationalException(
f'Exchange "{exchange}" is not known to the ccxt library '
f"and therefore not available for the bot.\n"
f"The following exchanges are available for Freqtrade: "
f'{", ".join(available_exchanges())}'
f"{', '.join(available_exchanges())}"
)

valid, reason, _ = validate_exchange(exchange)
if not valid:
if check_for_bad:
raise OperationalException(
f'Exchange "{exchange}" will not work with Freqtrade. ' f"Reason: {reason}"
f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}'
)
else:
logger.warning(f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}')

if MAP_EXCHANGE_CHILDCLASS.get(exchange, exchange) in SUPPORTED_EXCHANGES:
logger.info(
f'Exchange "{exchange}" is officially supported ' f"by the Freqtrade development team."
f'Exchange "{exchange}" is officially supported by the Freqtrade development team.'
)
else:
logger.warning(
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/exchange/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -3004,8 +3004,7 @@ async def _async_get_trade_history_id(
trades.extend(t[x])
if from_id == from_id_next or t[-1][0] > until:
logger.debug(
f"Stopping because from_id did not change. "
f"Reached {t[-1][0]} > {until}"
f"Stopping because from_id did not change. Reached {t[-1][0]} > {until}"
)
# Reached the end of the defined-download period - add last trade as well.
if has_overlap:
Expand Down
7 changes: 3 additions & 4 deletions freqtrade/freqai/RL/BaseReinforcementLearningModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ def train(self, unfiltered_df: DataFrame, pair: str, dk: FreqaiDataKitchen, **kw
)

logger.info(
f'Training model on {len(dk.data_dictionary["train_features"].columns)}'
f' features and {len(dd["train_features"])} data points'
f"Training model on {len(dk.data_dictionary['train_features'].columns)}"
f" features and {len(dd['train_features'])} data points"
)

self.set_train_and_eval_environments(dd, prices_train, prices_test, dk)
Expand Down Expand Up @@ -346,8 +346,7 @@ def build_ohlc_price_dataframes(
)
elif prices_train.empty:
raise OperationalException(
"No prices found, please follow log warning "
"instructions to correct the strategy."
"No prices found, please follow log warning instructions to correct the strategy."
)

prices_train.rename(columns=rename_dict, inplace=True)
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/freqai/base_models/FreqaiMultiOutputClassifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ def fit(self, X, y, sample_weight=None, fit_params=None):

if y.ndim == 1:
raise ValueError(
"y must have at least two dimensions for "
"multi-output regression but has only one."
"y must have at least two dimensions for multi-output regression but has only one."
)

if sample_weight is not None and not has_fit_parameter(self.estimator, "sample_weight"):
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/freqai/base_models/FreqaiMultiOutputRegressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ def fit(self, X, y, sample_weight=None, fit_params=None):

if y.ndim == 1:
raise ValueError(
"y must have at least two dimensions for "
"multi-output regression but has only one."
"y must have at least two dimensions for multi-output regression but has only one."
)

if sample_weight is not None and not has_fit_parameter(self.estimator, "sample_weight"):
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/freqai/freqai_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def start_live(
# append the historic data once per round
if self.dd.historic_data:
self.dd.update_historic_data(strategy, dk)
logger.debug(f'Updating historic data on pair {metadata["pair"]}')
logger.debug(f"Updating historic data on pair {metadata['pair']}")
self.track_current_candle()

(_, new_trained_timerange, data_load_timerange) = dk.check_if_new_training_required(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ def fit(self, data_dictionary: dict, dk: FreqaiDataKitchen, **kwargs) -> Any:

if self.freqai_info.get("continual_learning", False):
logger.warning(
"Continual learning is not supported for "
"SKLearnRandomForestClassifier, ignoring."
"Continual learning is not supported for SKLearnRandomForestClassifier, ignoring."
)

train_weights = data_dictionary["train_weights"]
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/optimize/analysis/lookahead.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ def start(self) -> None:
found_signals: int = self.full_varHolder.result["results"].shape[0] + 1
if found_signals >= self.targeted_trade_amount:
logger.info(
f"Found {found_signals} trades, "
f"calculating {self.targeted_trade_amount} trades."
f"Found {found_signals} trades, calculating {self.targeted_trade_amount} trades."
)
elif self.targeted_trade_amount >= found_signals >= self.minimum_trade_amount:
logger.info(f"Only found {found_signals} trades. Calculating all available trades.")
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/optimize/analysis/recursive_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,5 @@ def start(config: Config):
)
else:
logger.error(
"There was no strategy specified through --strategy "
"or timeframe was not specified."
"There was no strategy specified through --strategy or timeframe was not specified."
)
8 changes: 4 additions & 4 deletions freqtrade/optimize/optimize_reports/bt_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,15 @@ def text_table_strategy(strategy_results, stake_currency: str, title: str):

# Align drawdown string on the center two space separator.
if "max_drawdown_account" in strategy_results[0]:
drawdown = [f'{t["max_drawdown_account"] * 100:.2f}' for t in strategy_results]
drawdown = [f"{t['max_drawdown_account'] * 100:.2f}" for t in strategy_results]
else:
# Support for prior backtest results
drawdown = [f'{t["max_drawdown_per"]:.2f}' for t in strategy_results]
drawdown = [f"{t['max_drawdown_per']:.2f}" for t in strategy_results]

dd_pad_abs = max([len(t["max_drawdown_abs"]) for t in strategy_results])
dd_pad_per = max([len(dd) for dd in drawdown])
drawdown = [
f'{t["max_drawdown_abs"]:>{dd_pad_abs}} {stake_currency} {dd:>{dd_pad_per}}%'
f"{t['max_drawdown_abs']:>{dd_pad_abs}} {stake_currency} {dd:>{dd_pad_per}}%"
for t, dd in zip(strategy_results, drawdown, strict=False)
]

Expand Down Expand Up @@ -315,7 +315,7 @@ def text_table_add_metrics(strat_results: dict) -> None:
(
"Profit factor",
(
f'{strat_results["profit_factor"]:.2f}'
f"{strat_results['profit_factor']:.2f}"
if "profit_factor" in strat_results
else "N/A"
),
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/persistence/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ def init_db(db_url: str) -> None:
engine = create_engine(db_url, future=True, **kwargs)
except NoSuchModuleError:
raise OperationalException(
f"Given value for db_url: '{db_url}' "
f"is no valid database URL! (See {_SQL_DOCS_URL})"
f"Given value for db_url: '{db_url}' is no valid database URL! (See {_SQL_DOCS_URL})"
)

# https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope
Expand Down
7 changes: 3 additions & 4 deletions freqtrade/plot/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def add_indicators(fig, row, indicators: dict[str, dict], data: pd.DataFrame) ->
fig.add_trace(trace, row, 1)
else:
logger.info(
'Indicator "%s" ignored. Reason: This indicator is not found ' "in your strategy.",
'Indicator "%s" ignored. Reason: This indicator is not found in your strategy.',
indicator,
)

Expand Down Expand Up @@ -394,13 +394,12 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots:
)
elif indicator not in data:
logger.info(
'Indicator "%s" ignored. Reason: This indicator is not '
"found in your strategy.",
'Indicator "%s" ignored. Reason: This indicator is not found in your strategy.',
indicator,
)
elif indicator_b not in data:
logger.info(
'fill_to: "%s" ignored. Reason: This indicator is not ' "in your strategy.",
'fill_to: "%s" ignored. Reason: This indicator is not in your strategy.',
indicator_b,
)
return fig
Expand Down
6 changes: 2 additions & 4 deletions freqtrade/plugins/pairlist/PriceFilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@ def _validate_pair(self, pair: str, ticker: Ticker | None) -> bool:
if self._min_price != 0:
if price < self._min_price:
self.log_once(
f"Removed {pair} from whitelist, "
f"because last price < {self._min_price:.8f}",
f"Removed {pair} from whitelist, because last price < {self._min_price:.8f}",
logger.info,
)
return False
Expand All @@ -168,8 +167,7 @@ def _validate_pair(self, pair: str, ticker: Ticker | None) -> bool:
if self._max_price != 0:
if price > self._max_price:
self.log_once(
f"Removed {pair} from whitelist, "
f"because last price > {self._max_price:.8f}",
f"Removed {pair} from whitelist, because last price > {self._max_price:.8f}",
logger.info,
)
return False
Expand Down
5 changes: 2 additions & 3 deletions freqtrade/plugins/pairlist/RemotePairList.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,12 @@ def __init__(self, *args, **kwargs) -> None:

if self._mode not in ["whitelist", "blacklist"]:
raise OperationalException(
"`mode` not configured correctly. Supported Modes " 'are "whitelist","blacklist"'
'`mode` not configured correctly. Supported Modes are "whitelist","blacklist"'
)

if self._processing_mode not in ["filter", "append"]:
raise OperationalException(
"`processing_mode` not configured correctly. Supported Modes "
'are "filter","append"'
'`processing_mode` not configured correctly. Supported Modes are "filter","append"'
)

if self._pairlist_pos == 0 and self._mode == "blacklist":
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/plugins/pairlist/SpreadFilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ def short_desc(self) -> str:
Short whitelist method description - used for startup-messages
"""
return (
f"{self.name} - Filtering pairs with ask/bid diff above "
f"{self._max_spread_ratio:.2%}."
f"{self.name} - Filtering pairs with ask/bid diff above {self._max_spread_ratio:.2%}."
)

@staticmethod
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/rpc/api_server/ws/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# isort: off
from freqtrade.rpc.api_server.ws.types import WebSocketType # noqa: F401
from freqtrade.rpc.api_server.ws.ws_types import WebSocketType # noqa: F401
from freqtrade.rpc.api_server.ws.proxy import WebSocketProxy # noqa: F401
from freqtrade.rpc.api_server.ws.serializer import HybridJSONWebSocketSerializer # noqa: F401
from freqtrade.rpc.api_server.ws.channel import WebSocketChannel # noqa: F401
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/rpc/api_server/ws/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
HybridJSONWebSocketSerializer,
WebSocketSerializer,
)
from freqtrade.rpc.api_server.ws.types import WebSocketType
from freqtrade.rpc.api_server.ws.ws_types import WebSocketType
from freqtrade.rpc.api_server.ws_schemas import WSMessageSchemaType


Expand Down
2 changes: 1 addition & 1 deletion freqtrade/rpc/api_server/ws/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from fastapi import WebSocket as FastAPIWebSocket
from websockets.asyncio.client import ClientConnection as WebSocket

from freqtrade.rpc.api_server.ws.types import WebSocketType
from freqtrade.rpc.api_server.ws.ws_types import WebSocketType


class WebSocketProxy:
Expand Down
3 changes: 1 addition & 2 deletions freqtrade/rpc/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ def send_msg(self, msg: RPCSendMsg) -> None:
self._send_msg(payload)
except KeyError as exc:
logger.exception(
"Problem calling Webhook. Please check your webhook configuration. "
"Exception: %s",
"Problem calling Webhook. Please check your webhook configuration. Exception: %s",
exc,
)

Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
-r docs/requirements-docs.txt

coveralls==4.0.1
ruff==0.8.6
ruff==0.9.1
mypy==1.14.1
pre-commit==4.0.1
pytest==8.3.4
Expand Down
6 changes: 3 additions & 3 deletions tests/commands/test_startup_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ def test_startup_time():
start = time.time()
subprocess.run(["freqtrade", "-h"])
elapsed = time.time() - start
assert (
elapsed < MAXIMUM_STARTUP_TIME
), "The startup time is too long, try to use lazy import in the command entry function"
assert elapsed < MAXIMUM_STARTUP_TIME, (
"The startup time is too long, try to use lazy import in the command entry function"
)
3 changes: 1 addition & 2 deletions tests/data/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ def test_ohlcv_fill_up_missing_data(testdatadir, caplog):
assert (data.columns == data2.columns).all()

assert log_has_re(
f"Missing data fillup for UNITTEST/BTC, 1m: before: "
f"{len(data)} - after: {len(data2)}.*",
f"Missing data fillup for UNITTEST/BTC, 1m: before: {len(data)} - after: {len(data2)}.*",
caplog,
)

Expand Down
6 changes: 3 additions & 3 deletions tests/data/test_converter_orderflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,9 +556,9 @@ def test_analyze_with_orderflow(
assert col in df2.columns, f"Round2: Column {col} not found in df.columns"

if col not in ("stacked_imbalances_bid", "stacked_imbalances_ask"):
assert (
df2[col].count() == 5
), f"Round2: Column {col} has {df2[col].count()} non-NaN values"
assert df2[col].count() == 5, (
f"Round2: Column {col} has {df2[col].count()} non-NaN values"
)

lastval_trade2 = df2.at[len(df2) - 1, "trades"]
assert isinstance(lastval_trade2, list)
Expand Down
6 changes: 3 additions & 3 deletions tests/data/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_load_data_30min_timeframe(caplog, testdatadir) -> None:
ld = load_pair_history(pair="UNITTEST/BTC", timeframe="30m", datadir=testdatadir)
assert isinstance(ld, DataFrame)
assert not log_has(
'Download history data for pair: "UNITTEST/BTC", timeframe: 30m ' "and store in None.",
'Download history data for pair: "UNITTEST/BTC", timeframe: 30m and store in None.',
caplog,
)

Expand All @@ -86,7 +86,7 @@ def test_load_data_1min_timeframe(ohlcv_history, mocker, caplog, testdatadir) ->
load_data(datadir=testdatadir, timeframe="1m", pairs=["UNITTEST/BTC"])
assert file.is_file()
assert not log_has(
'Download history data for pair: "UNITTEST/BTC", interval: 1m ' "and store in None.", caplog
'Download history data for pair: "UNITTEST/BTC", interval: 1m and store in None.', caplog
)


Expand All @@ -96,7 +96,7 @@ def test_load_data_mark(ohlcv_history, mocker, caplog, testdatadir) -> None:
load_data(datadir=testdatadir, timeframe="1h", pairs=["UNITTEST/BTC"], candle_type="mark")
assert file.is_file()
assert not log_has(
'Download history data for pair: "UNITTEST/USDT:USDT", interval: 1m ' "and store in None.",
'Download history data for pair: "UNITTEST/USDT:USDT", interval: 1m and store in None.',
caplog,
)

Expand Down
2 changes: 1 addition & 1 deletion tests/exchange/test_binance.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test__get_params_binance(default_conf, mocker, side, order_type, time_in_for
)
def test_create_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode):
api_mock = MagicMock()
order_id = f"test_prod_buy_{randint(0, 10 ** 6)}"
order_id = f"test_prod_buy_{randint(0, 10**6)}"
order_type = "stop_loss_limit" if trademode == TradingMode.SPOT else "stop"

api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}})
Expand Down
Loading
Loading