Merge branch 'freqtrade:develop' into develop

This commit is contained in:
Simon Waiblinger
2024-05-20 11:10:58 +02:00
committed by GitHub
17 changed files with 1035 additions and 1035 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
markdown==3.6 markdown==3.6
mkdocs==1.6.0 mkdocs==1.6.0
mkdocs-material==9.5.22 mkdocs-material==9.5.23
mdx_truly_sane_lists==1.3 mdx_truly_sane_lists==1.3
pymdown-extensions==10.8.1 pymdown-extensions==10.8.1
jinja2==3.1.4 jinja2==3.1.4
+2
View File
@@ -17,4 +17,6 @@ class Bingx(Exchange):
_ft_has: Dict = { _ft_has: Dict = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"stoploss_on_exchange": False,
"stoploss_order_types": {"limit": "limit", "market": "market"},
} }
@@ -10,23 +10,26 @@ individual needs.
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import Config
from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.data.metrics import calculate_max_drawdown
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
# higher numbers penalize drawdowns more severely # smaller numbers penalize drawdowns more severely
DRAWDOWN_MULT = 0.075 DRAWDOWN_MULT = 0.075
class ProfitDrawDownHyperOptLoss(IHyperOptLoss): class ProfitDrawDownHyperOptLoss(IHyperOptLoss):
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, *args, **kwargs) -> float: def hyperopt_loss_function(results: DataFrame, config: Config, *args, **kwargs) -> float:
total_profit = results["profit_abs"].sum() total_profit = results["profit_abs"].sum()
try: try:
drawdown = calculate_max_drawdown(results, value_col="profit_abs") drawdown = calculate_max_drawdown(
results, starting_balance=config["dry_run_wallet"], value_col="profit_abs"
)
relative_account_drawdown = drawdown.relative_account_drawdown relative_account_drawdown = drawdown.relative_account_drawdown
except ValueError: except ValueError:
relative_account_drawdown = 0 relative_account_drawdown = 0
return -1 * (total_profit * (1 - relative_account_drawdown * DRAWDOWN_MULT)) return -1 * (total_profit - (relative_account_drawdown * total_profit) / DRAWDOWN_MULT)
+8 -15
View File
@@ -358,14 +358,15 @@ class HyperoptTools:
) )
@staticmethod @staticmethod
def prepare_trials_columns(trials: pd.DataFrame, has_drawdown: bool) -> pd.DataFrame: def prepare_trials_columns(trials: pd.DataFrame) -> pd.DataFrame:
trials["Best"] = "" trials["Best"] = ""
if "results_metrics.winsdrawslosses" not in trials.columns: if "results_metrics.winsdrawslosses" not in trials.columns:
# Ensure compatibility with older versions of hyperopt results # Ensure compatibility with older versions of hyperopt results
trials["results_metrics.winsdrawslosses"] = "N/A" trials["results_metrics.winsdrawslosses"] = "N/A"
if not has_drawdown: has_account_drawdown = "results_metrics.max_drawdown_account" in trials.columns
if not has_account_drawdown:
# Ensure compatibility with older versions of hyperopt results # Ensure compatibility with older versions of hyperopt results
trials["results_metrics.max_drawdown_account"] = None trials["results_metrics.max_drawdown_account"] = None
if "is_random" not in trials.columns: if "is_random" not in trials.columns:
@@ -389,7 +390,6 @@ class HyperoptTools:
"results_metrics.profit_total_abs", "results_metrics.profit_total_abs",
"results_metrics.profit_total", "results_metrics.profit_total",
"results_metrics.holding_avg", "results_metrics.holding_avg",
"results_metrics.max_drawdown",
"results_metrics.max_drawdown_account", "results_metrics.max_drawdown_account",
"results_metrics.max_drawdown_abs", "results_metrics.max_drawdown_abs",
"loss", "loss",
@@ -408,7 +408,6 @@ class HyperoptTools:
"Total profit", "Total profit",
"Profit", "Profit",
"Avg duration", "Avg duration",
"max_drawdown",
"max_drawdown_account", "max_drawdown_account",
"max_drawdown_abs", "max_drawdown_abs",
"Objective", "Objective",
@@ -437,9 +436,7 @@ class HyperoptTools:
tabulate.PRESERVE_WHITESPACE = True tabulate.PRESERVE_WHITESPACE = True
trials = json_normalize(results, max_level=1) trials = json_normalize(results, max_level=1)
has_account_drawdown = "results_metrics.max_drawdown_account" in trials.columns trials = HyperoptTools.prepare_trials_columns(trials)
trials = HyperoptTools.prepare_trials_columns(trials, has_account_drawdown)
trials["is_profit"] = False trials["is_profit"] = False
trials.loc[trials["is_initial_point"] | trials["is_random"], "Best"] = "* " trials.loc[trials["is_initial_point"] | trials["is_random"], "Best"] = "* "
@@ -471,23 +468,19 @@ class HyperoptTools:
stake_currency = config["stake_currency"] stake_currency = config["stake_currency"]
trials[f"Max Drawdown{' (Acct)' if has_account_drawdown else ''}"] = trials.apply( trials["Max Drawdown (Acct)"] = trials.apply(
lambda x: ( lambda x: (
"{} {}".format( "{} {}".format(
fmt_coin(x["max_drawdown_abs"], stake_currency, keep_trailing_zeros=True), fmt_coin(x["max_drawdown_abs"], stake_currency, keep_trailing_zeros=True),
( (f"({x['max_drawdown_account']:,.2%})").rjust(10, " "),
f"({x['max_drawdown_account']:,.2%})"
if has_account_drawdown
else f"({x['max_drawdown']:,.2%})"
).rjust(10, " "),
).rjust(25 + len(stake_currency)) ).rjust(25 + len(stake_currency))
if x["max_drawdown"] != 0.0 or x["max_drawdown_account"] != 0.0 if x["max_drawdown_account"] != 0.0
else "--".rjust(25 + len(stake_currency)) else "--".rjust(25 + len(stake_currency))
), ),
axis=1, axis=1,
) )
trials = trials.drop(columns=["max_drawdown_abs", "max_drawdown", "max_drawdown_account"]) trials = trials.drop(columns=["max_drawdown_abs", "max_drawdown_account"])
trials["Profit"] = trials.apply( trials["Profit"] = trials.apply(
lambda x: ( lambda x: (
@@ -497,7 +497,6 @@ def generate_strategy_stats(
} }
try: try:
max_drawdown_legacy = calculate_max_drawdown(results, value_col="profit_ratio")
drawdown = calculate_max_drawdown( drawdown = calculate_max_drawdown(
results, value_col="profit_abs", starting_balance=start_balance results, value_col="profit_abs", starting_balance=start_balance
) )
@@ -508,7 +507,6 @@ def generate_strategy_stats(
strat_stats.update( strat_stats.update(
{ {
"max_drawdown": max_drawdown_legacy.drawdown_abs, # Deprecated - do not use
"max_drawdown_account": drawdown.relative_account_drawdown, "max_drawdown_account": drawdown.relative_account_drawdown,
"max_relative_drawdown": underwater.relative_account_drawdown, "max_relative_drawdown": underwater.relative_account_drawdown,
"max_drawdown_abs": drawdown.drawdown_abs, "max_drawdown_abs": drawdown.drawdown_abs,
@@ -527,7 +525,6 @@ def generate_strategy_stats(
except ValueError: except ValueError:
strat_stats.update( strat_stats.update(
{ {
"max_drawdown": 0.0,
"max_drawdown_account": 0.0, "max_drawdown_account": 0.0,
"max_relative_drawdown": 0.0, "max_relative_drawdown": 0.0,
"max_drawdown_abs": 0.0, "max_drawdown_abs": 0.0,
+2 -2
View File
@@ -1,5 +1,5 @@
import logging import logging
from ipaddress import IPv4Address from ipaddress import ip_address
from typing import Any, Optional from typing import Any, Optional
import orjson import orjson
@@ -180,7 +180,7 @@ class ApiServer(RPCHandler):
rest_port = self._config["api_server"]["listen_port"] rest_port = self._config["api_server"]["listen_port"]
logger.info(f"Starting HTTP Server at {rest_ip}:{rest_port}") logger.info(f"Starting HTTP Server at {rest_ip}:{rest_port}")
if not IPv4Address(rest_ip).is_loopback and not running_in_docker(): if not ip_address(rest_ip).is_loopback and not running_in_docker():
logger.warning("SECURITY WARNING - Local Rest Server listening to external connections") logger.warning("SECURITY WARNING - Local Rest Server listening to external connections")
logger.warning( logger.warning(
"SECURITY WARNING - This is insecure please set to your loopback," "SECURITY WARNING - This is insecure please set to your loopback,"
+1 -1
View File
@@ -1,3 +1,3 @@
# Requirements for freqtrade client library # Requirements for freqtrade client library
requests==2.31.0 requests==2.31.0
python-rapidjson==1.16 python-rapidjson==1.17
+3 -3
View File
@@ -6,12 +6,12 @@
-r requirements-freqai-rl.txt -r requirements-freqai-rl.txt
-r docs/requirements-docs.txt -r docs/requirements-docs.txt
coveralls==4.0.0 coveralls==4.0.1
ruff==0.4.4 ruff==0.4.4
mypy==1.10.0 mypy==1.10.0
pre-commit==3.7.1 pre-commit==3.7.1
pytest==8.2.0 pytest==8.2.1
pytest-asyncio==0.23.6 pytest-asyncio==0.23.7
pytest-cov==5.0.0 pytest-cov==5.0.0
pytest-mock==3.14.0 pytest-mock==3.14.0
pytest-random-order==1.1.1 pytest-random-order==1.1.1
+3 -3
View File
@@ -2,7 +2,7 @@ numpy==1.26.4
pandas==2.2.2 pandas==2.2.2
pandas-ta==0.3.14b pandas-ta==0.3.14b
ccxt==4.3.24 ccxt==4.3.27
cryptography==42.0.7 cryptography==42.0.7
aiohttp==3.9.5 aiohttp==3.9.5
SQLAlchemy==2.0.30 SQLAlchemy==2.0.30
@@ -22,13 +22,13 @@ jinja2==3.1.4
tables==3.9.1 tables==3.9.1
joblib==1.4.2 joblib==1.4.2
rich==13.7.1 rich==13.7.1
pyarrow==16.0.0; platform_machine != 'armv7l' pyarrow==16.1.0; platform_machine != 'armv7l'
# find first, C search in arrays # find first, C search in arrays
py_find_1st==1.1.6 py_find_1st==1.1.6
# Load ticker files 30% faster # Load ticker files 30% faster
python-rapidjson==1.16 python-rapidjson==1.17
# Properly format api responses # Properly format api responses
orjson==3.10.3 orjson==3.10.3
+5 -2
View File
@@ -54,6 +54,7 @@ from tests.conftest import (
patch_exchange, patch_exchange,
patched_configuration_load_config_file, patched_configuration_load_config_file,
) )
from tests.conftest_hyperopt import hyperopt_test_result
from tests.conftest_trades import MOCK_TRADE_COUNT from tests.conftest_trades import MOCK_TRADE_COUNT
@@ -1137,7 +1138,8 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
pytest.fail(f"Expected well formed JSON, but failed to parse: {captured.out}") pytest.fail(f"Expected well formed JSON, but failed to parse: {captured.out}")
def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmp_path): def test_hyperopt_list(mocker, capsys, caplog, tmp_path):
saved_hyperopt_results = hyperopt_test_result()
csv_file = tmp_path / "test.csv" csv_file = tmp_path / "test.csv"
mocker.patch( mocker.patch(
"freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist", "freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist",
@@ -1507,7 +1509,8 @@ def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmp_path)
csv_file.unlink() csv_file.unlink()
def test_hyperopt_show(mocker, capsys, saved_hyperopt_results): def test_hyperopt_show(mocker, capsys):
saved_hyperopt_results = hyperopt_test_result()
mocker.patch( mocker.patch(
"freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist", "freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist",
return_value=True, return_value=True,
-998
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -47,7 +47,7 @@ def generate_result_metrics():
"profit_total_abs": 0.001, "profit_total_abs": 0.001,
"profit_total": 0.01, "profit_total": 0.01,
"holding_avg": timedelta(minutes=20), "holding_avg": timedelta(minutes=20),
"max_drawdown": 0.001, "max_drawdown_account": 0.001,
"max_drawdown_abs": 0.001, "max_drawdown_abs": 0.001,
"loss": 0.001, "loss": 0.001,
"is_initial_point": 0.001, "is_initial_point": 0.001,
@@ -52,7 +52,7 @@ class HyperoptableStrategy(StrategyTestV3):
bot_loop_started = False bot_loop_started = False
bot_started = False bot_started = False
def bot_loop_start(self): def bot_loop_start(self, **kwargs):
self.bot_loop_started = True self.bot_loop_started = True
def bot_start(self, **kwargs) -> None: def bot_start(self, **kwargs) -> None:
@@ -48,7 +48,7 @@ class HyperoptableStrategyV2(StrategyTestV2):
bot_loop_started = False bot_loop_started = False
def bot_loop_start(self): def bot_loop_start(self, **kwargs):
self.bot_loop_started = True self.bot_loop_started = True
def bot_start(self, **kwargs) -> None: def bot_start(self, **kwargs) -> None: