Skip to content
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 docs/exchanges.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ To use BNFCR futures, you will have to have the following combination of setting

The `stake_currency` setting defines the markets the bot will be operating in. This choice is really arbitrary.

On the exchange, you'll have to use "Multi-asset Mode" - and "Position Mode set to "One-way Mode".
On the exchange, you'll have to use "Multi-asset Mode" - and "Position Mode set to "One-way Mode".
Freqtrade will check these settings on startup, but won't attempt to change them.

## Bingx
Expand Down
16 changes: 11 additions & 5 deletions freqtrade/data/history/history_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,11 +679,17 @@ def download_data(
)
else:
if not exchange.get_option("ohlcv_has_history", True):
raise OperationalException(
f"Historic klines not available for {exchange.name}. "
"Please use `--dl-trades` instead for this exchange "
"(will unfortunately take a long time)."
)
if not exchange.get_option("trades_has_history", True):
raise OperationalException(
f"Historic data not available for {exchange.name}. "
f"{exchange.name} does not support downloading trades or ohlcv data."
)
else:
raise OperationalException(
f"Historic klines not available for {exchange.name}. "
"Please use `--dl-trades` instead for this exchange "
"(will unfortunately take a long time)."
)
migrate_data(config, exchange)
pairs_not_available = refresh_backtest_ohlcv_data(
exchange,
Expand Down
4 changes: 2 additions & 2 deletions freqtrade/freqai/freqai_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ def _set_train_queue(self):
"""
current_pairlist = self.config.get("exchange", {}).get("pair_whitelist")
if not self.dd.pair_dict:
logger.info("Set fresh train queue from whitelist. Queue: {current_pairlist}")
logger.info(f"Set fresh train queue from whitelist. Queue: {current_pairlist}")
return deque(current_pairlist)

best_queue = deque()
Expand All @@ -789,7 +789,7 @@ def _set_train_queue(self):
best_queue.appendleft(pair)

logger.info(
"Set existing queue from trained timestamps. Best approximation queue: {best_queue}"
f"Set existing queue from trained timestamps. Best approximation queue: {best_queue}"
)
return best_queue

Expand Down
2 changes: 1 addition & 1 deletion freqtrade/optimize/backtesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ def _exit_trade(
amount=amount,
filled=0,
remaining=amount,
cost=amount * close_rate,
cost=amount * close_rate * (1 + self.fee),
ft_order_tag=exit_reason,
)
order._trade_bt = trade
Expand Down
16 changes: 12 additions & 4 deletions freqtrade/optimize/optimize_reports/optimize_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,14 @@ def _generate_result_line(
}


def generate_pair_metrics(
def calculate_trade_volume(trades_dict: list[dict[str, Any]]) -> float:
# Aggregate the total volume traded from orders.cost.
# Orders is a nested dictionary within the trades list.

return sum(sum(order["cost"] for order in trade.get("orders", [])) for trade in trades_dict)


def generate_pair_metrics( #
pairlist: list[str],
stake_currency: str,
starting_balance: float,
Expand Down Expand Up @@ -368,7 +375,7 @@ def generate_strategy_stats(
:param market_change: float indicating the market change
:return: Dictionary containing results per strategy and a strategy summary.
"""
results: dict[str, DataFrame] = content["results"]
results: DataFrame = content["results"]
if not isinstance(results, DataFrame):
return {}
config = content["config"]
Expand Down Expand Up @@ -431,8 +438,9 @@ def generate_strategy_stats(

expectancy, expectancy_ratio = calculate_expectancy(results)
backtest_days = (max_date - min_date).days or 1
trades_dict = results.to_dict(orient="records")
strat_stats = {
"trades": results.to_dict(orient="records"),
"trades": trades_dict,
"locks": [lock.to_json() for lock in content["locks"]],
"best_pair": best_pair,
"worst_pair": worst_pair,
Expand All @@ -444,7 +452,7 @@ def generate_strategy_stats(
"total_trades": len(results),
"trade_count_long": len(results.loc[~results["is_short"]]),
"trade_count_short": len(results.loc[results["is_short"]]),
"total_volume": float(results["stake_amount"].sum()),
"total_volume": calculate_trade_volume(trades_dict),
"avg_stake_amount": results["stake_amount"].mean() if len(results) > 0 else 0,
"profit_mean": results["profit_ratio"].mean() if len(results) > 0 else 0,
"profit_median": results["profit_ratio"].median() if len(results) > 0 else 0,
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/persistence/trade_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ def to_json(self, entry_side: str, minified: bool = False) -> dict[str, Any]:
"order_filled_timestamp": dt_ts_none(self.order_filled_utc),
"ft_is_entry": self.ft_order_side == entry_side,
"ft_order_tag": self.ft_order_tag,
"cost": self.cost if self.cost else 0,
}
if not minified:
resp.update(
Expand All @@ -278,7 +279,6 @@ def to_json(self, entry_side: str, minified: bool = False) -> dict[str, Any]:
"order_id": self.order_id,
"status": self.status,
"average": round(self.average, 8) if self.average else 0,
"cost": self.cost if self.cost else 0,
"filled": self.filled,
"is_open": self.ft_is_open,
"order_date": (
Expand Down
13 changes: 13 additions & 0 deletions tests/data/test_download_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,16 @@ def test_download_data_main_data_invalid(mocker):
)
with pytest.raises(OperationalException, match=r"Historic klines not available for .*"):
download_data_main(config)

patch_exchange(mocker, exchange="hyperliquid")
mocker.patch(f"{EXMS}.get_markets", return_value={"ETH/USDC": {}})
config2 = setup_utils_configuration({"exchange": "hyperliquid"}, RunMode.UTIL_EXCHANGE)
config2.update(
{
"days": 20,
"pairs": ["ETH/USDC", "XRP/USDC"],
"timeframes": ["5m", "1h"],
}
)
with pytest.raises(OperationalException, match=r"Historic data not available for .*"):
download_data_main(config2)
6 changes: 5 additions & 1 deletion tests/optimize/test_backtesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import MagicMock, PropertyMock
from unittest.mock import ANY, MagicMock, PropertyMock

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -795,6 +795,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None:
"order_filled_timestamp": 1517251200000,
"ft_is_entry": True,
"ft_order_tag": "",
"cost": ANY,
},
{
"amount": 0.00957442,
Expand All @@ -803,6 +804,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None:
"order_filled_timestamp": 1517265300000,
"ft_is_entry": False,
"ft_order_tag": "roi",
"cost": ANY,
},
],
[
Expand All @@ -813,6 +815,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None:
"order_filled_timestamp": 1517283000000,
"ft_is_entry": True,
"ft_order_tag": "",
"cost": ANY,
},
{
"amount": 0.0097064,
Expand All @@ -821,6 +824,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None:
"order_filled_timestamp": 1517285400000,
"ft_is_entry": False,
"ft_order_tag": "roi",
"cost": ANY,
},
],
],
Expand Down
Loading